kaizheng(郑凯) 17b57b67ab fix: bug修改
2025-02-26 16:08:45 +08:00

3956 lines
120 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="app-container">
<div class="title">
<div>
<div>{{ detail.title }}</div>
<div>主播{{ detail.advisorBasic.name }}</div>
<div>
{{ detail.startTime }} -
{{ detail.endTime ? detail.endTime.slice(10) : "" }}
</div>
</div>
<!-- <div class="txonlineNum" v-show="detail.liveStatus === 1">
并发连接数:{{ txonlineNum }}
</div> -->
<div
v-if="detail.playType === 1 && [1, 4].includes(type)"
class="time-button"
>
<div
v-if="detail.liveStatus === 2 && nowDate.isBefore(detail.startTime)"
>
<CountDown :detail-data="detail.startTime" />
</div>
<el-button
v-if="detail.liveStatus === 3"
type="primary"
@click="controlLive(3)"
>开始直播</el-button
>
<el-button
v-if="detail.liveStatus === 1 || detail.liveStatus === 3"
type="danger"
@click="controlLive(2)"
>结束并生成回放</el-button
>
</div>
<overUniteLive
v-if="[5].includes(type) && detail.liveStatus === 1"
:live-status="detail.liveStatus"
:advisor-name="detail.advisorBasic.name"
/>
</div>
<div class="content">
<div class="audience">
<div class="audience-border">
<div class="box-tab">
<div class="tab-box">
<div
v-for="(item, index) in tabList"
:key="item.index"
class="tab-box_item"
:class="{ 'active-tab': curTabIndex === index }"
@click="tabChange(index, item)"
>
{{ item.name
}}{{
index === 0
? `(${messageCount ? messageCount : 0})`
: `(${onLineNum})`
}}
</div>
</div>
</div>
<div v-if="curTabIndex === 0" class="audience-interaction">
<div class="audience-button">
<el-button
v-for="(item, index) in buttonText"
:class="{ selected: selectedButton === index }"
size="small"
plain
@click="selected(index)"
>{{ item }}</el-button
>
</div>
<div
v-if="
detail.playType === 1 &&
detail.liveStatus !== 4 &&
[1, 2, 4].includes(type)
"
class="audience-box"
>
<div class="interaction-box">
<div class="interaction-box_switch">
开放观众发言
<el-switch
v-model="isSpeak"
:inactive-value="2"
:active-value="1"
@change="setUp()"
/>
</div>
<div class="interaction-box_switch">
开启审核
<el-switch
v-model="messageAudit"
:inactive-value="2"
:active-value="1"
@change="setUpTwo()"
/>
</div>
<div class="interaction-box_switch">
隐藏用户昵称
<el-switch
v-model="detail.showNickname"
:inactive-value="1"
:active-value="2"
@change="changeShowNickname"
/>
</div>
<div class="interaction-box_switch">
互动区仅展示投顾消息
<el-switch
v-model="interactType"
:inactive-value="1"
:active-value="2"
@change="setUpThree()"
/>
</div>
</div>
<el-checkbox
v-model="isSendMessage"
:true-label="1"
:false-label="2"
style="padding-bottom: 20px;"
>在聊天框中发送禁言/开启发言通知</el-checkbox
>
</div>
<div class="audience-chat-box">
<!-- 聊天 -->
<div class="chat-box">
<!-- <div class="button-box" v-if="false" >
<el-button :class="{ 'selected': isAutofocus }" plain @click="changeFocus(1)">聊天室 </el-button>
<el-button :class="{ 'selected': !isAutofocus }" plain @click="changeFocus(2)">已删除列表</el-button>
</div> -->
<div
ref="discussArea"
class="content-box"
@scroll="handleScroll"
>
<div
v-for="(item, index) in msgList"
v-if="item.type !== 3 && item.type !== 5 && item.type !== 4"
:key="item.id"
:class="[
'content-box_item',
item.type === 11
? 'question-item'
: item.isOpen === 2
? 'no-open-item'
: ''
]"
>
<div class="content-box_item--title">
<img
:class="[item.userId ? 'pointer' : '']"
:src="
item.advisorId && item.imgUrl
? item.imgUrl
: !item.advisorId && !item.userId
? defaultImg
: item.imgUrl
"
alt=""
/>
<span
style="margin-right: 5px;"
:class="!item.userId ? 'anchor' : ''"
>{{
!item.userId
? !item.advisorId
? "助教"
: "主播"
: ""
}}</span
>
<span class="name-time">
<span>
{{
!item.userId
? item.advisorBasic
? item.advisorBasic.name
: item.createUserVO
? item.createUserVO.name
: ""
: item.userName
}}</span
>
<span class="time"
>{{
dayjs(item.createTime).format("YYYY-MM-DD HH:mm:ss")
}}
</span></span
>
<i
v-if="
item.status === 1 &&
item.type === 1 &&
item.isForbid === 2 &&
[1, 2, 3].includes(detail.liveStatus) &&
[1, 4].includes(type) &&
!item.replyId &&
item.userId
"
class="el-icon-chat-dot-round"
@click="toReply(item)"
/>
<i
v-if="
item.status === 1 &&
(item.type === 1 || item.type === 2) &&
[1, 2, 4].includes(type)
"
class="el-icon-delete"
@click="toDeleted(item)"
/>
<el-button
v-if="item.status !== 1 && item.type === 1"
type="text"
style="color:#F56C6C ;"
>已删除</el-button
>
<el-button
v-if="
item.isForbid === 2 &&
item.type === 1 &&
item.userId &&
[1, 2, 4].includes(type) &&
item.status === 1
"
type="text"
style="color:#F56C6C ;"
@click="prohibition(item)"
>禁言</el-button
>
<el-button
v-if="
item.isOpen === 2 &&
item.type === 1 &&
item.userId &&
[1, 2, 4].includes(type) &&
item.status === 1
"
type="text"
@click="open(item)"
>公开</el-button
>
<el-button
v-if="
item.isOpen === 1 &&
item.type === 1 &&
item.userId &&
[1, 2, 4].includes(type) &&
item.status === 1
"
type="text"
@click="open(item)"
>取消公开</el-button
>
<el-button
v-if="
item.isForbid === 1 &&
item.type === 1 &&
item.userId &&
[1, 2, 4].includes(type) &&
item.status === 1
"
type="text"
@click="prohibition(item)"
>解除禁言</el-button
>
</div>
<div
v-if="
!item.productBasic && !item.content.startsWith('upImg-')
"
class="content-box_item--reply"
:class="{
'fw-600': !item.userId,
'questionnaire-color':
item.questionId && item.type === 11
}"
@click="
toQuestionnaire(
item.questionId && item.type === 11,
item.questionId
)
"
>
{{ item.content }}
</div>
<img
v-if="
item.type === 1 && item.content.startsWith('upImg-')
"
:src="item.content.replace('upImg-', '')"
class="content-img"
/>
<div
v-if="item.productBasic"
class="content-box_item--product"
:class="{ 'fw-600': !item.userId }"
>
<div class="product-title">
<span>{{
settingToC ? "自定义" : item.productBasic.typeLabel
}}</span>
{{ item.productBasic.name }}
</div>
<div class="product-content">
{{ item.productBasic.content }}
</div>
</div>
<div v-if="item.replyId" class="reply-box">
<div class="userName">
{{ item.replyBasic.userName }}
{{
dayjs(item.replyBasic.replyTime).format(
"YYYY-MM-DD HH:mm:ss"
)
}}
</div>
<div>{{ item.replyBasic.content }}</div>
</div>
<div
v-if="item.status === -1"
class="reply-delete"
style="margin-top: 10px"
>
<span style="margin-right: 10px"
>操作人:{{ item.userAdminVO.name }}</span
>时间:{{ item.deleteTime }}
</div>
</div>
</div>
<div
v-if="selectedButton === 0 && [1, 4].includes(type)"
class="send-box"
>
<el-select
v-model="sendForm.productType"
style="width: 100%;margin-bottom: 20px;"
:disabled="
[4].includes(detail.liveStatus) || detail.playType === 2
"
clearable
placeholder="请选择产品类型"
>
<el-option
v-for="(item, index) in productListType"
:key="index"
:label="item.name"
:value="item.id"
/>
</el-select>
<el-select
v-if="sendForm.productType && !settingToC"
v-model="sendForm.productListId"
style="width: 100%;margin-bottom: 20px;"
:disabled="
[4].includes(detail.liveStatus) || detail.playType === 2
"
filterable
remote
reserve-keyword
placeholder="请选择产品"
:remote-method="remoteMethodProduct"
:loading="loadingProduct"
@change="changeItem"
>
<el-option
v-for="item in productList"
:key="item.id"
:label="item.name"
:value="item.id"
/>
</el-select>
<el-select
v-if="sendForm.productType && settingToC"
v-model="sendForm.productListId"
style="width: 100%;margin-bottom: 20px;"
:disabled="
[4].includes(detail.liveStatus) || detail.playType === 2
"
filterable
remote
reserve-keyword
placeholder="请选择产品"
@change="changeItemToc"
>
<el-option
v-for="item in productList"
:key="item.productId"
:label="item.productName"
:value="item.productId"
/>
</el-select>
<el-input
v-if="!sendForm.productType"
v-model="input"
type="textarea"
maxlength="500"
show-word-limit
style="margin-bottom: 20px;"
:autosize="{ minRows: 6, maxRows: 10 }"
:disabled="
[4].includes(detail.liveStatus) || detail.playType === 2
"
:placeholder="placeholderList[detail.liveStatus]"
/>
<div class="send-box_btn">
<img v-if="imageUrl" :src="imageUrl" class="avatar" />
<el-upload
class="avatar-uploader"
:show-file-list="false"
:on-success="handleSuccessSend"
:on-error="handleError"
:headers="headers"
list-type="picture"
action="/admin/common/file/upload"
:before-upload="beforeUploadSend"
>
<el-button type="primary" icon="el-icon-picture-outline"
>上传图片</el-button
>
<el-button
v-if="imageUrl"
type="text"
@click.stop="deleteImage"
>删除图片</el-button
>
</el-upload>
<el-button
v-if="!imageUrl"
type="success"
style="width: 80px;height: 38px;"
@click="send()"
>发 送</el-button
>
<el-button
v-if="imageUrl"
type="success"
style="width: 120px;height: 38px;"
@click="sendPhono()"
>发 送 图 片</el-button
>
</div>
</div>
<!-- 已删除列表 -->
<!-- <div v-if="!isAutofocus" class="table-box">
<el-table :data="deletedData" border style="width: 100%;margin-bottom:20px">
<el-table-column type="index" label="序号" width="180">
</el-table-column>
<el-table-column prop="userName" label="用户名称" width="180">
<template slot-scope="scope">
<span v-if="scope.row.productBasic"> {{ scope.row.userAdminVO.name }}</span>
<span v-else>{{ scope.row.userName ? scope.row.userName : scope.row.createUserVO ? scope.row.createUserVO.name :'' }} </span>
</template>
</el-table-column>
<el-table-column prop="content" label="内容">
</el-table-column>
<el-table-column prop="createTime" label="发言时间">
</el-table-column>
<el-table-column prop="address" label="操作人">
<template slot-scope="scope">
{{ scope.row.userAdminVO.name }}
</template>
</el-table-column>
<el-table-column prop="deleteTime" label="操作时间">
</el-table-column>
</el-table>
<el-pagination
:total="queryParams.total"
:current-page="queryParams.current"
:page-size="queryParams.size"
layout="total, sizes, prev, pager, next, jumper"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
/>
</div> -->
</div>
</div>
</div>
<div v-if="curTabIndex === 1" class="audience-title">
<div
v-for="(item, index) in audienceList"
:key="index"
class="audience-title_user"
:class="{
'audience-title_user_active-text': item.isOnline === 1
}"
>
<img
:src="item.img ? item.img : defaultImg"
alt=""
:class="{ 'audience-title_user_active': item.isOnline !== 1 }"
/>
{{ item.userName }}
</div>
</div>
</div>
</div>
<div ref="videoBox" class="video-box">
<div class="video-box-top">
<!-- :class="{ 'sticky': isSticky }" :style="{'width': isSticky ? `${divWidth}px`:'100%'}" -->
<div
v-if="
(renderPlayer && renderStatus == 'living-player') ||
renderStatus == 'back-player'
"
ref="videoPlayer"
class="w-full h-full"
/>
<div v-else-if="renderStatus == 'before-img' || pullBuffer">
<img style="height:600px;width:100%;" :src="beforeImgUrl" />
</div>
<div
v-else-if="renderStatus == 'suspend-player' || !renderStatus"
class="suspend"
>
<img src="@/assets/logo/wait.png" class="user-avatar" />
<p>主播离开一会儿,请耐心等待~</p>
</div>
<div v-if="![5].includes(type)" class="video-box-bottom">
<el-tabs v-model="activeName" type="card" @tab-click="handleClick">
<!-- <el-tab-pane label="聊天" name="1"></el-tab-pane>
<el-tab-pane v-if="detail.liveStatus !== 4 " label="互动设置" name="2"></el-tab-pane> -->
<el-tab-pane
v-if="[1, 2, 4].includes(type)"
label="购物车"
name="3"
/>
<!-- <el-tab-pane
v-if="[1, 2, 4].includes(type)"
label="发优惠券"
name="4"
/> -->
<el-tab-pane
v-if="[1, 2, 4].includes(type)"
label="问卷"
name="5"
/>
<!-- <el-tab-pane
v-if="[1, 2, 4].includes(type)"
label="营销二维码"
name="6"
/> -->
<!-- <el-tab-pane
v-if="[1, 2, 4].includes(type) && detail.playType === 1"
label="转播设置"
name="7"
/> -->
<el-tab-pane
v-if="[1, 2, 4].includes(type)"
label="嘉宾设置"
name="8"
/>
</el-tabs>
<!-- 互动设置 -->
<div v-if="activeName === '2'" class="interaction-box">
<div class="interaction-box_switch">
开放观众发言
<el-switch
v-model="isSpeak"
:inactive-value="2"
:active-value="1"
@change="setUp()"
/>
<el-checkbox
v-model="isSendMessage"
:true-label="1"
:false-label="2"
>在聊天框中发送禁言/开启发言通知</el-checkbox
>
</div>
<div class="interaction-box_switch">
开启审核
<el-switch
v-model="messageAudit"
:inactive-value="2"
:active-value="1"
@change="setUpTwo()"
/>
</div>
<!-- <el-button type="success" @click="" :disabled="isSpeak === detail.isSpeak && messageAudit === detail.messageAudit">保存设置</el-button> -->
</div>
<!-- 购物车 -->
<div v-if="activeName === '3'" class="shopping-box">
<div class="shop-header">
<p>
产品列表 (说明:产品列表中仅展示申请上架直播时{{
settingToC ? "配置" : "选择"
}}的产品)
</p>
<div v-if="[1, 2, 4].includes(type)">
<el-button
type="success"
size="mini"
@click="cancelPushProduct"
>取消推送产品</el-button
>
</div>
</div>
<el-table
key="cartVOList"
:data="detail.cartVOList"
border
style="width: 100%"
>
<el-table-column
prop="productType"
label="产品类型"
width="180"
>
<template slot-scope="scope">
{{ productTypeList[scope.row.productType] }}
</template>
</el-table-column>
<el-table-column
prop="productName"
label="产品名称"
width="180"
/>
<el-table-column prop="address" label="投顾名称">
<template slot-scope="scope">
{{
scope.row.advisorBasic
? scope.row.advisorBasic.showName
: ""
}}
</template>
</el-table-column>
<!-- <el-table-column prop="subCount" label="产品订阅数">
</el-table-column> -->
<el-table-column prop="saleLimit" label="可销售数量">
<template slot-scope="scope">
{{
scope.row.saleLimit === null ? "" : scope.row.saleLimit
}}
</template>
</el-table-column>
<el-table-column
v-if="[1, 2, 4].includes(type)"
label="操作"
fixed="right"
width="160px"
>
<template slot-scope="scope">
<el-button
v-if="scope.row.status === 1"
type="text"
@click="updateStatus(scope.row, 4)"
>下架产品</el-button
>
<el-button
v-if="scope.row.status === 1 && !settingToC"
type="text"
@click="updateStatus(scope.row, 9)"
>修改可销售数量</el-button
>
<el-button
v-if="scope.row.status === 1 && !scope.row.weight"
type="text"
@click="updateStatus(scope.row, 10)"
>推荐</el-button
>
<el-button
v-if="scope.row.status === 1 && scope.row.weight"
type="text"
@click="updateStatus(scope.row, 11)"
>取消推荐</el-button
>
<el-button
v-if="scope.row.status === 2"
type="text"
@click="updateStatus(scope.row, 3)"
>上架产品</el-button
>
<el-button
v-if="scope.row.coverImgUrl"
type="text"
@click="setCartPush(scope.row)"
>推送产品</el-button
>
</template>
</el-table-column>
</el-table>
<p v-if="[1, 2, 4].includes(type)" style="text-align: right;">
(此处的上下架产品仅控制是否在购物车列表展示)
</p>
</div>
<!-- 发优惠券 && detail.liveStatus !== 4 -->
<div v-if="activeName === '4'" class="preferential-box">
<el-button
v-if="
[1, 2, 4].includes(type) &&
detail.liveStatus !== 4 &&
detail.playType === 1
"
type="primary"
@click="toOpenCoupon()"
>添加优惠券</el-button
>
<createCoupon ref="coupon" @child-event="queryListPreferential" />
<!-- <el-form v-if="detail.liveStatus !== 4" ref="form" :model="formPreferential">
<el-form-item label="选择优惠券">
<el-select v-model="formPreferential.couponId" filterable placeholder="请选择优惠券">
<el-option
v-for="(item,index) in couponList"
:key="index"
:label="item.name"
:value="item.couponId"
>
</el-option>
</el-select>
</el-form-item>
<el-form-item label="发放数量">
<el-input-number v-model="formPreferential.sendTotalNumber" :step="1" :precision="0" :min="0" :max="100" label="请输入发放数量"></el-input-number>
</el-form-item>
<el-form-item label="领取要求">
<el-radio-group v-model="formPreferential.receiveRequirement">
<el-radio :label="0">无要求</el-radio>
<el-radio :label="1">关注主播</el-radio>
<el-radio :label="2">评论互动</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="onSubmit()">发 放</el-button>
</el-form-item>
</el-form> -->
<div class="table-box">
<p>发放记录</p>
<el-table
key="preferentialData"
:data="preferentialData"
border
style="width: 100%"
>
<el-table-column type="index" label="序号" width="100" />
<el-table-column
prop="couponId"
label="优惠券ID"
width="180"
/>
<el-table-column prop="name" label="优惠券名称" />
<el-table-column prop="couponType" label="优惠券类型">
<template slot-scope="scope">
{{ couponTypeList[scope.row.couponType] }}
</template>
</el-table-column>
<!-- <el-table-column prop="uTime" label="用券时间">
</el-table-column> -->
<el-table-column label="领取要求">
<template slot-scope="scope">
{{ receiveRequirementList[scope.row.receiveRequirement] }}
</template>
</el-table-column>
<el-table-column prop="sendTotalNumber" label="发放数量" />
<el-table-column prop="sendGottenNumber" label="已领取数量" />
<el-table-column prop="sendUsedNumber" label="已用数量" />
<el-table-column prop="cTime" label="发放时间" />
<el-table-column prop="state" label="状态">
<template slot-scope="scope">
{{ stateList[scope.row.state] }}
</template>
</el-table-column>
<el-table-column
v-if="detail.liveStatus !== 4"
label="操作"
fixed="right"
>
<template slot-scope="scope">
<el-button
v-if="[1].includes(scope.row.state)"
type="text"
@click="sendcoupon(scope.row, 1)"
>发放</el-button
>
<el-button
v-if="[1].includes(scope.row.state)"
type="text"
@click="sendcoupon(scope.row, 2)"
>删除</el-button
>
</template>
</el-table-column>
</el-table>
<!--分页组件-->
<el-pagination
:total="queryParamsPreferential.total"
:current-page="queryParamsPreferential.current"
:page-size="queryParamsPreferential.size"
layout="total, sizes, prev, pager, next, jumper"
@size-change="handleSizeChangePreferential"
@current-change="handleCurrentChangePreferential"
/>
</div>
</div>
<!-- 问卷 -->
<div v-if="activeName === '5'" class="questionnaire-box">
<el-button
v-if="detail.liveStatus !== 4 && detail.playType === 1"
type="primary"
style="margin-bottom:50px;"
@click="createQuestionnaire"
>创建问卷</el-button
>
<el-table
key="questionnaireList"
:data="questionnaireList"
fit
highlight-current-row
border
style="width: 100%;"
:header-cell-style="{ 'text-align': 'center' }"
:cell-style="{ 'text-align': 'center' }"
>
<el-table-column type="index" label="序号" width="100" />
<el-table-column prop="title" label="问卷名称" width="200">
<template slot-scope="scope">
<span
class="button-text"
type="text"
@click="openView(scope.row)"
>{{ scope.row.title }}</span
>
</template>
</el-table-column>
<el-table-column prop="createTime" label="创建时间" width="200">
<template slot-scope="scope">
{{ scope.row.createTime }}
</template>
</el-table-column>
<el-table-column
prop="startTime"
label="发起时间"
width="200"
/>
<el-table-column prop="writeNum" label="填写人数" width="100" />
<el-table-column prop="status" label="状态" width="100">
<template slot-scope="scope">
{{ questionnaireStatusList[scope.row.status] }}
</template>
</el-table-column>
<el-table-column label="操作" fixed="right">
<template slot-scope="scope">
<el-button
v-if="[1].includes(scope.row.status) && operate !== 1"
type="text"
@click="question(scope.row, -1)"
>发起问卷</el-button
>
<el-button
v-if="[1].includes(scope.row.status)"
type="text"
@click="updateQuestion(scope.row, 1)"
>编辑</el-button
>
<el-button
v-if="[1, 2].includes(scope.row.status)"
type="text"
@click="updateQuestion(scope.row, 2)"
>复制</el-button
>
<el-button
v-if="[1, 2, 3].includes(scope.row.status)"
type="text"
@click="question(scope.row, -2)"
>删除</el-button
>
<el-button
v-if="[2].includes(scope.row.status) && operate !== 1"
type="text"
@click="question(scope.row, -3)"
>重发至互动区</el-button
>
<el-button
v-if="
[2, 3].includes(scope.row.status) && scope.row.writeNum
"
type="text"
@click="toExport(scope.row)"
>导出数据</el-button
>
</template>
</el-table-column>
</el-table>
<!--分页组件-->
<el-pagination
style="margin-top:10px;"
:total="queryParamsQuestionnaire.total"
:current-page="queryParamsQuestionnaire.current"
:page-size="queryParamsQuestionnaire.size"
layout="total, sizes, prev, pager, next, jumper"
@size-change="handleSizeChangeQuestionnaire"
@current-change="handleCurrentChangeQuestionnaire"
/>
</div>
<div v-if="activeName === '6'">
<div class="marketing-code">
<label>展示营销二维码</label>
<el-switch
v-model="detail.openQw"
:active-value="1"
:inactive-value="2"
@change="changeOpenQw"
/>
</div>
</div>
<div v-if="activeName === '7'">
<rebroadcast
:push-list="detail.pushList"
:live-status="detail.liveStatus"
:type="type"
@addPush="addPush"
/>
</div>
<div v-if="activeName === '8'">
<guestList :live-status="detail.liveStatus" />
</div>
</div>
</div>
</div>
</div>
<!-- 禁言弹窗 -->
<el-dialog
title="禁言"
:visible.sync="dialogFormVisible"
:modal-append-to-body="false"
>
<el-form :model="formProhibition">
<el-form-item label="请选择禁言时效">
<el-radio-group v-model="formProhibition.type">
<el-radio :label="0">次日解禁</el-radio>
<el-radio :label="1">一个月后解禁</el-radio>
<el-radio :label="2">永久禁言</el-radio>
</el-radio-group>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="cancelProhibition()">取 消</el-button>
<el-button type="primary" @click="submitProhibition()">确 定</el-button>
</div>
</el-dialog>
<!-- 回复弹窗 -->
<el-dialog
title="回复用户"
:visible.sync="dialogFormVisibleReply"
:modal-append-to-body="false"
center
>
<el-form :model="formReply">
<p>
to {{ clickItem.userName }}
{{ dayjs(clickItem.createTime).format("YYYY-MM-DD HH:mm:ss") }}
</p>
<p>{{ clickItem.content }}</p>
<el-form-item>
<el-input
v-model="formReply.context"
type="textarea"
placeholder="请输入回复内容"
maxlength="200"
show-word-limit
:autosize="{ minRows: 2, maxRows: 6 }"
/>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="cancelReply()">取 消</el-button>
<el-button type="primary" @click="submitReply()">确 定</el-button>
</div>
</el-dialog>
<!-- 上架产品 -->
<el-dialog
:title="updateStatustitle[updateStatusType]"
:visible.sync="dialogFormVisibleProduct"
:modal-append-to-body="false"
>
<el-form v-loading="loading" :model="formProduct">
<el-form-item
v-if="updateStatusType === 3 && !settingToC"
label="设置销售数量上限"
>
<el-switch v-model="formProduct.switch" @change="changeSwitch()" />
</el-form-item>
<el-form-item
v-if="updateStatusType === 3 && !settingToC"
label="输入可销售数量"
>
<el-input-number
v-model="formProduct.num"
:step="1"
:precision="0"
:min="0"
:max="999999"
:disabled="!formProduct.switch"
/>
</el-form-item>
<el-form-item v-if="updateStatusType === 9" label="输入可销售数量">
<el-input-number
v-model="formProduct.num"
:step="1"
:precision="0"
:min="0"
:max="999999"
/>
</el-form-item>
<el-form-item
v-if="settingToC && [3, 4].includes(updateStatusType)"
:label="
updateStatusType === 3 ? '是否确定上架产品' : '是否确定下架产品'
"
/>
<el-form-item v-if="[3, 4].includes(updateStatusType)">
<el-checkbox v-model="formProduct.isSendMessage"
>在聊天框中发送{{
updateStatusType === 4 ? "下架" : "上架"
}}产品通知</el-checkbox
>
</el-form-item>
<el-form-item v-if="updateStatusType === 10" label="推荐权重">
<el-input-number
v-model="formProduct.weight"
:min="1"
:max="10"
:step="1"
:precision="0"
/>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button
type="primary"
:disabled="loading"
@click="submitProduct()"
>{{ submitProductText[updateStatusType] }}</el-button
>
<el-button @click="cancelProduct()"> </el-button>
</div>
</el-dialog>
<el-dialog
class="questionnaire-dialog"
title="创建问卷"
:visible.sync="dialogFormVisibleQuestionnaire"
:close-on-click-modal="false"
:modal-append-to-body="false"
@close="cancel()"
>
<div class="questionnaire-Title">
<span>问卷标题</span>
<el-input
v-model="questionnaireTitle"
size="small"
placeholder="请输入问卷标题"
maxlength="50"
show-word-limit
/>
</div>
<div class="questionnaire-explain">
<span
><el-button type="text" @click="isTip = !isTip">{{
!isTip ? "添加问卷说明" : "取消问卷说明"
}}</el-button></span
>
<el-input
v-if="isTip"
v-model="explain"
size="small"
placeholder="问卷说明"
maxlength="100"
show-word-limit
/>
</div>
<div class="questionnaire-container">
<div class="questionnaire-preview">
<div v-if="!titleQueryList.length" class="tips">
请点击右侧题型进行添加
</div>
<draggable v-else v-model="titleQueryList">
<transition-group>
<!-- <div class="question-list"> -->
<div
v-for="(item, index) in titleQueryList"
:key="index"
class="quests"
>
<div class="quests-title">
<span>{{ index + 1 }} .</span>
<el-input
v-model="item.title"
size="small"
placeholder="请输入题目标题"
maxlength="200"
show-word-limit
clearable
/>
</div>
<div class="quests-answer">
<!-- 单选题 or 多选题 -->
<div
v-if="item.type === 1 || item.type === 2"
class="answer-container-block"
>
<div
v-for="(v, k) in item.optionQueryList"
:key="k"
class="answer-container"
>
<input type="radio" disabled />
<el-input
v-model="item.optionQueryList[k].optionDesc"
class="optionQueryList"
size="small"
maxlength="200"
clearable
/>
<i
class="el-icon-delete delete-item"
@click="deleteOptions(index, k)"
/>
</div>
<div class="button-box">
<el-button
v-if="titleQueryList[index].type !== 5"
size="mini"
class="delBtn button-icon"
icon="el-icon-plus"
circle
@click="addOptions(index)"
/>选项最多支持10个
</div>
</div>
<!-- 填空题 -->
<div v-if="item.type === 5" class="answer-container-block">
<div class="answer-container">
<el-input
disabled
type="textarea"
:rows="2"
resize="none"
maxlength="200"
clearable
/>
</div>
</div>
<!-- 图片单选题 or 图片多选题 -->
<div
v-if="item.type === 3 || item.type === 4"
class="answer-container-block"
>
<div
v-for="(v, k) in item.optionQueryList"
:key="k"
class="answer-container"
>
<el-upload
:ref="`picUpload${index}${k}`"
action="/admin/common/file/upload"
list-type="picture-card"
:multiple="false"
:show-file-list="true"
:limit="1"
:file-list="v.fileList"
accept="image/png,image/jpg,image/jpeg"
:on-remove="
file => {
return handleRemove(file, index, k);
}
"
:on-success="
(response, file) => {
return handleSuccess(response, file, index, k);
}
"
:on-error="handleError"
:before-upload="beforeUpload"
:headers="headers"
>
<i class="el-icon-upload" />
<div slot="tip" class="el-upload__tip">
建议尺寸:800*450px,小于4M,支持jpg,gif,png,bmp
</div>
</el-upload>
<div class="picture-opts">
<input type="radio" disabled />
<el-input
v-model="
titleQueryList[index].optionQueryList[k].optionDesc
"
class="optionQueryList"
size="small"
maxlength="200"
clearable
/>
<i
class="el-icon-delete delete-item"
@click="deleteOptions(index, k)"
/>
</div>
<div
v-show="
titleQueryList[index].type !== 5 &&
k ===
titleQueryList[index].optionQueryList.length - 1
"
class="button-box"
>
<el-button
size="mini"
class="delBtn button-icon"
icon="el-icon-plus"
circle
@click="addOptions(index)"
/>选项最多支持10个
</div>
</div>
</div>
<el-row class="questBtns">
<!-- <el-button v-if="titleQueryList[index].type !== 5 " size="mini" class="delBtn" icon="el-icon-plus" circle @click="addOptions(index)"></el-button> -->
<el-button
size="mini"
class="delBtn"
icon="el-icon-document-copy"
circle
@click="copyQuest(item, index)"
/>
<el-button
size="mini"
class="delBtn"
icon="el-icon-delete"
circle
@click="removeQuest(index)"
/>
</el-row>
</div>
</div>
<!-- </div> -->
</transition-group>
</draggable>
</div>
<div class="question-types-list">
<div
v-for="(item, index) in questionTypesList"
:key="index"
@click="addOpts(item)"
>
{{ item.name }}
</div>
</div>
</div>
<span slot="footer" class="dialog-footer">
<el-popover
style="margin-right:10px"
placement="bottom"
width="200"
title="微信扫一扫预览问卷"
:disabled="!showQrCode"
trigger="click"
>
<QrCode ref="QrCode" :is-show="0" @closePopover="closePopover" />
<el-button slot="reference" type="text" @click="confirm(1)"
>预览</el-button
>
</el-popover>
<el-button type="primary" :loading="loadingButton" @click="confirm(2)"
>确 定</el-button
>
<el-button @click="cancel()">取 消</el-button>
</span>
</el-dialog>
<el-dialog
:visible.sync="dialogVisible"
:modal-append-to-body="false"
width="450px"
center
:title="dialogVisibleTitle"
>
<iframe
v-if="dialogVisible"
:src="iframeUrl"
width="100%"
height="600px"
frameborder="0"
/>
</el-dialog>
</div>
</template>
<script>
import {
infoGet,
uVideoControl,
livePlayInfo,
getPlayerSign,
videoMessage,
messageDelete,
messageSend,
addCommentBlack,
cartUpdateStatus,
updateSaleLimit,
messageForbidden,
cartRecommend,
packageList,
productMessage,
removeCommentBlack,
messageOpen,
questionSave,
questionList,
questionUpdateStatus,
questionDetails,
questionExport,
openMessAudit,
messageCountApi,
txonline,
advisorMessageList,
updateInteractType,
updateQWParam,
cartPush,
cancelPush,
showNickname
} from "@/api/videoLive";
import {
getCouponList,
couponStatusCheck,
sendcoupon,
sendcouponSend,
sendcouponList,
deleteCouponAndSend,
liveSend
} from "@/api/coupon";
import { Client } from "@stomp/stompjs";
import dayjs from "dayjs";
import CountDown from "@/components/CountDown";
import createCoupon from "./components/createCoupon";
import { mapGetters, mapState } from "vuex";
import { getToken } from "@/utils/auth";
import draggable from "vuedraggable";
import QrCode from "@/components/QrCode";
import { timeFormat } from "@/utils/index";
import guestList from "./components/guestList.vue";
import rebroadcast from "./components/rebroadcast.vue";
import overUniteLive from "./components/overUniteLive.vue";
export default {
components: {
CountDown,
draggable,
QrCode,
createCoupon,
guestList,
rebroadcast,
overUniteLive
},
data() {
return {
placeholderList: {
1: "请输入内容", // 直播中
2: "请输入内容", // 未开始
3: "请输入内容", // 已暂停
4: "直播已结束不支持发送消息" // 已结束
},
stateList: {
0: "已失效", // 已失效'
1: "未发放", // 直播中
2: "待审核", // 未开始
3: "已驳回", // 已暂停
4: "已中止", // 已结束
5: "已发放" // 发放中
},
loading: false,
queryParams: {
current: 1,
size: 10,
total: 1
},
queryParamsPreferential: {
current: 1,
size: 10,
total: 1
},
queryParamsQuestionnaire: {
current: 1,
size: 10,
total: 1
},
videoId: "live-video-player",
value: "",
questionnaireStatusList: {
1: "未开始",
2: "进行中",
3: "已结束"
},
selectedButton: 0,
detail: {
advisorBasic: {
name: ""
},
cartVOList: [{ id: 1 }]
},
id: null, // 视频id
type: 1, // 1 投顾 2管理 3营销 4助教 5嘉宾
stompClient: null,
userId: null,
editableTabs: {},
audienceList: [],
activeName: "3",
curTabIndex: 0,
isAutofocus: true,
input: "",
isSpeak: 1,
messageAudit: 1,
isSendMessage: 2,
form: {},
formProhibition: {
type: 0
},
dialogFormVisible: false,
clickItem: {},
dialogFormVisibleReply: false,
formReply: {
context: ""
},
deletedData: [],
productTypeList: {
1: "观点包",
2: "单篇观点",
3: "单个视频",
4: "视频课程",
5: "图文直播间",
6: "分期直播",
7: "组合",
8: "股票池",
9: "交易圈",
21: "自定义"
},
productListType: [],
formProduct: {
switch: false,
num: 0,
isSendMessage: false,
weight: 0
},
dialogFormVisibleProduct: false,
updateStatusType: null, // 3 购物车上架 5购物车下架 9 修改可销售数量
updateStatustitle: {
3: "上架产品",
9: "修改可销售数量",
4: "确定下架产品",
10: "推荐"
},
submitProductText: {
3: "确定上架",
9: "确定",
4: "确定下架",
10: "确定推荐"
},
nowDate: dayjs(),
formPreferential: {},
couponList: [],
preferentialData: [],
couponTypeList: {
1: "体验券",
2: "折扣券",
3: "满减券"
},
receiveRequirementList: {
0: "无要求",
1: "关注主播",
2: "评论互动"
},
sendForm: {
productType: "",
productListId: null
},
loadingProduct: false,
renderPlayer: false,
pullBuffer: false,
playCtx: null,
stompClient: null,
hasBack: false,
backIndex: 0,
libraryList: [],
backArr: [],
backPlayer: false,
wsMessages: [],
cacheTop: 0,
current: 1,
total: 9999,
wsLoading: false,
replyID: null,
replyUserName: "",
libraryList: [],
backIndex: 0,
questionnaireList: [
{
name: "直播问卷任务A",
time: "2024-05-24 16:25:10",
num: "280",
status: "未开始"
}
],
dialogFormVisibleQuestionnaire: false,
questionnaireTitle: "",
explain: "",
titleQueryList: [],
questionTypesList: [
{ name: "单选题", type: 1 },
{ name: "多选题", type: 2 },
{ name: "图片单选题", type: 3 },
{ name: "图片多选题", type: 4 },
{ name: "填空题", type: 5 }
],
headers: {
Authorization: "Bearer " + getToken(),
"X-File-Size": 0
},
questionId: null,
showQrCode: false,
loadingButton: false,
isTip: false,
operate: 0,
tabList: [{ name: "互动区", id: 0 }, { name: "观众", id: 1 }],
buttonText: [
"全部消息",
"投顾消息",
"已删除列表"
// "用户消息",
// "直播消息",
],
isShow: false,
defaultImg: require("@/assets/images/defaultAvatar/assistant.png"),
userDefault: require("@/assets/images/defaultAvatar/student.png"),
messageCount: 0,
dialogVisible: false,
iframeUrl: "", // 你想在iframe中打开的网址
dialogVisibleTitle: "",
debounceTimer: false, // 用于存储防抖的定时器
onLineNum: 0,
intervalId: null, // 用来存储定时器的变量,以便稍后清除
txonlineNum: 0, // 腾讯云最新在线人数
imageUrl: "",
showDeleteIcon: false,
isGetNum: true,
loadingList: false,
interactType: "", // 互动类型 1 全部互动 2 主播内容
currentIndex: 1, // 分页
hisLastMessageId: "", // 通过历史接口获取的的数据条数
hisHasNextPage: true
};
},
created() {},
async mounted() {
// this.playLiveStream();
// try {
if (this.$route.query.id) {
this.id = Number(this.$route.query.id);
this.type = Number(this.$route.query.type);
this.operate = this.$route.query.operate
? Number(this.$route.query.operate)
: 0;
const ret = await this.getDetail(this.id);
if (!ret) return;
this.getConnectConfig(this.id);
this.getHistoryMessage(this.id);
// this.getMessageCount()
if ([1, 4, 5].includes(this.type)) {
this.userId = this.$store.state.user.user.advisorInfo
? this.$store.state.user.user.advisorInfo.id
: this.$store.state.user.user.user.id;
} else {
this.userId = this.$store.state.user.user.user.id;
}
if (this.type === 5) {
this.timerGetUniteLiveUser();
}
}
// } catch (error) {
// console.log('error',error)
// }
},
computed: {
...mapGetters(["user"]),
beforeImgUrl() {
const { imgUrl } = this.detail;
// 未开始或者已结束(已结束还需加一个无精彩回放的判断)
// 有暖场且是图片
return imgUrl;
},
renderStatus() {
const { fileId, isGenerateRecord } = this.detail;
if (this.detail.liveStatus == 2) return "before-img";
if (this.detail.liveStatus == 1) {
const element = document.getElementById("play-video");
if (!element) {
this.getDetail(this.id);
}
return "living-player";
}
if (this.detail.liveStatus == 3) {
if (fileId) {
const element = document.getElementById("play-video");
if (element) {
element.remove();
}
return "suspend-player";
}
return "before-img";
}
if (this.detail.liveStatus == 4) {
if (fileId && isGenerateRecord == 1) return "back-player";
}
},
...mapState({
settingToC: state => state.settings.settingToC
}),
msgList() {
return this.wsMessages.slice(0, this.currentIndex * 10);
}
},
watch: {
backIndex: {
async handler(newVal) {
if (newVal) {
if (newVal < this.libraryList.length) {
if (!this.backArr[newVal]) {
const { data } = await getPlayerSign({
fileId: this.libraryList[newVal]
});
this.backArr[newVal] = {
appID: data.appId,
fileID: data.fileId,
psign: data.psign
};
}
this.playCtx && this.playCtx.loadVideoByID(this.backArr[newVal]);
} else {
this.backIndex = 0;
}
}
}
}
},
beforeDestroy() {
this.stompClient && this.stompClient.deactivate();
if (this.playCtx) {
this.playCtx.dispose();
this.playCtx = null;
}
if (this.stompClient) {
this.stompClient.deactivate();
this.stompClient = null;
}
// this.stopPolling();
},
methods: {
dayjs,
timeFormat,
isValidImageUrl(url) {
return /\.(jpg|jpeg|png|webp|avif|gif|svg)$/.test(url);
},
handleSuccessSend(response, file, fileList) {
console.log(response);
console.log(file);
console.log(fileList);
setTimeout(() => {
this.imageUrl = response.data.url;
}, 100);
this.$notify({
title: "图片上传成功",
type: "success",
duration: 2500
});
},
beforeUploadSend(file) {
if (file.type !== "image/jpeg" && file.type !== "image/png") {
this.$message.error("上传只支持jpeg格式和png格式!");
return false;
}
const isLt = file.size / 1024 < 1024;
if (!isLt) {
this.$message.error("上传文件大小不能超过 1MB!");
}
this.headers["X-File-Size"] = file.size;
return isLt;
},
deleteImage() {
// 在这里实现删除图片的逻辑
this.imageUrl = "";
},
getMessageCount() {
const data = {
videoId: this.id
};
messageCountApi(data).then(res => {
if (res.code === 0) {
this.messageCount = res.data;
}
});
},
// 互动判断
isMessageAudit(val) {
const time1 = dayjs(val.createTime).format("YYYY-MM-DD HH:mm:ss");
if (
this.detail.messageAudit === 2 &&
this.detail.messageAuditTime &&
dayjs(time1).isBefore(dayjs(this.detail.messageAuditTime))
) {
return true;
} else if (
this.detail.messageAudit === 1 &&
!this.detail.messageAuditTime
) {
return true;
} else {
return false;
}
},
open(val) {
if (this.debounceTimer) return;
this.$confirm(`您确定${val.isOpen === 1 ? "取消公开" : "公开"}评论?`, {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning"
})
.then(() => {
this.debounceTimer = true;
const data = {
isOpen: val.isOpen === 1 ? 2 : 1,
messageId: val.id
};
messageOpen(data).then(res => {
if (res.code === 0) {
this.$message({
message: "操作成功",
showClose: false,
type: "success"
});
this.current = 1;
this.currentIndex = 1;
this.hisLastMessageId = "";
this.hisHasNextPage = true;
this.wsMessages = [];
if (this.selectedButton === 1) {
this.getAdvisorMessageList();
} else {
this.getHistoryMessage(this.id);
}
this.debounceTimer = false;
}
});
})
.catch(() => {
this.debounceTimer = false;
});
},
handleSizeChange(val) {
this.$set(this.queryParams, "size", val);
this.$set(this.queryParams, "current", 1);
this.queryList();
},
handleCurrentChange(val) {
this.$set(this.queryParams, "current", val || 1);
this.queryList();
},
handleSizeChangePreferential(val) {
this.$set(this.queryParamsPreferential, "size", val);
this.$set(this.queryParamsPreferential, "current", 1);
this.queryListPreferential();
},
handleCurrentChangePreferential(val) {
this.$set(this.queryParamsPreferential, "current", val || 1);
this.queryListPreferential();
},
handleSizeChangeQuestionnaire(val) {
this.$set(this.queryParamsQuestionnaire, "size", val);
this.$set(this.queryParamsQuestionnaire, "current", 1);
this.getQuestionList();
},
handleCurrentChangeQuestionnaire(val) {
this.$set(this.queryParamsQuestionnaire, "current", val || 1);
this.getQuestionList();
},
queryList() {
this.queryParams.videoId = this.id;
this.queryParams.status = -1;
this.queryParams.size = 999;
videoMessage(this.queryParams).then(data => {
this.wsMessages = data.data.list;
this.queryParams.total = data.data.total;
});
},
queryListPreferential() {
this.queryParamsPreferential.liveId = this.id;
sendcouponList(this.queryParamsPreferential).then(data => {
this.preferentialData = data.data.list;
this.queryParamsPreferential.total = data.data.total;
});
},
handleScroll(event) {
requestAnimationFrame(async () => {
const { scrollTop, clientHeight, scrollHeight } = event.target;
if (scrollTop + clientHeight + 10 >= scrollHeight) {
console.log(
"加载页",
this.current,
this.selectedButton,
this.hisHasNextPage
);
// 滚动条到达底部,触发懒加载
if (this.currentIndex * 10 < this.wsMessages.length) {
this.currentIndex++;
} else if (this.selectedButton === 1) {
await this.getAdvisorMessageList();
} else if (this.hisHasNextPage) {
this.current++;
await this.getHistoryMessage(this.id);
} else {
return; // 如果没有更多数据,直接返回
}
}
});
},
onSubmit() {
if (!this.formPreferential.couponId) {
return this.$message({
message: "请选择优惠券",
showClose: false,
type: "error"
});
}
if (!this.formPreferential.sendTotalNumber) {
return this.$message({
message: "请填写发放数量",
showClose: false,
type: "error"
});
}
if (
!this.formPreferential.receiveRequirement &&
this.formPreferential.receiveRequirement !== 0
) {
return this.$message({
message: "请选择领取要求",
showClose: false,
type: "error"
});
}
this.formPreferential.sendCh = 3;
this.formPreferential.liveId = this.id;
sendcouponSend(this.formPreferential).then(res => {
if (res.code === 0) {
this.$message({
message: "发放成功",
showClose: false,
type: "success"
});
this.formPreferential = {};
this.queryListPreferential();
} else {
this.$message({
message: "发放失败",
showClose: false,
type: "error"
});
}
});
},
selected(index) {
this.selectedButton = index;
if (index === 1) {
this.wsMessages = [];
this.current = 1;
this.hisLastMessageId = "";
this.hisHasNextPage = true;
this.currentIndex = 1;
this.getAdvisorMessageList();
} else {
this.wsMessages = [];
this.hisLastMessageId = "";
this.hisHasNextPage = true;
this.current = 1;
this.currentIndex = 1;
this.getHistoryMessage(this.id);
}
},
// 获取主播消息
getAdvisorMessageList() {
const data = {
advisorId: this.detail.advisorBasic.id,
size: 20,
type: 3,
id: this.id,
lastId:
this.wsMessages.length > 0
? this.wsMessages[this.wsMessages.length - 1].id
: null
};
advisorMessageList(data).then(res => {
if (res && res.code === 0) {
if (this.wsMessages.length > 0) {
this.currentIndex++;
} else {
this.currentIndex = 1;
}
this.hisHasNextPage = res.data.hasNext;
this.wsMessages = this.wsMessages.concat(res.data.list);
}
});
},
async getDetail(id) {
const res = await infoGet({ id: this.id });
if (res && res.code === 0) {
this.detail = res.data;
if (
this.detail.advisorBasic.deptName !==
this.$store.state.user.user.user.deptName &&
this.$store.state.user.user.user.deptId !== "1"
) {
// 不是一个部门的不让查看 总部的可以查看所有
this.$alert("暂无查看权限", "", {
confirmButtonText: "确定",
callback: action => {
this.$router.go(-1);
}
});
return false;
}
if (this.settingToC && !this.productListType.length) {
this.productListType.push({ id: 21, name: "自定义" });
this.productList = JSON.parse(JSON.stringify(this.detail.cartVOList));
} else if (!this.settingToC && !this.productListType.length) {
this.productListType.push({ id: 1, name: "观点包" });
this.remoteMethodProduct("");
}
this.isSpeak = this.detail.isSpeak;
this.messageAudit = this.detail.messageAudit;
this.interactType = this.detail.interactType;
if (this.detail.playType === 2) {
this.detail.liveStatus = 1;
}
const { beforeLiveUrl, liveStatus, fileId, playType } = this.detail;
if (this.renderStatus == "living-player" && playType === 2) {
this.initPlayer((await getPlayerSign({ fileId })).data);
}
if (this.renderStatus == "living-player" && playType === 1) {
this.initPlayer((await livePlayInfo({ id })).data);
}
if (this.renderStatus == "back-player") {
this.libraryList = fileId.split(",");
const { data } = await getPlayerSign({
fileId: this.libraryList[0],
audioVideoType:
this.detail.transStatus === 2 ? "RawAdaptive" : "Original"
});
this.hasBack = true;
this.backArr[0] = {
appID: data.appId,
fileID: data.fileId,
psign: data.psign
};
this.initPlayer(data);
}
if (liveStatus == 4) this.pullBuffer = true;
return true;
}
return false;
},
async getConnectConfig(id) {
if (!this.stompClient) {
this.connect({
brokerUrl:
process.env.NODE_ENV === "development"
? "ws://8.138.144.54:8080/tgim/chat"
: `${location.protocol === "http:" ? "ws://" : "wss://"}${
location.host
}/tgim/chat`,
adminUserTopic: `/admin/user/topic/${id}`,
chatSubscribeDest: `/sub/video/${id}`,
notifySubscribeDest: `/sub/notify/${id}`,
videoOnlineNotifyTopic: `/admin/audience/${id}`,
id
});
}
},
playLiveStream() {
const videoElement = this.$refs.videoElement;
if (videoElement) {
videoElement.src = "";
}
},
// websock 链接
connect(config) {
const fromUser = this.userId;
let {
brokerUrl,
adminUserTopic,
chatSubscribeDest,
notifySubscribeDest,
videoOnlineNotifyTopic,
id
} = config;
const sessionId = Math.random()
.toString(36)
.substring(2, 8);
if (window.location.protocol === "http:") {
console.log("当前URL是HTTP");
} else if (window.location.protocol === "https:") {
console.log("当前URL是HTTPS");
brokerUrl = brokerUrl.replace("ws://", "wss://");
}
this.stompClient = new Client({
brokerURL: brokerUrl,
connectHeaders: {
sessionId,
productType: 3, // 视频ProductType=3
Authorization: "Bearer " + this.$store.state.user.token,
productId: id
},
debug: function(str) {
console.log("debug: " + str);
},
reconnectDelay: 5000,
heartbeatIncoming: 10000,
heartbeatOutgoing: 10000,
discardWebsocketOnCommFailure: true
});
this.stompClient.onConnect = async frame => {
console.log("链接成功");
console.log("Connected: ", frame);
this.stompClient &&
this.stompClient.subscribe(adminUserTopic, msg => {
this.handleCommonMsg(msg);
});
this.stompClient &&
this.stompClient.subscribe(chatSubscribeDest, msg => {
this.handleHistory(msg);
});
this.stompClient &&
this.stompClient.subscribe(notifySubscribeDest, msg => {
this.notify(msg);
});
this.stompClient &&
this.stompClient.subscribe(videoOnlineNotifyTopic, msg => {
this.notify(msg);
});
// 查询在线人员列表
this.stompClient &&
this.stompClient.publish({
destination: `/admin/chat/listMember/${this.id}`
});
};
this.stompClient.onStompError = error => {
console.log("onStompError", error);
};
this.stompClient.onDisconnect = error => {
console.log("onDisconnect", error);
};
this.stompClient.onWebSocketClose = error => {
console.log("onWebSocketClose", error);
};
this.stompClient.activate();
},
// 解析返回的值
handleCommonMsg(msg) {
try {
const { code, message, type, data } = JSON.parse(msg.body);
if (code) {
this.$message.error(msg);
return;
}
console.log(data, "第一次获取观众列表");
if (data.list !== null && data.list.length) {
this.audienceList = data.list;
this.onLineNum = data.onLineTotal;
// 使用sort方法将status为1的对象排在前面
this.audienceList.sort((a, b) =>
a.isOnline === 1 ? -1 : b.isOnline === 1 ? 1 : 0
);
} else {
this.audienceList = [];
this.onLineNum = 0;
}
} catch (error) {
this.$message.error(error.message);
}
},
handleMsgData(arr) {
const arrR = arr.reverse();
if (arr && this.selectedButton === 0) {
this.wsMessages = arrR.concat(this.wsMessages);
} else if (this.selectedButton === 1) {
if ([2, 4].includes(this.type)) {
const list = arrR.filter(
item => ![10, 7, 6].includes(item.type) && !item.userId
);
this.wsMessages = list.concat(this.wsMessages);
} else {
const list = arrR.filter(item => !item.userId);
this.wsMessages = arrR.concat(this.wsMessages);
}
}
this.messageCount =
arrR.length && arrR[arrR.length - 1].messageCount
? arrR[arrR.length - 1].messageCount
: this.messageCount;
},
handleHistory(msg) {
try {
const { code, message, type, data } = JSON.parse(msg.body);
if (code) {
this.$message.error(msg);
return;
}
let arr = [];
if (Array.isArray(data)) {
arr = data;
} else {
arr.push(data);
}
this.handleMsgData(arr);
} catch (error) {
this.$message.error(error.message);
}
},
notify(msg) {
try {
const { data } = JSON.parse(msg.body);
console.log(data, "===notify");
// 最新直播状态
if (
data &&
data.type === 2 &&
(data.data === "1" ||
data.data === "2" ||
data.data === "3" ||
data.data === "4")
) {
this.detail.liveStatus = Number(data.data);
}
// 用户进出
if (data && data.type === 12) {
console.log(data, "===用户进出");
if (data.onlineUser.isOnline === 1) {
let foundItem = null;
for (let i = 0; i < this.audienceList.length; i++) {
if (
this.audienceList[i].userId === data.onlineUser.userId &&
this.audienceList[i].sessionId === data.onlineUser.sessionId
) {
foundItem = this.audienceList[i];
break;
}
}
if (foundItem === null) {
this.audienceList.unshift(data.onlineUser);
} else {
for (let i = 0; i < this.audienceList.length; i++) {
if (
this.audienceList[i].userId === data.onlineUser.userId &&
this.audienceList[i].sessionId === data.onlineUser.sessionId
) {
this.audienceList[i].isOnline = 1;
break; // 结束循环
}
}
}
} else {
for (let i = 0; i < this.audienceList.length; i++) {
if (
this.audienceList[i].userId === data.onlineUser.userId &&
this.audienceList[i].sessionId === data.onlineUser.sessionId
) {
this.audienceList[i].isOnline = 2;
break; // 结束循环
}
}
console.log(this.audienceList, "0131");
}
let count = 0;
for (let i = 0; i < this.audienceList.length; i++) {
if (this.audienceList[i].isOnline === 1) {
count++;
}
}
this.onLineNum = count;
this.audienceList.sort((a, b) =>
a.isOnline === 1 ? -1 : b.isOnline === 1 ? 1 : 0
);
}
} catch (error) {}
},
controlLive(option) {
const label = {
1: "暂停",
2: "取消",
3: "开始"
};
this.$confirm(`确定${label[option]}直播吗`, "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning"
}).then(async () => {
const res = await uVideoControl({ id: this.detail.id, option });
if (!res.code) {
this.$message({
type: "success",
message: "操作成功!"
});
this.getDetail(this.id);
} else {
this.$message({
type: "error",
message: res.message
});
}
});
},
async initPlayer(sign) {
if (this.playCtx) {
this.playCtx.dispose();
this.playCtx = null;
}
this.renderPlayer = true;
const baseConfig = {
autoplay: true,
poster: this.detail.imgUrl,
muted: true
};
const video = document.createElement("video");
video.setAttribute("id", "play-video");
video.setAttribute("playsinline", "true");
video.setAttribute("webkit-playsinline", "true");
await this.$nextTick(() => {
this.$refs.videoPlayer.appendChild(video);
console.log(sign, "录播获取数据");
if (sign.appId) {
if (this.renderStatus == "back-player") {
baseConfig.autoplay = false;
}
const { appId: appID, fileId: fileID, psign } = sign;
this.playCtx = window.TCPlayer("play-video", {
...baseConfig,
appID,
fileID,
psign,
loop: !this.hasBack
});
} else {
const { playUrl } = sign;
this.playCtx = window.TCPlayer("play-video", baseConfig);
playUrl && this.playCtx.src(playUrl);
}
this.playCtx.on("ended", () => {
if (this.hasBack && this.libraryList.length) {
this.backIndex++;
}
});
});
},
async getHistoryMessage(id) {
if (this.wsLoading) return;
this.wsLoading = true;
const res = await videoMessage({
videoId: id,
current: 1,
size: 10,
messageId: this.hisLastMessageId,
status: this.selectedButton === 2 ? -1 : 1
});
if (!res.code) {
this.currentIndex++;
if (!res.data.list || res.data.list.length < 10) {
this.hisHasNextPage = false;
}
this.hisLastMessageId =
res.data.list && res.data.list.length
? res.data.list[res.data.list.length - 1].id
: "";
this.wsMessages = this.wsMessages.concat(res.data.list);
await this.$nextTick(() => {
if (this.current == 1 && this.$refs.discussArea) {
this.cacheTop = this.$refs.discussArea.scrollHeight;
this.$refs.discussArea.scrollTop = 0;
}
if (this.isGetNum) {
this.getMessageCount();
}
this.wsLoading = false;
});
}
},
replyTo(item) {
this.replyUserName = `回复 ${item.userName || item.userId} `;
this.replyID = item.id;
},
handleClick() {
if (this.activeName === "4") {
this.reqCouponList();
this.queryListPreferential();
}
if (this.activeName === "1" && !this.settingToC) {
this.remoteMethodProduct("");
}
if (this.activeName === "5") {
this.getQuestionList();
}
},
tabChange(index, item) {
this.curTabIndex = index;
},
changeFocus(type) {
// type == 1 聊天室 type == 2 已删除聊天
this.isAutofocus = !this.isAutofocus;
if (type == 2) {
this.isAutofocus = false;
this.queryList();
} else {
this.isAutofocus = true;
}
},
prohibition(item) {
if (item.isForbid === 1) {
this.$confirm(`您确定${item.isForbid === 2 ? "禁言" : "取消禁言"}?`, {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning"
})
.then(() => {
removeCommentBlack({
userPhone: item.userId,
productId: item.videoId,
productType: 3
}).then(res => {
if (res.code === 0) {
this.$message({
message: "取消禁言成功",
showClose: false,
type: "success"
});
this.current = 1;
this.hisLastMessageId = "";
this.hisHasNextPage = true;
this.wsMessages = [];
this.currentIndex = 1;
if (this.selectedButton === 1) {
this.getAdvisorMessageList();
} else {
this.getHistoryMessage(this.id);
}
}
});
})
.catch(() => {});
} else {
this.dialogFormVisible = true;
this.clickItem = JSON.parse(JSON.stringify(item));
}
},
// 删除评论
toDeleted(item) {
this.$confirm(`您确定删除评论?`, {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning"
})
.then(() => {
const ids = [];
ids.push(item.id);
messageDelete({ id: this.id, ids }).then(async res => {
if (res.code === 0) {
this.$message({
message: "删除成功",
showClose: false,
type: "success"
});
this.current = 1;
this.currentIndex = 1;
this.hisLastMessageId = "";
this.hisHasNextPage = true;
this.wsMessages = [];
this.messageCount = res.data;
this.isGetNum = false;
if (this.selectedButton === 1) {
await this.getAdvisorMessageList();
} else {
await this.getHistoryMessage(this.id);
}
this.isGetNum = true;
// this.getMessageCount()
}
});
})
.catch(() => {});
},
toReply(item) {
this.dialogFormVisibleReply = true;
this.clickItem = JSON.parse(JSON.stringify(item));
},
send() {
if (this.sendForm.productType) {
if (!this.sendForm.productListId) {
return this.$message({ type: "error", message: "请选择发送产品" });
}
const data = {
id: this.sendForm.productListId,
type: this.sendForm.productType,
videoId: this.id,
name: this.settingToC
? this.clickItem.productName
: this.clickItem.name
};
productMessage(data).then(res => {
if (res.code === 0) {
this.$message({
type: "success",
message: "发送成功"
});
this.input = "";
this.sendForm.productType = null;
this.sendForm.productListId = null;
}
});
} else {
if (!this.input.trim()) {
return this.$message({
type: "error",
message: "请填写发送内容"
});
}
const data = {
text: this.input,
type: 1,
groupId: this.id,
from: this.userId
};
messageSend(data).then(res => {
if (res.code === 0) {
this.$message({
type: "success",
message: "发送成功"
});
this.input = "";
}
});
}
},
sendPhono() {
const data = {
text: "upImg-" + this.imageUrl,
type: 1,
groupId: this.id,
from: this.userId
};
messageSend(data).then(res => {
if (res.code === 0) {
this.$message({
type: "success",
message: "发送图片成功"
});
this.imageUrl = "";
}
});
},
changeItem(e) {
let item = null;
for (let index = 0; index < this.productList.length; index++) {
if (this.productList[index].id === e) {
item = this.productList[index];
}
}
this.clickItem = item;
},
changeItemToc(e) {
let item = null;
for (let index = 0; index < this.productList.length; index++) {
if (this.productList[index].productId === e) {
item = this.productList[index];
}
}
this.clickItem = item;
},
cancelProhibition() {
this.dialogFormVisible = false;
this.formProhibition.type = 0;
},
submitProhibition() {
if (![0, 1, 2].includes(this.formProhibition.type)) return;
const data = {
productId: this.id,
productType: 3, // 3 直播 41 交易圈
type: this.formProhibition.type,
userName: this.clickItem.userName,
userPhone: this.clickItem.phone,
scope: 2,
content: "无"
};
addCommentBlack(data).then(res => {
if (res.code === 0) {
this.$message({
message: "禁言成功",
showClose: false,
type: "success"
});
this.clickItem = {};
this.formProhibition.type = 0;
this.current = 1;
this.currentIndex = 1;
this.hisLastMessageId = "";
this.hisHasNextPage = true;
this.wsMessages = [];
if (this.selectedButton === 1) {
this.getAdvisorMessageList();
} else {
this.getHistoryMessage(this.id);
}
this.dialogFormVisible = false;
}
});
},
cancelReply() {
this.dialogFormVisibleReply = false;
this.formReply.context = "";
},
submitReply() {
if (!this.formReply.context) return;
const data = {
text: this.formReply.context,
type: 6,
groupId: this.id,
from: this.userId,
reply: this.clickItem.id,
replyUserId: this.clickItem.userId
};
messageSend(data).then(res => {
if (res.code === 0) {
this.$message({
type: "success",
message: "发送成功"
});
this.formReply.context = "";
if (this.clickItem.isOpen === 2) {
const data = {
isOpen: 1,
messageId: this.clickItem.id
};
messageOpen(data).then(res => {
if (res.code === 0) {
this.current = 1;
this.currentIndex = 1;
this.hisLastMessageId = "";
this.hisHasNextPage = true;
this.wsMessages = [];
if (this.selectedButton === 1) {
this.getAdvisorMessageList();
} else {
this.getHistoryMessage(this.id);
}
}
});
}
this.dialogFormVisibleReply = false;
}
});
},
updateStatus(val, type) {
this.clickItem = val;
// type 3 购物车上架 5购物车下架 9 修改可销售数量
if (type === 11) {
this.$confirm(`您确定取消推荐?`, {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning"
})
.then(() => {
const data = {
videoId: this.id,
productId: this.clickItem.productId,
productType: this.clickItem.productType,
weight: 0
};
cartRecommend(data).then(res => {
if (res.code === 0) {
this.$message({
message: "取消推荐成功",
showClose: false,
type: "success"
});
this.getDetail(this.id);
}
});
})
.catch(() => {});
} else {
this.updateStatusType = type;
this.dialogFormVisibleProduct = true;
if (type === 9) {
this.formProduct.num = JSON.parse(JSON.stringify(val.saleLimit));
}
// 推荐
if (type === 10) {
this.formProduct.weight = JSON.parse(JSON.stringify(val.weight));
}
}
},
setCartPush(item) {
this.$confirm(`是否确定推送产品?`, {
confirmButtonText: "确认",
cancelButtonText: "取消"
})
.then(async () => {
const ret = await cartPush({
id: this.id,
productId: item.productId,
productType: item.productType
});
if (ret && ret.code === 0) {
this.$message({
message: "推送成功",
showClose: false,
type: "success"
});
}
})
.catch(() => {});
},
changeSwitch() {
if (!this.formProduct.switch) {
this.formProduct.num = 0;
}
},
cancelProduct() {
this.formProduct = {
switch: true,
num: 0,
isSendMessage: false,
weight: 0
};
this.loading = false;
this.dialogFormVisibleProduct = false;
},
submitProduct() {
if (this.loading) return;
if (this.updateStatusType === 3) {
if (
this.formProduct.switch &&
!this.formProduct.num &&
!this.settingToC
) {
return this.$message({
message: "请填写销售数量",
showClose: false,
type: "error"
});
}
this.loading = true;
const data = {
id: this.id,
status: 3,
isSendMessage: this.formProduct.isSendMessage ? 1 : 2,
num:
this.formProduct.switch && !this.settingToC
? this.formProduct.num
: null,
productId: this.clickItem.productId,
productType: this.clickItem.productType
};
cartUpdateStatus(data).then(res => {
if (res.code === 0) {
this.$message({
message: "上架成功",
showClose: false,
type: "success"
});
this.loading = false;
this.getDetail(this.id);
this.cancelProduct();
}
});
} else if (this.updateStatusType === 4) {
this.loading = true;
// 购物车下架
const data = {
id: this.id,
status: 4,
isSendMessage: this.formProduct.isSendMessage ? 1 : 2,
num: null,
productId: this.clickItem.productId,
productType: this.clickItem.productType,
reason: ""
};
cartUpdateStatus(data).then(res => {
if (res.code === 0) {
this.$message({
message: "下架成功",
showClose: false,
type: "success"
});
this.loading = false;
this.getDetail(this.id);
this.cancelProduct();
}
});
} else if (this.updateStatusType === 9) {
this.loading = true;
updateSaleLimit({
id: this.id,
num: this.formProduct.num,
productId: this.clickItem.productId,
productType: this.clickItem.productType
}).then(res => {
if (res.code === 0) {
this.$message({
message: "修改数量成功",
showClose: false,
type: "success"
});
this.loading = false;
this.getDetail(this.id);
this.cancelProduct();
}
});
} else {
this.loading = true;
// 购物车修改可销售数量
if (!this.formProduct.weight) {
return this.$message({
type: "error",
message: "请填写权重"
});
}
const data = {
videoId: this.id,
productId: this.clickItem.productId,
productType: this.clickItem.productType,
weight: this.formProduct.weight
};
cartRecommend(data).then(res => {
if (res.code === 0) {
this.$message({
message: "推荐成功",
showClose: false,
type: "success"
});
this.loading = false;
this.getDetail(this.id);
this.cancelProduct();
}
});
}
},
setUp() {
const data = {
videoId: this.id,
isSpeak: this.isSpeak,
isSendMessage: this.isSendMessage
};
messageForbidden(data).then(res => {
if (res.code === 0) {
this.$message({
type: "success",
message: "保存成功"
});
this.getDetail(this.id);
}
});
},
setUpTwo() {
const data = {
videoId: this.id,
messageAudit: this.messageAudit
};
openMessAudit(data).then(res => {
if (res.code === 0) {
this.$message({
type: "success",
message: "保存成功"
});
this.getDetail(this.id);
}
});
},
setUpThree() {
const data = {
videoId: this.id,
interactType: this.interactType
};
updateInteractType(data).then(res => {
if (res.code === 0) {
this.$message({
type: "success",
message: "保存成功"
});
this.getDetail(this.id);
}
});
},
changeShowNickname() {
const data = {
isOpen: this.detail.showNickname,
videoId: this.id
};
showNickname(data).then(res => {
if (res.code === 0) {
this.$message({
type: "success",
message: "保存成功"
});
this.getDetail(this.id);
}
});
},
reqCouponList() {
const data = {
size: 999,
current: 1,
status: 1,
expired: 0
};
getCouponList(data).then(res => {
if (res.code === 0) {
this.couponList = res.data.list;
}
});
},
getSendcouponList() {},
remoteMethodProduct(query) {
this.loadingProduct = true;
const queryParams = {
packageName: query,
current: 1,
size: 99,
statusList: [3]
};
packageList(queryParams).then(data => {
this.productList = data.data.list;
this.loadingProduct = false;
});
},
// 创建问卷
createQuestionnaire() {
this.dialogFormVisibleQuestionnaire = true;
},
openView(val) {
this.dialogVisibleTitle = val.title + "(预览)";
this.iframeUrl = `${window.location.origin}/syzbh5/previewQuestion?id=${
val.id
}`;
// window.open(this.iframeUrl)
this.dialogVisible = true;
},
toQuestionnaire(isQuestionnaire, id) {
if (isQuestionnaire) {
this.iframeUrl = `${
window.location.origin
}/syzbh5/previewQuestion?id=${id}`;
this.dialogVisible = true;
}
},
// 添加选项
addOptions(index) {
if (this.titleQueryList[index].optionQueryList.length === 10) {
return this.$message({
message: "最多提供10个选项",
type: "warning"
});
}
if ([3, 4].includes(this.titleQueryList[index].type)) {
this.titleQueryList[index].optionQueryList.push({
fileList: [],
imgUrl: "",
option: this.titleQueryList[index].optionQueryList.length + 1,
optionDesc:
"选项" + (this.titleQueryList[index].optionQueryList.length + 1)
});
} else {
this.titleQueryList[index].optionQueryList.push({
option: this.titleQueryList[index].optionQueryList.length + 1,
optionDesc:
"选项" + (this.titleQueryList[index].optionQueryList.length + 1)
});
}
this.$forceUpdate();
},
// 删除选项
deleteOptions(index, k) {
console.log(index, k, this.titleQueryList[index].optionQueryList);
if (this.titleQueryList[index].optionQueryList.length > 2) {
this.titleQueryList[index].optionQueryList.splice(k, 1);
this.$forceUpdate();
} else {
this.$message({
message: "请至少提供两个选项!",
type: "warning"
});
}
},
// 添加问题
addOpts(item) {
console.log(item.type);
// 单选
if ([1, 2].includes(item.type)) {
this.titleQueryList.push({
title: "请输入题目标题",
type: item.type,
optionQueryList: [
{ option: 1, optionDesc: "选项1" },
{ option: 2, optionDesc: "选项2" }
]
});
} else if ([5].includes(item.type)) {
this.titleQueryList.push({
title: "请输入题目标题",
type: item.type,
optionQueryList: []
});
} else if ([3, 4].includes(item.type)) {
this.titleQueryList.push({
title: "请输入题目标题",
type: item.type,
optionQueryList: [
{ optionDesc: "选项1", fileList: [], imgUrl: "", option: 1 },
{ optionDesc: "选项2", fileList: [], imgUrl: "", option: 2 }
]
});
}
},
// 复制问题
copyQuest(item, index) {
const obj = { ...item };
const copyObj = JSON.parse(JSON.stringify(obj));
this.titleQueryList.splice(index + 1, 0, copyObj);
console.log("titleQueryList", this.titleQueryList);
},
// 删除问题
removeQuest(index) {
this.titleQueryList.splice(index, 1);
console.log("titleQueryList", this.titleQueryList);
},
// ========================================== 问卷
getQuestionList() {
this.queryParamsQuestionnaire.videoId = this.id;
questionList(this.queryParamsQuestionnaire).then(res => {
if (res.code === 0) {
this.queryParamsQuestionnaire.total = res.data.total;
this.questionnaireList = res.data.list;
}
});
},
async confirm(type) {
this.showQrCode = false;
if (this.loadingButton) return;
// type 1 预览 2 创建
if (!this.questionnaireTitle.trim()) {
return this.$message({
message: "请填写标题!",
type: "warning"
});
}
if (!this.titleQueryList.length) {
return this.$message({
message: "请添加题型!",
type: "warning"
});
}
for (let index = 0; index < this.titleQueryList.length; index++) {
if ([3, 4].includes(this.titleQueryList[index].type)) {
for (
let i = 0;
i < this.titleQueryList[index].optionQueryList.length;
i++
) {
if (
!this.titleQueryList[index].optionQueryList[i].imgUrl ||
!this.titleQueryList[index].optionQueryList[i].optionDesc
) {
return this.$message({
message: "请补充图片及其选项",
type: "warning"
});
}
}
}
if ([1, 2].includes(this.titleQueryList[index].type)) {
for (
let i = 0;
i < this.titleQueryList[index].optionQueryList.length;
i++
) {
if (
!this.titleQueryList[index].optionQueryList[i].optionDesc.trim()
) {
return this.$message({
message: "请补充选择题选项",
type: "warning"
});
}
}
}
if (
[5].includes(this.titleQueryList[index].type) &&
!this.titleQueryList[index].title.trim()
) {
return this.$message({
message: "请补充填空题",
type: "warning"
});
}
}
if (type == 2) {
this.loadingButton = true;
}
const data = {};
data.description = this.isTip ? this.explain : "";
data.title = this.questionnaireTitle;
data.videoId = this.id;
data.questionId = this.questionId ? this.questionId : null;
data.titleQueryList = JSON.parse(JSON.stringify(this.titleQueryList));
data.isReview = type === 1 ? 1 : 2;
await questionSave(data)
.then(res => {
if (res.code === 0) {
if (type === 1) {
this.questionId = res.data;
this.showQrCode = true;
this.downloadByBlob();
} else {
this.$message({
type: "success",
message: "创建问卷成功!"
});
this.getQuestionList();
this.cancel();
}
} else if (type === 2) {
this.loadingButton = false;
}
})
.catch(() => {
if (type === 2) this.loadingButton = false;
});
},
cancel() {
this.dialogFormVisibleQuestionnaire = false;
this.titleQueryList = [];
this.explain = "";
this.questionnaireTitle = "";
this.questionId = null;
this.showQrCode = false;
this.loadingButton = false;
this.isTip = false;
},
question(item, type) {
const label = {
"-1": "发起问卷",
"-2": "删除问卷",
"-3": "重发问卷"
};
this.$confirm(`您确定${label[type]}?`, {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning"
})
.then(() => {
const data = {
questionId: item.id,
operate: type
};
questionUpdateStatus(data).then(res => {
if (res.code === 0) {
this.$message({
type: "success",
message: "操作成功!"
});
this.getQuestionList();
}
});
})
.catch(() => {});
},
updateQuestion(item, type) {
questionDetails({ questionId: item.id }).then(res => {
if (res.code === 0) {
if (res.data.description) {
this.isTip = true;
this.explain = res.data.description;
}
this.questionnaireTitle = res.data.title;
this.questionId = type === 1 ? res.data.id : null;
this.titleQueryList = JSON.parse(
JSON.stringify(res.data.titleVOList)
);
for (let index = 0; index < this.titleQueryList.length; index++) {
this.titleQueryList[index].optionQueryList = [];
this.titleQueryList[index].optionQueryList = this.titleQueryList[
index
].optionVOList;
for (
let i = 0;
i < this.titleQueryList[index].optionQueryList.length;
i++
) {
if ([3, 4].includes(this.titleQueryList[index].type)) {
this.titleQueryList[index].optionQueryList[i].fileList = [];
this.titleQueryList[index].optionQueryList[i].fileList.push({
url: this.titleQueryList[index].optionQueryList[i].imgUrl
});
}
}
}
console.log(this.titleQueryList, "====复制的");
this.dialogFormVisibleQuestionnaire = true;
}
});
},
downloadByBlob(val) {
const url = `${window.location.origin}/syzbh5/previewQuestion?id=${
this.questionId
}`;
this.$nextTick(() => {
this.$refs.QrCode.createCode(url);
});
},
async toExport(item) {
const { data, code, message } = await questionExport({
questionId: item.id
});
if (code === 0) {
import("@/utils/export2Excel").then(excel => {
const tHeader = [
"用户昵称",
"用户ID",
"开始答题时间",
"结束答题时间",
"答题时长"
];
const filterVal = [
"nickName",
"userId",
"startTime",
"endTime",
"duration"
];
if (data[0].titleList.length) {
for (let index = 0; index < data[0].titleList.length; index++) {
filterVal.push(index);
}
}
tHeader.push(...data[0].titleList);
const excelData = this.formatJson(filterVal, data);
excel.export_json_to_excel({
header: tHeader,
data: excelData,
filename: `${item.title}问卷数据`
});
console.log("list=>", data.list);
});
} else {
this.$message.error(message);
}
},
formatJson(filterVal, jsonData) {
return jsonData.map(v =>
filterVal.map(j => {
if (!isNaN(j)) {
return v.answers[j];
} else if (j === "duration") {
return this.timeFormat(v[j] * 1000);
} else {
return v[j];
}
})
);
},
closePopover() {
this.showQrCode = false;
},
// ==============================================
handleSuccess(response, file, index, k) {
console.log("handleSuccess", response, file, index, k);
const uid = file.uid;
const name = file.name;
const url = response.data.url;
this.titleQueryList[index].optionQueryList[k].fileList.url = url;
this.titleQueryList[index].optionQueryList[k].fileList.name = name;
this.titleQueryList[index].optionQueryList[k].imgUrl = url;
this.$refs[`picUpload${index}${k}`][0].$el.classList.add("disabled");
},
handleError(err, file, fileList) {
console.log("handleError", err, file, fileList);
},
handleRemove(file, index, k) {
console.log("handleRemove", file);
this.titleQueryList[index].optionQueryList[k].fileList.url = "";
this.titleQueryList[index].optionQueryList[k].fileList.name = "";
this.titleQueryList[index].optionQueryList[k].imgUrl = "";
this.$refs[`picUpload${index}${k}`][0].$el.classList.remove("disabled");
},
beforeUpload(file) {
if (
file.type !== "image/jpeg" &&
file.type !== "image/png" &&
file.type !== "image/jpg" &&
file.type !== "image/bmp" &&
file.type !== "image/gif"
) {
this.$message.error("上传只支持支持jpg,gif,png,bmp!");
return false;
}
const isLt = file.size / 1024 / 1024 < 4;
if (!isLt) {
this.$message.error("上传文件大小不能超过 4MB!");
return false;
}
console.log("beforeUpload", file);
this.headers["X-File-Size"] = file.size;
},
sendcoupon(val, type) {
// type 1 未发放
this.$confirm(`您确定${type === 1 ? "发放" : "删除"}优惠券?`, {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning"
})
.then(() => {
const data = {
sendCouponId: val.id
};
if (type === 1) {
liveSend(data).then(res => {
if (res.code === 0) {
this.$message({
type: "success",
message: "操作成功!"
});
this.queryListPreferential();
}
});
} else {
deleteCouponAndSend(data).then(res => {
if (res.code === 0) {
this.$message({
type: "success",
message: "操作成功!"
});
this.queryListPreferential();
}
});
}
})
.catch(() => {});
},
toOpenCoupon() {
this.$refs.coupon.openCoupon();
},
async changeOpenQw() {
const ret = await updateQWParam({
isOpen: this.detail.openQw,
videoId: this.detail.id
});
if (ret.code === 0) {
console.log(this.detail.openQw);
}
},
addPush(item) {
this.detail.pushList.push(item);
},
async cancelPushProduct() {
// 取消推送产品
this.$confirm(`是否确认取消推送产品?`, {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning"
})
.then(async () => {
const ret = await cancelPush({ id: this.id });
if (ret && ret.code === 0) {
this.$message({
type: "success",
message: "操作成功!"
});
}
})
.catch(() => {});
}
}
};
</script>
<style scoped lang="scss">
.mb10 {
margin-bottom: 10px;
}
.flex-box {
display: inline-flex;
flex-direction: row;
&_item {
display: inline-flex;
flex-direction: column;
margin-right: 10px;
cursor: pointer;
background-color: rgb(255, 255, 255);
align-self: center;
div {
padding: 5px 0;
display: inline-flex;
align-self: center;
}
}
}
.charts-box {
padding: 20px;
width: 60%;
}
.selected {
background-color: #409eff !important;
color: #ffffff !important;
}
.title {
padding: 10px 10px;
border: 1px solid #666;
display: flex;
justify-content: space-between;
align-items: center;
}
.content {
width: 100%;
display: flex;
}
.audience {
min-width: 400px;
flex: 1;
.audience-border {
border: 1px solid rgba(153, 153, 153, 0.7);
padding: 10px 10px;
width: 100%;
cursor: pointer;
.audience-title {
padding-top: 20px;
height: 600px;
overflow: auto;
&_user {
margin-bottom: 15px;
font-size: 18px;
color: #303133;
display: inline-flex;
flex-direction: row;
align-items: center;
width: 100%;
img {
width: 40px !important;
height: 40px !important;
margin: 0 8px 0 0 !important;
border-radius: 6px !important;
}
.el-icon-user {
margin-right: 5px;
}
}
}
.audience-interaction {
padding-top: 20px;
.audience-button {
padding-bottom: 20px;
}
.audience-box {
display: flex;
flex-direction: column;
.interaction-box {
display: flex;
flex-direction: row;
justify-content: flex-start;
.interaction-box_switch {
padding-bottom: 15px;
}
}
}
}
.box-tab {
.tab-box {
width: 100%;
display: flex;
flex-direction: row;
justify-content: flex-start;
border-bottom: 0.5px solid #eee;
.tab-box_item {
padding: 7px 0 14px 0;
font-family: PingFangSC-Regular;
font-weight: 600;
font-size: 18px;
color: #5e6878;
letter-spacing: 0;
text-align: center;
line-height: 18px;
width: 50%;
div:not(:first-child) {
padding: 14px 0 14px 14px;
}
}
}
}
}
}
.video-box {
flex: 2;
display: flex;
flex-direction: column;
}
.video-box-bottom {
padding: 5px;
display: flex;
flex-direction: column;
min-height: 600px;
}
.audience-chat-box {
display: flex;
flex-direction: column;
.chat-box {
display: flex;
flex-direction: column;
justify-content: space-between;
height: 100%;
}
.button-box {
height: 10%;
padding-top: 20px;
margin-bottom: 20px;
}
.content-box {
width: 100%;
height: 90vh;
padding-top: 20px;
display: inline-flex;
flex-direction: column;
justify-content: flex-start;
overflow: auto;
&_item {
display: inline-flex;
flex-direction: column;
padding: 15px;
margin-bottom: 20px;
border: 1px solid #ebeef5;
width: 100%;
background-color: rgba(243, 245, 255, 1);
color: #606266;
border-radius: 10px;
font-size: 18px;
&--reply {
font-weight: 500;
font-size: 16px;
margin-top: 20px;
display: inline;
color: #303133;
}
&--product {
margin-top: 10px;
padding: 10px;
display: inline-flex;
flex-direction: column;
background-color: #99999930;
border-radius: 5px;
max-width: 400px;
.product-title {
margin-bottom: 10px;
display: flex;
justify-content: flex-start;
align-items: center;
span {
padding: 4px 6px;
font-size: 12px;
border-radius: 3px;
color: #fff;
background-color: #13ce66;
border-color: #13ce66;
margin-right: 10px;
}
}
.product-content {
word-break: break-all;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 1;
overflow: hidden;
text-overflow: ellipsis;
}
}
&--title {
display: inline-flex;
align-items: center;
flex-wrap: wrap;
flex-direction: row;
justify-content: flex-start;
width: 100%;
img {
width: 40px !important;
height: 40px !important;
margin: 0 8px 0 0 !important;
border-radius: 6px !important;
cursor: default;
&.pointer {
cursor: pointer;
}
}
}
}
.question-item {
background-color: rgba(65, 84, 253, 0.8);
color: #fff;
.time {
color: #fff;
}
.questionnaire-color {
cursor: pointer;
color: #fff !important;
}
}
.no-open-item {
background-color: rgba(231, 233, 242, 1);
}
}
.question-item {
background-color: rgba(65, 84, 253, 0.8);
color: #fff;
.time {
color: #fff;
}
.questionnaire-color {
cursor: pointer;
color: #fff !important;
}
}
.no-open-item {
background-color: rgba(231, 233, 242, 1);
}
.send-box {
width: 100%;
display: inline-flex;
flex-direction: column;
justify-content: flex-start;
align-items: flex-end;
padding-top: 20px;
}
}
.selected {
background-color: #409eff !important;
color: #ffffff !important;
}
.table-box {
padding-top: 10px;
height: 100%;
}
// .interaction-box {
// padding: 10px;
// .interaction-box_switch {
// margin-bottom: 20px;
// }
// }
::v-deep .el-switch {
margin-right: 10px;
}
.shopping-box {
padding-left: 20px;
.shop-header {
display: flex;
justify-content: space-between;
align-items: center;
p {
color: #666;
margin: 20px 0;
}
}
}
.preferential-box {
height: 100%;
padding: 20px;
.table-box {
p {
color: #666;
}
}
}
.questionnaire-box {
padding: 20px;
}
.questionnaire-dialog {
.questionnaire-Title {
margin-bottom: 20px;
.el-input {
width: 80%;
margin-left: 40px;
}
}
.questionnaire-explain {
margin-bottom: 20px;
.el-input {
width: 80%;
margin-left: 10px;
}
}
.questionnaire-container {
display: flex;
border: 1px solid #cccccc;
padding: 20px;
.questionnaire-preview {
flex: 1;
border: 1px solid #cccccc;
min-height: 100%;
max-height: 500px;
overflow-y: auto;
position: relative;
padding: 20px;
.tips {
text-align: center;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
.quests {
margin-bottom: 20px;
border: 1px solid #cccccc;
padding: 10px;
.quests-title {
margin-bottom: 10px;
.el-input {
width: 90%;
margin-left: 5px;
}
}
.quests-answer {
.el-input {
width: 50%;
margin-left: 5px;
}
.el-textarea {
width: 95%;
}
.answer-container-block {
.answer-container {
margin-bottom: 5px;
}
.picture-opts {
margin: 10px 0;
}
}
}
}
.questBtns {
height: 28px;
line-height: 28px;
display: flex;
padding-right: 20px;
justify-content: flex-end;
.delBtn {
visibility: hidden;
}
}
.quests:hover {
.delBtn {
visibility: inherit;
}
}
.delete-item {
color: red;
}
.disabled ::v-deep .el-upload--picture-card {
display: none;
}
}
.question-types-list {
display: flex;
align-content: center;
width: 330px;
padding: 20px 0 20px 20px;
flex-wrap: wrap;
div {
width: 150px;
height: 50px;
line-height: 50px;
text-align: center;
border: 1px solid #cccccc;
border-radius: 5px;
&:nth-of-type(2n + 1) {
margin: 0 5px 5px 0;
}
}
}
}
}
.fw-600 {
font-weight: 600;
}
.el-icon-chat-dot-round {
cursor: pointer;
margin-left: 10px;
}
.el-icon-delete {
cursor: pointer;
margin: 0 10px;
}
.reply-box {
display: inline-flex;
flex-direction: column;
margin-top: 10px;
padding: 10px;
background-color: #99999930;
border-radius: 5px;
.userName {
margin-bottom: 10px;
}
}
::v-deep .vjs-error-display {
display: none;
}
#play-video {
width: 100%;
}
::v-deep .tcplayer {
width: 100%;
height: 100%;
}
.center-tip {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
}
.video-open-an-account :deep(img),
.video-open-an-account :deep(.ml60) {
margin-left: 0;
width: 100%;
}
.w-full {
width: 100%;
}
.h-full {
height: 600px;
}
.suspend {
width: 100%;
height: 600px;
text-align: center;
color: #ccc;
background-color: #2c3e50;
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
}
.time-button {
display: flex;
flex-direction: row;
align-items: center;
div {
margin-right: 10px;
}
}
.button-box {
margin-top: 10px;
color: "#909399";
font-size: 14px;
}
.button-icon {
padding: 2px;
margin-right: 10px;
}
::v-deep .el-popover__title {
text-align: center;
}
.anchor {
padding: 4px;
background-color: #f56c6c;
color: #fff;
border-radius: 6px;
}
.user {
padding: 4px;
background-color: #67c23a;
color: #fff;
}
.time {
font-size: 14px;
color: #909399;
}
.active-tab {
font-family: PingFangSC-Medium !important;
font-weight: 600 !important;
color: #2e78fa !important;
}
.selected {
background-color: #409eff !important;
color: #ffffff !important;
}
.reply-delete {
color: #606266;
font-size: 14px !important;
width: 100%;
text-align: right;
position: relative;
}
::v-deep .submit-buttom {
display: none !important;
}
::v-deep .button-text {
width: 180px;
cursor: pointer;
color: #1890ff;
word-break: break-all;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 1;
overflow: hidden;
text-overflow: ellipsis;
}
::v-deep .el-dialog__title {
width: 80%;
word-break: break-all;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 1;
overflow: hidden;
text-overflow: ellipsis;
}
.sticky {
position: fixed;
top: 0px;
z-index: 999; /* 可以根据需要设置更高的z-index */
}
.video-box-top {
width: 100%;
}
.name-time {
display: inline-flex;
flex-direction: column;
justify-content: center;
padding-left: 5px;
height: 100%;
:first-child {
font-size: 16px;
}
:last-child {
font-size: 12px;
line-height: 14px;
}
}
.audience-title_user_active {
filter: grayscale(1); /* 置灰效果 */
}
.audience-title_user_active-text {
color: #409eff !important;
}
.txonlineNum {
font-weight: 600;
font-size: 18px;
}
.avatar-uploader .el-upload {
border: 1px dashed #d9d9d9;
border-radius: 6px;
cursor: pointer;
position: relative;
overflow: hidden;
}
.avatar-uploader .el-upload:hover {
border-color: #409eff;
}
.avatar-uploader-icon {
border: 1px dashed #d9d9d9;
border-radius: 6px;
font-size: 28px;
color: #8c939d;
width: 178px;
height: 178px;
line-height: 178px;
text-align: center;
}
// .avatar {
// display: block;
// }
.send-box_btn {
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: stretch;
.avatar {
margin-right: 20px;
border-radius: 6px;
max-width: 178px;
max-height: 178px;
}
.avatar-uploader {
margin-right: 20px;
}
}
.image-container {
position: relative;
}
.content-img {
margin: 20px 0 10px 0;
border-radius: 6px;
width: 200px;
height: 150px;
}
.marketing-code {
display: flex;
padding: 20px;
label {
margin-right: 20px;
}
}
</style>