Bladeren bron

直播优化代码

yuhongqi 1 dag geleden
bovenliggende
commit
a6075ddc80
3 gewijzigde bestanden met toevoegingen van 146 en 21 verwijderingen
  1. 10 10
      src/views/life/video/index.vue
  2. 70 3
      src/views/live/live/index.vue
  3. 66 8
      src/views/live/liveConsole/LiveConsole.vue

+ 10 - 10
src/views/life/video/index.vue

@@ -106,8 +106,8 @@
           <el-input v-model="form.fileName" placeholder="上传文件名或标识"/>
         </el-form-item>
         <el-form-item label="封面" prop="coverUrl">
-          <image-upload v-model="form.coverUrl" :limit="1" :file-size="2" aspect-ratio="16:9"/>
-          <div style="color:#909399;font-size:12px;line-height:1.4;margin-top:4px">比例 16:9,大小不超过 2MB</div>
+          <image-upload v-model="form.coverUrl" :limit="1" :file-size="2"/>
+          <div style="color:#909399;font-size:12px;line-height:1.4;margin-top:4px">大小不超过 2MB</div>
         </el-form-item>
         <video-upload
           :type="3"
@@ -144,12 +144,12 @@
             <el-radio :label="0">下架</el-radio>
           </el-radio-group>
         </el-form-item>
-        <el-form-item label="今日主推" prop="isHot">
-          <el-radio-group v-model="form.isHot">
-            <el-radio :label="1">是</el-radio>
-            <el-radio :label="0">否</el-radio>
-          </el-radio-group>
-        </el-form-item>
+<!--        <el-form-item label="今日主推" prop="isHot">-->
+<!--          <el-radio-group v-model="form.isHot">-->
+<!--            <el-radio :label="1">是</el-radio>-->
+<!--            <el-radio :label="0">否</el-radio>-->
+<!--          </el-radio-group>-->
+<!--        </el-form-item>-->
         <el-form-item label="是否关联商品" prop="isProduct">
           <el-radio-group v-model="form.isProduct" @change="onIsProductChange">
             <el-radio :label="1">是</el-radio>
@@ -208,8 +208,8 @@
               </el-select>
             </el-form-item>
             <el-form-item label="封面" required>
-              <image-upload v-model="row.coverUrl" :limit="1" :file-size="2" aspect-ratio="16:9"/>
-              <div style="color:#909399;font-size:12px">比例 16:9,大小不超过 2MB</div>
+              <image-upload v-model="row.coverUrl" :limit="1" :file-size="2"/>
+              <div style="color:#909399;font-size:12px">大小不超过 2MB</div>
             </el-form-item>
             <video-upload
               :type="3"

+ 70 - 3
src/views/live/live/index.vue

@@ -417,7 +417,7 @@
                     </el-radio-group>
                 </el-form-item>
                 <el-form-item label="直播马甲号" prop="vestId">
-                    <el-select v-model="form.vestId" filterable clearable placeholder="请选择马甲号" style="width:100%" :disabled="isViewOnly">
+                    <el-select v-model="form.vestId" filterable clearable placeholder="请选择马甲号" style="width:100%" :disabled="isViewOnly" @change="onVestChange">
                         <el-option
                             v-for="item in lifeVestOptions"
                             :key="item.id"
@@ -427,6 +427,23 @@
                     </el-select>
                     <div style="color:#909399;font-size:12px;line-height:1.4;margin-top:4px">公域直播必须绑定马甲号;私域可不选</div>
                 </el-form-item>
+                <el-form-item label="马甲号视频" prop="lifeVideoId" v-if="form.vestId && form.liveType == 2 && !isViewOnly">
+                    <el-select
+                        v-model="form.lifeVideoId"
+                        filterable
+                        clearable
+                        placeholder="可选:选用该马甲的短视频(自动带入时长)"
+                        style="width:100%"
+                        @change="onLifeVideoSelect"
+                    >
+                        <el-option
+                            v-for="item in lifeVideoOptions"
+                            :key="item.videoId"
+                            :label="(item.title || item.fileName || ('视频'+item.videoId)) + (item.duration ? (' / ' + secondsToTime(item.duration)) : '')"
+                            :value="item.videoId"
+                        />
+                    </el-select>
+                </el-form-item>
             </el-form>
             <div slot="footer" class="dialog-footer">
                 <el-button type="primary" @click="submitForm" v-show="!isViewOnly">确 定</el-button>
@@ -498,6 +515,7 @@ import VideoUpload from "@/components/LiveVideoUpload/single.vue";
 import {listTag} from "@/api/qw/tag";
 import {getAppAllLint, getAppAllList} from "../../../api/live/live";
 import { listLifeVest } from "@/api/life/vest";
+import { listLifeVideo } from "@/api/life/video";
 
 export default {
     name: "Live",
@@ -619,7 +637,8 @@ export default {
             defaultImg: "https://via.placeholder.com/400x400?text=二维码加载失败",  // 占位图
             // 导出评论状态:key为liveId,value为是否正在导出
             exportingComments: {},
-            lifeVestOptions: []
+            lifeVestOptions: [],
+            lifeVideoOptions: []
         };
     },
     created() {
@@ -900,9 +919,11 @@ export default {
                 uploadType:1,
                 transcodeFileKey:null,
                 visibleScope: 0,
-                vestId: null
+                vestId: null,
+                lifeVideoId: null
             };
             this.videoUrl = "";
+            this.lifeVideoOptions = [];
             this.resetForm("form");
         },
         /** 搜索按钮操作 */
@@ -950,6 +971,9 @@ export default {
                 if(this.form.duration){
                     this.form.durationTime = this.secondsToTime(this.form.duration)
                 }
+                if (this.form.vestId) {
+                    this.loadLifeVideoOptions(this.form.vestId);
+                }
                 this.liveDesc = this.form.liveDesc
 
                 setTimeout(() => {
@@ -974,6 +998,9 @@ export default {
                 if(this.form.duration){
                     this.form.durationTime = this.secondsToTime(this.form.duration)
                 }
+                if (this.form.vestId) {
+                    this.loadLifeVideoOptions(this.form.vestId);
+                }
                 this.liveDesc = this.form.liveDesc
 
                 setTimeout(() => {
@@ -1021,6 +1048,46 @@ export default {
                 this.lifeVestOptions = [];
             });
         },
+        onVestChange(vestId) {
+            this.form.lifeVideoId = null;
+            this.lifeVideoOptions = [];
+            if (vestId) {
+                this.loadLifeVideoOptions(vestId);
+            }
+        },
+        loadLifeVideoOptions(vestId) {
+            if (!vestId) {
+                this.lifeVideoOptions = [];
+                return;
+            }
+            listLifeVideo({ pageNum: 1, pageSize: 200, vestId, isShow: 1 }).then(res => {
+                this.lifeVideoOptions = res.rows || [];
+            }).catch(() => {
+                this.lifeVideoOptions = [];
+            });
+        },
+        onLifeVideoSelect(lifeVideoId) {
+            if (!lifeVideoId) {
+                return;
+            }
+            const video = (this.lifeVideoOptions || []).find(v => v.videoId === lifeVideoId);
+            if (!video) {
+                return;
+            }
+            this.form.videoUrl = video.videoUrl;
+            this.videoUrl = video.videoUrl || "";
+            this.form.fileName = video.fileName;
+            if (video.duration) {
+                this.form.duration = Number(video.duration);
+                this.form.durationTime = this.secondsToTime(this.form.duration);
+                if (typeof this.timeChange === 'function') {
+                    this.timeChange();
+                }
+            }
+            if (!this.form.liveImgUrl && video.coverUrl) {
+                this.form.liveImgUrl = video.coverUrl;
+            }
+        },
         /** 删除按钮操作 */
         handleDelete(row) {
             const liveIds = row.liveId || this.ids;

+ 66 - 8
src/views/live/liveConsole/LiveConsole.vue

@@ -113,7 +113,7 @@
         </div>
         <div class="message-container" @click="handleMessageBoxClick">
           <el-scrollbar class="custom-scrollbar" style="height: 500px; width: 100%;" ref="manageRightRef">
-            <el-row v-for="m in msgList" :key="m.msgId">
+            <el-row v-for="(m, msgIndex) in msgList" :key="msgRowKey(m, msgIndex)">
             <el-row v-if="m.userId !== userId" style="margin-top: 5px" type="flex" align="top" >
               <el-col :span="3" style="margin-left: 10px"><el-avatar :src="m.avatar"/></el-col>
               <el-col :span="15">
@@ -127,7 +127,7 @@
                   <el-col>
                     <a style="cursor: pointer;color: #ff0000;padding: 8px 8px 0 0;font-size: 12px;" @click.stop="changeUserState(m)">{{ m.msgStatus === 1 ? '解禁' : '禁言' }}</a>
                     <a style="cursor: pointer;color: #ff0000;padding: 8px 8px 0 0;font-size: 12px;" @click.stop="blockUser(m)">拉黑</a>
-                    <a v-if="m.singleVisible === 1" style="cursor: pointer;color: #ff0000;padding: 8px 8px 0 0;font-size: 12px;" @click.stop="singleVisible(m)">解除用户自见</a>
+                    <a v-if="isUserSingleVisible(m)" style="cursor: pointer;color: #ff0000;padding: 8px 8px 0 0;font-size: 12px;" @click.stop="singleVisible(m)">解除用户自见</a>
                     <a v-else style="cursor: pointer;color: #ff0000;padding: 8px 8px 0 0;font-size: 12px;" @click.stop="singleVisible(m)">用户自见</a>
                     <a style="cursor: pointer;color: #ff0000;padding: 8px 8px 0 0;font-size: 12px;" @click.stop="replyUser(m)">回复</a>
                     <a style="cursor: pointer;color: #ff0000;padding: 8px 8px 0 0;font-size: 12px;" @click.stop="deleteMsg(m)">删除</a>
@@ -293,6 +293,8 @@ export default {
         videoUrl: '',
       },
       msgList:[],
+      // 按用户维度维护自见状态,避免消息行共享/重渲染导致按钮状态串台
+      userSingleVisibleMap: {},
       currentTab: 'al',
       searchKeyword: '',
       globalVisible: false,
@@ -517,17 +519,46 @@ export default {
       // loadMsgList 中会自动滚动到底部,因为 isAutoScrollEnabled 已经是 true
       this.loadMsgList();
     },
+    msgRowKey(m, index) {
+      if (m && m.msgId != null && m.msgId !== '') {
+        return 'msg-' + m.msgId
+      }
+      return 'msg-tmp-' + (m && m.userId != null ? m.userId : 'x') + '-' + (m && m.createTime ? m.createTime : '') + '-' + index
+    },
+    isUserSingleVisible(m) {
+      if (!m || m.userId == null) {
+        return false
+      }
+      const uid = String(m.userId)
+      if (Object.prototype.hasOwnProperty.call(this.userSingleVisibleMap, uid)) {
+        return Number(this.userSingleVisibleMap[uid]) === 1
+      }
+      return Number(m.singleVisible) === 1
+    },
+    applyUserSingleVisible(userId, status) {
+      if (userId == null) {
+        return
+      }
+      const uid = String(userId)
+      const next = Number(status) === 1 ? 1 : 0
+      this.$set(this.userSingleVisibleMap, uid, next)
+      this.msgList.forEach(m1 => {
+        if (String(m1.userId) === uid) {
+          this.$set(m1, 'singleVisible', next)
+        }
+      })
+    },
     singleVisible(m){
-      // 过滤当前所有消息 找到userId的相同的消息 更改他们的自可见状态
-      m.singleVisible= m.singleVisible === 1 ? 0 : 1
-      this.msgList.forEach(m1 => {m1.singleVisible = m1.userId === m.userId ? m.singleVisible : !m.singleVisible})
+      // 仅更新同一用户的消息自见状态,不影响其他用户
+      const status = this.isUserSingleVisible(m) ? 0 : 1
+      this.applyUserSingleVisible(m.userId, status)
       // 消息自可见
       let msg = {
         liveId: this.liveId,
         userId: m.userId,
         userType: 0,
         cmd: 'singleVisible',
-        status:m.singleVisible
+        status
       }
       this.socket.send(JSON.stringify(msg))
     },
@@ -710,14 +741,15 @@ export default {
         return;
       }
 
+      // userId 使用专用 fs_user(400001),与后端入库账号一致
       let msg = {
         msg: this.newMsg,
         liveId: this.liveId,
-        userId: this.userId,
+        userId: 400001,
         userType: 1,
         cmd: 'sendMsg',
         avatar: this.$store.state.user.user.avatar,
-        nickName: this.$store.state.user.user.nickName,
+        nickName: this.$store.state.user.user.nickName || '直播管理员',
       }
 
       this.socket.send(JSON.stringify(msg))
@@ -748,6 +780,14 @@ export default {
             message.msgStatus = 0
           }
           delete message.params
+          if (message.userId != null) {
+            const uid = String(message.userId)
+            if (Object.prototype.hasOwnProperty.call(this.userSingleVisibleMap, uid)) {
+              message.singleVisible = Number(this.userSingleVisibleMap[uid]) === 1 ? 1 : 0
+            } else {
+              this.$set(this.userSingleVisibleMap, uid, Number(message.singleVisible) === 1 ? 1 : 0)
+            }
+          }
           if(this.msgList.length > 50){
             this.msgList.shift()
           }
@@ -807,6 +847,8 @@ export default {
             this.silencedDisplayList = this.silencedDisplayList.filter(u => u.userId !== user.userId);
           }
 
+        } else if (cmd === 'singleVisible') {
+          this.applyUserSingleVisible(data.userId, data.status)
         } else if (cmd === 'live_start') {
 
         } else if (cmd === 'live_end') {
@@ -1202,6 +1244,14 @@ export default {
                 row.msgStatus = 0
               }
               this.msgList.push(row)
+              if (row.userId != null) {
+                const uid = String(row.userId)
+                if (!Object.prototype.hasOwnProperty.call(this.userSingleVisibleMap, uid)) {
+                  this.$set(this.userSingleVisibleMap, uid, Number(row.singleVisible) === 1 ? 1 : 0)
+                } else {
+                  this.$set(row, 'singleVisible', Number(this.userSingleVisibleMap[uid]) === 1 ? 1 : 0)
+                }
+              }
             }
           })
 
@@ -1210,6 +1260,14 @@ export default {
           this.alDisplayList.forEach(u => {
             this.msgList.filter(m => m.userId === u.userId).forEach(m => m.msgStatus = u.msgStatus)
           })
+          Object.keys(this.userSingleVisibleMap).forEach(uid => {
+            const status = Number(this.userSingleVisibleMap[uid]) === 1 ? 1 : 0
+            this.msgList.forEach(m => {
+              if (String(m.userId) === uid) {
+                this.$set(m, 'singleVisible', status)
+              }
+            })
+          })
 
           // 所有消息加载完成后,根据自动滚动状态决定是否滚动
           this.$nextTick(() => {