Ver código fonte

直播代码提交

yuhongqi 1 semana atrás
pai
commit
f5e2d66636

+ 0 - 53
src/api/live/liveAfterSales.js

@@ -90,56 +90,3 @@ export function audit2(data) {
     data: data
   })
 }
-
-// 查询售后记录列表
-export function listStoreAfterSales(query) {
-  return request({
-    url: '/live/liveAfterSales/list',
-    method: 'get',
-    params: query
-  })
-}
-
-// 查询售后记录详细
-export function getStoreAfterSales(id) {
-  return request({
-    url: '/live/liveAfterSales/' + id,
-    method: 'get'
-  })
-}
-
-// 删除售后记录
-export function delStoreAfterSales(id) {
-  return request({
-    url: '/live/liveAfterSales/' + id,
-    method: 'delete'
-  })
-}
-
-
-// 新增售后记录
-export function addStoreAfterSales(data) {
-  return request({
-    url: '/live/liveAfterSales',
-    method: 'post',
-    data: data
-  })
-}
-
-// 修改售后记录
-export function updateStoreAfterSales(data) {
-  return request({
-    url: '/live/liveAfterSales',
-    method: 'put',
-    data: data
-  })
-}
-
-// 导出售后记录
-export function exportStoreAfterSales(query) {
-  return request({
-    url: '/live/liveAfterSales/export',
-    method: 'get',
-    params: query
-  })
-}

+ 1 - 1
src/api/live/liveGoods.js

@@ -63,7 +63,7 @@ export function exportLiveGoods(query) {
 // 直播商品
 export function listStoreProduct(data) {
   return request({
-    url: '/his/storeProduct/list',
+    url: '/live/liveGoods/liveList',
     method: 'get',
     params: data
   })

+ 0 - 123
src/api/live/liveOrder.js

@@ -275,126 +275,3 @@ export function updateErp(data) {
     data: data
   })
 }
-
-export function importDeliveryNoteExpressTemplate() {
-  return request({
-    url: '/live/liveOrder/importDeliveryNoteExpressTemplate',
-    method: 'get'
-  })
-}
-
-export function importTemplate() {
-  return request({
-    url: '/live/liveOrder/importTemplate',
-    method: 'get'
-  })
-}
-
-
-// 导出订单
-export function exportHealthStoreOrderItems(query) {
-  return request({
-    url: '/live/liveOrder/healthExportItems',
-    method: 'get',
-    params: query
-  })
-}
-export function createUserOrder(data) {
-  return request({
-    url: '/live/liveOrder/createUserOrder',
-    method: 'post',
-    data: data
-  })
-}
-// 查询健康商城订单列表
-export function listHealthStoreOrder(data) {
-  return request({
-    url: '/live/liveOrder/healthList',
-    method: 'post',
-    data: data
-  })
-}
-// 查询订单详细
-export function getStoreOrder(id) {
-  return request({
-    url: '/live/liveOrder/' + id,
-    method: 'get'
-  })
-}
-// 删除订单
-export function delStoreOrder(id) {
-  return request({
-    url: '/live/liveOrder/' + id,
-    method: 'delete'
-  })
-}
-// 导出订单
-export function exportHealthStoreOrder(data) {
-  return request({
-    url: '/live/liveOrder/healthExport',
-    method: 'post',
-    data: data
-  })
-}
-// 导出订单
-export function exportHealthStoreOrderDetails(data) {
-  return request({
-    url: '/live/liveOrder/healthExportDetails',
-    method: 'post',
-    data: data
-  })
-}
-// 导出订单
-export function exportHealthStoreOrderItemsDetails(query) {
-  return request({
-    url: '/live/liveOrder/healthExportItemsDetails',
-    method: 'get',
-    params: query
-  })
-}
-export function getErpAccount() {
-  return request({
-    url: '/live/liveOrder/getErpAccount',
-    method: 'get'
-  })
-}
-export function queryErpPhone(query) {
-  return request({
-    url: '/live/liveOrder/queryErpPhone',
-    method: 'get',
-    params: query
-  })
-}
-
-
-export function saveErpPhone(data) {
-  return request({
-    url: '/live/liveOrder/saveErpPhone',
-    method: 'post',
-    data: data
-  })
-}
-
-export function editErpPhone(data) {
-  return request({
-    url: '/live/liveOrder/editErpPhone',
-    method: 'post',
-    data: data
-  })
-}
-
-export function batchCreateErpOrder(data) {
-  return request({
-    url: '/live/liveOrder/batchCreateErpOrder',
-    method: 'post',
-    data: data
-  })
-}
-
-export function batchSetErpOrder(data) {
-  return request({
-    url: '/live/liveOrder/batchSetErpOrder',
-    method: 'post',
-    data: data
-  })
-}

+ 4 - 4
src/api/live/liveQuestionLive.js

@@ -54,18 +54,18 @@ export function getConfig(id) {
 }
 
 // 新增直播观看奖励设置
-export function addConfig(data) {
+export function addConfig(data,liveId) {
   return request({
-    url: '/live/config',
+    url: '/live/config' + '?liveId=' + liveId,
     method: 'post',
     data: data
   })
 }
 
 // 修改直播观看奖励设置
-export function updateConfig(data) {
+export function updateConfig(data,liveId) {
   return request({
-    url: '/live/config',
+    url: '/live/config' + '?liveId=' + liveId,
     method: 'put',
     data: data
   })

+ 8 - 0
src/api/live/liveWatchUser.js

@@ -8,6 +8,14 @@ export function watchUserList(query) {
     params: query
   })
 }
+// 查询直播间用户列表
+export function getLiveUserTotals(query) {
+  return request({
+    url: '/live/liveWatchUser/liveUserTotals',
+    method: 'get',
+    params: query
+  })
+}
 
 // 直播间用户禁言
 export function changeUserStatus(query) {

+ 0 - 1
src/components/LiveVideoUpload/index.vue

@@ -218,7 +218,6 @@ export default {
         } else {
           line_1 = `${process.env.VUE_APP_VIDEO_LINE_1}${data.urlPath}`;
         }
-        line_1 = line_1.replace(/\.mp4$/, '.m3u8');
 
         let urlPathWithoutFirstSlash = data.urlPath.substring(1);
         this.$emit("update:fileKey", urlPathWithoutFirstSlash);

+ 2 - 1
src/components/LiveVideoUpload/single.vue

@@ -48,7 +48,8 @@
       </div>
     </el-form-item>
     <el-form-item label="视频播放">
-      <video v-if="videoUrl" ref="myvideo" :src="videoUrl" id="video" width="100%" height="300px" controls></video>
+      <video v-if="videoUrl.endsWith('mp4')" ref="myvideo" :src="videoUrl" id="video" width="100%" height="300px" controls></video>
+      <video v-if="videoUrl.endsWith('m3u8')" ref="myM3u8Video" :src="videoUrl" id="meu8Video" width="100%" height="300px" controls type="application/x-mpegURL"></video>
       <div v-if="fileName">视频文件名: {{ fileName }}</div>
       <div v-if="fileKey">文件Key: {{ fileKey }}</div>
       <div v-if="fileSize">文件大小(MB): {{ (fileSize / (1024 * 1024)).toFixed(2) }} MB</div>

+ 48 - 0
src/utils/util.js

@@ -0,0 +1,48 @@
+import {isExternal} from '@/utils/validate'
+import store from '@/store'
+
+export function formatDictText(dicts, values) {
+    if (!(Array.isArray(dicts) && dicts.length > 0)) {
+        return values;
+    }
+    if (!values) {
+        return ""
+    }
+    let valueArr = values.split(",")
+    let contentArr = []
+    dicts.forEach(dict => {
+        for (let i = 0; i < valueArr.length; i++) {
+            if (valueArr[i] === dict.value) {
+                contentArr.push(dict.content)
+                break;
+            }
+        }
+    })
+    return contentArr.toString()
+}
+
+export function randomString(len) {
+    len = len || 32
+    const $chars = 'ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz2345678'
+    /** **默认去掉了容易混淆的字符oOLl,9gq,Vv,Uu,I1****/
+    const maxPos = $chars.length
+    let pwd = ''
+    for (let i = 0; i < len; i++) {
+        pwd += $chars.charAt(Math.floor(Math.random() * maxPos))
+    }
+    return pwd
+}
+
+function isEmpty(str) {
+  // 检查是否为 null 或 undefined
+  if (str == null) return true;
+
+  // 检查是否为字符串类型
+  if (typeof str !== 'string') {
+    // 如果不是字符串,尝试转换为字符串再判断
+    return String(str).trim() === '';
+  }
+
+  // 字符串类型直接去除空格后判断
+  return str.trim() === '';
+}

+ 1 - 1
src/views/live/live/index.vue

@@ -432,7 +432,7 @@ import {
 } from "@/api/live/live";
 import Editor from '@/components/Editor/wang';
 import user from '@/store/modules/user';
-import VideoUpload from "@/components/LiveVideoUpload/single.vue";
+import VideoUpload from "@/components/VideoUpload/index.vue";
 
 export default {
   name: "Live",

+ 109 - 118
src/views/live/liveAfteraSales/index.vue

@@ -1,19 +1,9 @@
 <template>
   <div class="app-container">
     <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
-      <el-form-item label="公司名" prop="companyId">
-        <el-select filterable  v-model="queryParams.companyId" placeholder="请选择公司名"  @change="companyChange" clearable size="small">
-          <el-option
-            v-for="item in companys"
-            :key="item.companyId"
-            :label="item.companyName"
-            :value="item.companyId"
-          />
-        </el-select>
-      </el-form-item>
 
-      <el-form-item >
-        <treeselect style="width: 220px" :clearable="false"  v-model="queryParams.deptId"  :options="deptOptions" :show-count="true" placeholder="请选择属部门" />
+      <el-form-item label="所属部门" prop="deptId">
+        <treeselect style="width:220px" v-model="queryParams.deptId" :options="deptOptions" :show-count="true" placeholder="请选择所属部门" />
       </el-form-item>
 
       <el-form-item label="订单单号" prop="orderCode">
@@ -34,9 +24,9 @@
           @keyup.enter.native="handleQuery"
         />
       </el-form-item>
-      <el-form-item label="手机号" prop="consigneePhone">
+      <el-form-item label="会员手机号" prop="userPhone">
         <el-input
-          v-model="queryParams.consigneePhone"
+          v-model="queryParams.userPhone"
           placeholder="请输入手机号"
           clearable
           size="small"
@@ -46,8 +36,8 @@
 
 
 
-      <el-form-item label="退款类型" prop="serviceType">
-        <el-select  v-model="queryParams.serviceType" placeholder="请选择退款类型" clearable size="small" >
+      <el-form-item label="退款类型" prop="refundType">
+        <el-select  v-model="queryParams.refundType" placeholder="请选择退款类型" clearable size="small" >
           <el-option
             v-for="item in serviceTypeOptions"
             :key="item.dictValue"
@@ -58,7 +48,7 @@
       </el-form-item>
 
 
-      <el-form-item label="状态" prop="status">
+      <el-form-item label="审核状态" prop="status">
         <el-select  v-model="queryParams.status" placeholder="请选择状态" clearable size="small" >
           <el-option
             v-for="item in statusOptions"
@@ -89,7 +79,7 @@
         </el-select>
       </el-form-item>
 
-      <el-form-item label="物流状态" prop="deliveryStatus">
+      <el-form-item label="客户退回物流状态" prop="deliveryStatus">
         <el-select   v-model="queryParams.deliveryStatus" placeholder="请选择物流状态" clearable size="small" >
           <el-option
             v-for="item in deliveryStatusOptions"
@@ -100,7 +90,7 @@
         </el-select>
       </el-form-item>
 
-      <el-form-item label="物流单号" prop="deliverySn">
+      <el-form-item label="客户退回物流单号" prop="deliverySn">
         <el-input
           v-model="queryParams.deliverySn"
           placeholder="物流/退回物流单号"
@@ -126,47 +116,49 @@
         <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
       </el-form-item>
     </el-form>
-    <el-row :gutter="10" class="mb8">
 
-      <el-col :span="1.5">
-        <el-button
-          type="warning"
-          icon="el-icon-download"
-          size="mini"
-          @click="handleExport"
-          v-hasPermi="['store:storeAfterSales:export']"
-        >导出</el-button>
-      </el-col>
+    <el-row :gutter="10" class="mb8">
+<!--      <el-col :span="1.5">-->
+<!--        <el-button-->
+<!--          type="warning"-->
+<!--          icon="el-icon-download"-->
+<!--          size="mini"-->
+<!--          @click="handleExport"-->
+<!--          v-hasPermi="['store:storeAfterSales:export']"-->
+<!--        >导出</el-button>-->
+<!--      </el-col>-->
       <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
     </el-row>
-    <el-table  height="500" border v-loading="loading" :data="storeAfterSalesList" @selection-change="handleSelectionChange">
+
+    <el-table border v-loading="loading" :data="liveAfterSalesList" @selection-change="handleSelectionChange">
       <el-table-column type="selection" width="55" align="center" />
       <el-table-column label="ID" align="center" prop="id" />
       <el-table-column label="所属公司" align="center" prop="companyName" />
       <el-table-column label="所属员工" align="center" prop="companyUserNickName" />
+      <el-table-column label="购买用户" align="center" prop="userName" />
       <el-table-column label="订单单号" align="center" prop="orderCode" />
       <el-table-column label="会员手机号" align="center" prop="userPhone" />
       <el-table-column label="退款金额" align="center" prop="refundAmount" />
-      <el-table-column label="退款类型" align="center" prop="serviceType" >
+      <el-table-column label="退款类型" align="center" prop="refundType" >
         <template slot-scope="scope">
-          <div prop="serviceType" v-for="(item, index) in serviceTypeOptions"    v-if="scope.row.serviceType==item.dictValue">{{item.dictLabel}}</div>
+          <dict-tag :options="serviceTypeOptions" :value="scope.row.refundType"/>
         </template>
       </el-table-column>
       <el-table-column label="申请原因" align="center" prop="reasons" />
       <el-table-column label="说明" align="center" prop="explains" />
       <el-table-column label="状态" align="center" prop="status" >
         <template slot-scope="scope">
-          <div prop="status" v-for="(item, index) in statusOptions"    v-if="scope.row.status==item.dictValue">{{item.dictLabel}}</div>
+          <dict-tag :options="statusOptions" :value="scope.row.status"/>
         </template>
       </el-table-column>
       <el-table-column label="售后状态" align="center" prop="salesStatus" >
         <template slot-scope="scope">
-          <div prop="status" v-for="(item, index) in salesStatusOptions"    v-if="scope.row.salesStatus==item.dictValue">{{item.dictLabel}}</div>
+          <dict-tag :options="salesStatusOptions" :value="scope.row.salesStatus"/>
         </template>
       </el-table-column>
       <el-table-column label="订单状态" align="center" prop="orderStatus" >
         <template slot-scope="scope">
-          <div prop="status" v-for="(item, index) in orderStatusOptions"    v-if="scope.row.orderStatus==item.dictValue">{{item.dictLabel}}</div>
+          <dict-tag :options="orderStatusOptions" :value="scope.row.orderStatus"/>
         </template>
       </el-table-column>
       <el-table-column label="物流状态" align="center" prop="deliveryStatus" >
@@ -178,9 +170,12 @@
       <el-table-column label="物流单号" align="center" prop="deliveryId" />
 
       <el-table-column label="客户退回物流单号" align="center" prop="deliverySn" />
+      <el-table-column label="客户退货物流名称" align="center" prop="deliveryName" />
 
       <el-table-column label="提交时间" align="center" prop="createTime" />
 
+
+
       <el-table-column label="操作" align="center" fixed="right" width="100px" class-name="small-padding fixed-width">
         <template slot-scope="scope">
           <el-button
@@ -211,36 +206,25 @@
 </template>
 
 <script>
-import { listStoreAfterSales, getStoreAfterSales, delStoreAfterSales, addStoreAfterSales, updateStoreAfterSales, exportStoreAfterSales } from "@/api/live/liveAfterSales";
-import productAfterSalesOrder from "../components/productAfterSalesOrder";
-import { getCompanyList } from "@/api/company/company";
-import { treeselect } from "@/api/company/companyDept";
+import { listLiveAfterSales, getLiveAfterSales, delLiveAfterSales, addLiveAfterSales, updateLiveAfterSales, exportLiveAfterSales } from "@/api/live/liveAfterSales";
+import {getCompanyList} from "@/api/company/company";
 import Treeselect from "@riophae/vue-treeselect";
+import {treeselect} from "@/api/company/companyDept";
 import "@riophae/vue-treeselect/dist/vue-treeselect.css";
+import productAfterSalesOrder from "../components/productAfterSalesOrder";
 
 export default {
-  watch: {
-    // 监听deptId
-    'deptId': 'currDeptChange'
-  },
-  components: { productAfterSalesOrder,Treeselect },
-  name: "HisStoreAfterSales",
+  name: "LiveAfterSales",
+  components: {productAfterSalesOrder, Treeselect},
   data() {
     return {
-      companyId:undefined,
-      deptId:undefined,
-      orderStatusOptions:[],
-      companys:[],
-      deliveryStatusOptions:[],
-      serviceTypeOptions:[],
-      salesStatusOptions:[],
-      statusOptions:[],
-      deptOptions:[],
+      dateRange:[],
       // 遮罩层
       loading: true,
+      // 导出遮罩层
+      exportLoading: false,
       // 选中数组
       ids: [],
-      dateRange:[],
       // 非单个禁用
       single: true,
       // 非多个禁用
@@ -250,7 +234,7 @@ export default {
       // 总条数
       total: 0,
       // 售后记录表格数据
-      storeAfterSalesList: [],
+      liveAfterSalesList: [],
       // 弹出层标题
       title: "",
       // 是否显示弹出层
@@ -260,37 +244,50 @@ export default {
         pageNum: 1,
         pageSize: 10,
         orderCode: null,
-        refundAmount: null,
-        serviceType: null,
+        refundType: null,             // 退款类型,替代serviceType
         reasons: null,
         explains: null,
         explainImg: null,
-        shipperCode: null,
+        deliveryCode: null,
         deliverySn: null,
         deliveryName: null,
         status: null,
-        salesStatus: null,
+        Status: null,
         isDel: null,
         userId: null,
         consignee: null,
-        phoneNumber: null,
-        address: null
+        phoneNumber: null,            // 之前是consigneePhone,改成phoneNumber
+        address: null,
+        companyId: null,
+        companyUserId: null,
+        deptId: null,
+        companyUserNickName: null,    // 新增员工姓名
+        deliveryStatus: null,         // 物流状态,确认是否接口支持
+        createTimeBegin: null,        // 创建时间起
+        createTimeEnd: null,          // 创
+        userPhone: null
       },
       // 表单参数
       form: {},
       // 表单校验
       rules: {
-      }
+      },
+      orderStatusOptions:[],
+      deliveryStatusOptions:[],
+      serviceTypeOptions:[],
+      salesStatusOptions:[],
+      statusOptions:[],
+      deptOptions:[],
     };
   },
+  watch: {
+    // 根据名称筛选部门树
+    deptName(val) {
+      this.$refs.tree.filter(val);
+    },
+  },
   created() {
-    getCompanyList().then(response => {
-      this.companys = response.data;
-      if(this.companys!=null&&this.companys.length>0){
-        this.companyId=this.companys[0].companyId;
-        this.getTreeselect();
-      }
-    });
+    this.getTreeselect();
     this.getDicts("store_after_sales_sales_status").then((response) => {
       this.salesStatusOptions = response.data;
     });
@@ -303,34 +300,25 @@ export default {
     this.getDicts("store_order_delivery_status").then((response) => {
       this.deliveryStatusOptions = response.data;
     });
-    this.getDicts("store_order_status").then((response) => {
+    this.getDicts("live_order_status").then((response) => {
       this.orderStatusOptions = response.data;
     });
 
     this.getList();
   },
   methods: {
-    handleDetails(row){
-      this.show.open=true;
-      const orderId = row.id ;
-      setTimeout(() => {
-        this.$refs.order.getOrder(orderId);
-      }, 500);
-    },
     handleShow(row){
       this.title="售后订单"
       this.open=true;
       setTimeout(() => {
-        this.$refs.afterSalesOrder.getStoreAfterSales(row.id);
+        this.$refs.afterSalesOrder.getLiveAfterSales(row.id);
       }, 200);
-
     },
-
     /** 查询售后记录列表 */
     getList() {
       this.loading = true;
-      listStoreAfterSales(this.addDateRange(this.queryParams, this.dateRange)).then(response => {
-        this.storeAfterSalesList = response.rows;
+      listLiveAfterSales(this.queryParams).then(response => {
+        this.liveAfterSalesList = response.rows;
         this.total = response.total;
         this.loading = false;
       });
@@ -344,23 +332,29 @@ export default {
     reset() {
       this.form = {
         id: null,
-        orderCode: null,
+        liveId: null,
+        storeId: null,
+        orderId: null,
         refundAmount: null,
-        serviceType: null,
+        refundType: null,
         reasons: null,
         explains: null,
         explainImg: null,
-        shipperCode: null,
+        deliveryCode: null,
         deliverySn: null,
         deliveryName: null,
         status: 0,
         salesStatus: 0,
+        orderStatus: 0,
         createTime: null,
         isDel: null,
         userId: null,
         consignee: null,
         phoneNumber: null,
-        address: null
+        address: null,
+        companyId: null,
+        companyUserId: null,
+        deptId: null
       };
       this.resetForm("form");
     },
@@ -390,7 +384,7 @@ export default {
     handleUpdate(row) {
       this.reset();
       const id = row.id || this.ids
-      getStoreAfterSales(id).then(response => {
+      getLiveAfterSales(id).then(response => {
         this.form = response.data;
         this.open = true;
         this.title = "修改售后记录";
@@ -401,20 +395,16 @@ export default {
       this.$refs["form"].validate(valid => {
         if (valid) {
           if (this.form.id != null) {
-            updateStoreAfterSales(this.form).then(response => {
-              if (response.code === 200) {
-                this.msgSuccess("修改成功");
-                this.open = false;
-                this.getList();
-              }
+            updateLiveAfterSales(this.form).then(response => {
+              this.msgSuccess("修改成功");
+              this.open = false;
+              this.getList();
             });
           } else {
-            addStoreAfterSales(this.form).then(response => {
-              if (response.code === 200) {
-                this.msgSuccess("新增成功");
-                this.open = false;
-                this.getList();
-              }
+            addLiveAfterSales(this.form).then(response => {
+              this.msgSuccess("新增成功");
+              this.open = false;
+              this.getList();
             });
           }
         }
@@ -424,31 +414,32 @@ export default {
     handleDelete(row) {
       const ids = row.id || this.ids;
       this.$confirm('是否确认删除售后记录编号为"' + ids + '"的数据项?', "警告", {
-        confirmButtonText: "确定",
-        cancelButtonText: "取消",
-        type: "warning"
-      }).then(function() {
-        return delStoreAfterSales(ids);
-      }).then(() => {
-        this.getList();
-        this.msgSuccess("删除成功");
-      }).catch(function() {});
+          confirmButtonText: "确定",
+          cancelButtonText: "取消",
+          type: "warning"
+        }).then(function() {
+          return delLiveAfterSales(ids);
+        }).then(() => {
+          this.getList();
+          this.msgSuccess("删除成功");
+        }).catch(() => {});
     },
     /** 导出按钮操作 */
     handleExport() {
       const queryParams = this.queryParams;
       this.$confirm('是否确认导出所有售后记录数据项?', "警告", {
-        confirmButtonText: "确定",
-        cancelButtonText: "取消",
-        type: "warning"
-      }).then(function() {
-        return exportStoreAfterSales(queryParams);
-      }).then(response => {
-        this.download(response.msg);
-      }).catch(function() {});
+          confirmButtonText: "确定",
+          cancelButtonText: "取消",
+          type: "warning"
+        }).then(() => {
+          this.exportLoading = true;
+          return exportLiveAfterSales(queryParams);
+        }).then(response => {
+          this.download(response.msg);
+          this.exportLoading = false;
+        }).catch(() => {});
     },
     getTreeselect() {
-      var that=this;
       var param={companyId:this.companyId}
       treeselect(param).then((response) => {
         this.deptOptions = response.data;

+ 24 - 15
src/views/live/liveConfig/goods.vue

@@ -65,7 +65,7 @@
         <template slot-scope="scope">
           <el-switch
             v-model="scope.row.isShow"
-            @click.native.capture.prevent="handleSwitchClick(scope.row)"
+            @change="handleSwitchClick(scope.row)"
             active-color="#13ce66"
             inactive-color="#ff4949">
           </el-switch>
@@ -162,11 +162,6 @@
             width="55"
           >
           </el-table-column>
-          <el-table-column
-            prop="storeName"
-            label="商铺名称"
-            class-name="clickable-column"
-          ></el-table-column>
           <el-table-column
             prop="productName"
             label="产品"
@@ -267,7 +262,7 @@ export default {
       queryGoodParams: {
         pageNum: 1,
         pageSize: 10,
-        productName: null,
+        keywords: null,
       },
       goodsLiveList: [],
       goodsLiveTotal: 0,
@@ -341,25 +336,39 @@ export default {
   methods: {
     handleSwitchClick(row) {
       // 1. 获取「即将切换到的目标状态」(当前状态取反)
-      const targetStatus = !row.isShow
+      const targetStatus = row.isShow;
+      const originalIsShow = !row.isShow
       const goodsList = [row.goodsId];
+      if (this.socket == null) {
+        this.$message.error("请从直播间开启展示状态!");
+
+        this.$nextTick(() => {
+          row.isShow = originalIsShow;
+        });
+        return;
+      }
       handleIsShowChange({"goodsIds":goodsList,"isShow":targetStatus,"liveId":this.liveId}).then(res=>{
         if(res.code == 200){
           row.isShow = res.isShow
           if (res.msg == "目前仅支持单一物品展示") {
             this.$message.error(res.msg)
           }
-          if (this.socket == null) {
-            this.$message.error("请从直播间开启展示状态!");
-          } else {
+          if (this.socket != null) {
             const msg = {
               cmd: 'goods',
               data: {"liveId":this.liveId,"goodsId":goodsList[0],"status":targetStatus ? 1 : 0}
-            }
-            this.$store.state.liveWs[this.liveId].send(JSON.stringify(msg));
+            };
+            this.socket.send(JSON.stringify(msg));
+          } else {
+            row.isShow = originalIsShow;
           }
         }
-      })
+      }).catch(err => {
+        // 网络错误时,同样恢复原始状态
+        row.isShow = originalIsShow;
+        // this.$set(row, 'isShow', originalIsShow); // 响应式修复
+        console.error("状态切换失败:", err);
+      });
     },
     handleShelf(){
       this.handleShelfOrUn(1)
@@ -444,7 +453,7 @@ export default {
     },
     handleGoodsSearch(){
       this.queryGoodParams.pageNum = 1
-      this.queryGoodParams.productName = this.searchTitle
+      this.queryGoodParams.keywords = this.searchTitle
       this.getStoreProductLists()
     },
     handleGoodsChange(goods) {

+ 3 - 3
src/views/live/liveConfig/liveLotteryConf.vue

@@ -254,11 +254,11 @@
                 >
                 <el-option
                   v-for="product in productOptions"
-                  :key="product.goodsId"
+                  :key="product.productId"
                   :label="product.productName"
-                  :value="product.goodsId"
+                  :value="product.productId"
                 />
-                <span style="float: left">{{ product.goodsId }}</span>
+                <span style="float: left">{{ product.productId }}</span>
                 <span style="margin-left: 30px ;">{{product.productName}}</span>
                 </el-select>
               </el-form-item>

+ 3 - 1
src/views/live/liveConfig/preview.vue

@@ -147,9 +147,11 @@ export default {
   methods: {
     submitForm() {
       this.open = false;
+      var line_1 = this.videoUrl;
+      line_1 = line_1.replace(/\.mp4$/, '.m3u8');
       const doParam = {
         liveId: this.liveId,
-        videoUrl: this.videoUrl,
+        videoUrl: line_1,
         videoType: 3,
         duration: this.form.duration,
         fileSize: this.form.fileSize,

+ 2 - 2
src/views/live/liveConfig/watchReward.vue

@@ -283,13 +283,13 @@ export default {
           if (this.watchRewardForm.id == null) {
             // 调用保存观看奖励接口
             // 实现保存逻辑
-            addConfig(this.watchRewardForm).then(res => {
+            addConfig(this.watchRewardForm,this.liveId).then(res => {
               if (res.code == 200) {
                 this.msgSuccess("修改成功");
               }
             })
           } else {
-            updateConfig(this.watchRewardForm).then(response => {
+            updateConfig(this.watchRewardForm,this.liveId).then(response => {
               this.msgSuccess("修改成功");
             });
           }

+ 398 - 44
src/views/live/liveConsole/index.vue

@@ -1,4 +1,5 @@
 <template>
+  <div>
   <!-- 直播中控台 start -->
   <el-row type="flex" justify="center" class="live-console" :gutter="10" v-loading="loading">>
     <!-- 聊天 start -->
@@ -6,7 +7,7 @@
       <el-tabs class="live-console-tab-left" v-model="tabRight.activeName" @tab-click="handleClick" :stretch="true">
         <el-tab-pane label="讨论" name="talk">
           <el-scrollbar style="height: 500px; width: 100%;" ref="manageRightRef">
-            <el-row v-for="m in msgList" >
+            <el-row v-for="m in msgList" :key="m.msgId">
               <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">
@@ -185,7 +186,7 @@
       <el-tabs class="live-console-tab-right" v-model="tabLeft.activeName" @tab-click="handleClick" :stretch="true">
         <el-tab-pane :label="onlineLabel" name="online">
           <el-scrollbar ref="manageLeftRef_online" style="height: 800px; width: 100%;">
-            <el-row style="margin-top: 10px" type="flex" align="middle" v-for="u in onlineUserList">
+            <el-row style="margin-top: 10px" type="flex" align="middle" v-for="u in onlineDisplayList" :key="u.userId">
               <el-col :span="20">
                 <el-row type="flex" align="middle">
                   <el-col :span="4" style="padding-left: 10px;"><el-avatar :src="u.avatar"></el-avatar></el-col>
@@ -207,7 +208,7 @@
         </el-tab-pane>
         <el-tab-pane :label="offlineLabel" name="offline">
           <el-scrollbar ref="manageLeftRef_offline" style="height: 800px; width: 100%;">
-            <el-row style="margin-top: 10px" type="flex" align="middle" v-for="u in offlineUserList">
+            <el-row style="margin-top: 10px" type="flex" align="middle" v-for="u in offlineDisplayList" :key="u.userId">
               <el-col :span="20">
                 <el-row type="flex" align="middle">
                   <el-col :span="4" style="padding-left: 10px;"><el-avatar :src="u.avatar"></el-avatar></el-col>
@@ -229,7 +230,7 @@
         </el-tab-pane>
         <el-tab-pane :label="silencedUserLabel" name="silenced">
           <el-scrollbar ref="manageLeftRef_silenced" style="height: 800px; width: 100%;">
-            <el-row style="margin-top: 10px" type="flex" align="middle" v-for="u in silencedUserList">
+            <el-row style="margin-top: 10px" type="flex" align="middle" v-for="u in silencedDisplayList" :key="u.userId">
               <el-col :span="20">
                 <el-row type="flex" align="middle">
                   <el-col :span="4" style="padding-left: 10px;"><el-avatar :src="u.avatar"></el-avatar></el-col>
@@ -254,10 +255,11 @@
     <!-- 用户列表 end -->
   </el-row>
   <!-- 直播中控台  end -->
+  </div>
 </template>
 
 <script>
-import { blockUser,changeUserStatus, watchUserList } from '@/api/live/liveWatchUser'
+import { blockUser,changeUserStatus,getLiveUserTotals, watchUserList } from '@/api/live/liveWatchUser'
 import { getLiveVideoByLiveId } from '@/api/live/liveVideo'
 import {getLive, getLivingUrl} from '@/api/live/live'
 import { getLiveOrderTimeGranularity } from '@/api/live/liveOrder'
@@ -321,6 +323,50 @@ export default {
       // ... 其他数据
       chatScrollTop: 0, // 保存聊天滚动位置
       socket: null,
+      userTotal: {
+        online: 0,       // 在线总人数
+        offline: 0,      // 离线总人数
+        silenced: 0      // 禁言总人数
+      },
+      // 各Tab的显示列表(仅存储当前需要展示的数据)
+      onlineDisplayList: [],    // 在线用户显示列表
+      offlineDisplayList: [],   // 离线用户显示列表
+      silencedDisplayList: [],  // 禁言用户显示列表
+      // 各Tab的分页参数
+      pageParams: {
+        online: {
+          currentPage: 1,       // 当前页(下一页加载用)
+          pageSize: 20,       // 当前页(下一页加载用)
+          prevPage: 0,          // 上一页页码(上一页加载用)
+          totalLoaded: 0,       // 已加载总条数
+          total: 0,             // 总数据量
+          hasMore: true,        // 是否有下一页
+          hasPrev: false        // 是否有上一页
+        },
+        offline: {
+          currentPage: 1,
+          pageSize: 20,
+          prevPage: 0,
+          totalLoaded: 0,
+          total: 0,
+          hasMore: true,
+          hasPrev: false
+        },
+        silenced: {
+          currentPage: 1,
+          pageSize: 20,
+          prevPage: 0,
+          totalLoaded: 0,
+          total: 0,
+          hasMore: true,
+          hasPrev: false
+        }
+      },
+      scrLoading: {
+        online: { next: false, prev: false },
+        offline: { next: false, prev: false },
+        silenced: { next: false, prev: false }
+      }
     }
   },
   created() {
@@ -340,32 +386,14 @@ export default {
     companyId() {
       return this.$store.state.user.user.companyId
     },
-    onlineUserList() {
-      return this.userList.filter(u => u.online === 0)
-    },
     onlineLabel() {
-      if (this.onlineUserList.length > 0) {
-        return '在线(' + this.onlineUserList.length + ')'
-      }
-      return '在线'
-    },
-    offlineUserList() {
-      return this.userList.filter(u => u.online === 1)
+      return `在线(${this.userTotal.online})`;
     },
     offlineLabel() {
-      if (this.offlineUserList.length > 0) {
-        return '离线(' + this.offlineUserList.length + ')'
-      }
-      return '离线'
-    },
-    silencedUserList() {
-      return this.userList.filter(u => u.msgStatus === 1)
+      return `离线(${this.userTotal.offline})`;
     },
     silencedUserLabel() {
-      if (this.silencedUserList.length > 0) {
-        return '禁言(' + this.silencedUserList.length + ')'
-      }
-      return '禁言'
+      return `禁言(${this.userTotal.silenced})`;
     }
   },
   mounted() {
@@ -380,6 +408,24 @@ export default {
         this.$refs.manageRightRef.wrap.addEventListener('scroll', this.saveChatScrollPosition);
       }
     });
+    this.initScrollListeners();
+  },
+  beforeDestroy() {
+    this.saveTabScrollPositions()
+    // 移除滚动监听(避免内存泄漏)
+    const scrollRefs = {
+      online: this.$refs.manageLeftRef_online,
+      offline: this.$refs.manageLeftRef_offline,
+      silenced: this.$refs.manageLeftRef_silenced
+    };
+    Object.keys(scrollRefs).forEach(tabName => {
+      const scrollEl = scrollRefs[tabName]?.wrap;
+      if (scrollEl) {
+        scrollEl.removeEventListener('scroll', () =>
+          this.handleTabScroll(tabName, scrollEl)
+        );
+      }
+    })
   },
   // 使用 deactivated 和 activated 钩子替代 beforeDestroy 和 destroyed
   deactivated() {
@@ -539,7 +585,6 @@ export default {
 
       const currentTime = new Date().getTime();
       const elapsedTime = currentTime - this.startTime; // 总流逝时间(毫秒)
-      console.log(this.startTime)
 
       if (elapsedTime < 0) {
         elapsedTimeEl.textContent = '00:00:00';
@@ -661,8 +706,158 @@ export default {
       }
     },
     handleClick(tab) {
-      console.log("click",tab.name)
-      console.log("liveId", this.liveId)
+      const tabName = tab.name;
+      const params = this.pageParams[tabName];
+      const displayList = this[`${tabName}DisplayList`];
+      // 首次切换到该Tab或列表为空时初始化
+      if (displayList.length < 20) {
+        // 重置分页参数
+        params.currentPage = 1;
+        params.pageSize = 20;
+        params.prevPage = 0;
+        params.totalLoaded = 0;
+        params.hasMore = true;
+        params.hasPrev = false;
+        // 加载第一页
+        this.loadNextPage(tabName);
+      } else {
+        // 非首次切换,恢复滚动位置
+        this.$nextTick(() => {
+          const scrollEl = this.getScrollElement(tabName);
+          if (scrollEl) {
+            scrollEl.scrollTop = this.tabScrollPositions[tabName] || 0;
+          }
+        });
+      }
+    },
+    saveTabScrollPositions() {
+      this.tabScrollPositions = {
+        online: this.getScrollElement('online')?.scrollTop || 0,
+        offline: this.getScrollElement('offline')?.scrollTop || 0,
+        silenced: this.getScrollElement('silenced')?.scrollTop || 0
+      };
+    },
+    // 加载指定Tab的用户列表(核心加载逻辑)
+    loadNextPage(tabName) {
+      const params = this.pageParams[tabName];
+      const displayList = this[`${tabName}DisplayList`];
+      console.log(`加载 ${tabName} 用户列表`)
+      console.log(!params.hasMore || this.scrLoading[tabName].next)
+      console.log(params.currentPage)
+      // 若没有更多数据或正在加载,直接返回
+      if (!params.hasMore || this.scrLoading[tabName].next) {
+        return;
+      }
+
+      this.scrLoading[tabName].next = true;
+      const queryParams = {
+        liveId: this.liveId,
+        pageNum: params.currentPage,
+        pageSize: 20,
+        online: tabName === 'online' ? 0 : 1,
+        msgStatus: tabName === 'silenced' ? 1 : 0
+      };
+      // 调用接口加载对应状态的分页数据(需后端支持按状态筛选)
+      watchUserList(queryParams).then(response => {
+        this.scrLoading[tabName].next = false;
+        if (response.code !== 200) return;
+
+        const { rows, total } = response;
+        params.total = total; // 记录总数据量
+        // 过滤重复数据(基于userId)
+        const newRows = rows.filter(row =>
+          !displayList.some(u => u.userId === row.userId)
+        );
+        displayList.push(...newRows)
+        // 添加新数据并限制最大长度(避免内存占用过大)
+        if (displayList.length >= 40) { // 最大保留100条
+          this[`${tabName}DisplayList`] = displayList.slice(-40);
+          // 记录滚动位置(用于加载后校准)
+          const scrollEl = this.getScrollElement(tabName);
+          // 校准滚动位置(保持视觉连续性)
+          this.$nextTick(() => {
+            if (scrollEl) {
+              scrollEl.scrollTop = scrollEl.scrollHeight * 0.5;
+            }
+          });
+        }
+        // 更新分页状态
+        params.hasMore = params.currentPage * params.pageSize < total;
+        params.currentPage += 1;
+        params.hasPrev = params.currentPage > 2; // 当前页>2时一定有上一页
+        params.prevPage = params.currentPage - 2;
+      }).catch(() => {
+        this.scrLoading[tabName].next = false;
+      });
+    },
+    // 新增:加载上一页(向上滚动时)
+    loadPrevPage(tabName) {
+      const params = this.pageParams[tabName];
+      const displayList = this[`${tabName}DisplayList`];
+      // 边界校验:无上一页/正在加载/当前页<=1
+      console.log(`加载 ${tabName} 上一页`);
+      console.log(!params.hasPrev || this.scrLoading[tabName].prev || params.currentPage <= 1)
+      if (!params.hasPrev || this.scrLoading[tabName].prev || params.currentPage <= 1) {
+        return;
+      }
+      this.scrLoading[tabName].prev = true;
+      const targetPage = params.prevPage > 0 ? params.prevPage : params.currentPage - 2;
+      const queryParams = {
+        liveId: this.liveId,
+        pageNum: targetPage,
+        pageSize: 20,
+        online: tabName === 'online' ? 0 : 1,
+        msgStatus: tabName === 'silenced' ? 1 : 0
+      };
+      watchUserList(queryParams).then(response => {
+        this.scrLoading[tabName].prev = false;
+        if (response.code !== 200) return;
+
+        const { rows } = response;
+        if (rows.length === 0) {
+          params.hasPrev = false;
+          return;
+        }
+
+        // 记录滚动位置(用于加载后校准)
+        const scrollEl = this.getScrollElement(tabName);
+        const scrollTop = scrollEl?.scrollTop || 0;
+        const itemHeight = 80; // 预估行高(根据实际样式调整)
+        const newItemsHeight = rows.length * itemHeight;
+
+        // 过滤重复数据并添加到列表头部
+        const newRows = rows.filter(row => !displayList.some(u => u.userId === row.userId));
+        this[`${tabName}DisplayList`] = [...newRows, ...displayList];
+        params.totalLoaded += newRows.length;
+
+        // 限制最大长度
+        if (this[`${tabName}DisplayList`].length > 40) {
+          this[`${tabName}DisplayList`] = this[`${tabName}DisplayList`].slice(0, 40);
+        }
+
+        // 更新分页状态
+        params.prevPage = targetPage - 1;
+        params.hasPrev = targetPage > 1; // 上一页页码>1时还有更多上一页
+        params.currentPage = params.currentPage - 1;
+        if(params.currentPage * 20 < params.total) params.hasMore = true;
+        // 校准滚动位置(保持视觉连续性)
+        this.$nextTick(() => {
+          if (scrollEl) {
+            scrollEl.scrollTop = scrollEl.scrollHeight * 0.5;
+          }
+        });
+      }).catch(() => {
+        this.scrLoading[tabName].prev = false;
+      });
+    },
+    // 辅助:获取Tab对应的滚动容器
+    getScrollElement(tabName) {
+      const scrollRefs = {
+        online: this.$refs.manageLeftRef_online,
+        offline: this.$refs.manageLeftRef_offline,
+        silenced: this.$refs.manageLeftRef_silenced
+      };
+      return scrollRefs[tabName]?.wrap;
     },
     getLiveVideo() {
       getLiveVideoByLiveId(this.liveId).then(res => {
@@ -671,22 +866,62 @@ export default {
     },
     getList() {
       this.resetParams()
-      this.loadUserList()
+      // this.loadUserList()
+      this.loadUserTotals(); // 先加载总人数
+      // this.handleClick('online')
+      this.handleClick({name:'online'})
       this.loadMsgList()
     },
+    loadUserTotals() {
+      if (!this.liveId) return;
+      // 假设后端提供一个接口返回总人数(如果没有,可通过首次加载全量数据后统计)
+      getLiveUserTotals({ liveId: this.liveId }).then(res => {
+        if (res.code === 200) {
+          this.userTotal = res.data; // { online, offline, silenced }
+        }
+      });
+    },
     resetParams() {
-      this.userList= []
-      this.userParams = {
-        pageNum: 1,
-        pageSize: 10,
-        liveId: this.liveId
-      }
-      this.msgList = []
+      // 重置各Tab的显示列表和分页参数
+      this.onlineDisplayList = [];
+      this.offlineDisplayList = [];
+      this.silencedDisplayList = [];
+      this.pageParams = {
+        online: {
+          currentPage: 1,       // 当前页(下一页加载用)
+          pageSize: 20,       // 当前页(下一页加载用)
+          prevPage: 0,          // 上一页页码(上一页加载用)
+          totalLoaded: 0,       // 已加载总条数
+          total: 0,             // 总数据量
+          hasMore: true,        // 是否有下一页
+          hasPrev: false        // 是否有上一页
+        },
+        offline: {
+          currentPage: 1,
+          pageSize: 20,
+          prevPage: 0,
+          totalLoaded: 0,
+          total: 0,
+          hasMore: true,
+          hasPrev: false
+        },
+        silenced: {
+          currentPage: 1,
+          pageSize: 20,
+          prevPage: 0,
+          totalLoaded: 0,
+          total: 0,
+          hasMore: true,
+          hasPrev: false
+        }
+      };
+      // 消息参数保留
+      this.msgList = [];
       this.msgParams = {
         pageNum: 1,
         pageSize: 10,
         liveId: this.liveId
-      }
+      };
     },
     loadUserList() {
       if(this.liveId == null)  return
@@ -804,6 +1039,8 @@ export default {
               user.msgStatus = u.msgStatus;
             }
           });
+          // 4. 关键:重新筛选所有Tab的显示列表,确保状态同步
+          this.refreshUserDisplayLists(u);
 
           let msg = u.msgStatus === 0 ? "已解禁" : "已禁言"
           this.msgSuccess(msg);
@@ -812,6 +1049,49 @@ export default {
         this.msgError("操作失败");
       })
     },
+    // 新增:重新筛选所有Tab的显示列表
+    refreshUserDisplayLists(user) {
+      const { userId, msgStatus: newStatus, online } = user;
+      const oldStatus = newStatus === 1 ? 0 : 1; // 操作前的状态(反向)
+
+      // 1. 禁言操作(newStatus=1):从原在线/离线列表移除,加入禁言列表
+      if (newStatus === 1) {
+        // 从在线/离线列表中移除该用户(根据当前在线状态)
+        if (online === 0) {
+          this.onlineDisplayList = this.onlineDisplayList.filter(u => u.userId !== userId);
+          this.userTotal.online = Math.max(0, this.userTotal.online - 1);
+        } else {
+          this.offlineDisplayList = this.offlineDisplayList.filter(u => u.userId !== userId);
+          this.userTotal.offline = Math.max(0, this.userTotal.offline - 1);
+        }
+        this.userTotal.silenced = this.userTotal.silenced + 1;
+        // 添加到禁言列表(去重+限制长度)
+        const silencedList = this.silencedDisplayList.filter(u => u.userId !== userId);
+        silencedList.push(user);
+        this.silencedDisplayList = silencedList.slice(-40);
+      }
+
+      // 2. 解禁操作(newStatus=0):从禁言列表移除,回到原在线/离线列表
+      else {
+        // 从禁言列表移除
+        this.silencedDisplayList = this.silencedDisplayList.filter(u => u.userId !== userId);
+        this.userTotal.silenced = Math.max(0, this.userTotal.silenced - 1);
+        // 回到对应的在线/离线列表(根据当前在线状态)
+        if (online === 0) {
+          // 加入在线列表(去重+限制长度)
+          const onlineList = this.onlineDisplayList.filter(u => u.userId !== userId);
+          onlineList.push(user);
+          this.onlineDisplayList = onlineList.slice(-40);
+          this.userTotal.online = this.userTotal.online + 1;
+        } else {
+          // 加入离线列表(去重+限制长度)
+          const offlineList = this.offlineDisplayList.filter(u => u.userId !== userId);
+          offlineList.push(user);
+          this.offlineDisplayList = offlineList.slice(-40);
+          this.userTotal.offline = this.userTotal.offline + 1;
+        }
+      }
+    },
     connectWebSocket() {
       this.$store.dispatch('initLiveWs', {
         liveWsUrl: this.liveWsUrl,
@@ -835,6 +1115,9 @@ export default {
             message.msgStatus = 0
           }
           delete message.params
+          if(this.msgList.length > 50){
+            this.msgList.shift()
+          }
           this.msgList.push(message)
           // 移动到底部
           this.$nextTick(() => {
@@ -844,12 +1127,48 @@ export default {
           })
         }
         else if (cmd === 'entry' || cmd === 'out') {
-          this.loadUserList()
-          let user = data
-          if(this.userList.length > 0){
-            this.userList = this.userList.filter(u => u.userId !== user.userId)
+          const user = data;
+          const online = cmd === 'entry' ? 0 : 1; // 0=在线,1=离线
+          const info = {
+            online:online,
+            msgStatus: user.msgStatus || 0,
+            nickName: user.nickName || '',
+            userType: user.userType || 0,
+            userId: user.userId || '',
+          };
+
+          // 1. 更新总人数(在线/离线互转)
+          if (cmd === 'entry') {
+            this.userTotal.online += 1;
+            this.userTotal.offline = Math.max(0, this.userTotal.offline - 1); // 确保不小于0
+          } else {
+            this.userTotal.offline += 1;
+            this.userTotal.online = Math.max(0, this.userTotal.online - 1); // 确保不小于0
+          }
+          // 2. 强制更新相关列表(无论当前激活哪个Tab)
+          if (cmd === 'entry') {
+            // 用户进入:从离线列表删除,添加到在线列表
+            this.offlineDisplayList = this.offlineDisplayList.filter(u => u.userId !== user.userId);
+            const newOnlineList = this.onlineDisplayList.filter(u => u.userId !== user.userId);
+            newOnlineList.push(info);
+            this.onlineDisplayList = newOnlineList.slice(-40); // 限制最大50条
+          } else {
+            // 用户离开:从在线列表删除,添加到离线列表
+            this.onlineDisplayList = this.onlineDisplayList.filter(u => u.userId !== user.userId);
+            const newOfflineList = this.offlineDisplayList.filter(u => u.userId !== user.userId);
+            newOfflineList.push(info);
+            this.offlineDisplayList = newOfflineList.slice(-40); // 限制最大50条
+          }
+          // 3. 处理禁言列表(如果用户是禁言状态,无论进出都要同步)
+          if (info.msgStatus === 1) {
+            // 禁言用户:从禁言列表删除旧数据,添加新数据
+            const newSilencedList = this.silencedDisplayList.filter(u => u.userId !== user.userId);
+            newSilencedList.push(info);
+            this.silencedDisplayList = newSilencedList.slice(-40);
+          } else {
+            // 非禁言用户:从禁言列表删除(如果存在)
+            this.silencedDisplayList = this.silencedDisplayList.filter(u => u.userId !== user.userId);
           }
-          this.userList.push(user)
         } else if (cmd === 'live_start') {
 
         } else if (cmd === 'live_end') {
@@ -876,7 +1195,42 @@ export default {
       this.socket.send(JSON.stringify(msg))
 
       this.newMsg = '';
-    }
+    },
+    // 初始化滚动监听(在mounted中调用)
+    initScrollListeners() {
+      // 为每个Tab的滚动容器添加监听
+      this.$nextTick(() => {
+        const scrollRefs = {
+          online: this.$refs.manageLeftRef_online,
+          offline: this.$refs.manageLeftRef_offline,
+          silenced: this.$refs.manageLeftRef_silenced
+        };
+
+        Object.keys(scrollRefs).forEach(tabName => {
+          const scrollEl = scrollRefs[tabName]?.wrap;
+          if (scrollEl) {
+            scrollEl.addEventListener('scroll', () =>
+              this.handleTabScroll(tabName, scrollEl)
+            );
+          }
+        });
+      });
+    },
+    handleTabScroll(tabName, scrollEl) {
+      const { scrollTop, scrollHeight, clientHeight } = scrollEl;
+      const bottomThreshold = 50; // 距离底部100px触发下一页
+      const topThreshold = 50;    // 距离顶部100px触发上一页
+
+      // 加载下一页(滚动到底部附近)
+      if (scrollHeight - scrollTop - clientHeight < bottomThreshold) {
+        this.loadNextPage(tabName);
+      }
+
+      // 加载上一页(滚动到顶部附近)
+      if (scrollTop < topThreshold) {
+        this.loadPrevPage(tabName);
+      }
+    },
   },
   destroyed() {
     this.hls?.destroy();

Diferenças do arquivo suprimidas por serem muito extensas
+ 144 - 1003
src/views/live/liveOrder/index.vue


+ 4 - 1
src/views/live/liveVideo/index.vue

@@ -38,7 +38,6 @@
             controlsList="nodownload"
             class="video-player"
             @contextmenu.prevent
-            type="application/x-mpegURL"
           ></video>
         </template>
       </el-table-column>
@@ -166,6 +165,9 @@ export default {
       this.loading = true;
       listLiveVideo(this.queryParams).then(response => {
         this.liveVideoList = response.rows;
+        this.liveVideoList.forEach(item => {
+          item.videoUrl = item.videoUrl.replace(".m3u8", ".mp4");
+        });
         this.total = response.total;
         this.loading = false;
       });
@@ -241,6 +243,7 @@ export default {
             this.msgError("请上传视频");
             return;
           }
+          this.form.videoUrl.replace(/\.mp4$/, '.m3u8');
           addLiveVideo(this.form).then(response => {
             this.msgSuccess("新增成功");
             this.open = false;

Alguns arquivos não foram mostrados porque muitos arquivos mudaram nesse diff