Browse Source

直播间优化版本

yuhongqi 4 days ago
parent
commit
7b69559f61
58 changed files with 2263 additions and 1137 deletions
  1. 9 0
      src/api/store/storeProduct.js
  2. 1 0
      src/assets/styles/index.scss
  3. 432 0
      src/assets/styles/live.scss
  4. 76 20
      src/store/index.js
  5. 55 10
      src/utils/liveWS.js
  6. 3 1
      src/views/live/components/addBatchPublish.vue
  7. 5 5
      src/views/live/components/productAfterSalesOrder.vue
  8. 13 13
      src/views/live/components/productOrder.vue
  9. 11 3
      src/views/live/gift/index.vue
  10. 326 282
      src/views/live/live/index.vue
  11. 14 5
      src/views/live/liveAfterSalesItem/index.vue
  12. 14 5
      src/views/live/liveAfterSalesLogs/index.vue
  13. 12 4
      src/views/live/liveAfteraSales/index.vue
  14. 11 3
      src/views/live/liveAnchor/index.vue
  15. 12 2
      src/views/live/liveConfig/answer.vue
  16. 12 5
      src/views/live/liveConfig/barrage.vue
  17. 18 38
      src/views/live/liveConfig/goods.vue
  18. 10 3
      src/views/live/liveConfig/idCard.vue
  19. 98 88
      src/views/live/liveConfig/index.vue
  20. 16 8
      src/views/live/liveConfig/liveCoupon.vue
  21. 18 9
      src/views/live/liveConfig/liveLotteryConf.vue
  22. 18 9
      src/views/live/liveConfig/liveRedConf.vue
  23. 9 2
      src/views/live/liveConfig/liveReplay.vue
  24. 69 71
      src/views/live/liveConfig/preview.vue
  25. 17 7
      src/views/live/liveConfig/task.vue
  26. 10 1
      src/views/live/liveConfig/watchReward.vue
  27. 379 243
      src/views/live/liveConsole/index.vue
  28. 16 7
      src/views/live/liveCoupon/index.vue
  29. 14 5
      src/views/live/liveCouponIssue/index.vue
  30. 14 5
      src/views/live/liveCouponIssueUser/index.vue
  31. 12 4
      src/views/live/liveCouponUser/index.vue
  32. 46 75
      src/views/live/liveData/index.vue
  33. 14 5
      src/views/live/liveEventConf/index.vue
  34. 12 4
      src/views/live/liveFeedbackLog/index.vue
  35. 14 5
      src/views/live/liveFeedbackType/index.vue
  36. 14 6
      src/views/live/liveGoods/index.vue
  37. 14 5
      src/views/live/liveLotteryConf/index.vue
  38. 14 5
      src/views/live/liveLotteryProductConf/index.vue
  39. 14 5
      src/views/live/liveLotteryRecord/index.vue
  40. 14 5
      src/views/live/liveLotteryRegistration/index.vue
  41. 16 7
      src/views/live/liveMiniLives/index.vue
  42. 14 5
      src/views/live/liveMsg/index.vue
  43. 11 3
      src/views/live/liveOrder/index.vue
  44. 133 82
      src/views/live/liveOrder/liveOrderDetails.vue
  45. 14 5
      src/views/live/liveOrderLogs/index.vue
  46. 14 5
      src/views/live/liveOrderStatus/index.vue
  47. 14 5
      src/views/live/liveOrderitems/index.vue
  48. 14 6
      src/views/live/liveProfit/index.vue
  49. 14 5
      src/views/live/liveQuestion/index.vue
  50. 13 5
      src/views/live/liveRewardRecord/index.vue
  51. 12 4
      src/views/live/liveTrafficLog/index.vue
  52. 14 5
      src/views/live/liveUserLotteryRecord/index.vue
  53. 14 5
      src/views/live/liveUserRedRecord/index.vue
  54. 54 14
      src/views/live/liveVideo/index.vue
  55. 8 2
      src/views/live/liveWatchUser/index.vue
  56. 11 3
      src/views/live/words/index.vue
  57. 12 2
      src/views/store/components/productAttrValueSelect.vue
  58. 1 1
      src/views/store/storeProductPackage/index.vue

+ 9 - 0
src/api/store/storeProduct.js

@@ -79,6 +79,15 @@ export function getStoreProductAttrValueList(query) {
   })
 }
 
+/** 查询未下架商品对应的规格列表(套餐添加商品) */
+export function getOnSaleProductAttrValueList(query) {
+  return request({
+    url: '/store/storeProduct/getOnSaleProductAttrValueList',
+    method: 'get',
+    params: query
+  })
+}
+
 
 export function getStoreTuiProductAttrValueList(query) {
   return request({

+ 1 - 0
src/assets/styles/index.scss

@@ -4,6 +4,7 @@
 @import './element-ui.scss';
 @import './sidebar.scss';
 @import './btn.scss';
+@import './live.scss';
 
 body {
   height: 100%;

+ 432 - 0
src/assets/styles/live.scss

@@ -0,0 +1,432 @@
+/* 直播模块统一视觉 —— 简洁、轻量、少彩色标签 */
+
+$live-border: #e8eaed;
+$live-text: #1f2329;
+$live-sub: #8a919f;
+$live-accent: #1a6d5b;
+$live-soft: #f4f6f8;
+$live-radius: 10px;
+
+.live-page {
+  --live-border: #{$live-border};
+  --live-text: #{$live-text};
+  --live-sub: #{$live-sub};
+  --live-accent: #{$live-accent};
+  --live-soft: #{$live-soft};
+}
+
+.live-page__header {
+  display: flex;
+  align-items: flex-end;
+  justify-content: space-between;
+  gap: 16px;
+  margin-bottom: 16px;
+}
+
+.live-page__title {
+  margin: 0;
+  font-size: 22px;
+  font-weight: 600;
+  color: $live-text;
+  letter-spacing: 0.02em;
+}
+
+.live-page__desc {
+  margin: 4px 0 0;
+  font-size: 13px;
+  color: $live-sub;
+}
+
+.live-page__actions {
+  display: flex;
+  align-items: center;
+  gap: 8px;
+}
+
+.live-panel {
+  background: #fff;
+  border: 1px solid $live-border;
+  border-radius: $live-radius;
+  padding: 14px 16px 4px;
+  margin-bottom: 14px;
+}
+
+.live-panel--table {
+  padding: 12px 12px 4px;
+}
+
+.live-search-form .el-form-item {
+  margin-bottom: 12px;
+}
+
+/* 未手工包 panel 的列表页:直接美化第一层表单/工具条/表格 */
+.live-page > .el-form,
+.live-page > form.el-form {
+  background: #fff;
+  border: 1px solid $live-border;
+  border-radius: $live-radius;
+  padding: 14px 16px 4px;
+  margin-bottom: 14px;
+}
+
+.live-page > .el-row.mb8,
+.live-page > .mb8,
+.live-page > .selection-toolbar {
+  display: flex;
+  flex-wrap: wrap;
+  align-items: center;
+  gap: 8px;
+  background: #fff;
+  border: 1px solid $live-border;
+  border-radius: $live-radius;
+  padding: 10px 12px;
+  margin-bottom: 12px !important;
+}
+
+.live-page > .el-row.mb8 .el-col {
+  width: auto !important;
+  float: none !important;
+}
+
+.live-page .el-table {
+  border-radius: 8px;
+  overflow: hidden;
+}
+
+.live-page .el-table th,
+.live-table th {
+  background: $live-soft !important;
+  color: #5c6470;
+  font-weight: 500;
+}
+
+.live-page .el-table td,
+.live-table td {
+  padding: 10px 0;
+  color: $live-text;
+}
+
+.live-page .el-table--border,
+.live-page .el-table--border th,
+.live-page .el-table--border td {
+  border-color: #eef0f3;
+}
+
+.live-page .el-tag {
+  border-radius: 4px;
+  border: none;
+  font-weight: 500;
+}
+
+.live-cell {
+  display: flex;
+  align-items: center;
+  gap: 12px;
+  min-width: 0;
+}
+
+.live-cell__cover {
+  width: 56px;
+  height: 74px;
+  border-radius: 8px;
+  flex-shrink: 0;
+  background: $live-soft;
+  overflow: hidden;
+  object-fit: cover;
+}
+
+.live-cell__cover--square {
+  width: 48px;
+  height: 48px;
+}
+
+.live-cell__meta {
+  min-width: 0;
+  text-align: left;
+}
+
+.live-cell__name {
+  font-size: 14px;
+  font-weight: 600;
+  color: $live-text;
+  line-height: 1.35;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+}
+
+.live-cell__sub {
+  margin-top: 4px;
+  font-size: 12px;
+  color: $live-sub;
+}
+
+.live-status {
+  display: inline-flex;
+  align-items: center;
+  gap: 6px;
+  font-size: 13px;
+  color: $live-text;
+}
+
+.live-status__dot {
+  width: 7px;
+  height: 7px;
+  border-radius: 50%;
+  background: #c0c4cc;
+}
+
+.live-status.is-1 .live-status__dot { background: #e6a23c; }
+.live-status.is-2 .live-status__dot { background: #f56c6c; box-shadow: 0 0 0 3px rgba(245, 108, 108, 0.18); }
+.live-status.is-3 .live-status__dot { background: #909399; }
+.live-status.is-4 .live-status__dot { background: #409eff; }
+
+.live-muted { color: $live-sub; font-size: 13px; }
+.live-ok { color: $live-accent; font-size: 13px; }
+.live-warn { color: #c45656; font-size: 13px; }
+
+/* 配置壳 */
+.live-config-container {
+  padding: 12px 16px 20px;
+}
+
+.live-config-container .live-config-header {
+  border: 1px solid $live-border !important;
+  border-radius: $live-radius !important;
+  margin-bottom: 12px;
+  box-shadow: none !important;
+}
+
+.live-config-container .live-config-header .el-card__body {
+  padding: 0;
+}
+
+.live-config-container .detail-header {
+  padding: 16px 18px !important;
+  border-bottom: none !important;
+  align-items: center;
+}
+
+.live-config-container .header-left-area {
+  flex: 0 0 64px !important;
+}
+
+.live-config-container .header-left-area img {
+  width: 64px !important;
+  height: 84px !important;
+  border-radius: 8px !important;
+  object-fit: cover;
+}
+
+.live-config-container .live-info-title .title-text {
+  font-size: 18px;
+  font-weight: 600;
+  color: $live-text;
+}
+
+.live-config-container .live-info-desc,
+.live-config-container .info-state-text {
+  color: $live-sub !important;
+  font-size: 13px;
+}
+
+.live-config-container .live-config-left {
+  border: 1px solid $live-border !important;
+  border-radius: $live-radius !important;
+  box-shadow: none !important;
+}
+
+.live-config-container .live-config-left .el-card__body {
+  padding: 0;
+}
+
+.live-config-container .left-menu {
+  width: 180px !important;
+  background: #fafbfc;
+  border-right: 1px solid $live-border !important;
+  padding: 10px 8px;
+  min-height: 560px;
+}
+
+.live-config-container .left-menu .el-menu {
+  border-right: none !important;
+  background: transparent;
+}
+
+.live-config-container .el-menu-item {
+  height: 40px;
+  line-height: 40px;
+  margin: 2px 0 !important;
+  border-radius: 8px !important;
+  color: #5c6470;
+}
+
+.live-config-container .el-menu-item.is-active,
+.live-config-container .el-menu-item:hover {
+  background-color: rgba(26, 109, 91, 0.1) !important;
+  color: $live-accent !important;
+}
+
+.live-config-container .right-info {
+  padding: 16px 18px !important;
+  background: #fff;
+}
+
+/* 中控台 */
+.live-console {
+  padding: 10px 12px 16px !important;
+  background: $live-soft;
+  min-height: calc(100vh - 84px);
+  align-items: stretch !important;
+}
+
+.live-console .live-console-col {
+  background: #fff;
+  border: 1px solid $live-border;
+  border-radius: $live-radius;
+  overflow: hidden;
+}
+
+.live-console .msg-box,
+.live-console .msg-box-right {
+  border-radius: 10px !important;
+  line-height: 1.5;
+}
+
+.live-console .shuru {
+  padding: 12px 14px !important;
+  min-height: auto !important;
+  border-top: 1px solid $live-border !important;
+}
+
+.live-console .shuru .el-textarea__inner {
+  min-height: 72px !important;
+  border-radius: 8px;
+}
+
+.live-console .top-box img {
+  width: 36px !important;
+  height: 36px !important;
+}
+
+.live-console .top-box span {
+  margin-top: 6px !important;
+  color: #5c6470;
+}
+
+.live-console .row-box {
+  padding: 8px 4px;
+  border-radius: 8px;
+}
+
+.live-console .row-box:hover {
+  background: $live-soft;
+}
+
+/* 数据看板指标卡 */
+.live-page .stat-card,
+.live-data-page .el-card {
+  border: 1px solid $live-border !important;
+  border-radius: $live-radius !important;
+  box-shadow: none !important;
+}
+
+.live-stats-grid {
+  display: grid;
+  grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
+  gap: 10px;
+  margin-bottom: 14px;
+}
+
+.live-stats-grid .statistics-item {
+  text-align: center;
+  padding: 10px 8px;
+  background: $live-soft;
+  border-radius: 8px;
+  border: 1px solid $live-border;
+}
+
+.live-stats-grid .statistics-title {
+  font-size: 12px;
+  color: $live-sub;
+  margin-bottom: 6px;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  gap: 2px;
+  line-height: 1.3;
+}
+
+.live-stats-grid .statistics-value {
+  font-size: 18px;
+  font-weight: 600;
+  color: $live-text;
+}
+
+.live-video-thumb {
+  position: relative;
+  width: 48px;
+  height: 48px;
+  margin: 0 auto;
+  cursor: pointer;
+}
+
+.live-video-thumb__empty {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  color: $live-sub;
+  background: $live-soft;
+}
+
+.live-video-thumb__play {
+  position: absolute;
+  right: 2px;
+  bottom: 2px;
+  font-size: 14px;
+  color: #fff;
+  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.45);
+}
+
+.live-video-dialog-player {
+  width: 100%;
+  max-height: 70vh;
+  background: #000;
+}
+
+/* 商品/媒体列表内过大图片收敛 */
+.live-page img[style*="100px"],
+.live-page .el-image[style*="100px"] {
+  max-width: 56px !important;
+  max-height: 56px !important;
+  border-radius: 8px;
+  object-fit: cover;
+}
+
+/* 订单详情通用 */
+.live-order-detail {
+  background: $live-soft;
+  min-height: 100%;
+  padding: 16px;
+}
+
+.live-order-detail .operate-container {
+  display: flex;
+  flex-wrap: wrap;
+  align-items: center;
+  gap: 8px;
+  margin-bottom: 12px;
+}
+
+.live-order-detail .desct {
+  font-size: 15px;
+  font-weight: 600;
+  color: $live-text;
+  margin: 8px 0 12px;
+}
+
+.live-order-detail .el-card {
+  border: 1px solid $live-border !important;
+  border-radius: $live-radius !important;
+  box-shadow: none !important;
+}

+ 76 - 20
src/store/index.js

@@ -10,6 +10,15 @@ import {LiveWS} from "@/utils/liveWS";
 
 Vue.use(Vuex)
 
+function normalizeLiveId(liveId) {
+  return liveId == null ? '' : String(liveId)
+}
+
+function getLiveWs(state, liveId) {
+  const key = normalizeLiveId(liveId)
+  return state.liveWs[key] || state.liveWs[liveId] || null
+}
+
 const store = new Vuex.Store({
   modules: {
     app,
@@ -20,40 +29,87 @@ const store = new Vuex.Store({
   },
   getters,
   state: {
-    liveWs: {}
+    liveWs: {},
+    // { [liveId]: { console: true, config: true } }
+    liveWsOwners: {}
   },
   mutations: {
     // 更新为支持多个 WebSocket 连接
     setLiveWs(state, { ws, liveId }) {
-      // 使用 liveId 作为键来存储不同的 WebSocket 连接
-      Vue.set(state.liveWs, liveId, ws);
+      const key = normalizeLiveId(liveId)
+      Vue.set(state.liveWs, key, ws);
     },
     // 添加移除 WebSocket 连接的 mutation
     removeLiveWs(state, liveId) {
-      Vue.delete(state.liveWs, liveId);
+      const key = normalizeLiveId(liveId)
+      Vue.delete(state.liveWs, key);
+    },
+    addLiveWsOwner(state, { liveId, owner }) {
+      const key = normalizeLiveId(liveId)
+      if (!state.liveWsOwners[key]) {
+        Vue.set(state.liveWsOwners, key, {})
+      }
+      Vue.set(state.liveWsOwners[key], owner, true)
+    },
+    removeLiveWsOwner(state, { liveId, owner }) {
+      const key = normalizeLiveId(liveId)
+      if (!state.liveWsOwners[key]) return
+      Vue.delete(state.liveWsOwners[key], owner)
+      if (Object.keys(state.liveWsOwners[key]).length === 0) {
+        Vue.delete(state.liveWsOwners, key)
+      }
     }
   },
   actions: {
-    // 修改 action 以正确传递参数
-    initLiveWs({ commit, state }, { liveWsUrl, liveId, userId }) {
-      // 如果已经存在对应 liveId 的连接,先关闭它
-      if (state.liveWs[liveId]) {
-        state.liveWs[liveId].close();
+    /**
+     * 获取/复用直播间 WebSocket。同一 liveId 多页面共享一条连接。
+     * owner: 'console' | 'config' | 其它业务标识
+     */
+    acquireLiveWs({ commit, state }, { liveWsUrl, liveId, userId, owner }) {
+      const key = normalizeLiveId(liveId)
+      if (!key) {
+        console.error('[liveWs] acquireLiveWs: liveId is empty')
+        return null
+      }
+      let ws = getLiveWs(state, key)
+      const needCreate = !ws || ws.isManualClose
+      if (needCreate) {
+        ws = new LiveWS(liveWsUrl, key, userId)
+        commit('setLiveWs', { ws, liveId: key })
+      }
+      if (owner) {
+        commit('addLiveWsOwner', { liveId: key, owner })
+      }
+      return ws
+    },
+    /**
+     * 释放占用。当该 liveId 已无任何 owner 时真正关闭连接。
+     */
+    releaseLiveWs({ commit, state, dispatch }, { liveId, owner }) {
+      const key = normalizeLiveId(liveId)
+      if (!key) return
+      if (owner) {
+        commit('removeLiveWsOwner', { liveId: key, owner })
+      }
+      const owners = state.liveWsOwners[key]
+      if (!owners || Object.keys(owners).length === 0) {
+        dispatch('closeLiveWs', key)
       }
-
-      // 创建新的 WebSocket 连接
-      const ws = new LiveWS(liveWsUrl, liveId, userId);
-
-      // 提交到 mutation
-      commit('setLiveWs', { ws, liveId });
-
-      return ws;
+    },
+    // 兼容旧调用:不再强行重连;已有连接则复用
+    initLiveWs({ dispatch }, { liveWsUrl, liveId, userId, owner = 'legacy' }) {
+      return dispatch('acquireLiveWs', { liveWsUrl, liveId, userId, owner })
     },
     // 添加关闭特定 WebSocket 连接的 action
     closeLiveWs({ commit, state }, liveId) {
-      if (state.liveWs[liveId]) {
-        state.liveWs[liveId].close();
-        commit('removeLiveWs', liveId);
+      const key = normalizeLiveId(liveId)
+      const ws = getLiveWs(state, key)
+      if (ws) {
+        ws.close();
+        commit('removeLiveWs', key);
+      }
+      if (state.liveWsOwners[key]) {
+        Vue.delete(state.liveWsOwners, key)
       }
     }
   }

+ 55 - 10
src/utils/liveWS.js

@@ -1,10 +1,17 @@
 import CryptoJS from 'crypto-js'
 
+/** 从 Vuex 取直播间共享 WebSocket */
+export function getLiveSocket(store, liveId) {
+  if (!store || liveId == null) return null
+  const key = String(liveId)
+  return store.state.liveWs[key] || store.state.liveWs[liveId] || null
+}
+
 export class LiveWS {
   /**
    * @param {string} url - WebSocket 服务器地址
-   * @param {number} liveId - 直播间ID
-   * @param {number} userId - 用户ID
+   * @param {number|string} liveId - 直播间ID
+   * @param {number|string} userId - 用户ID
    * @param {number} checkInterval - 检查连接状态的时间间隔,单位毫秒
    * @param {number} reconnectDelay - 连接断开后重连的延迟,单位毫秒
    */
@@ -23,6 +30,7 @@ export class LiveWS {
     this.reconnectTimer = null;
     this.heartbeatTimer = null;
     this.isManualClose = false;
+    this._messageHandlers = new Set();
     this.connect();
     this.startHeartbeat();
   }
@@ -36,8 +44,7 @@ export class LiveWS {
     this.ws = new WebSocket(this.url);
 
     // 绑定事件
-    this.ws.onopen = (event) => {
-      // 连接成功后,清除重连计时器
+    this.ws.onopen = () => {
       if (this.reconnectTimer) {
         clearTimeout(this.reconnectTimer);
         this.reconnectTimer = null;
@@ -45,13 +52,12 @@ export class LiveWS {
     };
 
     this.ws.onmessage = (event) => {
-      this.onmessage(event);
+      this._dispatchMessage(event);
     };
 
-    this.ws.onerror = (error) => {
-    };
+    this.ws.onerror = () => {};
 
-    this.ws.onclose = (event) => {
+    this.ws.onclose = () => {
       // 如果不是主动关闭,则重连
       if (!this.isManualClose) {
         setTimeout(() => this.reconnect(), this.reconnectDelay);
@@ -59,8 +65,38 @@ export class LiveWS {
     };
   }
 
+  /** @deprecated 兼容旧写法,请优先使用 addMessageListener */
   onmessage(event) {}
 
+  _dispatchMessage(event) {
+    if (this._messageHandlers.size > 0) {
+      this._messageHandlers.forEach((handler) => {
+        try {
+          handler(event);
+        } catch (e) {
+          console.error('[LiveWS] message handler error', e);
+        }
+      });
+      return;
+    }
+    // 兼容:外部直接赋值 socket.onmessage = fn
+    if (typeof this.onmessage === 'function') {
+      this.onmessage(event);
+    }
+  }
+
+  addMessageListener(handler) {
+    if (typeof handler === 'function') {
+      this._messageHandlers.add(handler);
+    }
+  }
+
+  removeMessageListener(handler) {
+    if (handler) {
+      this._messageHandlers.delete(handler);
+    }
+  }
+
   reconnect() {
     this.connect();
   }
@@ -89,23 +125,32 @@ export class LiveWS {
   // 主动关闭 WebSocket 连接,并清除定时任务
   close() {
     this.isManualClose = true;
+    this._messageHandlers.clear();
     if (this.heartbeatTimer) {
       clearInterval(this.heartbeatTimer);
+      this.heartbeatTimer = null;
     }
     if (this.reconnectTimer) {
       clearTimeout(this.reconnectTimer);
+      this.reconnectTimer = null;
     }
     if (this.ws) {
       this.ws.close();
+      this.ws = null;
     }
   }
 
+  get readyState() {
+    return this.ws ? this.ws.readyState : WebSocket.CLOSED;
+  }
+
   // 发送消息方法
   send(message) {
     if (this.ws && this.ws.readyState === WebSocket.OPEN) {
       this.ws.send(message);
-    } else {
-      console.error("WebSocket is not open. Message not sent.");
+      return true;
     }
+    console.error("WebSocket is not open. Message not sent.");
+    return false;
   }
 }

+ 3 - 1
src/views/live/components/addBatchPublish.vue

@@ -1,5 +1,6 @@
 <template>
-    <div>
+    <div class="live-page">
+      <div class="live-panel">
             <el-form ref="form" :model="form" :rules="rules" label-width="120px">
                 <el-form-item label="优惠券数量" >
                     {{ids.length}}个
@@ -24,6 +25,7 @@
                 <el-input-number v-model="form.totalCount" :min="0" placeholder="请输入优惠券领取数量" />
                 </el-form-item>
             </el-form>
+             </div>
              <div   class="footer">
             <el-button type="primary" @click="submitForm">确 定</el-button>
             <el-button @click="cancel">取 消</el-button>

+ 5 - 5
src/views/live/components/productAfterSalesOrder.vue

@@ -1,5 +1,5 @@
 <template>
-  <div class="order-content">
+  <div class="order-content live-page">
     <div class="order-status" v-if="afterSales!=null" >
       <el-steps :active="afterSales.status==4?afterSales.status+1:afterSales.status" align-center>
         <el-step title="待审核(24小时自动审核)"></el-step>
@@ -13,7 +13,7 @@
       <el-card shadow="never"  style="margin-top: 15px">
         <div class="operate-container">
           <span style="margin-left: 20px" class="color-danger">售后状态:
-            <el-tag prop="status" v-for="(item, index) in salesStatusOptions"    v-if="afterSales.salesStatus==item.dictValue">{{item.dictLabel}}</el-tag>
+            <span class="live-muted" prop="status" v-for="(item, index) in salesStatusOptions"    v-if="afterSales.salesStatus==item.dictValue">{{item.dictLabel}}</span>
           </span>
           <div class="operate-button-container"  >
             <el-button size="mini"  v-hasPermi="['store:storeAfterSales:edit']" v-show="afterSales.salesStatus==0&&afterSales.status===1"  @click="addDelivery">编辑物流</el-button>
@@ -45,7 +45,7 @@
           </el-descriptions-item>
           <el-descriptions-item label="申请类型"  >
                 <span v-if="afterSales!=null">
-                  <el-tag prop="serviceType" v-for="(item, index) in serviceTypeOptions"    v-if="afterSales.serviceType==item.dictValue">{{item.dictLabel}}</el-tag>
+                  <span class="live-muted" prop="serviceType" v-for="(item, index) in serviceTypeOptions"    v-if="afterSales.serviceType==item.dictValue">{{item.dictLabel}}</span>
                 </span>
           </el-descriptions-item>
           <el-descriptions-item label="申请原因"  >
@@ -60,12 +60,12 @@
           </el-descriptions-item>
           <el-descriptions-item label="状态"  >
                 <span v-if="afterSales!=null">
-                  <el-tag   v-for="(item, index) in statusOptions"    v-if="afterSales.status==item.dictValue" >{{item.dictLabel}}</el-tag>
+                  <span class="live-muted"   v-for="(item, index) in statusOptions"    v-if="afterSales.status==item.dictValue" >{{item.dictLabel}}</span>
                 </span>
           </el-descriptions-item>
           <el-descriptions-item label="售后状态"  >
                 <span v-if="afterSales!=null">
-                  <el-tag  v-for="(item, index) in salesStatusOptions"    v-if="afterSales.salesStatus==item.dictValue" >{{item.dictLabel}}</el-tag>
+                  <span class="live-muted"  v-for="(item, index) in salesStatusOptions"    v-if="afterSales.salesStatus==item.dictValue" >{{item.dictLabel}}</span>
                 </span>
           </el-descriptions-item>
         </el-descriptions>

+ 13 - 13
src/views/live/components/productOrder.vue

@@ -1,5 +1,5 @@
 <template>
-  <div class="order-content">
+  <div class="order-content live-page">
     <div class="order-status" v-if="order!=null" >
       <el-steps  :active="order.status==3?order.status+1:order.status" align-center>
         <el-step title="待支付"></el-step>
@@ -12,7 +12,7 @@
       <el-card shadow="never" style="margin-top: 15px">
         <div class="operate-container"  v-if="order!=null">
         <span style="margin-left: 20px" class="color-danger">订单状态:
-           <el-tag prop="status" v-for="(item, index) in statusOptions"   v-if="order.status==item.dictValue">{{item.dictLabel}}</el-tag>
+           <span class="live-muted" prop="status" v-for="(item, index) in statusOptions"   v-if="order.status==item.dictValue">{{item.dictLabel}}</span>
         </span>
           <div class="operate-button-container" >
             <el-button size="mini" @click="editOrder()"  v-hasPermi="['store:storeOrder:edit']" >修改订单</el-button>
@@ -66,8 +66,8 @@
                 <span v-if="order!=null">
                   {{order.orderCode}}
                 </span>
-            <el-tag  v-for="(item, index) in createTypeOptions"    v-if="order!=null&&order.orderCreateType==item.dictValue">{{item.dictLabel}}
-            </el-tag>
+            <span class="live-muted"  v-for="(item, index) in createTypeOptions"    v-if="order!=null&&order.orderCreateType==item.dictValue">{{item.dictLabel}}
+            </span>
           </el-descriptions-item>
           <el-descriptions-item label="会员"  >
                 <span v-if="user!=null">
@@ -75,11 +75,11 @@
                 </span>
           </el-descriptions-item>
           <el-descriptions-item label="支付方式"  >
-            <el-tag prop="payType" v-for="(item, index) in payTypeOptions"    v-if="order!=null&&order.payType==item.dictValue">{{item.dictLabel}}
-            </el-tag>
+            <span class="live-muted" prop="payType" v-for="(item, index) in payTypeOptions"    v-if="order!=null&&order.payType==item.dictValue">{{item.dictLabel}}
+            </span>
           </el-descriptions-item>
           <el-descriptions-item label="订单类型"  >
-            <el-tag prop="orderType" v-for="(item, index) in orderTypeOptions"    v-if="order!=null&&order.orderType==item.dictValue">{{item.dictLabel}}</el-tag>
+            <span class="live-muted" prop="orderType" v-for="(item, index) in orderTypeOptions"    v-if="order!=null&&order.orderType==item.dictValue">{{item.dictLabel}}</span>
           </el-descriptions-item>
           <!--            <el-descriptions-item label="物流公司编号"  >-->
           <!--                <span v-if="order!=null">-->
@@ -98,12 +98,12 @@
           <!--            </el-descriptions-item>-->
           <!--            <el-descriptions-item label="物流状态"  >-->
           <!--                <span v-if="order!=null">-->
-          <!--                  <el-tag prop="deliveryId" v-for="(item, index) in deliveryStatusOptions"    v-if="order!=null&&order.deliveryStatus==item.dictValue">{{item.dictLabel}}</el-tag>-->
+          <!--                  <span class="live-muted" prop="deliveryId" v-for="(item, index) in deliveryStatusOptions"    v-if="order!=null&&order.deliveryStatus==item.dictValue">{{item.dictLabel}}</span>-->
           <!--                </span>-->
           <!--            </el-descriptions-item>-->
           <!--            <el-descriptions-item label="物流跟踪状态"  >-->
           <!--                <span v-if="order!=null">-->
-          <!--                  <el-tag prop="deliveryId" v-for="(item, index) in deliveryTypeOptions"    v-if="order!=null&&order.deliveryType==item.dictValue">{{item.dictLabel}}</el-tag>-->
+          <!--                  <span class="live-muted" prop="deliveryId" v-for="(item, index) in deliveryTypeOptions"    v-if="order!=null&&order.deliveryType==item.dictValue">{{item.dictLabel}}</span>-->
           <!--                </span>-->
           <!--            </el-descriptions-item>-->
           <el-descriptions-item label="物流结算费用"  >
@@ -150,8 +150,8 @@
             </el-popover>
           </el-descriptions-item>
           <el-descriptions-item label="档期归属"  >
-            <el-tag prop="scheduleId" v-for="(item, index) in scheduleOptions"    v-if="order!=null&&order.scheduleId==item.id">{{item.name}}
-            </el-tag>
+            <span class="live-muted" prop="scheduleId" v-for="(item, index) in scheduleOptions"    v-if="order!=null&&order.scheduleId==item.id">{{item.name}}
+            </span>
           </el-descriptions-item>
           <el-descriptions-item label="用户备注"  >
                 <span v-if="order!=null">
@@ -187,9 +187,9 @@
           <el-table-column label="物流状态" width="300" align="center">
             <template slot-scope="scope">
               <span>
-              <el-tag v-for="(item, index) in deliveryStatusOptions"    v-if="scope.row!=null&&scope.row.status==item.dictValue">
+              <span class="live-muted" v-for="(item, index) in deliveryStatusOptions"    v-if="scope.row!=null&&scope.row.status==item.dictValue">
               {{item.dictLabel}}
-              </el-tag>
+              </span>
               </span>
             </template>
           </el-table-column>

+ 11 - 3
src/views/live/gift/index.vue

@@ -1,6 +1,11 @@
 <template>
-  <div class="app-container">
-    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
+  <div class="app-container live-page">
+    <div class="live-page__header">
+      <h2 class="live-page__title">直播礼物</h2>
+    </div>
+
+    <div class="live-panel" v-show="showSearch">
+      <el-form class="live-search-form" :model="queryParams" ref="queryForm" :inline="true" label-width="68px">
       <el-form-item label="礼物名称" prop="giftName">
         <el-input
           v-model="queryParams.giftName"
@@ -25,8 +30,10 @@
         <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
       </el-form-item>
     </el-form>
+    </div>
 
-    <el-row :gutter="10" class="mb8">
+    <div class="live-panel live-panel--table">
+<el-row :gutter="10" class="mb8">
       <el-col :span="1.5">
         <el-button
           type="primary"
@@ -108,6 +115,7 @@
       :limit.sync="queryParams.pageSize"
       @pagination="getList"
     />
+    </div>
 
     <!-- 添加或修改直播间礼物配置对话框 -->
     <el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>

+ 326 - 282
src/views/live/live/index.vue

@@ -1,287 +1,181 @@
 <template>
-  <div class="app-container">
-    <el-row :gutter="10" class="mb8">
-      <el-col :span="1.5">
+  <div class="app-container live-page">
+    <div class="live-page__header">
+      <div class="live-page__title-wrap">
+        <h2 class="live-page__title">直播间</h2>
+        <p class="live-page__desc">管理直播、录播与回放场次</p>
+      </div>
+      <div class="live-page__actions">
         <el-button
           type="primary"
-          plain
           icon="el-icon-plus"
-          size="mini"
+          size="small"
           @click="handleAdd"
           v-hasPermi="['live:live:add']"
-        >新增</el-button>
-      </el-col>
-      <!--      <el-col :span="1.5">-->
-      <!--        <el-button-->
-      <!--          type="success"-->
-      <!--          plain-->
-      <!--          icon="el-icon-edit"-->
-      <!--          size="mini"-->
-      <!--          :disabled="single"-->
-      <!--          @click="handleUpdate"-->
-      <!--          v-hasPermi="['live:live:edit']"-->
-      <!--        >修改</el-button>-->
-      <!--      </el-col>-->
-      <!--      <el-col :span="1.5">-->
-      <!--        <el-button-->
-      <!--          type="warning"-->
-      <!--          plain-->
-      <!--          icon="el-icon-download"-->
-      <!--          size="mini"-->
-      <!--          :loading="exportLoading"-->
-      <!--          @click="handleExport"-->
-      <!--          v-hasPermi="['live:live:export']"-->
-      <!--        >导出</el-button>-->
-      <!--      </el-col>-->
-      <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
-    </el-row>
-    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
-      <el-form-item label="直播名称" prop="liveName">
-        <el-input
-          v-model="queryParams.liveName"
-          placeholder="请输入直播名称"
-          clearable
-          size="small"
-        />
-      </el-form-item>
-      <el-form-item label="直播ID" prop="liveId">
-        <el-input
-          v-model="queryParams.liveId"
-          placeholder="请输入直播ID"
-          clearable
-          size="small"
-        />
-      </el-form-item>
-      <el-form-item label="直播状态" prop="liveStatus">
-        <el-select
-          v-model="queryParams.status"
-          placeholder="请选择直播状态"
-          clearable
-          size="small"
-        >
-          <el-option label="待直播" value="1"></el-option>
-          <el-option label="直播中" value="2"></el-option>
-          <el-option label="已结束" value="3"></el-option>
-          <el-option label="直播回放中" value="4"></el-option>
-        </el-select>
-      </el-form-item>
-      <el-form-item label="公司名称" prop="companyName">
-        <el-input
-          v-model="queryParams.companyName"
-          placeholder="请输入公司名称"
-          clearable
-          size="small"
-        />
-      </el-form-item>
-      <el-form-item label="直播类型" prop="liveType">
-        <el-select
-          v-model="queryParams.liveType"
-          placeholder="请选择直播类型"
-          clearable
-          size="small"
-        >
-          <el-option label="直播" value="1"></el-option>
-          <el-option label="录播" value="2"></el-option>
-          <el-option label="直播回放" value="3"></el-option>
-        </el-select>
-      </el-form-item>
-      <el-form-item label="开始时间" prop="startTimeS">
-        <el-date-picker
-          v-model="queryParams.startTimeS"
-          type="datetime"
-          placeholder="选择起始时间"
-          value-format="yyyy-MM-dd HH:mm:ss"
-          size="small"
-        ></el-date-picker>
-      </el-form-item>
-      <!-- 开始时间-范围结束 -->
-      <el-form-item label="结束时间" prop="endTimeE">
-        <el-date-picker
-          v-model="queryParams.endTimeE"
-          type="datetime"
-          placeholder="选择结束时间"
-          value-format="yyyy-MM-dd HH:mm:ss"
-          size="small"
-          :disabled="!queryParams.startTimeS"
-        ></el-date-picker>
-      </el-form-item>
-      <el-form-item label="上下架" prop="isShow">
-        <el-select
-          v-model="queryParams.isShow"
-          placeholder="请选择上下架状态"
-          clearable
-          size="small"
-        >
-          <el-option label="上架" value="1"></el-option>
-          <el-option label="下架" value="2"></el-option>
-        </el-select>
-      </el-form-item>
-      <!-- 审核状态 -->
-      <el-form-item label="审核状态" prop="isAudit">
-        <el-select
-          v-model="queryParams.isAudit"
-          placeholder="请选择审核状态"
-          clearable
-          size="small"
-        >
-          <el-option label="审核未通过" value="0"></el-option>
-          <el-option label="审核通过" value="1"></el-option>
-        </el-select>
-      </el-form-item>
-      <el-form-item>
-        <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
-                <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
-        <el-button icon="el-icon-download" size="mini" v-hasPermi="['live:live:export']" @click="handleExport">导出</el-button>
-      </el-form-item>
-    </el-form>
-    <div class="selection-toolbar">
-      <el-checkbox :indeterminate="isIndeterminate" v-model="allChecked" @change="toggleSelectAll">
-        {{ multipleSelection.length > 0 ? `已选 ${multipleSelection.length} 条` : '选中本页' }}
-      </el-checkbox>
-      <el-button  plain size="mini" @click="handleShelf" v-hasPermi="['live:live:edit']">上架</el-button>
-      <el-button  plain size="mini" @click="handleUnshelf" v-hasPermi="['live:live:edit']">下架</el-button>
-      <el-button  plain size="mini" @click="handleDeleteSelected" v-hasPermi="['live:live:remove']">删除</el-button>
-      <!--      <el-dropdown>-->
-      <!--        <el-button plain size="mini">-->
-      <!--          更多操作<i class="el-icon-arrow-down el-icon&#45;&#45;right"></i>-->
-      <!--        </el-button>-->
-      <!--        <el-dropdown-menu slot="dropdown">-->
-      <!--          <el-dropdown-item>操作一</el-dropdown-item>-->
-      <!--          <el-dropdown-item>操作二</el-dropdown-item>-->
-      <!--        </el-dropdown-menu>-->
-      <!--      </el-dropdown>-->
+        >新建直播间</el-button>
+        <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
+      </div>
     </div>
-    <el-table ref="liveTable" v-loading="loading" :data="liveList"  @selection-change="handleSelectionChange">
-      <el-table-column type="selection" width="55"></el-table-column>
-      <el-table-column label="直播ID" align="center" prop="liveId" />
-      <el-table-column label="直播封面" align="center" prop="liveImgUrl" width="180">
-        <template slot-scope="scope">
-          <el-image style="width: 90px;height: 90px;" :src="scope.row.liveImgUrl" mode="aspectFill" :preview-src-list="[scope.row.liveImgUrl]" />
-        </template>
-      </el-table-column>
-      <el-table-column label="直播名称" align="center" prop="liveName" />
-      <el-table-column label="显示类型" align="center" prop="showType">
-        <template slot-scope="scope">
-          <el-tag v-if="scope.row.showType == 1">横屏</el-tag>
-          <el-tag v-if="scope.row.showType == 2">竖屏</el-tag>
-        </template>
-      </el-table-column>
-      <el-table-column label="直播状态" align="center" prop="status">
-        <template slot-scope="scope">
-          <el-tag type="warning" v-if="scope.row.status == 1">待直播</el-tag>
-          <el-tag style="color:#3491FA;background-color:#E8F7FF;border-color: #E8F7FF;" v-if="scope.row.status == 2">直播中</el-tag>
-          <el-tag type="info" v-if="scope.row.status == 3">已结束</el-tag>
-          <el-tag style="color:#9B6DDA;background-color:#F5E8FF;border-color: #F5E8FF;" v-if="scope.row.status == 4">直播回放中</el-tag>
-        </template>
-      </el-table-column>
-      <el-table-column label="公司名称" align="center" prop="companyName" >
-        <template slot-scope="scope">
-          <el-tag v-if="scope.row.companyName">{{ scope.row.companyName }}</el-tag>
-          <el-tag v-else>总台</el-tag>
-        </template>
-      </el-table-column>
-      <el-table-column label="直播类型" align="center" prop="liveType">
-        <template slot-scope="scope">
-          <el-tag type="danger" v-if="scope.row.liveType == 1">直播</el-tag>
-          <el-tag type="success" v-if="scope.row.liveType == 2">录播</el-tag>
-          <el-tag v-if="scope.row.liveType == 3">直播回放</el-tag>
-        </template>
-      </el-table-column>
-      <el-table-column label="开始时间" align="center" prop="startTime" width="180" />
-      <el-table-column label="结束时间" align="center" prop="finishTime" width="180" />
-      <el-table-column label="上下架" align="center" prop="isShow">
-        <template slot-scope="scope">
-          <el-tag v-if="scope.row.isShow == 1">上架</el-tag>
-          <el-tag type="info" v-if="scope.row.isShow == 2">下架</el-tag>
-        </template>
-      </el-table-column>
-      <el-table-column label="审核状态" align="center" prop="isAudit">
-        <template slot-scope="scope">
-          <el-tag type="danger" v-if="scope.row.isAudit == 0">审核未通过</el-tag>
-          <el-tag v-if="scope.row.isAudit == 1">审核通过</el-tag>
-        </template>
-      </el-table-column>
-      <el-table-column label="操作" align="center" width="200" class-name="small-padding fixed-width">
-        <template slot-scope="scope">
-          <el-button
-            size="mini"
-            type="text"
-            icon="el-icon-edit"
-            @click="handleUpdate(scope.row)"
-            v-hasPermi="['live:live:edit']"
-            v-if="scope.row.status != 2"
-          >修改</el-button>
-          <el-button
-            size="mini"
-            type="text"
-            icon="el-icon-setting"
-            @click="handleConfig(scope.row)"
-            v-hasPermi="['live:config:list']"
-          >配置</el-button>
-          <el-button
-            size="mini"
-            type="text"
-            icon="el-icon-service"
-            @click="handleManage(scope.row)"
-            v-hasPermi="['live:console:list']"
-          >进入直播间</el-button>
-          <el-button
-            size="mini"
-            type="text"
-            icon="el-icon-delete"
-            @click="handleClearCache(scope.row)"
-            v-hasPermi="['live:live:edit']"
-          >清理缓存</el-button
-          >
-          <el-dropdown trigger="hover">
-            <el-button size="mini" type="text" icon="el-icon-more">
-              更多
-            </el-button>
-            <el-dropdown-menu slot="dropdown">
-              <el-dropdown-item
-                v-if="scope.row.status == 2 && scope.row.liveType == 1"
-                @click.native="showLivingUrl(scope.row)"
-              >
-                <i class="el-icon-switch-button"></i> 推流码
-              </el-dropdown-item>
-              <!-- 去直播按钮 -->
-              <el-dropdown-item
-                v-if="scope.row.status != 2"
-                @click.native="handleStart(scope.row)"
-              >
-                <i class="el-icon-monitor"></i> 去直播
-              </el-dropdown-item>
-              <el-dropdown-item
-                v-if="scope.row.status == 2"
-                @click.native="handleEnded(scope.row)"
-              >
-                <i class="el-icon-service"></i> 结束
-              </el-dropdown-item>
-              <el-dropdown-item
-                @click.native="handleCopy(scope.row)"
-              >
-                <i class="el-icon-service"></i> 复制直播间
-              </el-dropdown-item>
-              <el-dropdown-item
-                @click.native="handleAudit(scope.row)"
+
+    <div class="live-panel" v-show="showSearch">
+      <el-form :model="queryParams" ref="queryForm" :inline="true" label-width="72px" class="live-search-form">
+        <el-form-item label="直播名称" prop="liveName">
+          <el-input v-model="queryParams.liveName" placeholder="搜索名称" clearable size="small" />
+        </el-form-item>
+        <el-form-item label="直播ID" prop="liveId">
+          <el-input v-model="queryParams.liveId" placeholder="直播ID" clearable size="small" />
+        </el-form-item>
+        <el-form-item label="状态" prop="liveStatus">
+          <el-select v-model="queryParams.status" placeholder="全部状态" clearable size="small">
+            <el-option label="待直播" value="1"></el-option>
+            <el-option label="直播中" value="2"></el-option>
+            <el-option label="已结束" value="3"></el-option>
+            <el-option label="直播回放中" value="4"></el-option>
+          </el-select>
+        </el-form-item>
+        <el-form-item label="公司" prop="companyName">
+          <el-input v-model="queryParams.companyName" placeholder="公司名称" clearable size="small" />
+        </el-form-item>
+        <el-form-item label="类型" prop="liveType">
+          <el-select v-model="queryParams.liveType" placeholder="全部类型" clearable size="small">
+            <el-option label="直播" value="1"></el-option>
+            <el-option label="录播" value="2"></el-option>
+            <el-option label="直播回放" value="3"></el-option>
+          </el-select>
+        </el-form-item>
+        <el-form-item label="开始时间" prop="startTimeS">
+          <el-date-picker
+            v-model="queryParams.startTimeS"
+            type="datetime"
+            placeholder="起始时间"
+            value-format="yyyy-MM-dd HH:mm:ss"
+            size="small"
+          />
+        </el-form-item>
+        <el-form-item label="结束时间" prop="endTimeE">
+          <el-date-picker
+            v-model="queryParams.endTimeE"
+            type="datetime"
+            placeholder="结束时间"
+            value-format="yyyy-MM-dd HH:mm:ss"
+            size="small"
+            :disabled="!queryParams.startTimeS"
+          />
+        </el-form-item>
+        <el-form-item label="上下架" prop="isShow">
+          <el-select v-model="queryParams.isShow" placeholder="全部" clearable size="small">
+            <el-option label="上架" value="1"></el-option>
+            <el-option label="下架" value="2"></el-option>
+          </el-select>
+        </el-form-item>
+        <el-form-item label="审核" prop="isAudit">
+          <el-select v-model="queryParams.isAudit" placeholder="全部" clearable size="small">
+            <el-option label="审核未通过" value="0"></el-option>
+            <el-option label="审核通过" value="1"></el-option>
+          </el-select>
+        </el-form-item>
+        <el-form-item class="live-search-form__ops">
+          <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
+          <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
+          <el-button icon="el-icon-download" size="mini" v-hasPermi="['live:live:export']" @click="handleExport">导出</el-button>
+        </el-form-item>
+      </el-form>
+    </div>
+
+    <div class="live-panel live-panel--table">
+      <div class="selection-toolbar">
+        <el-checkbox :indeterminate="isIndeterminate" v-model="allChecked" @change="toggleSelectAll">
+          {{ multipleSelection.length > 0 ? `已选 ${multipleSelection.length} 条` : '选中本页' }}
+        </el-checkbox>
+        <div class="selection-toolbar__btns">
+          <el-button plain size="mini" @click="handleShelf" v-hasPermi="['live:live:edit']">上架</el-button>
+          <el-button plain size="mini" @click="handleUnshelf" v-hasPermi="['live:live:edit']">下架</el-button>
+          <el-button plain size="mini" @click="handleDeleteSelected" v-hasPermi="['live:live:remove']">删除</el-button>
+        </div>
+      </div>
+      <el-table
+        ref="liveTable"
+        v-loading="loading"
+        :data="liveList"
+        class="live-table"
+        @selection-change="handleSelectionChange"
+      >
+        <el-table-column type="selection" width="48" align="center" />
+        <el-table-column label="直播间" min-width="260">
+          <template slot-scope="scope">
+            <div class="live-cell">
+              <el-image
+                class="live-cell__cover"
+                :src="scope.row.liveImgUrl"
+                fit="cover"
+                :preview-src-list="scope.row.liveImgUrl ? [scope.row.liveImgUrl] : []"
               >
-                <i class="el-icon-service"></i> 审核
-              </el-dropdown-item>
-
-            </el-dropdown-menu>
-          </el-dropdown>
-<!--          <el-button-->
-<!--            v-if="scope.row.status == 2"-->
-<!--            size="mini"-->
-<!--            type="text"-->
-<!--            icon="el-icon-switch-button"-->
-<!--            @click="handleEnded(scope.row)"-->
-<!--            v-hasPermi="['live:config:edit']"-->
-<!--          >结束</el-button>-->
-
-        </template>
-      </el-table-column>
-    </el-table>
+                <div slot="error" class="live-cell__cover-empty">无封面</div>
+              </el-image>
+              <div class="live-cell__meta">
+                <div class="live-cell__name" :title="scope.row.liveName">{{ scope.row.liveName }}</div>
+                <div class="live-cell__sub">ID {{ scope.row.liveId }} · {{ scope.row.companyName || '总台' }}</div>
+              </div>
+            </div>
+          </template>
+        </el-table-column>
+        <el-table-column label="状态" align="center" width="110">
+          <template slot-scope="scope">
+            <span class="live-status" :class="'is-' + scope.row.status">
+              <i class="live-status__dot"></i>
+              <template v-if="scope.row.status == 1">待直播</template>
+              <template v-else-if="scope.row.status == 2">直播中</template>
+              <template v-else-if="scope.row.status == 3">已结束</template>
+              <template v-else-if="scope.row.status == 4">回放中</template>
+            </span>
+          </template>
+        </el-table-column>
+        <el-table-column label="类型" align="center" width="90">
+          <template slot-scope="scope">
+            <span class="live-muted">
+              <template v-if="scope.row.liveType == 1">直播</template>
+              <template v-else-if="scope.row.liveType == 2">录播</template>
+              <template v-else-if="scope.row.liveType == 3">回放</template>
+            </span>
+          </template>
+        </el-table-column>
+        <el-table-column label="画面" align="center" width="80">
+          <template slot-scope="scope">
+            <span class="live-muted">{{ scope.row.showType == 2 ? '竖屏' : '横屏' }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="开始时间" align="center" prop="startTime" width="168" />
+        <el-table-column label="结束时间" align="center" prop="finishTime" width="168" />
+        <el-table-column label="上架" align="center" width="80">
+          <template slot-scope="scope">
+            <span :class="scope.row.isShow == 1 ? 'live-ok' : 'live-muted'">{{ scope.row.isShow == 1 ? '上架' : '下架' }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="审核" align="center" width="96">
+          <template slot-scope="scope">
+            <span :class="scope.row.isAudit == 1 ? 'live-ok' : 'live-warn'">{{ scope.row.isAudit == 1 ? '已通过' : '未通过' }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="操作" align="left" width="220" fixed="right" class-name="small-padding fixed-width">
+          <template slot-scope="scope">
+            <el-button size="mini" type="text" @click="handleUpdate(scope.row)" v-hasPermi="['live:live:edit']" v-if="scope.row.status != 2">修改</el-button>
+            <el-button size="mini" type="text" @click="handleConfig(scope.row)" v-hasPermi="['live:config:list']">配置</el-button>
+            <el-button size="mini" type="text" @click="handleManage(scope.row)" v-hasPermi="['live:console:list']">进入</el-button>
+            <el-dropdown trigger="hover">
+              <el-button size="mini" type="text">更多<i class="el-icon-arrow-down el-icon--right"></i></el-button>
+              <el-dropdown-menu slot="dropdown">
+                <el-dropdown-item v-if="scope.row.status == 2 && scope.row.liveType == 1" @click.native="showLivingUrl(scope.row)">推流码</el-dropdown-item>
+                <el-dropdown-item v-if="scope.row.status != 2" @click.native="handleStart(scope.row)">去直播</el-dropdown-item>
+                <el-dropdown-item v-if="scope.row.status == 2" @click.native="handleEnded(scope.row)">结束</el-dropdown-item>
+                <el-dropdown-item @click.native="handleCopy(scope.row)">复制直播间</el-dropdown-item>
+                <el-dropdown-item @click.native="handleAudit(scope.row)">审核</el-dropdown-item>
+                <el-dropdown-item @click.native="handleClearCache(scope.row)">清理缓存</el-dropdown-item>
+              </el-dropdown-menu>
+            </el-dropdown>
+          </template>
+        </el-table-column>
+      </el-table>
+    </div>
     <el-dialog
       title="直播二维码"
       :visible.sync="qrcodeDialogVisible"
@@ -982,19 +876,169 @@ export default {
 </script>
 
 <style scoped>
+.live-page {
+  --live-border: #e8eaed;
+  --live-text: #1f2329;
+  --live-sub: #8a919f;
+  --live-accent: #1a6d5b;
+  --live-soft: #f4f6f8;
+}
+
+.live-page__header {
+  display: flex;
+  align-items: flex-end;
+  justify-content: space-between;
+  gap: 16px;
+  margin-bottom: 16px;
+}
+
+.live-page__title {
+  margin: 0;
+  font-size: 22px;
+  font-weight: 600;
+  color: var(--live-text);
+  letter-spacing: 0.02em;
+}
+
+.live-page__desc {
+  margin: 4px 0 0;
+  font-size: 13px;
+  color: var(--live-sub);
+}
+
+.live-page__actions {
+  display: flex;
+  align-items: center;
+  gap: 8px;
+}
+
+.live-panel {
+  background: #fff;
+  border: 1px solid var(--live-border);
+  border-radius: 10px;
+  padding: 14px 16px 4px;
+  margin-bottom: 14px;
+}
+
+.live-panel--table {
+  padding: 12px 12px 4px;
+}
+
+.live-search-form .el-form-item {
+  margin-bottom: 12px;
+}
+
+.live-search-form__ops {
+  margin-left: 4px;
+}
+
 .selection-toolbar {
   display: flex;
   align-items: center;
+  justify-content: space-between;
+  gap: 12px;
   margin-bottom: 10px;
-  padding-left: 10px;
+  padding: 2px 4px 8px;
+  border-bottom: 1px solid var(--live-soft);
+}
+
+.selection-toolbar__btns {
+  display: flex;
+  gap: 8px;
+}
+
+.live-table ::v-deep .el-table__header th {
+  background: var(--live-soft);
+  color: #5c6470;
+  font-weight: 500;
+}
+
+.live-table ::v-deep .el-table__row td {
+  padding: 12px 0;
+}
+
+.live-cell {
+  display: flex;
+  align-items: center;
+  gap: 12px;
+  min-width: 0;
+}
+
+.live-cell__cover {
+  width: 56px;
+  height: 74px;
+  border-radius: 8px;
+  flex-shrink: 0;
+  background: var(--live-soft);
+  overflow: hidden;
+}
+
+.live-cell__cover-empty {
+  width: 100%;
+  height: 100%;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  font-size: 12px;
+  color: var(--live-sub);
+  background: var(--live-soft);
+}
+
+.live-cell__meta {
+  min-width: 0;
+  text-align: left;
 }
 
-.selection-toolbar .el-checkbox {
-  margin-right: 10px;
+.live-cell__name {
+  font-size: 14px;
+  font-weight: 600;
+  color: var(--live-text);
+  line-height: 1.35;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+}
+
+.live-cell__sub {
+  margin-top: 4px;
+  font-size: 12px;
+  color: var(--live-sub);
+  overflow: hidden;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+}
+
+.live-status {
+  display: inline-flex;
+  align-items: center;
+  gap: 6px;
+  font-size: 13px;
+  color: var(--live-text);
+}
+
+.live-status__dot {
+  width: 7px;
+  height: 7px;
+  border-radius: 50%;
+  background: #c0c4cc;
+}
+
+.live-status.is-1 .live-status__dot { background: #e6a23c; }
+.live-status.is-2 .live-status__dot { background: #f56c6c; box-shadow: 0 0 0 3px rgba(245, 108, 108, 0.18); }
+.live-status.is-3 .live-status__dot { background: #909399; }
+.live-status.is-4 .live-status__dot { background: #409eff; }
+
+.live-muted { color: var(--live-sub); font-size: 13px; }
+.live-ok { color: var(--live-accent); font-size: 13px; }
+.live-warn { color: #c45656; font-size: 13px; }
+
+.qrcode-img-container {
+  text-align: center;
+  padding: 20px 0;
 }
 
-/* 调整 checkbox 内部输入框的对齐 */
-.selection-toolbar .el-checkbox .el-checkbox__inner {
-  top: 8px; /* 根据实际需求调整 */
+.qrcode-img {
+  max-width: 100%;
+  max-height: 400px;
 }
 </style>

+ 14 - 5
src/views/live/liveAfterSalesItem/index.vue

@@ -1,6 +1,11 @@
 <template>
-  <div class="app-container">
-    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
+  <div class="app-container live-page">
+        <div class="live-page__header">
+      <h2 class="live-page__title">售后明细</h2>
+    </div>
+
+<div class="live-panel" v-show="showSearch">
+<el-form class="live-search-form" :model="queryParams" ref="queryForm" :inline="true" label-width="68px">
       <el-form-item label="售后id" prop="afterSalesId">
         <el-input
           v-model="queryParams.afterSalesId"
@@ -51,8 +56,10 @@
         <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
       </el-form-item>
     </el-form>
+    </div>
 
-    <el-row :gutter="10" class="mb8">
+    <div class="live-panel live-panel--table">
+<el-row :gutter="10" class="mb8">
       <el-col :span="1.5">
         <el-button
           type="primary"
@@ -99,7 +106,7 @@
       <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
     </el-row>
 
-    <el-table border v-loading="loading" :data="liveAfterSalesItemList" @selection-change="handleSelectionChange">
+    <el-table border v-loading="loading" :data="liveAfterSalesItemList" @selection-change="handleSelectionChange" class="live-table">
       <el-table-column type="selection" width="55" align="center" />
       <el-table-column label="主键id" align="center" prop="id" />
       <el-table-column label="售后id" align="center" prop="afterSalesId" />
@@ -137,7 +144,9 @@
     />
 
     <!-- 添加或修改售后子对话框 -->
-    <el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
+    </div>
+
+<el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
       <el-form ref="form" :model="form" :rules="rules" label-width="80px">
         <el-form-item label="售后id" prop="afterSalesId">
           <el-input v-model="form.afterSalesId" placeholder="请输入售后id" />

+ 14 - 5
src/views/live/liveAfterSalesLogs/index.vue

@@ -1,6 +1,11 @@
 <template>
-  <div class="app-container">
-    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
+  <div class="app-container live-page">
+        <div class="live-page__header">
+      <h2 class="live-page__title">售后日志</h2>
+    </div>
+
+<div class="live-panel" v-show="showSearch">
+<el-form class="live-search-form" :model="queryParams" ref="queryForm" :inline="true" label-width="68px">
       <el-form-item label="售后id" prop="storeAfterSalesId">
         <el-input
           v-model="queryParams.storeAfterSalesId"
@@ -64,8 +69,10 @@
         <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
       </el-form-item>
     </el-form>
+    </div>
 
-    <el-row :gutter="10" class="mb8">
+    <div class="live-panel live-panel--table">
+<el-row :gutter="10" class="mb8">
       <el-col :span="1.5">
         <el-button
           type="primary"
@@ -112,7 +119,7 @@
       <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
     </el-row>
 
-    <el-table border v-loading="loading" :data="liveAfterSalesLogsList" @selection-change="handleSelectionChange">
+    <el-table border v-loading="loading" :data="liveAfterSalesLogsList" @selection-change="handleSelectionChange" class="live-table">
       <el-table-column type="selection" width="55" align="center" />
       <el-table-column label="店铺id" align="center" prop="logsId" />
       <el-table-column label="售后id" align="center" prop="storeAfterSalesId" />
@@ -155,7 +162,9 @@
     />
 
     <!-- 添加或修改售后订单操作对话框 -->
-    <el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
+    </div>
+
+<el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
       <el-form ref="form" :model="form" :rules="rules" label-width="80px">
         <el-form-item label="售后id" prop="storeAfterSalesId">
           <el-input v-model="form.storeAfterSalesId" placeholder="请输入售后id" />

+ 12 - 4
src/views/live/liveAfteraSales/index.vue

@@ -1,6 +1,11 @@
 <template>
-  <div class="app-container">
-    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
+  <div class="app-container live-page">
+    <div class="live-page__header">
+      <h2 class="live-page__title">直播售后</h2>
+    </div>
+
+    <div class="live-panel" v-show="showSearch">
+      <el-form class="live-search-form" :model="queryParams" ref="queryForm" :inline="true" label-width="68px">
 
       <el-form-item label="所属部门" prop="deptId">
         <treeselect style="width:220px;" v-model="queryParams.deptId" :options="deptOptions" :show-count="true" placeholder="请选择所属部门" />
@@ -152,8 +157,10 @@
       </div>
 
     </el-form>
+    </div>
 
-    <el-row :gutter="10" class="mb8">
+    <div class="live-panel live-panel--table">
+<el-row :gutter="10" class="mb8">
       <el-col :span="1.5">
         <el-button
           type="warning"
@@ -200,7 +207,7 @@
       </el-table-column>
       <el-table-column label="物流状态" align="center" prop="deliveryStatus" >
         <template slot-scope="scope">
-          <el-tag prop="status" v-for="(item, index) in deliveryStatusOptions"    v-if="scope.row.deliveryStatus==item.dictValue">{{item.dictLabel}}</el-tag>
+          <span class="live-muted" v-for="(item, index) in deliveryStatusOptions" v-if="scope.row.deliveryStatus==item.dictValue" :key="index">{{item.dictLabel}}</span>
         </template>
       </el-table-column>
 
@@ -233,6 +240,7 @@
       :limit.sync="queryParams.pageSize"
       @pagination="getList"
     />
+    </div>
     <el-drawer
       size="75%"
       :title="title" :visible.sync="open"

+ 11 - 3
src/views/live/liveAnchor/index.vue

@@ -1,6 +1,11 @@
 <template>
-  <div class="app-container">
-    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
+  <div class="app-container live-page">
+    <div class="live-page__header">
+      <h2 class="live-page__title">直播主播</h2>
+    </div>
+
+    <div class="live-panel" v-show="showSearch">
+      <el-form class="live-search-form" :model="queryParams" ref="queryForm" :inline="true" label-width="68px">
       <el-row :gutter="10">
         <el-col :span="6">
           <el-form-item label="昵称" prop="nickName">
@@ -53,8 +58,10 @@
           <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
         </el-form-item>
     </el-form>
+    </div>
 
-    <el-row :gutter="10" class="mb8">
+    <div class="live-panel live-panel--table">
+<el-row :gutter="10" class="mb8">
       <el-col :span="1.5">
         <el-button
           type="primary"
@@ -139,6 +146,7 @@
       :limit.sync="queryParams.pageSize"
       @pagination="getList"
     />
+    </div>
 
     <!-- 添加或修改主播对话框 -->
     <el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>

+ 12 - 2
src/views/live/liveConfig/answer.vue

@@ -1,5 +1,10 @@
 <template>
-  <div class="container-md">
+  <div class="app-container live-page">
+        <div class="live-page__header">
+      <h2 class="live-page__title">答题配置</h2>
+    </div>
+
+<div class="live-panel">
     <div class="tip-box">
       选择用于本节直播课程的题库试题,试题可用于直播间内发送
       <el-link
@@ -9,9 +14,12 @@
       >配置题库试题 >></el-link>
     </div>
 
+    </div>
+
+    <div class="live-panel live-panel--table">
     <el-button type="primary" icon="el-icon-plus" style="margin: 20px 0;" @click="handleAddQuestion">添加试题</el-button>
     <!-- 试题列表表格 -->
-    <el-table
+    <el-table class="live-table"
       :data="questionLiveList"
       style="width: 100%"
       v-loading="loading"
@@ -66,6 +74,8 @@
       @pagination="getLiveQuestionLiveList"
       style="margin-top: 20px;"
     />
+    </div>
+
 
     <!-- 添加试题弹窗 -->
     <el-dialog

+ 12 - 5
src/views/live/liveConfig/barrage.vue

@@ -1,6 +1,11 @@
 <template>
-  <div class="app-container">
-    <el-row :gutter="10" class="mb8">
+  <div class="app-container live-page">
+        <div class="live-page__header">
+      <h2 class="live-page__title">弹幕任务</h2>
+    </div>
+
+<div class="live-panel live-panel--table">
+<el-row :gutter="10" class="mb8">
       <el-col :span="1.5">
         <el-button
           type="primary" plain icon="el-icon-refresh" size="mini" @click="handleReload" v-hasPermi="['live:task:add']"
@@ -29,7 +34,7 @@
       <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
     </el-row>
 
-    <el-table border v-loading="loading" :data="taskList" @selection-change="handleSelectionChange">
+    <el-table border v-loading="loading" :data="taskList" @selection-change="handleSelectionChange" class="live-table">
       <el-table-column type="selection" width="55" align="center" />
       <el-table-column label="编号" align="center" prop="id" />
       <el-table-column label="人物名称" align="center" prop="taskName" />
@@ -53,7 +58,9 @@
       v-show="total>0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize" @pagination="getList"
     />
     <!-- 添加或修改直播间自动化任务配置对话框 -->
-    <el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
+    </div>
+
+<el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
       <el-form ref="form" :model="form" :rules="rules" label-width="80px">
         <el-form-item label="直播间ID" prop="liveId" v-show="false">
           <el-input :disabled="liveAbled" v-model="form.liveId" placeholder="请输入直播间ID" />
@@ -228,7 +235,7 @@ export default {
     }
   },
   created() {
-    this.socket = this.$store.state.liveWs[this.liveId]
+    this.socket = (this.$store.state.liveWs[String(this.liveId)] || this.$store.state.liveWs[this.liveId])
   },
   methods: {
     triggerTypeFormatter(row, column, value){

+ 18 - 38
src/views/live/liveConfig/goods.vue

@@ -1,7 +1,10 @@
 <template>
-  <div class="app-container">
-  <div class="el-container-md">
+  <div class="app-container live-page">
+    <div class="live-page__header">
+      <h2 class="live-page__title">直播商品配置</h2>
+    </div>
 
+    <div class="live-panel live-panel--table">
     <div class="selection-toolbar">
       <el-checkbox :indeterminate="isIndeterminate" v-model="allChecked" @change="toggleSelectAll">
         {{ multipleSelection.length > 0 ? `已选 ${multipleSelection.length} 条` : '选中本页' }}
@@ -14,7 +17,7 @@
     <el-table
       ref="goodTable"
       :data="goodsLiveList"
-      style="width: 100%; "
+      style="width: 100%"
       v-loading="loading"
       @selection-change="handleSelectionChange"
     >
@@ -32,7 +35,7 @@
         <template slot-scope="scope">
           <img
             :src="scope.row.imgUrl"
-            style="display: block; max-width: 100%; width: 100px; height: 100px"
+            class="live-cell__cover live-cell__cover--square" style="object-fit: cover;"
           />
         </template>
 
@@ -84,8 +87,8 @@
         label="上下架"
       >
         <template slot-scope="scope">
-          <el-tag v-if="scope.row.status == 1">上架</el-tag>
-          <el-tag type="info" v-if="scope.row.status == 0">下架</el-tag>
+          <span v-if="scope.row.status == 1" class="live-ok">上架</span>
+          <span v-if="scope.row.status == 0" class="live-muted">下架</span>
         </template>
       </el-table-column>
 
@@ -94,8 +97,8 @@
         label="店铺上下架"
       >
         <template slot-scope="scope">
-          <el-tag v-if="scope.row.fsStatus == 1">上架</el-tag>
-          <el-tag type="info" v-if="scope.row.fsStatus == 0">下架</el-tag>
+          <span v-if="scope.row.fsStatus == 1" class="live-ok">上架</span>
+          <span v-if="scope.row.fsStatus == 0" class="live-muted">下架</span>
         </template>
       </el-table-column>
 
@@ -141,8 +144,9 @@
       :page.sync="goodsParams.pageNum"
       :limit.sync="goodsParams.pageSize"
       @pagination="getLiveGoodsList"
-      style="margin-top: 20px;background-color: inherit;"
+      style="margin-top: 20px;"
     />
+    </div>
 
     <!-- 添加商品弹窗 -->
     <el-dialog
@@ -206,7 +210,7 @@
           style="margin-top: 20px;"
         />
       </div>
-      <div slot="footer" class="dialog-footer" style="background-color: inherit;">
+      <div slot="footer" class="dialog-footer">
         <div style="display: flex; justify-content: space-between; align-items: center;">
           <span class="selected-count">当前已选择 <span style="color: #00BFFF; font-style: italic;">{{ selectedGoods.length }}</span> 商品</span>
           <div>
@@ -288,8 +292,6 @@
         <el-button type="primary" @click="confirmSortChange">确 定</el-button>
       </div>
     </el-dialog>
-
-  </div>
   </div>
 </template>
 
@@ -373,7 +375,7 @@ export default {
           return;
         }
         this.getLiveGoodsList();
-        this.socket = this.$store.state.liveWs[this.liveId]
+        this.socket = (this.$store.state.liveWs[String(this.liveId)] || this.$store.state.liveWs[this.liveId])
       },
       // 初始化时立即执行一次
       immediate: true
@@ -393,8 +395,9 @@ export default {
       const targetStatus = row.isShow;
       const originalIsShow = !row.isShow
       const goodsList = [row.goodsId];
+      this.socket = this.$store.state.liveWs[String(this.liveId)] || this.$store.state.liveWs[this.liveId]
       if (this.socket == null) {
-        this.$message.error("请从直播间开启展示状态!");
+        this.$message.error("直播连接未就绪,请稍后重试");
 
         this.$nextTick(() => {
           row.isShow = originalIsShow;
@@ -646,43 +649,20 @@ export default {
 };
 </script >
 
-<style lang="scss" scoped>
-
+<style scoped>
 .selection-toolbar {
   display: flex;
   align-items: center;
   margin-bottom: 10px;
   padding-left: 10px;
-  background-color: #f0f2f5;
 }
 
 .selection-toolbar .el-checkbox {
   margin-right: 10px;
-  background-color: #f0f2f5;
 }
 
 /* 调整 checkbox 内部输入框的对齐 */
 .selection-toolbar .el-checkbox .el-checkbox__inner {
   top: 8px; /* 根据实际需求调整 */
-  background-color: #f0f2f5;
-}
-/* 1. 让组件根容器继承 app-main 背景 */
-.goods-table-container {
-  background-color: inherit; /* 继承父容器(app-main)的背景色 */
-  padding: 20px; /* 可选:保留内边距,避免内容贴边 */
-}
-
-/* 2. 让 Element UI 容器/表格匹配背景(若其有默认白色背景) */
-.el-container-md {
-  background-color: inherit; /* 取消 el-container 的默认背景 */
-}
-
-.el-table {
-  background-color: inherit; /* 取消 el-table 的默认白色背景(可选,若需要表格透明) */
-  /* 若希望表格有背景,但与 app-main 协调:可改为 app-main 背景色的相近色 */
-  /* background-color: #f8f9fa; */
-}
-::v-deep .app-main .goods-table-container {
-  background-color: #f5f7fa; /* 与 app-main 全局背景统一 */
 }
 </style>

+ 10 - 3
src/views/live/liveConfig/idCard.vue

@@ -1,14 +1,21 @@
 <template>
-  <el-form ref="form"  label-width="80px">
+  <div class="app-container live-page">
+    <div class="live-page__header">
+      <h2 class="live-page__title">身份认证</h2>
+    </div>
+
+<div class="live-panel">
+<el-form ref="form"  label-width="80px">
     <el-form-item>
       <el-form-item label="上传图片" prop="materialUrl">
-        <img :src="idCardUrl" alt="身份证" style="height: 150px; width: 150px" v-if="idCardUrl != null">
+        <img :src="idCardUrl" alt="身份证" class="live-cell__cover" style="width: 96px; height: 96px; object-fit: cover;" v-if="idCardUrl != null">
         <ImageUpload @input="handleUrl" type="image" :num="10" :width="150" :height="150" />
       </el-form-item>
       <el-button type="primary" size="mini" @click="submit">保存</el-button>
     </el-form-item>
   </el-form>
-</template>
+    </div>
+</div></template>
 
 <script>
 import ImageUpload from '@/components/ImageUpload/index';

+ 98 - 88
src/views/live/liveConfig/index.vue

@@ -1,5 +1,5 @@
 <template>
-  <div class="live-config-container">
+  <div class="live-config-container live-page">
     <el-card class="box-card live-config-header">
       <div class="detail-header">
         <div class="header-left-area">
@@ -21,32 +21,21 @@
             <div class="live-type">{{ liveInfo.liveType == 1 ? '视频直播' : '录播' }}</div>
             <div class="line"></div>
             <div class="live-mode">{{ liveInfo.showType == 1 ? '横屏' : '竖屏' }}</div>
-            <div class="big-screen-info">
-              <div class="dataLarge">
-                <a class="dataLarge__screen ss-popover__reference" aria-describedby="ss-popover-7614" tabindex="0" data-auth-id="818">
-                  <span class="dataLarge__screen__icon"></span>
-                  <span class="dataLarge__screen__txt">实时大屏</span>
-                </a>
-              </div>
-            </div>
           </div>
 
           <div class="live-info-state">
             <div class="info-state-text live-time">直播时间:{{ liveInfo.startTime }} 至 {{ liveInfo.finishTime }}</div>
           </div>
           <div class="live-info-state">
-            <!-- <div class="info-state-text live-state">
-            {{ liveInfo.status == 3 ? '已结束' : liveInfo.status == 2 ? '直播中' : '未开始'}}
-            </div> -->
-            <el-tag type="warning" v-if="liveInfo.status == 1">待直播</el-tag>
-            <el-tag style="color:#3491FA;background-color:#E8F7FF;border-color: #E8F7FF;" v-if="liveInfo.status == 2">直播中</el-tag>
-            <el-tag type="info" v-if="liveInfo.status == 3">已结束</el-tag>
-            <el-tag style="color:#9B6DDA;background-color:#F5E8FF;border-color: #F5E8FF;" v-if="liveInfo.status == 4">直播回放中</el-tag>
-            <div class="info-state-text live-active-state" style="margin-left: 5px;">
-            <el-tag v-if="liveInfo.isShow == 1">上架</el-tag>
-            <el-tag type="info" v-if="liveInfo.isShow == 2">下架</el-tag>
-            </div>
-
+            <span class="live-status" :class="'is-' + liveInfo.status">
+              <i class="live-status__dot"></i>
+              <template v-if="liveInfo.status == 1">待直播</template>
+              <template v-else-if="liveInfo.status == 2">直播中</template>
+              <template v-else-if="liveInfo.status == 3">已结束</template>
+              <template v-else-if="liveInfo.status == 4">直播回放中</template>
+            </span>
+            <span class="shelf-text" v-if="liveInfo.isShow == 1">上架</span>
+            <span class="shelf-text is-off" v-if="liveInfo.isShow == 2">下架</span>
           </div>
         </div>
 
@@ -144,6 +133,7 @@ export default {
       liveId: null,
       liveInfo: {},
       currentComponent: WatchReward,
+      liveWsUrl: process.env.VUE_APP_LIVE_WS_URL + '/app/webSocket',
       menuList:[
         { name: '观看奖励', label: '观看奖励', index: 'watchReward',icon:'el-icon-coin'},
         { name: '直播预告', label: '直播预告', index: 'preview',icon:'el-icon-video-play'},
@@ -161,11 +151,40 @@ export default {
 
     }
   },
+  computed: {
+    userId() {
+      return this.$store.state.user.user && this.$store.state.user.user.userId
+    }
+  },
+  watch: {
+    '$route.params.liveId'(val, oldVal) {
+      if (!val || String(val) === String(oldVal)) return
+      if (oldVal) {
+        this.$store.dispatch('releaseLiveWs', { liveId: oldVal, owner: 'config' })
+      }
+      this.liveId = val
+      this.connectLiveWs()
+      this.getLiving()
+    }
+  },
   created() {
     this.liveId = this.$route.params.liveId
+    this.connectLiveWs()
     this.getLiving()
   },
+  beforeDestroy() {
+    this.$store.dispatch('releaseLiveWs', { liveId: this.liveId, owner: 'config' })
+  },
   methods: {
+    connectLiveWs() {
+      if (!this.liveId || !this.userId) return
+      this.$store.dispatch('acquireLiveWs', {
+        liveWsUrl: this.liveWsUrl,
+        liveId: this.liveId,
+        userId: this.userId,
+        owner: 'config'
+      })
+    },
     handleSelect(index){
       this.currentComponent = index; // 切换当前显示的组件
     },
@@ -183,107 +202,95 @@ export default {
 .el-tabs__header .is-top {
   display: none !important;
 }
-.live-config-header{
+.live-config-header {
   padding: 5px;
-  border: 1px solid #ebeef5;
+  border: 1px solid #e8eaed;
+  border-radius: 10px;
 }
 .detail-header {
   display: flex;
-  padding: 20px;
-  border-bottom: 1px solid #ebeef5;
+  align-items: center;
+  padding: 16px 18px;
 }
 
 .header-left-area {
-  flex: 0 0 100px;
+  flex: 0 0 64px;
   position: relative;
 }
 
 .header-left-area img {
-  width: 100px;
-  height: 100px;
-  border-radius: 4px;
+  width: 64px;
+  height: 84px;
+  border-radius: 8px;
+  object-fit: cover;
 }
 
 .header-middle-area {
   flex: 1;
-  padding: 0 20px;
-}
-
-.header-right-area {
-  flex: 0 0 200px;
+  padding: 0 18px;
+  min-width: 0;
 }
 
 .live-info-title .title-text {
   font-size: 18px;
-  font-weight: 500;
-  color: #303133;
+  font-weight: 600;
+  color: #1f2329;
+  line-height: 1.35;
 }
 
 .live-info-desc {
   display: flex;
   align-items: center;
-  margin-top: 10px;
-  color: #999999;
+  margin-top: 8px;
+  color: #8a919f;
+  font-size: 13px;
 }
 
 .live-info-desc .line {
   width: 1px;
   height: 12px;
-  background: #c0c4cc;
+  background: #e8eaed;
   margin: 0 10px;
 }
 
 .live-info-state {
   display: flex;
   align-items: center;
-  margin-top: 10px;
-}
-
-.live-info-state .line {
-  width: 1px;
-  height: 12px;
-  background: #c0c4cc;
-  margin: 0 10px;
-}
-
-.live-state-dot {
-  width: 8px;
-  height: 8px;
-  border-radius: 50%;
-}
-
-.live-state-dot-liveEnd {
-  background: #909399;
+  gap: 12px;
+  margin-top: 8px;
 }
 
 .info-state-text {
-  font-size: 14px;
-  color: #999999;
+  font-size: 13px;
+  color: #8a919f;
 }
 
-.operation-wrapper {
-  display: flex;
+.live-status {
+  display: inline-flex;
   align-items: center;
+  gap: 6px;
+  font-size: 13px;
+  color: #1f2329;
 }
 
-.operation-item {
-  display: flex;
-  flex-direction: column;
-  align-items: center;
-  cursor: pointer;
+.live-status__dot {
+  width: 7px;
+  height: 7px;
+  border-radius: 50%;
+  background: #c0c4cc;
 }
 
-.operation-item-text {
+.live-status.is-1 .live-status__dot { background: #e6a23c; }
+.live-status.is-2 .live-status__dot { background: #f56c6c; box-shadow: 0 0 0 3px rgba(245, 108, 108, 0.18); }
+.live-status.is-3 .live-status__dot { background: #909399; }
+.live-status.is-4 .live-status__dot { background: #409eff; }
+
+.shelf-text {
   font-size: 12px;
-  color: #606266;
-  margin-top: 4px;
+  color: #1a6d5b;
 }
-
-.operation-wrapper .line {
-  width: 1px;
-  height: 20px;
-  background: #c0c4cc;
-  margin: 0 10px;
+.shelf-text.is-off {
+  color: #8a919f;
 }
 
 .container {
@@ -291,24 +298,27 @@ export default {
   height: 100%;
 }
 .left-menu {
-  width: 200px;
-  border-right: 1px solid #e4e7ed;
-  flex-shrink: 0; /* 不收缩 */
+  width: 180px;
+  border-right: 1px solid #e8eaed;
+  flex-shrink: 0;
+  background: #fafbfc;
+  padding: 10px 8px;
 }
-.el-menu-item{
-  margin-right: 20px;
+.el-menu-item {
+  margin: 2px 0;
+  height: 40px;
+  line-height: 40px;
+  border-radius: 8px;
+  color: #5c6470;
 }
-.el-menu-item.is-active,.el-menu-item:hover{
-  background-color: rgba(19,194,194,0.1);
-  border-radius: 8px 8px 8px 8px;
+.el-menu-item.is-active,
+.el-menu-item:hover {
+  background-color: rgba(26, 109, 91, 0.1);
+  color: #1a6d5b;
 }
 .right-info {
   flex: 1;
-  padding: 20px;
-}
-
-.right-tabs {
-  height: 100%;
+  padding: 16px 18px;
 }
 
 </style>

+ 16 - 8
src/views/live/liveConfig/liveCoupon.vue

@@ -1,8 +1,13 @@
 <template>
-  <div class="app-container">
-  <div class="el-container-md">
+  <div class="app-container live-page">
+      <div class="live-page__header">
+      <h2 class="live-page__title">直播优惠券</h2>
+    </div>
+
+<div class="el-container-md">
 
-    <div class="selection-toolbar">
+    <div class="live-panel live-panel--table">
+<div class="selection-toolbar">
       <el-checkbox :indeterminate="isIndeterminate" v-model="allChecked" @change="toggleSelectAll">
         {{ multipleSelection.length > 0 ? `已选 ${multipleSelection.length} 条` : '选中本页' }}
       </el-checkbox>
@@ -17,7 +22,7 @@
       style="width: 100%"
       v-loading="loading"
       @selection-change="handleSelectionChange"
-    >
+     class="live-table">
       <el-table-column type="selection" width="55"></el-table-column>
       <!-- 题干列:显示试题的主要内容 -->
       <el-table-column
@@ -100,7 +105,9 @@
     />
 
     <!-- 添加优惠券弹窗 -->
-    <el-dialog
+    </div>
+
+<el-dialog
       title="添加优惠券"
       :visible.sync="couponDialogVisible"
       width="800px"
@@ -308,7 +315,7 @@ export default {
           return;
         }
         this.getLiveCouponList();
-        this.socket = this.$store.state.liveWs[this.liveId]
+        this.socket = (this.$store.state.liveWs[String(this.liveId)] || this.$store.state.liveWs[this.liveId])
       },
       // 初始化时立即执行一次
       immediate: true
@@ -322,7 +329,7 @@ export default {
     // }
     // this.goodsParams.liveId = this.liveId
     // this.getLiveGoodsList();
-    // this.socket = this.$store.state.liveWs[this.liveId]
+    // this.socket = (this.$store.state.liveWs[String(this.liveId)] || this.$store.state.liveWs[this.liveId])
   },
   methods: {
     handleGoodsChange(row){
@@ -342,8 +349,9 @@ export default {
       // 1. 获取「即将切换到的目标状态」(当前状态取反)
       const targetStatus = !row.isShow
       const couponList = row.id;
+      this.socket = this.$store.state.liveWs[String(this.liveId)] || this.$store.state.liveWs[this.liveId]
       if (this.socket == null) {
-        this.$message.error("请从直播间开启展示状态!");
+        this.$message.error("直播连接未就绪,请稍后重试");
         return;
       }
       handleIsShowChange({"couponId":couponList,"isShow":targetStatus,"liveId":this.liveId}).then(res=>{

+ 18 - 9
src/views/live/liveConfig/liveLotteryConf.vue

@@ -1,6 +1,11 @@
 <template>
-  <div class="app-container">
-    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
+  <div class="app-container live-page">
+        <div class="live-page__header">
+      <h2 class="live-page__title">抽奖配置</h2>
+    </div>
+
+<div class="live-panel" v-show="showSearch">
+<el-form class="live-search-form" :model="queryParams" ref="queryForm" :inline="true" label-width="68px">
       <el-form-item label="直播间ID" prop="liveId">
         <el-input
           v-model="queryParams.liveId"
@@ -43,8 +48,10 @@
         <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
       </el-form-item>
     </el-form>
+    </div>
 
-    <el-row :gutter="10" class="mb8">
+    <div class="live-panel live-panel--table">
+<el-row :gutter="10" class="mb8">
       <el-col :span="1.5">
         <el-button
           type="primary"
@@ -91,7 +98,7 @@
       <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
     </el-row>
 
-    <el-table border v-loading="loading" :data="liveLotteryConfList" @selection-change="handleSelectionChange">
+    <el-table border v-loading="loading" :data="liveLotteryConfList" @selection-change="handleSelectionChange" class="live-table">
       <el-table-column type="selection" width="55" align="center" />
       <el-table-column label="抽奖ID" align="center" prop="lotteryId" />
       <el-table-column label="操作商品" align="center" class-name="small-padding fixed-width">
@@ -174,7 +181,9 @@
     />
 
     <!-- 添加或修改直播抽奖配置对话框 -->
-    <el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
+    </div>
+
+<el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
       <el-form ref="form" :model="form" :rules="rules" label-width="80px">
         <el-form-item label="直播间ID" prop="liveId">
           <el-input v-model="form.liveId" placeholder="请输入直播间ID"
@@ -727,8 +736,8 @@ export default {
     },
     /** 抽奖状态修改 */
     handleStatusChange(row, status) {
-      if (this.$store.state.liveWs[this.liveId] == null) {
-        this.msgError("请从直播间进行操作!");
+      if ((this.$store.state.liveWs[String(this.liveId)] || this.$store.state.liveWs[this.liveId]) == null) {
+        this.msgError("直播连接未就绪,请稍后重试");
         return;
       }
       //结束的抽奖不能进行操作
@@ -774,7 +783,7 @@ export default {
             cmd: 'lottery',
             data: doLotteryParam
           };
-          this.$store.state.liveWs[this.liveId].send(JSON.stringify(msg));
+          (this.$store.state.liveWs[String(this.liveId)] || this.$store.state.liveWs[this.liveId]).send(JSON.stringify(msg));
           this.msgSuccess("修改成功");
         }else{
           this.msgError(response.msg);
@@ -840,7 +849,7 @@ export default {
               duration: row.duration
             }
           };
-          this.$store.state.liveWs[this.liveId].send(JSON.stringify(msg));
+          (this.$store.state.liveWs[String(this.liveId)] || this.$store.state.liveWs[this.liveId]).send(JSON.stringify(msg));
         }).catch(() => {});
     },
     showInfo(row) {

+ 18 - 9
src/views/live/liveConfig/liveRedConf.vue

@@ -1,6 +1,11 @@
 <template>
-  <div class="app-container">
-    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
+  <div class="app-container live-page">
+        <div class="live-page__header">
+      <h2 class="live-page__title">红包配置</h2>
+    </div>
+
+<div class="live-panel" v-show="showSearch">
+<el-form class="live-search-form" :model="queryParams" ref="queryForm" :inline="true" label-width="68px">
       <el-form-item label="红包状态" prop="redStatus">
         <el-select v-model="queryParams.redStatus" placeholder="请选择红包状态" clearable size="small">
           <el-option v-for="(item,index) in redStatusOptions" :key="item.dictValue+index" :label="item.dictLabel" :value="item.dictValue" />
@@ -51,8 +56,10 @@
         <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
       </el-form-item>
     </el-form>
+    </div>
 
-    <el-row :gutter="10" class="mb8">
+    <div class="live-panel live-panel--table">
+<el-row :gutter="10" class="mb8">
       <el-col :span="1.5">
         <el-button
           type="primary"
@@ -88,7 +95,7 @@
       <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
     </el-row>
 
-    <el-table border v-loading="loading" :data="liveRedConfList" @selection-change="handleSelectionChange">
+    <el-table border v-loading="loading" :data="liveRedConfList" @selection-change="handleSelectionChange" class="live-table">
       <el-table-column type="selection" width="55" align="center" />
       <el-table-column label="红包ID" align="center" prop="redId" />
       <el-table-column label="红包状态" align="center" prop="redStatus" :formatter="redStatusFormatter"/>
@@ -171,7 +178,9 @@
     />
 
     <!-- 领取记录 -->
-    <el-dialog :title="claimDialog.title" :visible.sync="claimDialog.open" width="800px" append-to-body>
+    </div>
+
+<el-dialog :title="claimDialog.title" :visible.sync="claimDialog.open" width="800px" append-to-body>
       <el-table v-loading="claimDialog.loading" :data="claimDialog.list" border>
         <el-table-column label="用户ID" align="center" prop="userId" width="100" />
         <el-table-column label="昵称" align="center" prop="nickname" min-width="120" />
@@ -343,8 +352,8 @@ export default {
   },
   methods: {
     handleStatusChange(row, status) {
-      if (this.$store.state.liveWs[this.liveId] == null) {
-        this.msgError("请从直播间进行操作!");
+      if ((this.$store.state.liveWs[String(this.liveId)] || this.$store.state.liveWs[this.liveId]) == null) {
+        this.msgError("直播连接未就绪,请稍后重试");
         return;
       }
       //结束的抽奖不能进行操作
@@ -391,7 +400,7 @@ export default {
             cmd: 'red',
             data: doRedParam
           };
-          this.$store.state.liveWs[this.liveId].send(JSON.stringify(msg));
+          (this.$store.state.liveWs[String(this.liveId)] || this.$store.state.liveWs[this.liveId]).send(JSON.stringify(msg));
 
           this.msgSuccess("修改成功");
         }else{
@@ -551,7 +560,7 @@ export default {
               duration: row.duration
             }
           };
-          this.$store.state.liveWs[this.liveId].send(JSON.stringify(msg));
+          (this.$store.state.liveWs[String(this.liveId)] || this.$store.state.liveWs[this.liveId]).send(JSON.stringify(msg));
         }).catch(() => {});
     },
     /** 导出按钮操作 */

+ 9 - 2
src/views/live/liveConfig/liveReplay.vue

@@ -1,5 +1,11 @@
 <template>
-  <div class="app-container" v-loading.fullscreen.lock="loading">
+<div class="app-container live-page" v-loading.fullscreen.lock="loading">
+    <div class="live-page__header">
+      <h2 class="live-page__title">直播回放</h2>
+    </div>
+
+    <div class="live-panel">
+
     <!-- 直播回放开关区域 -->
 
     <div class="switch-area">
@@ -151,7 +157,8 @@
 <!--        </el-table-column>-->
       </el-table>
     </div>
-  </div>
+    </div>
+</div>
 </template>
 
 <script>

+ 69 - 71
src/views/live/liveConfig/preview.vue

@@ -1,83 +1,81 @@
 <template>
-  <div class="app-container" v-loading.fullscreen.lock="loading">
-    <!-- 直播回放开关区域 -->
+  <div class="app-container live-page" v-loading.fullscreen.lock="loading">
+    <div class="live-page__header">
+      <h2 class="live-page__title">直播预告</h2>
+    </div>
 
-    <!-- 回放内容区域 -->
-    <div class="playback-content">
-      <div>
-        <span>预告内容:</span>
-        <el-button class="upload-btn" type="normal" @click="handleUploadVideo">上传视频</el-button>
-        <!--        <el-button-->
-        <!--          class="upload-btn"-->
-        <!--          type="text"-->
-        <!--          icon="el-icon-plus"-->
-        <!--          @click="handleUploadVideo"-->
-        <!--        >上传视频</el-button>-->
-        <!--        <span class="upload-tip">上传视频大小不可超过5GB</span>-->
-      </div>
-      <el-dialog title="添加直播预告" :visible.sync="open" width="900px" append-to-body>
-        <el-form ref="form" :model="form"  label-width="80px">
-          <video-upload
-            :type = "1"
-            :isPrivate = "isPrivate"
-            :fileKey.sync = "form.fileKey"
-            :fileSize.sync = "form.fileSize"
-            :videoUrl.sync="videoUrl"
-            :fileName.sync="form.fileName"
-            :line_2.sync="form.lineTwo"
-            :line_1.sync="form.lineOne"
-            :thumbnail.sync="form.thumbnail"
-            :uploadType.sync="form.uploadType"
-            :isTranscode.sync="form.isTranscode"
-            :transcodeFileKey.sync="form.transcodeFileKey"
-            @video-duration="handleVideoDuration"
-            @change="handleVideoChange"
-            ref="videoUpload"
-            append-to-body
-          />
-        </el-form>
-        <div slot="footer" class="dialog-footer">
-          <el-button type="primary" @click="submitForm">确 定</el-button>
-          <el-button @click="open = false">取 消</el-button>
+    <div class="live-panel live-panel--table">
+      <div class="playback-content">
+        <div>
+          <span>预告内容:</span>
+          <el-button class="upload-btn" type="normal" @click="handleUploadVideo">上传视频</el-button>
         </div>
-      </el-dialog>
 
-      <!-- 视频列表 -->
-      <el-table
-        :data="videoList"
-        border
-        style="width: 100%; margin-top: 10px"
-      >
-        <el-table-column prop="duration" label="时长" />
-        <el-table-column prop="updateTime" label="更新时间" />
-        <el-table-column label="视频地址(可点击查看)" prop="videoUrl" >
-          <template slot-scope="scope">
-            <el-tooltip
-              :content="scope.row.videoUrl"
-              placement="top"
-              effect="dark"
-            >
-              <a
-                :href="scope.row.videoUrl"
-                target="_blank"
-                class="video-url-container"
-                rel="noopener noreferrer"
+        <el-table
+          :data="videoList"
+          border
+          class="live-table"
+          style="width: 100%; margin-top: 10px"
+        >
+          <el-table-column prop="duration" label="时长" />
+          <el-table-column prop="updateTime" label="更新时间" />
+          <el-table-column label="视频地址(可点击查看)" prop="videoUrl">
+            <template slot-scope="scope">
+              <el-tooltip
+                :content="scope.row.videoUrl"
+                placement="top"
+                effect="dark"
               >
-                {{
-                  scope.row.videoUrl.length > 32
-                    ? scope.row.videoUrl.substring(0, 32) + '...'
-                    : scope.row.videoUrl
-                }}
-                <i class="el-icon-external-link video-url-icon"></i>
-              </a>
-            </el-tooltip>
-          </template>
-        </el-table-column>
-      </el-table>
+                <a
+                  :href="scope.row.videoUrl"
+                  target="_blank"
+                  class="video-url-container"
+                  rel="noopener noreferrer"
+                >
+                  {{
+                    scope.row.videoUrl.length > 32
+                      ? scope.row.videoUrl.substring(0, 32) + '...'
+                      : scope.row.videoUrl
+                  }}
+                  <i class="el-icon-external-link video-url-icon"></i>
+                </a>
+              </el-tooltip>
+            </template>
+          </el-table-column>
+        </el-table>
+      </div>
     </div>
+
+    <el-dialog title="添加直播预告" :visible.sync="open" width="900px" append-to-body>
+      <el-form ref="form" :model="form" label-width="80px">
+        <video-upload
+          :type = "1"
+          :isPrivate = "isPrivate"
+          :fileKey.sync = "form.fileKey"
+          :fileSize.sync = "form.fileSize"
+          :videoUrl.sync="videoUrl"
+          :fileName.sync="form.fileName"
+          :line_2.sync="form.lineTwo"
+          :line_1.sync="form.lineOne"
+          :thumbnail.sync="form.thumbnail"
+          :uploadType.sync="form.uploadType"
+          :isTranscode.sync="form.isTranscode"
+          :transcodeFileKey.sync="form.transcodeFileKey"
+          @video-duration="handleVideoDuration"
+          @change="handleVideoChange"
+          ref="videoUpload"
+          append-to-body
+        />
+      </el-form>
+      <div slot="footer" class="dialog-footer">
+        <el-button type="primary" @click="submitForm">确 定</el-button>
+        <el-button @click="open = false">取 消</el-button>
+      </div>
+    </el-dialog>
   </div>
 </template>
 
+
 <script>
 import { getLive,} from '@/api/live/live'
 import { addLiveVideo, getLiveVideoByLiveIdAndType} from '@/api/live/liveVideo'

+ 17 - 7
src/views/live/liveConfig/task.vue

@@ -1,6 +1,11 @@
 <template>
-  <div class="app-container">
-    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
+  <div class="app-container live-page">
+        <div class="live-page__header">
+      <h2 class="live-page__title">自动化任务</h2>
+    </div>
+
+<div class="live-panel" v-show="showSearch">
+<el-form class="live-search-form" :model="queryParams" ref="queryForm" :inline="true" label-width="68px">
       <el-form-item label="直播间ID" prop="liveId">
         <el-input
           v-model="queryParams.liveId"
@@ -40,8 +45,10 @@
         <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
       </el-form-item>
     </el-form>
+    </div>
 
-    <el-row :gutter="10" class="mb8">
+    <div class="live-panel live-panel--table">
+<el-row :gutter="10" class="mb8">
       <el-col :span="1.5">
         <el-button
           type="primary"
@@ -88,7 +95,7 @@
       <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
     </el-row>
 
-    <el-table border v-loading="loading" :data="taskList" @selection-change="handleSelectionChange">
+    <el-table border v-loading="loading" :data="taskList" @selection-change="handleSelectionChange" class="live-table">
       <el-table-column type="selection" width="55" align="center" />
       <el-table-column label="编号" align="center" prop="id" />
       <el-table-column label="任务名称" align="center" prop="taskName" />
@@ -136,7 +143,9 @@
     />
 
     <!-- 添加或修改直播间自动化任务配置对话框 -->
-    <el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
+    </div>
+
+<el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
       <el-form ref="form" :model="form" :rules="rules" label-width="80px">
         <el-form-item label="直播间ID" prop="liveId">
           <el-input :disabled="liveAbled" v-model="form.liveId" placeholder="请输入直播间ID" />
@@ -427,7 +436,7 @@ export default {
   },
   created() {
 
-    this.socket = this.$store.state.liveWs[this.liveId]
+    this.socket = (this.$store.state.liveWs[String(this.liveId)] || this.$store.state.liveWs[this.liveId])
   },
   methods: {
 
@@ -779,8 +788,9 @@ export default {
     },
     /** 删除按钮操作 */
     handleDelete(row) {
+      this.socket = this.$store.state.liveWs[String(this.liveId)] || this.$store.state.liveWs[this.liveId]
       if(this.socket == null) {
-        this.$message.error("请进入直播间后,在进行操作!")
+        this.$message.error("直播连接未就绪,请稍后重试")
         return;
       }
       const ids = row.id || this.ids;

+ 10 - 1
src/views/live/liveConfig/watchReward.vue

@@ -1,5 +1,12 @@
 <template >
-  <div v-loading.fullscreen.lock="loading">
+<div class="app-container live-page">
+    <div class="live-page__header">
+      <h2 class="live-page__title">观看奖励</h2>
+    </div>
+
+    <div class="live-panel">
+<div v-loading.fullscreen.lock="loading">
+
     <!-- 提示信息 -->
     <div class="tip-message" >
       设置观看奖励,用户达到直播观看时长后可领取奖励
@@ -230,6 +237,8 @@
       </el-form>
     </div>
   </div>
+    </div>
+</div>
 </template>
 
 <script>

+ 379 - 243
src/views/live/liveConsole/index.vue

@@ -1,67 +1,54 @@
 <template>
   <!-- 直播中控台 start -->
-  <el-row type="flex" justify="center" class="live-console" :gutter="10" v-loading="loading">>
+  <el-row type="flex" justify="center" class="live-console live-page" :gutter="10" v-loading="loading">
     <!-- 聊天 start -->
     <el-col class="live-console-col" :span="6">
       <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" style="margin-bottom: 20px;">
-              <el-row v-if="m.userId !== userId" type="flex" align="top" >
-                <el-col :span="24" style="display: flex; align-items: center;">
-                  <el-avatar style="width: 36px;height: 36px;" :src="m.avatar!==null?m.avatar: require('@/assets/image/default-a.png')"/>
-                   <div style="font-size: 14px; color: #999;margin: 0 12px;">{{ m.nickName }}</div>
-                   <div style="display: flex; align-items: center; gap: 8px;">
-                     <a style="cursor: pointer;color: #13C2C2;font-size: 14px;" @click="changeUserState(m)">{{ m.msgStatus === 1 ? '解禁' : '禁言' }}</a>
-                     <span style="color:#13C2C2;">|</span>
-                     <a style="cursor: pointer;color: #13C2C2;font-size: 14px;" @click="blockUser(m)">拉黑</a>
-                     <span style="color:#13C2C2;">|</span>
-                     <a style="cursor: pointer;color: #ff0000;padding: 8px 8px 0 0;font-size: 12px;" @click="deleteMsg(m)">删除</a>
-                   </div>
-                  </el-col>
-              </el-row>
-              <el-row v-if="m.userId !== userId" type="flex" align="top">
-                <el-col :span="19" :offset="3">
-                  <div class="msg-box">
-                    {{ m.msg }}
-                  </div>
-                </el-col>
-              </el-row>
-              <el-row v-if="m.userId === userId" style="padding: 8px 0;margin-right: 10px;" type="flex" align="top" justify="end">
-                <div style="display: flex;justify-content: flex-end">
-                  <div style="display: flex;justify-content: flex-end;flex-direction: column;max-width: 200px;align-items: flex-end">
-                    <div style="display: flex;align-items: center;">
-                      <div style="font-size: 14px; color: #999;margin-right: 12px;">{{ m.nickName }}</div>
-                       <el-avatar :src="m.avatar?m.avatar: require('@/assets/image/default-a.png')" style="width: 36px;height: 36px;"/>
+          <el-scrollbar class="chat-scroll" ref="manageRightRef">
+            <div class="chat-msg" v-for="m in msgList" :key="m.msgId || m.userId + '-' + m.msg">
+              <div v-if="m.userId !== userId" class="chat-msg__row chat-msg__row--left">
+                <el-avatar class="chat-msg__avatar" :src="m.avatar!==null?m.avatar: require('@/assets/image/default-a.png')"/>
+                <div class="chat-msg__body">
+                  <div class="chat-msg__meta">
+                    <span class="chat-msg__name">{{ m.nickName }}</span>
+                    <div class="chat-msg__ops">
+                      <a class="chat-msg__op" @click="changeUserState(m)">{{ m.msgStatus === 1 ? '解禁' : '禁言' }}</a>
+                      <a class="chat-msg__op" @click="blockUser(m)">拉黑</a>
+                      <a class="chat-msg__op chat-msg__op--danger" @click="deleteMsg(m)">删除</a>
                     </div>
-
-                   <div class="msg-box-right">
-                   {{ m.msg }}</div>
                   </div>
-
+                  <div class="msg-box">{{ m.msg }}</div>
                 </div>
-              </el-row>
-            </el-row>
-            <!-- 底部留白 -->
-            <div style="height: 20px;"></div>
+              </div>
+              <div v-else class="chat-msg__row chat-msg__row--right">
+                <div class="chat-msg__body chat-msg__body--right">
+                  <div class="chat-msg__meta chat-msg__meta--right">
+                    <span class="chat-msg__name">{{ m.nickName }}</span>
+                    <el-avatar class="chat-msg__avatar" :src="m.avatar?m.avatar: require('@/assets/image/default-a.png')"/>
+                  </div>
+                  <div class="msg-box-right">{{ m.msg }}</div>
+                </div>
+              </div>
+            </div>
+            <div class="chat-scroll__spacer"></div>
           </el-scrollbar>
 
           <!-- 消息输入区域 -->
-          <div class="shuru" style="padding: 20px; border-top: 1px solid #ebeef5; background-color: #fff; min-height: 120px;">
+          <div class="shuru">
             <el-input
               type="textarea"
               v-model="newMsg"
               placeholder="请输入消息..."
-              :rows="8"
+              :rows="3"
               @keyup.enter.native="sendMessage"
               clearable
               resize="none"
-              style="flex: 1; margin-right: 10px;"
             >
             </el-input>
-            <div style="display: flex; justify-content: flex-end; margin-top: 10px;">
-              <el-button style="background: #13C2C2;color: #fff;" @click="showTopMsgDialog">顶部消息</el-button>
-              <el-button style="background: #13C2C2;color: #fff;" @click="sendMessage">发送</el-button>
+            <div class="shuru__actions">
+              <el-button class="btn-accent" @click="showTopMsgDialog">顶部消息</el-button>
+              <el-button class="btn-accent" @click="sendMessage">发送</el-button>
             </div>
           </div>
         </el-tab-pane>
@@ -72,14 +59,14 @@
 
     <!-- 直播/视频 start -->
     <el-col class="live-console-col" :span="12">
-      <div style="background: #000; border-radius: 5px; overflow: hidden; margin: 10px 5px;">
-        <div style="border-radius: 5px; overflow: hidden;" v-if="!isAudit">
-          <img :src="require('@/assets/image/videoIsAudit.png')" style="width: 100%; height: 45vh;">
+      <div class="player-panel">
+        <div class="player-panel__inner" v-if="!isAudit">
+          <img :src="require('@/assets/image/videoIsAudit.png')" class="player-panel__poster">
         </div>
-        <div style="border-radius: 5px; overflow: hidden;" v-else-if="status != 2 && status != 4">
-          <img :src="require('@/assets/image/videoNotStart.png')" style="width: 100%; height: 45vh;">
+        <div class="player-panel__inner" v-else-if="status != 2 && status != 4">
+          <img :src="require('@/assets/image/videoNotStart.png')" class="player-panel__poster">
         </div>
-        <div style="border-radius: 5px; overflow: hidden;" v-else-if="liveType == 1">
+        <div class="player-panel__inner" v-else-if="liveType == 1">
           <video
             id="live-tcplayer-container"
             ref="livingPlayer"
@@ -94,7 +81,7 @@
             已播放:<span id="liveElapsedTime">00:00:00</span>
           </div>
         </div>
-        <div style="border-radius: 5px; overflow: hidden;" v-else-if="liveType == 2">
+        <div class="player-panel__inner" v-else-if="liveType == 2">
           <video
             controls
             ref="videoPlayer"
@@ -120,7 +107,7 @@
             已播放:<span id="elapsedTime">00:00:00</span>
           </div>
         </div>
-        <div style="border-radius: 5px; overflow: hidden;" v-else-if="liveType == 3">
+        <div class="player-panel__inner" v-else-if="liveType == 3">
           <video
             controls
             ref="liveReplay"
@@ -133,44 +120,39 @@
             <source :src="videoUrl" type="application/x-mpegURL">
           </video>
         </div>
-        <div style="border-radius: 5px; overflow: hidden;" v-else>
-          <img :src="require('@/assets/image/videoNotStart.png')" style="width: 100%; height: 45vh;">
+        <div class="player-panel__inner" v-else>
+          <img :src="require('@/assets/image/videoNotStart.png')" class="player-panel__poster">
         </div>
       </div>
       <!-- 底部导航栏 -->
-      <div style="display: flex; justify-content: space-around; padding: 15px 0; background: #fff; border-top: 1px solid #f0f0f0;">
-        <div class="top-box" style="display: flex; flex-direction: column; align-items: center; cursor: pointer;" @click="handleClickRed">
-          <!-- <i class="el-icon-money" style="font-size: 20px;"></i> -->
-          <img src="@/assets/image/hbpz.png" width="48"/>
-          <span style="font-size: 12px; margin-top: 10px;">红包配置</span>
+      <div class="tool-bar">
+        <div class="top-box" @click="handleClickRed">
+          <img src="@/assets/image/hbpz.png"/>
+          <span>红包配置</span>
         </div>
-        <div class="top-box" style="display: flex; flex-direction: column; align-items: center; cursor: pointer;" @click="handleClickLottery">
-          <!-- <i class="el-icon-present" style="font-size: 20px;"></i> -->
-          <img src="@/assets/image/cjpz.png" width="48"/>
-          <span style="font-size: 12px; margin-top: 10px;">抽奖配置</span>
+        <div class="top-box" @click="handleClickLottery">
+          <img src="@/assets/image/cjpz.png"/>
+          <span>抽奖配置</span>
         </div>
-        <div class="top-box" style="display: flex; flex-direction: column; align-items: center; cursor: pointer;" @click="handleClickGoods">
-          <!-- <i class="el-icon-goods" style="font-size: 20px;"></i> -->
-          <img src="@/assets/image/sp.png" width="48"/>
-          <span style="font-size: 12px; margin-top: 10px;">商品</span>
+        <div class="top-box" @click="handleClickGoods">
+          <img src="@/assets/image/sp.png"/>
+          <span>商品</span>
         </div>
-        <div class="top-box" style="display: flex; flex-direction: column; align-items: center; cursor: pointer;" @click="handleClickOrder">
-          <!-- <i class="el-icon-goods" style="font-size: 20px;"></i> -->
-          <img src="@/assets/image/zbdd.png" width="48"/>
-          <span style="font-size: 12px;margin-top: 10px;">直播订单</span>
+        <div class="top-box" @click="handleClickOrder">
+          <img src="@/assets/image/zbdd.png"/>
+          <span>直播订单</span>
         </div>
-        <div class="top-box" style="display: flex; flex-direction: column; align-items: center; cursor: pointer;" @click="handleClickCoupon">
-          <!-- <i class="el-icon-goods" style="font-size: 20px;"></i> -->
-          <img src="@/assets/image/zbyhq.png" width="48"/>
-          <span style="font-size: 12px; margin-top: 10px;">直播优惠券</span>
+        <div class="top-box" @click="handleClickCoupon">
+          <img src="@/assets/image/zbyhq.png"/>
+          <span>直播优惠券</span>
         </div>
       </div>
      <!-- <el-radio-group v-model="tableRadio" >
         <el-radio-button label="订单数">订单数</el-radio-button>
       </el-radio-group> -->
-      <div  style="position: relative;width: 100%; height: 270px;margin-top: 30px;">
-        <div ref="chartContainer" style="width: 100%; height: 100%;"></div>
-        <div style="position: absolute; top: 10px; right: 10px; background: #fff; padding: 5px; z-index: 1;">
+      <div class="chart-panel">
+        <div ref="chartContainer" class="chart-panel__canvas"></div>
+        <div class="chart-panel__filters">
           <el-select v-model="searchQuery.timeOptions" placeholder="请选择" style="width: 150px"  @change="timeChange">
             <el-option
               v-for="item in timeOptions"
@@ -197,69 +179,51 @@
     <el-col class="live-console-col" :span="6">
       <el-tabs class="live-console-tab-right" v-model="tabLeft.activeName" @tab-click="handleClick" :stretch="true">
         <el-tab-pane :label="onlineLabel" name="online" style="height: 100%;">
-          <el-scrollbar ref="manageLeftRef_online" style="height:100%; width: 100%;padding: 10px;">
-            <el-row  class="row-box" style="margin-bottom: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>
-                  <el-col :span="19" :offset="1">{{ u.nickName }}</el-col>
-                  <el-col :span="19" :offset="1">{{ u.userId }}</el-col>
-                </el-row>
-              </el-col>
-              <el-col :span="4" >
-                <el-popover
-                  width="100"
-                  trigger="click">
-                  <a style="cursor: pointer;color: #ff0000;" @click="changeUserState(u)">{{ u.msgStatus === 1 ? '解禁' : '禁言' }}</a>
-                  <a style="cursor: pointer;color: #ff0000;margin-left:10px" @click="blockUser(u)">拉黑</a>
-                  <i class="el-icon-more" slot="reference"></i>
-                </el-popover>
-              </el-col>
-            </el-row>
+          <el-scrollbar ref="manageLeftRef_online" class="user-scroll">
+            <div class="row-box" v-for="u in onlineDisplayList" :key="u.userId">
+              <el-avatar class="user-row__avatar" :src="u.avatar"></el-avatar>
+              <div class="user-row__info">
+                <div class="user-row__name">{{ u.nickName }}</div>
+                <div class="user-row__id">{{ u.userId }}</div>
+              </div>
+              <el-popover width="100" trigger="click">
+                <a class="user-row__pop-op" @click="changeUserState(u)">{{ u.msgStatus === 1 ? '解禁' : '禁言' }}</a>
+                <a class="user-row__pop-op" @click="blockUser(u)">拉黑</a>
+                <i class="el-icon-more" slot="reference"></i>
+              </el-popover>
+            </div>
           </el-scrollbar>
         </el-tab-pane>
         <el-tab-pane :label="offlineLabel" name="offline" style="height: 100%;">
-          <el-scrollbar ref="manageLeftRef_offline" style="height:100%; width: 100%;padding: 10px;">
-            <el-row  class="row-box" style="margin-bottom: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>
-                  <el-col :span="19" :offset="1">{{ u.nickName }}</el-col>
-                  <el-col :span="19" :offset="1">{{ u.userId }}</el-col>
-                </el-row>
-              </el-col>
-              <el-col :span="4" >
-                <el-popover
-                  width="100"
-                  trigger="click">
-                  <a style="cursor: pointer;color: #ff0000;" @click="changeUserState(u)">{{ u.msgStatus === 1 ? '解禁' : '禁言' }}</a>
-                  <a style="cursor: pointer;color: #ff0000;margin-left:10px" @click="blockUser(u)">拉黑</a>
-                  <i class="el-icon-more" slot="reference"></i>
-                </el-popover>
-              </el-col>
-            </el-row>
+          <el-scrollbar ref="manageLeftRef_offline" class="user-scroll">
+            <div class="row-box" v-for="u in offlineDisplayList" :key="u.userId">
+              <el-avatar class="user-row__avatar" :src="u.avatar"></el-avatar>
+              <div class="user-row__info">
+                <div class="user-row__name">{{ u.nickName }}</div>
+                <div class="user-row__id">{{ u.userId }}</div>
+              </div>
+              <el-popover width="100" trigger="click">
+                <a class="user-row__pop-op" @click="changeUserState(u)">{{ u.msgStatus === 1 ? '解禁' : '禁言' }}</a>
+                <a class="user-row__pop-op" @click="blockUser(u)">拉黑</a>
+                <i class="el-icon-more" slot="reference"></i>
+              </el-popover>
+            </div>
           </el-scrollbar>
         </el-tab-pane>
         <el-tab-pane :label="silencedUserLabel" name="silenced" style="height: 100%;">
-          <el-scrollbar ref="manageLeftRef_silenced" style="height: 800px; width: 100%;padding: 10px;">
-            <el-row class="row-box" style="margin-bottom: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>
-                  <el-col :span="19" :offset="1">{{ u.nickName }}</el-col>
-                  <el-col :span="19" :offset="1">{{ u.userId }}</el-col>
-                </el-row>
-              </el-col>
-              <el-col :span="4" >
-                <el-popover
-                  width="100"
-                  trigger="click">
-                  <a style="cursor: pointer;color: #ff0000;" @click="changeUserState(u)">{{ u.msgStatus === 1 ? '解禁' : '禁言' }}</a>
-                  <a style="cursor: pointer;color: #ff0000;margin-left:10px" @click="blockUser(u)">拉黑</a>
-                  <i class="el-icon-more" slot="reference"></i>
-                </el-popover>
-              </el-col>
-            </el-row>
+          <el-scrollbar ref="manageLeftRef_silenced" class="user-scroll user-scroll--tall">
+            <div class="row-box" v-for="u in silencedDisplayList" :key="u.userId">
+              <el-avatar class="user-row__avatar" :src="u.avatar"></el-avatar>
+              <div class="user-row__info">
+                <div class="user-row__name">{{ u.nickName }}</div>
+                <div class="user-row__id">{{ u.userId }}</div>
+              </div>
+              <el-popover width="100" trigger="click">
+                <a class="user-row__pop-op" @click="changeUserState(u)">{{ u.msgStatus === 1 ? '解禁' : '禁言' }}</a>
+                <a class="user-row__pop-op" @click="blockUser(u)">拉黑</a>
+                <i class="el-icon-more" slot="reference"></i>
+              </el-popover>
+            </div>
           </el-scrollbar>
         </el-tab-pane>
       </el-tabs>
@@ -470,6 +434,7 @@ export default {
         );
       }
     })
+    this.releaseWebSocket()
   },
   // 使用 deactivated 和 activated 钩子替代 beforeDestroy 和 destroyed
   deactivated() {
@@ -1270,13 +1235,29 @@ export default {
       }
     },
     connectWebSocket() {
-      this.$store.dispatch('initLiveWs', {
+      this._wsMessageHandler = (event) => this.handleWsMessage(event)
+      this.$store.dispatch('acquireLiveWs', {
         liveWsUrl: this.liveWsUrl,
         liveId: this.liveId,
-        userId: this.userId
+        userId: this.userId,
+        owner: 'console'
+      }).then((ws) => {
+        this.socket = ws || this.$store.state.liveWs[String(this.liveId)]
+        if (this.socket) {
+          this.socket.addMessageListener(this._wsMessageHandler)
+        }
+      })
+    },
+    releaseWebSocket() {
+      if (this.socket && this._wsMessageHandler) {
+        this.socket.removeMessageListener(this._wsMessageHandler)
+      }
+      this._wsMessageHandler = null
+      this.$store.dispatch('releaseLiveWs', {
+        liveId: this.liveId,
+        owner: 'console'
       })
-      this.socket = this.$store.state.liveWs[this.liveId]
-      this.socket.onmessage = (event) => this.handleWsMessage(event)
+      this.socket = null
     },
     handleWsMessage(event) {
       let { code, data } = JSON.parse(event.data)
@@ -1425,112 +1406,267 @@ export default {
 }
 </script>
 
-<style lang="stylus" scoped>
-  .top-box{
-    justify-content: center;
-    width: 140px;
-    height: 110px;
-    background: #FFFFFF;
-    border-radius: 16px 16px 16px 16px;
-    border: 1px solid #EBF0F9;
-  }
-  .row-box{
-    padding: 10px 16px;
-   background: #FAFBFD;
-   border-radius: 8px 8px 8px 8px;
-  }
-  .msg-box{
-   word-break: break-all;
-       word-wrap: break-word;
-       white-space: pre-wrap;
-     background-color: #F7F8FA;
-     padding: 10px;
-     border-radius: 0px 12px 12px 12px;
-     font-size: 14px;
-    display: inline-block;
-  }
-  .msg-box-right{
-    margin-right: 50px;
-   word-break: break-all;
-       word-wrap: break-word;
-       white-space: pre-wrap;
-     background-color:#13C2C2;
-     padding: 10px;
-     color: #fff;
-     border-radius: 12px 0px 12px 12px;
-     font-size: 14px;
-    display: inline-block;
-  }
-  .shuru ::v-deep.el-textarea__inner{
-    background-color: #F7F8FA !important;
-    border-radius: 12px;
-  }
-  .el-icon-more{
-    color:#D9D9D9
-  }
-  .live-console-tab-right{
-    height: 100%;
-  }
- .live-console-tab-right .el-scrollbar__view{
-    padding: 10px;
-  }
-  .live-console-tab-right .el-col-offset-1{
-    color:#666
-  }
- .live-console-tab-right ::v-deep.el-tabs__content{
-    height: 90%;
-  }
-</style>
 <style scoped>
-.talk-list{
+.live-console {
+  width: 90vw;
+  padding: 10px 0;
+}
+.live-console .live-console-col {
+  height: 88vh;
+  margin-left: 5px;
+  padding: 0 10px;
+  background-color: #fff;
+  border: 1px solid #e8eaed;
+  border-radius: 10px;
+}
+.chat-scroll {
+  height: 500px;
+  width: 100%;
+}
+.chat-scroll__spacer {
+  height: 12px;
+}
+.chat-msg {
+  margin-bottom: 14px;
+}
+.chat-msg__row {
   display: flex;
+  align-items: flex-start;
+  gap: 10px;
+}
+.chat-msg__row--right {
+  justify-content: flex-end;
+}
+.chat-msg__avatar {
+  width: 32px !important;
+  height: 32px !important;
+  flex-shrink: 0;
+}
+.chat-msg__body {
+  min-width: 0;
+  flex: 1;
+}
+.chat-msg__body--right {
+  flex: 0 1 auto;
+  max-width: 220px;
+  display: flex;
+  flex-direction: column;
+  align-items: flex-end;
+}
+.chat-msg__meta {
+  display: flex;
+  align-items: center;
+  flex-wrap: wrap;
+  gap: 6px 10px;
+  margin-bottom: 6px;
+}
+.chat-msg__meta--right {
+  justify-content: flex-end;
+}
+.chat-msg__name {
+  font-size: 13px;
+  color: #8a919f;
+}
+.chat-msg__ops {
+  display: flex;
+  align-items: center;
+  gap: 8px;
+}
+.chat-msg__op {
+  cursor: pointer;
+  font-size: 12px;
+  color: #8a919f;
+  line-height: 1;
+}
+.chat-msg__op:hover {
+  color: #1a6d5b;
+}
+.chat-msg__op--danger:hover {
+  color: #c45656;
+}
+.msg-box {
+  word-break: break-all;
+  word-wrap: break-word;
+  white-space: pre-wrap;
+  background-color: #f4f6f8;
+  padding: 8px 10px;
+  border-radius: 0 10px 10px 10px;
+  font-size: 13px;
+  color: #1f2329;
+  display: inline-block;
+  line-height: 1.5;
+}
+.msg-box-right {
+  word-break: break-all;
+  word-wrap: break-word;
+  white-space: pre-wrap;
+  background-color: #1a6d5b;
+  padding: 8px 10px;
+  color: #fff;
+  border-radius: 10px 0 10px 10px;
+  font-size: 13px;
+  display: inline-block;
+  line-height: 1.5;
+}
+.shuru {
+  padding: 12px 14px;
+  border-top: 1px solid #e8eaed;
+  background-color: #fff;
+}
+.shuru__actions {
+  display: flex;
+  justify-content: flex-end;
+  gap: 8px;
+  margin-top: 8px;
+}
+.btn-accent {
+  background: #1a6d5b !important;
+  border-color: #1a6d5b !important;
+  color: #fff !important;
+}
+.shuru ::v-deep .el-textarea__inner {
+  background-color: #f4f6f8 !important;
+  border-radius: 8px;
+  border: none !important;
+  box-shadow: none !important;
+  resize: none !important;
+  min-height: 72px !important;
+}
+.player-panel {
+  background: #000;
+  border-radius: 10px;
+  overflow: hidden;
+  margin: 10px 5px;
+}
+.player-panel__inner {
+  overflow: hidden;
+}
+.player-panel__poster {
+  width: 100%;
+  height: 45vh;
+  display: block;
+  object-fit: cover;
+}
+.tool-bar {
+  display: flex;
+  justify-content: space-around;
+  align-items: center;
+  padding: 10px 8px;
+  background: #fff;
+  border-top: 1px solid #e8eaed;
+}
+.top-box {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+  cursor: pointer;
+  min-width: 64px;
+  padding: 4px 6px;
+  border-radius: 8px;
+}
+.top-box:hover {
+  background: #f4f6f8;
+}
+.top-box img {
+  width: 36px;
+  height: 36px;
+  display: block;
+}
+.top-box span {
+  font-size: 12px;
+  margin-top: 6px;
+  color: #5c6470;
+}
+.chart-panel {
+  position: relative;
+  width: 100%;
+  height: 270px;
+  margin-top: 20px;
+}
+.chart-panel__canvas {
+  width: 100%;
+  height: 100%;
+}
+.chart-panel__filters {
+  position: absolute;
+  top: 10px;
+  right: 10px;
+  background: #fff;
+  padding: 5px;
+  z-index: 1;
+  display: flex;
+  gap: 8px;
+}
+.user-scroll {
+  height: 100%;
+  width: 100%;
+  padding: 8px;
+}
+.user-scroll--tall {
+  height: 800px;
+}
+.row-box {
+  display: flex;
+  align-items: center;
+  gap: 10px;
+  padding: 10px 12px;
+  margin-bottom: 8px;
+  background: #fafbfc;
+  border-radius: 8px;
+  border: 1px solid transparent;
+}
+.row-box:hover {
+  border-color: #e8eaed;
+}
+.user-row__avatar {
+  width: 36px !important;
+  height: 36px !important;
+  flex-shrink: 0;
+}
+.user-row__info {
+  flex: 1;
+  min-width: 0;
+}
+.user-row__name {
+  font-size: 13px;
+  color: #1f2329;
+  line-height: 1.35;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+}
+.user-row__id {
+  margin-top: 2px;
+  font-size: 12px;
+  color: #8a919f;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+}
+.user-row__pop-op {
+  cursor: pointer;
+  color: #c45656;
+  font-size: 12px;
+  margin-right: 8px;
+}
+.el-icon-more {
+  color: #c0c4cc;
+  cursor: pointer;
+}
+.live-console-tab-right {
+  height: 100%;
+}
+.live-console-tab-right ::v-deep .el-tabs__content {
+  height: 90%;
+}
+::v-deep .el-scrollbar__wrap {
+  overflow-x: hidden;
+}
+.tcplayer-live {
+  width: 100%;
+}
+::v-deep .tcplayer {
+  margin: 0 auto;
 }
-  .live-console {
-    width: 90vw;
-    padding: 10px 0;
-  }
-  .live-console .live-console-col {
-    height: 88vh;
-    margin-left: 5px;
-    padding: 0 10px;
-    background-color: white;
-    border-radius: 4px;
-  }
-  /*隐藏水平滚动条*/
-  ::v-deep .el-scrollbar__wrap {
-    overflow-x: hidden;
-  }
-  /* 消息输入区域 */
-  .chat-input {
-    display: flex;
-    padding: 10px;
-    border-top: 1px solid #ebeef5;
-    background-color: #fff;
-    min-height: 120px;
-  }
-
-  .chat-input .el-input {
-    flex: 1;
-    margin-right: 10px;
-  }
-
-  .chat-input .el-textarea__inner {
-    resize: none;
-    min-height: 100px;
-  }
-  ::v-deep .el-textarea__inner {
-    border: none !important;
-    box-shadow: none !important;
-    resize: none !important;
-  }
-  ::v-deep .el-textarea__inner:focus {
-    border: none !important;
-    box-shadow: none !important;
-  }
-  .tcplayer-live {
-    width: 100%;
-  }
-  ::v-deep .tcplayer {
-    margin: 0 auto;
-  }
 </style>

+ 16 - 7
src/views/live/liveCoupon/index.vue

@@ -1,6 +1,11 @@
 <template>
-  <div class="app-container">
-    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="100px">
+  <div class="app-container live-page">
+        <div class="live-page__header">
+      <h2 class="live-page__title">优惠券</h2>
+    </div>
+
+<div class="live-panel" v-show="showSearch">
+<el-form class="live-search-form" :model="queryParams" ref="queryForm" :inline="true" label-width="100px">
       <el-form-item label="优惠券名称" prop="title">
         <el-input
           v-model="queryParams.title"
@@ -49,8 +54,10 @@
         <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
       </el-form-item>
     </el-form>
+    </div>
 
-    <el-row :gutter="10" class="mb8">
+    <div class="live-panel live-panel--table">
+<el-row :gutter="10" class="mb8">
       <el-col :span="1.5">
         <el-button
           type="primary"
@@ -84,7 +91,7 @@
 	  <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
     </el-row>
 
-    <el-table height="500" border v-loading="loading" :data="storeCouponList" @selection-change="handleSelectionChange">
+    <el-table height="500" border v-loading="loading" :data="storeCouponList" @selection-change="handleSelectionChange" class="live-table">
       <el-table-column type="selection" width="55" align="center" />
       <el-table-column label="优惠券表ID" align="center" prop="couponId" />
       <el-table-column label="优惠券名称" align="center" prop="title" />
@@ -94,12 +101,12 @@
       <el-table-column label="排序" align="center" prop="sort" />
       <el-table-column label="类型" align="center" prop="type" >
           <template slot-scope="scope">
-              <el-tag prop="type" v-for="(item, index) in couponTypeOptions"    v-if="scope.row.type==item.dictValue">{{item.dictLabel}}</el-tag>
+              <span class="live-muted" v-for="(item, index) in couponTypeOptions" :key="'t-'+index" v-if="scope.row.type==item.dictValue">{{item.dictLabel}}</span>
           </template>
       </el-table-column>
       <el-table-column label="状态" align="center" prop="status" >
           <template slot-scope="scope">
-              <el-tag prop="status" v-for="(item, index) in statusOptions"    v-if="scope.row.status==item.dictValue">{{item.dictLabel}}</el-tag>
+              <span class="live-muted" v-for="(item, index) in statusOptions" :key="'s-'+index" v-if="scope.row.status==item.dictValue">{{item.dictLabel}}</span>
           </template>
       </el-table-column>
 <!--      <el-table-column label="绑定商品" align="center" prop="productName" />-->
@@ -141,7 +148,9 @@
     />
 
     <!-- 添加或修改优惠券对话框 -->
-    <el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
+    </div>
+
+<el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
       <el-form ref="form" :model="form" :rules="rules" label-width="150px">
         <el-form-item label="优惠券名称" prop="title">
           <el-input v-model="form.title" placeholder="请输入优惠券名称" />

+ 14 - 5
src/views/live/liveCouponIssue/index.vue

@@ -1,6 +1,11 @@
 <template>
-  <div class="app-container">
-    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="100px">
+  <div class="app-container live-page">
+        <div class="live-page__header">
+      <h2 class="live-page__title">优惠券发放</h2>
+    </div>
+
+<div class="live-panel" v-show="showSearch">
+<el-form class="live-search-form" :model="queryParams" ref="queryForm" :inline="true" label-width="100px">
       <el-form-item label="优惠券名称" prop="couponName">
         <el-input
           v-model="queryParams.couponName"
@@ -37,7 +42,9 @@
         <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
       </el-form-item>
     </el-form>
-    <el-row :gutter="10" class="mb8">
+    </div>
+    <div class="live-panel live-panel--table">
+<el-row :gutter="10" class="mb8">
       <el-col :span="1.5">
         <el-button
           type="warning"
@@ -47,7 +54,7 @@
       </el-col>
     </el-row>
 
-    <el-table  height="500" border v-loading="loading" :data="storeCouponIssueList" @selection-change="handleSelectionChange">
+    <el-table  height="500" border v-loading="loading" :data="storeCouponIssueList" @selection-change="handleSelectionChange" class="live-table">
       <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="couponName" />
@@ -106,7 +113,9 @@
     />
 
     <!-- 添加或修改优惠券领取对话框 -->
-    <el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
+    </div>
+
+<el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
       <el-form ref="form" :model="form" :rules="rules" label-width="80px">
          <el-form-item label="状态">
             <el-radio-group v-model="form.status">

+ 14 - 5
src/views/live/liveCouponIssueUser/index.vue

@@ -1,6 +1,11 @@
 <template>
-  <div class="app-container">
-    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
+  <div class="app-container live-page">
+        <div class="live-page__header">
+      <h2 class="live-page__title">发放用户</h2>
+    </div>
+
+<div class="live-panel" v-show="showSearch">
+<el-form class="live-search-form" :model="queryParams" ref="queryForm" :inline="true" label-width="68px">
       <el-form-item label="领取优惠券用户ID" prop="userId">
         <el-input
           v-model="queryParams.userId"
@@ -33,8 +38,10 @@
         <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
       </el-form-item>
     </el-form>
+    </div>
 
-    <el-row :gutter="10" class="mb8">
+    <div class="live-panel live-panel--table">
+<el-row :gutter="10" class="mb8">
       <el-col :span="1.5">
         <el-button
           type="primary"
@@ -76,7 +83,7 @@
 	  <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
     </el-row>
 
-    <el-table  height="500" border v-loading="loading" :data="storeCouponIssueUserList" @selection-change="handleSelectionChange">
+    <el-table  height="500" border v-loading="loading" :data="storeCouponIssueUserList" @selection-change="handleSelectionChange" class="live-table">
       <el-table-column type="selection" width="55" align="center" />
       <el-table-column label="优惠券领取ID" align="center" prop="id" />
       <el-table-column label="领取优惠券用户ID" align="center" prop="userId" />
@@ -111,7 +118,9 @@
     />
 
     <!-- 添加或修改优惠券用户领取记录对话框 -->
-    <el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
+    </div>
+
+<el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
       <el-form ref="form" :model="form" :rules="rules" label-width="80px">
         <el-form-item label="领取优惠券用户ID" prop="userId">
           <el-input v-model="form.userId" placeholder="请输入领取优惠券用户ID" />

+ 12 - 4
src/views/live/liveCouponUser/index.vue

@@ -1,6 +1,11 @@
 <template>
-  <div class="app-container">
-    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="100px">
+  <div class="app-container live-page">
+        <div class="live-page__header">
+      <h2 class="live-page__title">优惠券用户</h2>
+    </div>
+
+<div class="live-panel" v-show="showSearch">
+<el-form class="live-search-form" :model="queryParams" ref="queryForm" :inline="true" label-width="100px">
 
       <el-form-item label="优惠券名称" prop="couponTitle">
         <el-input
@@ -29,7 +34,9 @@
         <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
       </el-form-item>
     </el-form>
-    <el-row :gutter="10" class="mb8">
+    </div>
+    <div class="live-panel live-panel--table">
+<el-row :gutter="10" class="mb8">
       <!-- <el-col :span="1.5">
         <el-button
           type="danger"
@@ -52,7 +59,7 @@
 	  <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
     </el-row>
 
-    <el-table   height="500" border   v-loading="loading" :data="storeCouponUserList" @selection-change="handleSelectionChange">
+    <el-table   height="500" border   v-loading="loading" :data="storeCouponUserList" @selection-change="handleSelectionChange" class="live-table">
       <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="nickname" />
@@ -72,6 +79,7 @@
           </template>
       </el-table-column>
     </el-table>
+    </div>
 
     <pagination
       v-show="total>0"

+ 46 - 75
src/views/live/liveData/index.vue

@@ -1,16 +1,14 @@
 <template>
-  <div class="el-container-md">
-    <!-- 数据统计指标展示区域 -->
-    <el-card class="statistics-card" shadow="never">
-      <el-row :gutter="20">
-        <el-col :span="4">
-          <div class="statistics-item">
+  <div class="app-container live-page">
+        <div class="live-page__header">
+      <h2 class="live-page__title">直播数据</h2>
+    </div>
+    <div class="live-stats-grid">
+      <div class="statistics-item">
             <div class="statistics-title">累计观看人数</div>
             <div class="statistics-value">{{ statisticsData.totalViewers || 0 }}</div>
           </div>
-        </el-col>
-        <el-col :span="4">
-          <div class="statistics-item">
+      <div class="statistics-item">
             <div class="statistics-title">
               累计完课人数
               <el-popover placement="top" width="200" trigger="click">
@@ -23,15 +21,11 @@
             </div>
             <div class="statistics-value">{{ statisticsData.totalCompletedCourses || 0 }}</div>
           </div>
-        </el-col>
-        <el-col :span="4">
-          <div class="statistics-item">
+      <div class="statistics-item">
             <div class="statistics-title">直播观看人数</div>
             <div class="statistics-value">{{ statisticsData.liveViewers || 0 }}</div>
           </div>
-        </el-col>
-        <el-col :span="4">
-          <div class="statistics-item">
+      <div class="statistics-item">
             <div class="statistics-title">
               直播完课人数
               <el-popover placement="top" width="200" trigger="click">
@@ -44,15 +38,11 @@
             </div>
             <div class="statistics-value">{{ statisticsData.liveCompletedCourses || 0 }}</div>
           </div>
-        </el-col>
-        <el-col :span="4">
-          <div class="statistics-item">
+      <div class="statistics-item">
             <div class="statistics-title">回放观看人数</div>
             <div class="statistics-value">{{ statisticsData.playbackViewers || 0 }}</div>
           </div>
-        </el-col>
-        <el-col :span="4">
-          <div class="statistics-item">
+      <div class="statistics-item">
             <div class="statistics-title">
               回放完课人数
               <el-popover placement="top" width="200" trigger="click">
@@ -65,58 +55,39 @@
             </div>
             <div class="statistics-value">{{ statisticsData.playbackCompletedCourses || 0 }}</div>
           </div>
-        </el-col>
-      </el-row>
-      <el-row :gutter="20" style="margin-top: 20px;">
-        <el-col :span="4">
-          <div class="statistics-item">
+      <div class="statistics-item">
             <div class="statistics-title">直播峰值</div>
             <div class="statistics-value">{{ statisticsData.livePeak || 0 }}</div>
           </div>
-        </el-col>
-        <el-col :span="4">
-          <div class="statistics-item">
+      <div class="statistics-item">
             <div class="statistics-title">直播平均时长</div>
             <div class="statistics-value">{{ formatDuration(statisticsData.liveAvgDuration) }}</div>
           </div>
-        </el-col>
-        <el-col :span="4">
-          <div class="statistics-item">
+      <div class="statistics-item">
             <div class="statistics-title">回放平均时长</div>
             <div class="statistics-value">{{ formatDuration(statisticsData.playbackAvgDuration) }}</div>
           </div>
-        </el-col>
-        <el-col :span="4">
-          <div class="statistics-item">
+      <div class="statistics-item">
             <div class="statistics-title">GMV</div>
             <div class="statistics-value">¥{{ statisticsData.gmv || 0 }}</div>
           </div>
-        </el-col>
-        <el-col :span="4">
-          <div class="statistics-item">
+      <div class="statistics-item">
             <div class="statistics-title">付费人数</div>
             <div class="statistics-value">{{ statisticsData.paidUsers || 0 }}</div>
           </div>
-        </el-col>
-        <el-col :span="4">
-          <div class="statistics-item">
+      <div class="statistics-item">
             <div class="statistics-title">付费单数</div>
             <div class="statistics-value">{{ statisticsData.paidOrders || 0 }}</div>
           </div>
-        </el-col>
-      </el-row>
-      <el-row :gutter="20" style="margin-top: 20px;">
-        <el-col :span="4">
-          <div class="statistics-item">
+      <div class="statistics-item">
             <div class="statistics-title">销量统计</div>
             <div class="statistics-value">{{ statisticsData.salesCount || 0 }}</div>
           </div>
-        </el-col>
-      </el-row>
-    </el-card>
+    </div>
 
     <!-- 筛选条件区域 -->
-    <el-form :model="queryParams" class="live-data-css" ref="queryForm" :inline="true" v-show="showSearch" label-width="100px">
+    <div class="live-panel" v-show="showSearch">
+      <el-form :model="queryParams" class="live-data-css live-search-form" ref="queryForm" :inline="true" label-width="100px">
       <el-form-item label="直播名称" prop="liveName">
         <el-input
           v-model="queryParams.liveName"
@@ -169,9 +140,11 @@
         <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
       </el-form-item>
     </el-form>
+    </div>
 
     <!-- 操作工具栏 -->
-    <div class="selection-toolbar">
+    <div class="live-panel live-panel--table">
+<div class="selection-toolbar">
       <el-checkbox :indeterminate="isIndeterminate" v-model="allChecked" @change="toggleSelectAll">
         {{ multipleSelection.length > 0 ? `已选 ${multipleSelection.length} 条` : '选中本页' }}
       </el-checkbox>
@@ -193,17 +166,20 @@
       <el-table-column prop="liveName" label="直播间名称" min-width="160" show-overflow-tooltip></el-table-column>
       <el-table-column prop="liveType" label="直播类型" width="100" align="center">
         <template slot-scope="scope">
-          <el-tag type="danger" v-if="scope.row.liveType == 1">直播</el-tag>
-          <el-tag type="success" v-else-if="scope.row.liveType == 2">录播</el-tag>
-          <el-tag v-else-if="scope.row.liveType == 3">直播回放</el-tag>
+          <span v-if="scope.row.liveType == 1" class="live-muted">直播</span>
+          <span v-else-if="scope.row.liveType == 2" class="live-muted">录播</span>
+          <span v-else-if="scope.row.liveType == 3" class="live-muted">直播回放</span>
           <span v-else>-</span>
         </template>
       </el-table-column>
       <el-table-column prop="status" label="状态" width="100" align="center">
         <template slot-scope="scope">
-          <el-tag type="info" v-if="scope.row.status == 1">未开始</el-tag>
-          <el-tag type="warning" v-else-if="scope.row.status == 2">进行中</el-tag>
-          <el-tag type="success" v-else-if="scope.row.status == 3">已结束</el-tag>
+          <span class="live-status" :class="'is-' + scope.row.status" v-if="scope.row.status == 1 || scope.row.status == 2 || scope.row.status == 3">
+            <i class="live-status__dot"></i>
+            <template v-if="scope.row.status == 1">未开始</template>
+            <template v-else-if="scope.row.status == 2">进行中</template>
+            <template v-else-if="scope.row.status == 3">已结束</template>
+          </span>
           <span v-else>-</span>
         </template>
       </el-table-column>
@@ -243,6 +219,7 @@
       @pagination="getList"
       style="margin-top: 20px;"
     />
+    </div>
 
     <!-- 自动打标签弹窗 -->
     <el-dialog
@@ -979,30 +956,25 @@ export default {
 </script>
 
 <style scoped>
-.statistics-card {
-  margin-bottom: 20px;
-}
-
-.statistics-item {
+.live-stats-grid .statistics-item {
   text-align: center;
-  padding: 15px;
-  background: #f5f7fa;
-  border-radius: 4px;
+  padding: 10px 8px;
 }
 
-.statistics-title {
-  font-size: 14px;
-  color: #606266;
-  margin-bottom: 10px;
+.live-stats-grid .statistics-title {
+  font-size: 12px;
+  color: #8a919f;
+  margin-bottom: 6px;
   display: flex;
   align-items: center;
   justify-content: center;
+  gap: 2px;
 }
 
-.statistics-value {
-  font-size: 24px;
-  font-weight: bold;
-  color: #303133;
+.live-stats-grid .statistics-value {
+  font-size: 18px;
+  font-weight: 600;
+  color: #1f2329;
 }
 
 .selection-toolbar {
@@ -1017,8 +989,7 @@ export default {
 }
 
 .live-data-css {
-  padding-left: 10px;
-  padding-top: 30px;
+  padding: 0;
 }
 
 .detail-content {

+ 14 - 5
src/views/live/liveEventConf/index.vue

@@ -1,6 +1,11 @@
 <template>
-  <div class="app-container">
-    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
+  <div class="app-container live-page">
+        <div class="live-page__header">
+      <h2 class="live-page__title">活动配置</h2>
+    </div>
+
+<div class="live-panel" v-show="showSearch">
+<el-form class="live-search-form" :model="queryParams" ref="queryForm" :inline="true" label-width="68px">
       <el-form-item label="直播间ID" prop="liveId">
         <el-input
           v-model="queryParams.liveId"
@@ -29,8 +34,10 @@
         <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
       </el-form-item>
     </el-form>
+    </div>
 
-    <el-row :gutter="10" class="mb8">
+    <div class="live-panel live-panel--table">
+<el-row :gutter="10" class="mb8">
       <el-col :span="1.5">
         <el-button
           type="primary"
@@ -77,7 +84,7 @@
       <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
     </el-row>
 
-    <el-table border v-loading="loading" :data="liveEventConfList" @selection-change="handleSelectionChange">
+    <el-table border v-loading="loading" :data="liveEventConfList" @selection-change="handleSelectionChange" class="live-table">
       <el-table-column type="selection" width="55" align="center" />
       <el-table-column label="触发阈值" align="center" prop="eventId" />
       <el-table-column label="直播间ID" align="center" prop="liveId" />
@@ -123,7 +130,9 @@
     />
 
     <!-- 添加或修改直播触发事件配置对话框 -->
-    <el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
+    </div>
+
+<el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
       <el-form ref="form" :model="form" :rules="rules" label-width="80px">
         <el-form-item label="直播间ID" prop="liveId">
           <el-input v-model="form.liveId" placeholder="请输入直播间ID" />

+ 12 - 4
src/views/live/liveFeedbackLog/index.vue

@@ -1,6 +1,11 @@
 <template>
-  <div class="app-container">
-    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch">
+  <div class="app-container live-page">
+        <div class="live-page__header">
+      <h2 class="live-page__title">反馈日志</h2>
+    </div>
+
+<div class="live-panel" v-show="showSearch">
+<el-form class="live-search-form" :model="queryParams" ref="queryForm" :inline="true">
       <el-form-item label="主播名称" prop="streamerName">
         <el-input
           v-model="queryParams.streamerName"
@@ -25,12 +30,14 @@
         <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
       </el-form-item>
     </el-form>
+    </div>
 
-    <el-table
+    <div class="live-panel live-panel--table">
+<el-table
       v-loading="loading"
       :data="logList"
       border
-    >
+     class="live-table">
       <el-table-column label="ID" align="center" prop="id"/>
       <el-table-column label="主播ID" align="center" prop="streamerId"/>
       <el-table-column label="主播名称" align="center" prop="streamerName"/>
@@ -46,6 +53,7 @@
         </template>
       </el-table-column>
     </el-table>
+    </div>
 
     <pagination
       v-show="total>0"

+ 14 - 5
src/views/live/liveFeedbackType/index.vue

@@ -1,6 +1,11 @@
 <template>
-  <div class="app-container">
-    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch">
+  <div class="app-container live-page">
+        <div class="live-page__header">
+      <h2 class="live-page__title">反馈类型</h2>
+    </div>
+
+<div class="live-panel" v-show="showSearch">
+<el-form class="live-search-form" :model="queryParams" ref="queryForm" :inline="true">
       <el-form-item label="类型名称" prop="typeName">
         <el-input
           v-model="queryParams.typeName"
@@ -25,8 +30,10 @@
         <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
       </el-form-item>
     </el-form>
+    </div>
 
-    <el-row :gutter="10" class="mb8">
+    <div class="live-panel live-panel--table">
+<el-row :gutter="10" class="mb8">
       <el-col :span="1.5">
         <el-button
           type="primary"
@@ -58,7 +65,7 @@
       row-key="id"
       @selection-change="handleSelectionChange"
       :tree-props="{children: 'children', hasChildren: 'hasChildren'}"
-    >
+     class="live-table">
       <el-table-column type="selection" width="50" align="center" />
       <el-table-column prop="typeName" label="类型名称" :show-overflow-tooltip="true"/>
       <el-table-column prop="status" align="center" label="状态">
@@ -106,7 +113,9 @@
     </el-table>
 
     <!-- 添加或修改直播反馈类型对话框 -->
-    <el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
+    </div>
+
+<el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
       <el-form ref="form" :model="form" :rules="rules" label-width="80px">
         <el-row>
           <el-col :span="24">

+ 14 - 6
src/views/live/liveGoods/index.vue

@@ -1,6 +1,11 @@
 <template>
-  <div class="app-container">
-    <el-form :model="queryParams" class="live-goods-css" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
+  <div class="app-container live-page">
+    <div class="live-page__header">
+      <h2 class="live-page__title">直播商品</h2>
+    </div>
+
+    <div class="live-panel" v-show="showSearch">
+      <el-form :model="queryParams" class="live-goods-css live-search-form" ref="queryForm" :inline="true" label-width="68px">
       <el-form-item label="商品名称" prop="keywords" >
         <el-input
           v-model="queryParams.keywords"
@@ -24,8 +29,10 @@
         <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
       </el-form-item>
     </el-form>
+    </div>
 
-    <div class="selection-toolbar">
+    <div class="live-panel live-panel--table">
+<div class="selection-toolbar">
       <el-checkbox :indeterminate="isIndeterminate" v-model="allChecked" @change="toggleSelectAll">
         {{ multipleSelection.length > 0 ? `已选 ${multipleSelection.length} 条` : '选中本页' }}
       </el-checkbox>
@@ -56,7 +63,7 @@
         <template slot-scope="scope">
           <img
             :src="scope.row.imgUrl"
-            style="display: block; max-width: 100%; width: 100px; height: 100px"
+            class="live-cell__cover live-cell__cover--square" style="object-fit: cover;"
           />
         </template>
 
@@ -87,8 +94,8 @@
         label="上下架"
       >
         <template slot-scope="scope">
-          <el-tag v-if="scope.row.status == 1">上架</el-tag>
-          <el-tag v-if="scope.row.status == 0">下架</el-tag>
+          <span v-if="scope.row.status == 1" class="live-ok">上架</span>
+          <span v-if="scope.row.status == 0" class="live-muted">下架</span>
         </template>
       </el-table-column>
 
@@ -118,6 +125,7 @@
       @pagination="getLiveGoodsList"
       style="margin-top: 20px;"
     />
+    </div>
 
     <!-- 添加商品弹窗 -->
     <el-dialog

+ 14 - 5
src/views/live/liveLotteryConf/index.vue

@@ -1,6 +1,11 @@
 <template>
-  <div class="app-container">
-    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
+  <div class="app-container live-page">
+        <div class="live-page__header">
+      <h2 class="live-page__title">抽奖配置</h2>
+    </div>
+
+<div class="live-panel" v-show="showSearch">
+<el-form class="live-search-form" :model="queryParams" ref="queryForm" :inline="true" label-width="68px">
       <el-form-item label="直播间ID" prop="liveId">
         <el-input
           v-model="queryParams.liveId"
@@ -43,8 +48,10 @@
         <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
       </el-form-item>
     </el-form>
+    </div>
 
-    <el-row :gutter="10" class="mb8">
+    <div class="live-panel live-panel--table">
+<el-row :gutter="10" class="mb8">
       <el-col :span="1.5">
         <el-button
           type="primary"
@@ -91,7 +98,7 @@
       <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
     </el-row>
 
-    <el-table border v-loading="loading" :data="liveLotteryConfList" @selection-change="handleSelectionChange">
+    <el-table border v-loading="loading" :data="liveLotteryConfList" @selection-change="handleSelectionChange" class="live-table">
       <el-table-column type="selection" width="55" align="center" />
       <el-table-column label="抽奖ID" align="center" prop="lotteryId" />
       <el-table-column label="操作商品" align="center" class-name="small-padding fixed-width">
@@ -175,7 +182,9 @@
     />
 
     <!-- 添加或修改直播抽奖配置对话框 -->
-    <el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
+    </div>
+
+<el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
       <el-form ref="form" :model="form" :rules="rules" label-width="80px">
         <el-form-item label="直播间ID" prop="liveId">
           <el-input v-model="form.liveId" placeholder="请输入直播间ID"

+ 14 - 5
src/views/live/liveLotteryProductConf/index.vue

@@ -1,6 +1,11 @@
 <template>
-  <div class="app-container">
-    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
+  <div class="app-container live-page">
+        <div class="live-page__header">
+      <h2 class="live-page__title">抽奖商品配置</h2>
+    </div>
+
+<div class="live-panel" v-show="showSearch">
+<el-form class="live-search-form" :model="queryParams" ref="queryForm" :inline="true" label-width="68px">
       <el-form-item label="抽奖ID" prop="lotteryId">
         <el-input
           v-model="queryParams.lotteryId"
@@ -77,8 +82,10 @@
         <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
       </el-form-item>
     </el-form>
+    </div>
 
-    <el-row :gutter="10" class="mb8">
+    <div class="live-panel live-panel--table">
+<el-row :gutter="10" class="mb8">
       <el-col :span="1.5">
         <el-button
           type="primary"
@@ -125,7 +132,7 @@
       <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
     </el-row>
 
-    <el-table border v-loading="loading" :data="liveLotteryProductConfList" @selection-change="handleSelectionChange">
+    <el-table border v-loading="loading" :data="liveLotteryProductConfList" @selection-change="handleSelectionChange" class="live-table">
       <el-table-column type="selection" width="55" align="center" />
       <el-table-column label="ID" align="center" prop="id" />
       <el-table-column label="抽奖ID" align="center" prop="lotteryId" />
@@ -164,7 +171,9 @@
     />
 
     <!-- 添加或修改直播抽奖记录对话框 -->
-    <el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
+    </div>
+
+<el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
       <el-form ref="form" :model="form" :rules="rules" label-width="80px">
         <el-form-item label="抽奖ID" prop="lotteryId">
           <el-input v-model="form.lotteryId" placeholder="请输入抽奖ID" />

+ 14 - 5
src/views/live/liveLotteryRecord/index.vue

@@ -1,6 +1,11 @@
 <template>
-  <div class="app-container">
-    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
+  <div class="app-container live-page">
+        <div class="live-page__header">
+      <h2 class="live-page__title">抽奖记录</h2>
+    </div>
+
+<div class="live-panel" v-show="showSearch">
+<el-form class="live-search-form" :model="queryParams" ref="queryForm" :inline="true" label-width="68px">
       <el-form-item label="直播间ID" prop="liveId">
         <el-input
           v-model="queryParams.liveId"
@@ -32,8 +37,10 @@
         <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
       </el-form-item>
     </el-form>
+    </div>
 
-    <el-row :gutter="10" class="mb8">
+    <div class="live-panel live-panel--table">
+<el-row :gutter="10" class="mb8">
       <el-col :span="1.5">
         <el-button
           type="primary"
@@ -80,7 +87,7 @@
       <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
     </el-row>
 
-    <el-table border v-loading="loading" :data="liveLotteryRecordList" @selection-change="handleSelectionChange">
+    <el-table border v-loading="loading" :data="liveLotteryRecordList" @selection-change="handleSelectionChange" class="live-table">
       <el-table-column type="selection" width="55" align="center" />
       <el-table-column label="抽奖ID" align="center" prop="lotteryId" />
       <el-table-column label="直播间ID" align="center" prop="liveId" />
@@ -119,7 +126,9 @@
     />
 
     <!-- 添加或修改直播抽奖记录对话框 -->
-    <el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
+    </div>
+
+<el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
       <el-form ref="form" :model="form" :rules="rules" label-width="80px">
       </el-form>
       <div slot="footer" class="dialog-footer">

+ 14 - 5
src/views/live/liveLotteryRegistration/index.vue

@@ -1,13 +1,20 @@
 <template>
-  <div class="app-container">
-    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
+  <div class="app-container live-page">
+        <div class="live-page__header">
+      <h2 class="live-page__title">抽奖报名</h2>
+    </div>
+
+<div class="live-panel" v-show="showSearch">
+<el-form class="live-search-form" :model="queryParams" ref="queryForm" :inline="true" label-width="68px">
       <el-form-item>
         <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
         <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
       </el-form-item>
     </el-form>
+    </div>
 
-    <el-row :gutter="10" class="mb8">
+    <div class="live-panel live-panel--table">
+<el-row :gutter="10" class="mb8">
       <el-col :span="1.5">
         <el-button
           type="primary"
@@ -54,7 +61,7 @@
       <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
     </el-row>
 
-    <el-table border v-loading="loading" :data="liveLotteryRegistrationList" @selection-change="handleSelectionChange">
+    <el-table border v-loading="loading" :data="liveLotteryRegistrationList" @selection-change="handleSelectionChange" class="live-table">
       <el-table-column type="selection" width="55" align="center" />
       <el-table-column label="登记ID" align="center" prop="registrationId" />
       <el-table-column label="直播间ID" align="center" prop="liveId" />
@@ -95,7 +102,9 @@
     />
 
     <!-- 添加或修改直播抽奖登记对话框 -->
-    <el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
+    </div>
+
+<el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
       <el-form ref="form" :model="form" :rules="rules" label-width="80px">
       </el-form>
       <div slot="footer" class="dialog-footer">

+ 16 - 7
src/views/live/liveMiniLives/index.vue

@@ -1,6 +1,11 @@
 <template>
-  <div class="app-container">
-    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
+  <div class="app-container live-page">
+        <div class="live-page__header">
+      <h2 class="live-page__title">小程序直播</h2>
+    </div>
+
+<div class="live-panel" v-show="showSearch">
+<el-form class="live-search-form" :model="queryParams" ref="queryForm" :inline="true" label-width="68px">
       <el-form-item label="公司名称" prop="companyName">
         <el-input
           v-model="queryParams.companyName"
@@ -27,8 +32,10 @@
         <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
       </el-form-item>
     </el-form>
+    </div>
 
-    <el-row :gutter="10" class="mb8">
+    <div class="live-panel live-panel--table">
+<el-row :gutter="10" class="mb8">
       <el-col :span="3">
         <el-select
           v-model="batchLiveShow"
@@ -45,7 +52,7 @@
       <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
     </el-row>
 
-    <el-table v-loading="loading" :data="companyList" @selection-change="handleSelectionChange">
+    <el-table v-loading="loading" :data="companyList" @selection-change="handleSelectionChange" class="live-table">
       <el-table-column
         type="selection"
         width="55"
@@ -55,8 +62,8 @@
       <el-table-column label="公司名称" align="center" prop="companyName" />
       <el-table-column label="显示状态" align="center" prop="liveShow" width="120">
         <template slot-scope="scope">
-          <el-tag v-if="scope.row.liveShow == 1" type="success">显示</el-tag>
-          <el-tag v-else type="info">不显示</el-tag>
+          <span v-if="scope.row.liveShow == 1" class="live-ok">显示</span>
+          <span v-else class="live-muted">不显示</span>
         </template>
       </el-table-column>
       <el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="150">
@@ -80,7 +87,9 @@
     />
 
     <!-- 修改显示状态对话框 -->
-    <el-dialog title="修改显示状态" :visible.sync="editLiveShowDialog" width="500px" append-to-body>
+    </div>
+
+<el-dialog title="修改显示状态" :visible.sync="editLiveShowDialog" width="500px" append-to-body>
       <el-form :model="editLiveShowForm" label-width="100px">
         <el-form-item label="公司名称">
           <el-input v-model="editLiveShowForm.companyName" :disabled="true" />

+ 14 - 5
src/views/live/liveMsg/index.vue

@@ -1,6 +1,11 @@
 <template>
-  <div class="app-container">
-    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
+  <div class="app-container live-page">
+        <div class="live-page__header">
+      <h2 class="live-page__title">直播消息</h2>
+    </div>
+
+<div class="live-panel" v-show="showSearch">
+<el-form class="live-search-form" :model="queryParams" ref="queryForm" :inline="true" label-width="68px">
       <el-form-item label="直播ID" prop="liveId">
         <el-input
           v-model="queryParams.liveId"
@@ -33,8 +38,10 @@
         <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
       </el-form-item>
     </el-form>
+    </div>
 
-    <el-row :gutter="10" class="mb8">
+    <div class="live-panel live-panel--table">
+<el-row :gutter="10" class="mb8">
       <el-col :span="1.5">
         <el-button
           type="primary"
@@ -81,7 +88,7 @@
       <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
     </el-row>
 
-    <el-table border v-loading="loading" :data="liveMsgList" @selection-change="handleSelectionChange">
+    <el-table border v-loading="loading" :data="liveMsgList" @selection-change="handleSelectionChange" class="live-table">
       <el-table-column type="selection" width="55" align="center" />
       <el-table-column label="id" align="center" prop="msgId" />
       <el-table-column label="直播ID" align="center" prop="liveId" />
@@ -117,7 +124,9 @@
     />
 
     <!-- 添加或修改直播讨论对话框 -->
-    <el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
+    </div>
+
+<el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
       <el-form ref="form" :model="form" :rules="rules" label-width="80px">
         <el-form-item label="直播ID" prop="liveId">
           <el-input v-model="form.liveId" placeholder="请输入直播ID" />

+ 11 - 3
src/views/live/liveOrder/index.vue

@@ -1,6 +1,11 @@
 <template>
-  <div class="app-container">
-    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
+  <div class="app-container live-page">
+    <div class="live-page__header">
+      <h2 class="live-page__title">直播订单</h2>
+    </div>
+
+    <div class="live-panel" v-show="showSearch">
+      <el-form class="live-search-form" :model="queryParams" ref="queryForm" :inline="true" label-width="68px">
       <el-form-item label="公司名" prop="companyId">
         <el-select filterable v-model="queryParams.companyId" placeholder="请选择公司名" @change="companyChange" clearable size="small">
           <el-option
@@ -229,8 +234,10 @@
     </el-form-item>
       </div>
     </el-form>
+    </div>
 
-    <el-row :gutter="10" class="mb8">
+    <div class="live-panel live-panel--table">
+<el-row :gutter="10" class="mb8">
       <el-col :span="1.5">
         <el-button
           type="warning"
@@ -316,6 +323,7 @@
       :limit.sync="queryParams.pageSize"
       @pagination="getList"
     />
+    </div>
 
     <el-drawer
       :with-header="false"

+ 133 - 82
src/views/live/liveOrder/liveOrderDetails.vue

@@ -1,9 +1,9 @@
 <template>
-  <div style="background-color: #f0f2f5; padding-bottom: 20px; min-height: 100%; " >
-    <div style="padding: 20px; background-color: #fff;">
-      订单详情
+  <div class="live-page live-order-detail">
+    <div class="live-page__header">
+      <h2 class="live-page__title">订单详情</h2>
     </div>
-    <div class="contentx" v-if="item!=null">
+    <div class="contentx live-panel" v-if="item!=null">
       <div class="desct"></div>
       <div class="order-status" v-if="item!=null" >
         <el-steps  :active="item.status==4?item.status:item.status" align-center finish-status="success">
@@ -13,9 +13,9 @@
           <el-step title="已完成"></el-step>
         </el-steps>
       </div>
-      <el-card shadow="never" style="margin-top: 15px">
+      <el-card shadow="never" class="detail-card">
         <div class="operate-container"  v-if="item!=null">
-               <span style="margin-left: 20px" class="color-danger">订单状态:
+               <span class="operate-status color-danger">订单状态:
                   <el-tag prop="status" v-for="(ite, index) in orderOptions"    v-if="item.status==ite.dictValue">{{ite.dictLabel}}</el-tag>
                </span>
           <div class="operate-button-container" v-if="item.status==3">
@@ -56,10 +56,10 @@
           <el-descriptions-item label="支付方式"  ><dict-tag :options="PayOptions" :value="item.payType"/></el-descriptions-item>
           <el-descriptions-item label="收货人" >  <span v-if="item!=null">{{item.userName}}</span>  </el-descriptions-item>
           <el-descriptions-item label="手机号码" > <span v-if="item!=null">{{item.userPhone}}</span>
-            <el-button icon="el-icon-search" size="mini" @click="handlePhone()" style="margin-left: 20px;" circle v-hasPermi="['live:liveOrder:queryPhone']"></el-button>
+            <el-button icon="el-icon-search" size="mini" @click="handlePhone()" class="inline-search-btn" circle v-hasPermi="['live:liveOrder:queryPhone']"></el-button>
           </el-descriptions-item>
           <el-descriptions-item label="收货地址" > <span v-if="item!=null">{{item.userAddress}}</span>
-            <el-button icon="el-icon-search" size="mini" @click="handleAddress()" style="margin-left: 20px;" circle v-hasPermi="['live:liveOrder:queryAddress']"></el-button>
+            <el-button icon="el-icon-search" size="mini" @click="handleAddress()" class="inline-search-btn" circle v-hasPermi="['live:liveOrder:queryAddress']"></el-button>
           </el-descriptions-item>
           <el-descriptions-item label="所属公司"><span v-if="item!=null">{{item.companyName}}</span></el-descriptions-item>
           <el-descriptions-item label="员工"><span v-if="item!=null">{{item.companyUserName}}</span></el-descriptions-item>
@@ -78,8 +78,8 @@
         </el-descriptions>
       </el-card>
     </div>
-    <div class="contentx" v-if="item!=null" style="padding-bottom: 70px;">
-      <div style="margin-top: 20px">
+    <div class="contentx live-panel contentx--pad" v-if="item!=null">
+      <div class="section-head">
         <div class="desct">
           物流信息
         </div>
@@ -90,7 +90,7 @@
         border
         :data="deliverList"
         size="small"
-        style="width: 100%;margin-top: 20px" >
+        class="detail-table" >
         <el-table-column label="物流公司编码" width="150" align="center">
           <template slot-scope="scope">
             <p>{{scope.row.deliverSn}}</p>
@@ -129,17 +129,17 @@
         </el-table-column>
       </el-table>
     </div>
-    <div class="contentx" v-if="item!=null" style="padding-bottom: 70px;">
+    <div class="contentx live-panel contentx--pad" v-if="item!=null">
       <div class="desct">
         商品信息
       </div>
-      <el-tooltip class="item" effect="dark" :content="showList ? '显示全部' : '隐藏'" placement="top" style="float: right;">
+      <el-tooltip class="item toggle-tip" effect="dark" :content="showList ? '显示全部' : '隐藏'" placement="top">
         <el-button size="mini" circle icon="el-icon-search" @click="showListD()" />
       </el-tooltip>
-      <el-table border v-if="showProd!=null" :data="showProd" size="small" style="width: 100%;margin-top: 20px" >
+      <el-table border v-if="showProd!=null" :data="showProd" size="small" class="detail-table" >
         <el-table-column label="商品图片" width="150" align="center">
           <template slot-scope="scope">
-            <img :src="scope.row.imgUrl" style="height: 80px">
+            <img :src="scope.row.imgUrl" class="goods-thumb">
           </template>
         </el-table-column>
         <el-table-column label="商品编码" width="300" align="center">
@@ -178,15 +178,15 @@
           </template>
         </el-table-column>
       </el-table>
-      <div style="float: right;margin: 20px" v-if="item.totalPrice!=null">
+      <div class="section-total" v-if="item.totalPrice!=null">
         商品总价:<span class="color-danger">¥{{(parseFloat(item.totalPrice)).toFixed(2)}}</span>
       </div>
     </div>
-    <div class="contentx" v-if="item!=null" style="padding-bottom: 70px;">
+    <div class="contentx live-panel contentx--pad" v-if="item!=null">
       <div class="desct">
         费用信息
       </div>
-      <el-tooltip class="item" effect="dark" :content="showList ? '显示全部' : '隐藏'" placement="top" style="float: right;">
+      <el-tooltip class="item toggle-tip" effect="dark" :content="showList ? '显示全部' : '隐藏'" placement="top">
         <el-button size="mini" circle icon="el-icon-search" @click="showListD()" />
       </el-tooltip>
       <el-descriptions :column="3" border  >
@@ -208,12 +208,12 @@
         </el-descriptions-item>
         <el-descriptions-item label=""/>
       </el-descriptions>
-      <div style="float: right;margin: 20px" v-if="item.payMoney!=null">
+      <div class="section-total" v-if="item.payMoney!=null">
         实付合计:<span class="color-danger">¥{{(parseFloat(item.payMoney)).toFixed(2)}}</span>
       </div>
     </div>
 
-    <div class="contentx" v-if="payments!=null" style="padding-bottom: 70px;">
+    <div class="contentx live-panel contentx--pad" v-if="payments!=null">
       <div class="desct">
         支付信息
       </div>
@@ -221,7 +221,7 @@
       border
       :data="payments"
       size="small"
-      style="width: 100%;margin-top: 20px" >
+      class="detail-table" >
       <el-table-column label="支付单号" align="center" prop="payCode" width="120px" />
       <el-table-column label="支付金额" align="center" prop="payMoney" />
       <el-table-column label="类型" align="center" prop="payTypeCode" />
@@ -232,12 +232,12 @@
     </el-table>
     </div>
 
-    <div class="contentx" v-if="logs!=null" style="padding-bottom: 70px;">
+    <div class="contentx live-panel contentx--pad" v-if="logs!=null">
 
       <div class="desct">
         操作信息
       </div>
-    <el-table style="margin-top: 20px;width: 100%"
+    <el-table class="detail-table"
               ref="orderHistoryTable"
               :data="logs" border>
       <el-table-column label="操作时间"  width="160" align="center">
@@ -253,7 +253,7 @@
     </el-table>
 </div>
 
-    <div class="contentx" v-if="tuiMoneyLogs!=null" style="padding-bottom: 70px;">
+    <div class="contentx live-panel contentx--pad" v-if="tuiMoneyLogs!=null">
 
       <div class="desct">
         分佣信息
@@ -262,7 +262,7 @@
       border
       :data="tuiMoneyLogs"
       size="small"
-      style="width: 100%;margin-top: 20px" >
+      class="detail-table" >
       <el-table-column label="公司名称" align="center" prop="companyName" width="120px" />
       <el-table-column label="金额" align="center" prop="money" />
       <el-table-column label="余额" align="center" prop="balance" />
@@ -368,11 +368,11 @@
 
     <el-dialog :title="editDy.title" :visible.sync="editDy.open" width="600px" append-to-body>
       <el-form ref="editDyForm" :model="editDyForm" :rules="editDyRules" label-width="100px">
-        <div  v-hasPermi="['live:liveOrder:updateErpOrder']"  style="margin-bottom: 20px;" >
+        <div  v-hasPermi="['live:liveOrder:updateErpOrder']"  class="dialog-action-bar" >
           <el-button size="mini" @click="updateErpOrder" >同步物流单号信息</el-button>
         </div>
         <el-form-item label="物流公司" prop="deliveryCode" >
-          <el-select style="width:220px" v-model="editDyForm.deliveryCode" placeholder="请选择" clearable size="small" filterable>
+          <el-select class="delivery-select" v-model="editDyForm.deliveryCode" placeholder="请选择" clearable size="small" filterable>
             <el-option
               v-for="dict in deliveryOptions"
               :key="dict.dictValue"
@@ -395,7 +395,7 @@
       <div  v-hasPermi="['live:liveOrder:syncExpress']"  >
         <el-button size="mini" @click="syncExpress()" >同步快递鸟物流状态</el-button>
       </div>
-      <el-table style="margin-top: 20px;width: 100%"
+      <el-table class="detail-table"
                 ref="orderHistoryTable"
                 :data="traces" border>
         <el-table-column label="操作时间"  width="160" align="center">
@@ -1116,99 +1116,150 @@ export default {
 
 
 <style scoped>
-.content{
-  height: 100%;
-  background-color: #fff;
-  padding: 0px 20px;
-
+.live-order-detail {
+  min-height: 100%;
+  padding-bottom: 20px;
+  background-color: #f4f6f8;
 }
-.contentx{
-  height: 100%;
-  background-color: #fff;
-  padding: 0px 20px 20px;
 
+.live-order-detail .live-page__header {
+  margin: 0 0 16px;
+  padding: 16px 20px;
+  background: #fff;
+  border: 1px solid #e8eaed;
+  border-radius: 10px;
+}
 
-  margin: 20px;
+.contentx {
+  margin: 0 0 14px;
+  padding: 14px 16px 16px;
 }
-.el-descriptions-item__label.is-bordered-label{
-  font-weight: normal;
+
+.contentx--pad {
+  padding-bottom: 24px;
 }
-.el-descriptions-item__content {
-  max-width: 150px;
-  min-width: 100px;
+
+.detail-card {
+  margin-top: 15px;
+  border: 1px solid #e8eaed;
+  border-radius: 10px;
 }
-.desct{
-  padding-top: 20px;
-  padding-bottom: 20px;
-  color: #524b4a;
-  font-weight: bold;
+
+.desct {
+  padding: 12px 0;
+  color: #1f2329;
+  font-size: 15px;
+  font-weight: 600;
 }
 
-.order-content{
-  margin: 10px;
+.section-head {
+  display: flex;
+  align-items: center;
+  flex-wrap: wrap;
+  gap: 8px;
+  margin-top: 4px;
+}
 
+.section-head .desct {
+  padding: 12px 0;
 }
 
 .operate-container {
-  background: #F2F6FC;
-  height: 60px;
+  display: flex;
+  flex-wrap: wrap;
+  align-items: center;
+  gap: 8px 10px;
+  background: #f4f6f8;
   margin: -20px -20px 0;
-  line-height: 60px;
+  padding: 12px 16px;
+  border-bottom: 1px solid #e8eaed;
+  min-height: auto;
+  line-height: 1.4;
+}
+
+.operate-status {
+  margin-right: auto;
 }
 
 .operate-button-container {
+  float: none;
+  margin: 0;
+}
+
+.inline-search-btn {
+  margin-left: 12px;
+}
+
+.toggle-tip {
   float: right;
-  margin-right: 20px
 }
-</style>
 
-<style scoped>
-.chat-records {
+.detail-table {
+  width: 100%;
+  margin-top: 16px;
+}
+
+.goods-thumb {
+  height: 80px;
+  border-radius: 8px;
+  object-fit: cover;
+}
+
+.section-total {
+  float: right;
+  margin: 20px 0;
+  color: #1f2329;
+}
+
+.dialog-action-bar {
+  margin-bottom: 20px;
+}
+
+.delivery-select {
+  width: 220px;
+}
 
+.el-descriptions-item__label.is-bordered-label {
+  font-weight: normal;
+  color: #8a919f;
+}
+
+.el-descriptions-item__content {
+  max-width: 150px;
+  min-width: 100px;
+  color: #1f2329;
+}
+
+.chat-records {
   overflow-y: auto;
 }
+
 .timestamp {
   font-size: 12px;
-  color: #A9A9A9;
+  color: #8a919f;
 }
+
 .chat-record {
   margin: 10px;
   flex-direction: column;
   align-items: flex-start;
 }
+
 .sent {
   background-color: #fbfdff;
-  color: #000839;
+  color: #1f2329;
 }
+
 .sent .timestamp {
   float: right;
 }
-.right{
+
+.right {
   float: right;
 }
+
 .received {
   background-color: #fbfdff;
-  color: #000000;
-}
-
-.el-descriptions-item__content {
-  max-width: 150px;
-  min-width: 100px;
-}
-.order-content{
-  margin: 10px;
-
-}
-
-.operate-container {
-  background: #F2F6FC;
-  height: 60px;
-  margin: -20px -20px 0;
-  line-height: 60px;
-}
-
-.operate-button-container {
-  float: right;
-  margin-right: 20px
+  color: #1f2329;
 }
 </style>

+ 14 - 5
src/views/live/liveOrderLogs/index.vue

@@ -1,6 +1,11 @@
 <template>
-  <div class="app-container">
-    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
+  <div class="app-container live-page">
+        <div class="live-page__header">
+      <h2 class="live-page__title">订单日志</h2>
+    </div>
+
+<div class="live-panel" v-show="showSearch">
+<el-form class="live-search-form" :model="queryParams" ref="queryForm" :inline="true" label-width="68px">
       <el-form-item label="操作类型" prop="changeType">
         <el-select v-model="queryParams.changeType" placeholder="请选择操作类型" clearable size="small">
           <el-option label="请选择字典生成" value="" />
@@ -37,8 +42,10 @@
         <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
       </el-form-item>
     </el-form>
+    </div>
 
-    <el-row :gutter="10" class="mb8">
+    <div class="live-panel live-panel--table">
+<el-row :gutter="10" class="mb8">
       <el-col :span="1.5">
         <el-button
           type="primary"
@@ -85,7 +92,7 @@
       <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
     </el-row>
 
-    <el-table border v-loading="loading" :data="liveOrderLogsList" @selection-change="handleSelectionChange">
+    <el-table border v-loading="loading" :data="liveOrderLogsList" @selection-change="handleSelectionChange" class="live-table">
       <el-table-column type="selection" width="55" align="center" />
       <el-table-column label="操作员" align="center" prop="logsId" />
       <el-table-column label="订单id" align="center" prop="orderId" />
@@ -126,7 +133,9 @@
     />
 
     <!-- 添加或修改订单操作记录对话框 -->
-    <el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
+    </div>
+
+<el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
       <el-form ref="form" :model="form" :rules="rules" label-width="80px">
         <el-form-item label="操作类型" prop="changeType">
           <el-select v-model="form.changeType" placeholder="请选择操作类型">

+ 14 - 5
src/views/live/liveOrderStatus/index.vue

@@ -1,6 +1,11 @@
 <template>
-  <div class="app-container">
-    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
+  <div class="app-container live-page">
+        <div class="live-page__header">
+      <h2 class="live-page__title">订单状态</h2>
+    </div>
+
+<div class="live-panel" v-show="showSearch">
+<el-form class="live-search-form" :model="queryParams" ref="queryForm" :inline="true" label-width="68px">
       <el-form-item label="操作类型" prop="changeType">
         <el-select v-model="queryParams.changeType" placeholder="请选择操作类型" clearable size="small">
           <el-option label="请选择字典生成" value="" />
@@ -28,8 +33,10 @@
         <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
       </el-form-item>
     </el-form>
+    </div>
 
-    <el-row :gutter="10" class="mb8">
+    <div class="live-panel live-panel--table">
+<el-row :gutter="10" class="mb8">
       <el-col :span="1.5">
         <el-button
           type="primary"
@@ -76,7 +83,7 @@
       <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
     </el-row>
 
-    <el-table border v-loading="loading" :data="liveOrderStatusList" @selection-change="handleSelectionChange">
+    <el-table border v-loading="loading" :data="liveOrderStatusList" @selection-change="handleSelectionChange" class="live-table">
       <el-table-column type="selection" width="55" align="center" />
       <el-table-column label="操作时间" align="center" prop="id" />
       <el-table-column label="订单id" align="center" prop="orderId" />
@@ -116,7 +123,9 @@
     />
 
     <!-- 添加或修改订单操作记录对话框 -->
-    <el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
+    </div>
+
+<el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
       <el-form ref="form" :model="form" :rules="rules" label-width="80px">
         <el-form-item label="操作类型" prop="changeType">
           <el-select v-model="form.changeType" placeholder="请选择操作类型">

+ 14 - 5
src/views/live/liveOrderitems/index.vue

@@ -1,6 +1,11 @@
 <template>
-  <div class="app-container">
-    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
+  <div class="app-container live-page">
+        <div class="live-page__header">
+      <h2 class="live-page__title">订单明细</h2>
+    </div>
+
+<div class="live-panel" v-show="showSearch">
+<el-form class="live-search-form" :model="queryParams" ref="queryForm" :inline="true" label-width="68px">
       <el-form-item label="商品ID" prop="goodsId">
         <el-input
           v-model="queryParams.goodsId"
@@ -56,8 +61,10 @@
         <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
       </el-form-item>
     </el-form>
+    </div>
 
-    <el-row :gutter="10" class="mb8">
+    <div class="live-panel live-panel--table">
+<el-row :gutter="10" class="mb8">
       <el-col :span="1.5">
         <el-button
           type="primary"
@@ -104,7 +111,7 @@
       <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
     </el-row>
 
-    <el-table border v-loading="loading" :data="liveOrderitemsList" @selection-change="handleSelectionChange">
+    <el-table border v-loading="loading" :data="liveOrderitemsList" @selection-change="handleSelectionChange" class="live-table">
       <el-table-column type="selection" width="55" align="center" />
       <el-table-column label="ID" align="center" prop="id" />
       <el-table-column label="商品ID" align="center" prop="goodsId" />
@@ -144,7 +151,9 @@
     />
 
     <!-- 添加或修改订单商品对话框 -->
-    <el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
+    </div>
+
+<el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
       <el-form ref="form" :model="form" :rules="rules" label-width="80px">
         <el-form-item label="商品ID" prop="goodsId">
           <el-input v-model="form.goodsId" placeholder="请输入商品ID" />

+ 14 - 6
src/views/live/liveProfit/index.vue

@@ -1,6 +1,11 @@
 <template>
-  <div class="app-container">
-    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="100px">
+  <div class="app-container live-page">
+        <div class="live-page__header">
+      <h2 class="live-page__title">直播收益</h2>
+    </div>
+
+<div class="live-panel" v-show="showSearch">
+<el-form class="live-search-form" :model="queryParams" ref="queryForm" :inline="true" label-width="100px">
       <el-form-item label="订单号" prop="orderCode">
         <el-input
           v-model="queryParams.orderCode"
@@ -42,12 +47,14 @@
         <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
       </el-form-item>
     </el-form>
+    </div>
 
-    <el-row :gutter="10" class="mb8">
+    <div class="live-panel live-panel--table">
+<el-row :gutter="10" class="mb8">
       <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
     </el-row>
 
-    <el-table border v-loading="loading" :data="dataList">
+    <el-table border v-loading="loading" :data="dataList" class="live-table">
       <el-table-column label="订单ID" align="center" prop="orderId" />
       <el-table-column label="订单号" align="center" prop="orderCode" />
       <el-table-column label="用户名称" align="center" prop="userName" />
@@ -56,8 +63,8 @@
       <el-table-column label="支付价格" align="center" prop="payPrice" />
       <el-table-column label="是否支付" align="center" prop="isPay">
         <template slot-scope="scope">
-          <el-tag type="success" v-if="scope.row.isPay == 1">已支付</el-tag>
-          <el-tag type="info" v-else>未支付</el-tag>
+          <el-tag v-if="scope.row.isPay == 1">已支付</el-tag>
+          <el-tag v-else>未支付</el-tag>
         </template>
       </el-table-column>
       <el-table-column label="订单状态" align="center" prop="status">
@@ -77,6 +84,7 @@
       <el-table-column label="销售名称" align="center" prop="companyUserName" />
       <el-table-column label="公司名称" align="center" prop="companyName" />
     </el-table>
+    </div>
 
     <pagination
       v-show="total > 0"

+ 14 - 5
src/views/live/liveQuestion/index.vue

@@ -1,6 +1,11 @@
 <template>
-  <div class="app-container">
-    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
+  <div class="app-container live-page">
+        <div class="live-page__header">
+      <h2 class="live-page__title">直播答题</h2>
+    </div>
+
+<div class="live-panel" v-show="showSearch">
+<el-form class="live-search-form" :model="queryParams" ref="queryForm" :inline="true" label-width="68px">
       <el-form-item label="用户ID" prop="userId">
         <el-input
           v-model="queryParams.userId"
@@ -41,8 +46,10 @@
         <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
       </el-form-item>
     </el-form>
+    </div>
 
-    <el-row :gutter="10" class="mb8">
+    <div class="live-panel live-panel--table">
+<el-row :gutter="10" class="mb8">
       <el-col :span="1.5">
         <el-button
           type="primary"
@@ -89,7 +96,7 @@
       <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
     </el-row>
 
-    <el-table border v-loading="loading" :data="liveQuestionList" @selection-change="handleSelectionChange">
+    <el-table border v-loading="loading" :data="liveQuestionList" @selection-change="handleSelectionChange" class="live-table">
       <el-table-column type="selection" width="55" align="center" />
       <el-table-column label="ID" align="center" prop="questionId" />
       <el-table-column label="用户ID" align="center" prop="userId" />
@@ -130,7 +137,9 @@
     />
 
     <!-- 添加或修改问答对话框 -->
-    <el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
+    </div>
+
+<el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
       <el-form ref="form" :model="form" :rules="rules" label-width="80px">
         <el-form-item label="用户ID" prop="userId">
           <el-input v-model="form.userId" placeholder="请输入用户ID" />

+ 13 - 5
src/views/live/liveRewardRecord/index.vue

@@ -1,7 +1,12 @@
 <template>
-  <div class="app-container">
-    <div class="reward-container">
-      <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
+  <div class="app-container live-page">
+        <div class="live-page__header">
+      <h2 class="live-page__title">打赏记录</h2>
+    </div>
+
+<div class="reward-container">
+      <div class="live-panel" v-show="showSearch">
+<el-form class="live-search-form" :model="queryParams" ref="queryForm" :inline="true" label-width="68px">
         <el-form-item label="公司名" prop="companyId">
           <el-select filterable  v-model="queryParams.companyId" placeholder="请选择公司名"  @change="companyChange" clearable size="small">
             <el-option
@@ -36,6 +41,7 @@
           >导出</el-button>
         </el-form-item>
       </el-form>
+    </div>
       <!-- 时间步长选择 -->
       <span class="demonstration">月份选择(可多选)</span>
 
@@ -44,13 +50,14 @@
     <!-- 直播间数据表格 + 总消耗 -->
     <div style="margin-top: 24px;">
       <h3 style="font-size: 16px; font-weight: 600; margin-bottom: 12px;">直播间积分消耗明细</h3>
-      <el-table
+      <div class="live-panel live-panel--table">
+<el-table
         :data="tableData"
         border
         show-summary
         stripe
         style="width: 100%; margin-bottom: 12px;"
-      >
+       class="live-table">
         <el-table-column label="直播名称" prop="liveNameAndId" width="100" align="center" />
         <el-table-column label="公司主题" prop="companyName" width="100" align="center" />
         <el-table-column
@@ -65,6 +72,7 @@
         </el-table-column>
         <el-table-column label="单直播间总计" prop="total" width="120" align="center" />
       </el-table>
+      </div>
     </div>
   </div>
 </template>

+ 12 - 4
src/views/live/liveTrafficLog/index.vue

@@ -1,6 +1,11 @@
 <template>
-  <div class="app-container">
-    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
+  <div class="app-container live-page">
+        <div class="live-page__header">
+      <h2 class="live-page__title">流量日志</h2>
+    </div>
+
+<div class="live-panel" v-show="showSearch">
+<el-form class="live-search-form" :model="queryParams" ref="queryForm" :inline="true" label-width="68px">
       <el-form-item label="技术主体" prop="companyId" >
         <el-select v-model="queryParams.companyId" placeholder="请选择技术主体" filterable clearable size="small">
           <el-option
@@ -25,8 +30,10 @@
         <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
       </el-form-item>
     </el-form>
+    </div>
 
-    <el-row :gutter="10" class="mb8">
+    <div class="live-panel live-panel--table">
+<el-row :gutter="10" class="mb8">
       <el-col :span="1.5">
         <el-button
           type="warning"
@@ -41,7 +48,7 @@
       <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
     </el-row>
 
-    <el-table border v-loading="loading" :data="courseTrafficLogList" @selection-change="handleSelectionChange">
+    <el-table border v-loading="loading" :data="courseTrafficLogList" @selection-change="handleSelectionChange" class="live-table">
       <el-table-column type="selection" width="55" align="center" />
       <el-table-column label="技术主体" align="center" prop="companyName" />
       <el-table-column label="月份" align="center" prop="month" />
@@ -56,6 +63,7 @@
         </template>
       </el-table-column>
     </el-table>
+    </div>
 
     <pagination
       v-show="total>0"

+ 14 - 5
src/views/live/liveUserLotteryRecord/index.vue

@@ -1,6 +1,11 @@
 <template>
-  <div class="app-container">
-    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
+  <div class="app-container live-page">
+        <div class="live-page__header">
+      <h2 class="live-page__title">用户抽奖记录</h2>
+    </div>
+
+<div class="live-panel" v-show="showSearch">
+<el-form class="live-search-form" :model="queryParams" ref="queryForm" :inline="true" label-width="68px">
       <el-form-item label="直播间ID" prop="liveId">
         <el-input
           v-model="queryParams.liveId"
@@ -41,8 +46,10 @@
         <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
       </el-form-item>
     </el-form>
+    </div>
 
-    <el-row :gutter="10" class="mb8">
+    <div class="live-panel live-panel--table">
+<el-row :gutter="10" class="mb8">
       <el-col :span="1.5">
         <el-button
           type="primary"
@@ -89,7 +96,7 @@
       <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
     </el-row>
 
-    <el-table border v-loading="loading" :data="liveUserLotteryRecordList" @selection-change="handleSelectionChange">
+    <el-table border v-loading="loading" :data="liveUserLotteryRecordList" @selection-change="handleSelectionChange" class="live-table">
       <el-table-column type="selection" width="55" align="center" />
       <el-table-column label="创建日期" align="center" prop="id" />
       <el-table-column label="抽奖ID" align="center" prop="lotteryId" />
@@ -131,7 +138,9 @@
     />
 
     <!-- 添加或修改直播用户中奖记录对话框 -->
-    <el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
+    </div>
+
+<el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
       <el-form ref="form" :model="form" :rules="rules" label-width="80px">
       </el-form>
       <div slot="footer" class="dialog-footer">

+ 14 - 5
src/views/live/liveUserRedRecord/index.vue

@@ -1,6 +1,11 @@
 <template>
-  <div class="app-container">
-    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
+  <div class="app-container live-page">
+        <div class="live-page__header">
+      <h2 class="live-page__title">用户红包记录</h2>
+    </div>
+
+<div class="live-panel" v-show="showSearch">
+<el-form class="live-search-form" :model="queryParams" ref="queryForm" :inline="true" label-width="68px">
       <el-form-item label="直播间ID" prop="liveId">
         <el-input
           v-model="queryParams.liveId"
@@ -32,8 +37,10 @@
         <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
       </el-form-item>
     </el-form>
+    </div>
 
-    <el-row :gutter="10" class="mb8">
+    <div class="live-panel live-panel--table">
+<el-row :gutter="10" class="mb8">
       <el-col :span="1.5">
         <el-button
           type="primary"
@@ -80,7 +87,7 @@
       <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
     </el-row>
 
-    <el-table border v-loading="loading" :data="liveUserRedRecordList" @selection-change="handleSelectionChange">
+    <el-table border v-loading="loading" :data="liveUserRedRecordList" @selection-change="handleSelectionChange" class="live-table">
       <el-table-column type="selection" width="55" align="center" />
       <el-table-column label="创建日期" align="center" prop="id" />
       <el-table-column label="抽奖ID" align="center" prop="redId" />
@@ -122,7 +129,9 @@
     />
 
     <!-- 添加或修改直播用户红包记录对话框 -->
-    <el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
+    </div>
+
+<el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
       <el-form ref="form" :model="form" :rules="rules" label-width="80px">
       </el-form>
       <div slot="footer" class="dialog-footer">

+ 54 - 14
src/views/live/liveVideo/index.vue

@@ -1,7 +1,12 @@
 <template>
-  <div class="app-container">
+  <div class="app-container live-page">
 
-    <el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
+    <div class="live-page__header">
+      <h2 class="live-page__title">直播视频</h2>
+    </div>
+
+    <div class="live-panel" v-show="showSearch">
+      <el-form class="live-search-form" :model="queryParams" ref="queryForm" size="small" :inline="true" label-width="68px">
       <el-form-item label="视频名称" prop="videoName">
         <el-input
           v-model="queryParams.videoName"
@@ -55,8 +60,10 @@
         <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
       </el-form-item>
     </el-form>
+    </div>
 
-    <el-row :gutter="10" class="mb8">
+    <div class="live-panel live-panel--table">
+<el-row :gutter="10" class="mb8">
       <el-col :span="1.5">
         <el-button
           type="primary"
@@ -107,15 +114,20 @@
       <el-table-column label="分类" align="center" prop="category" :formatter="categoryFormatter" />
       <el-table-column label="排序" align="center" prop="sort" />
       <el-table-column label="备注" align="center" prop="remark" />
-      <el-table-column label="视频地址" align="center" prop="videoUrl">
+      <el-table-column label="视频" align="center" prop="videoUrl" width="96">
         <template slot-scope="scope">
-          <video
-            :src="scope.row.videoUrl"
-            controls
-            controlsList="nodownload"
-            class="video-player"
-            @contextmenu.prevent
-          ></video>
+          <div class="live-video-thumb" @click="openVideoPlay(scope.row)">
+            <img
+              v-if="scope.row.thumbnail"
+              :src="scope.row.thumbnail"
+              class="live-cell__cover live-cell__cover--square"
+              alt=""
+            />
+            <div v-else class="live-cell__cover live-cell__cover--square live-video-thumb__empty">
+              <i class="el-icon-video-camera"></i>
+            </div>
+            <i class="el-icon-video-play live-video-thumb__play"></i>
+          </div>
         </template>
       </el-table-column>
       <el-table-column label="文件大小" align="center">
@@ -126,9 +138,9 @@
       <el-table-column label="时长" align="center" prop="duration" :formatter="formatDuration"/>
       <el-table-column label="转码状态" align="center" prop="finishStatus">
         <template slot-scope="scope">
-          <el-tag v-if="scope.row.finishStatus == 1">转码成功</el-tag>
-          <el-tag v-else-if="scope.row.finishStatus == 0">转码中</el-tag>
-          <el-tag v-else>---</el-tag>
+          <span v-if="scope.row.finishStatus == 1" class="live-ok">转码成功</span>
+          <span v-else-if="scope.row.finishStatus == 0" class="live-warn">转码中</span>
+          <span v-else class="live-muted">---</span>
         </template>
       </el-table-column>
       <el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="250">
@@ -162,6 +174,7 @@
       :limit.sync="queryParams.pageSize"
       @pagination="getList"
     />
+    </div>
 
     <!-- 添加或修改直播视频对话框 -->
     <el-dialog :title="title" :visible.sync="open" width="800px" append-to-body>
@@ -415,6 +428,23 @@
         <el-button @click="cancelReuploadVideo">取 消</el-button>
       </div>
     </el-dialog>
+    <el-dialog
+      title="视频播放"
+      :visible.sync="videoPlayVisible"
+      width="720px"
+      append-to-body
+      @closed="currentPlayUrl = ''"
+    >
+      <video
+        v-if="currentPlayUrl"
+        :src="currentPlayUrl"
+        controls
+        controlsList="nodownload"
+        class="live-video-dialog-player"
+        @contextmenu.prevent
+      ></video>
+    </el-dialog>
+
   </div>
 </template>
 
@@ -429,6 +459,8 @@ export default {
   components: { LiveVideoUpload },
   data() {
     return {
+      videoPlayVisible: false,
+      currentPlayUrl: '',
       //字典
       videoTypeOptions: [],
       categoryOptions: [],
@@ -543,6 +575,14 @@ export default {
     this.getAllCompanyOptions();
   },
   methods: {
+    openVideoPlay(row) {
+      if (!row || !row.videoUrl) {
+        this.$message.warning('暂无视频地址');
+        return;
+      }
+      this.currentPlayUrl = row.videoUrl;
+      this.videoPlayVisible = true;
+    },
     categoryFormatter(row, column) {
       return this.selectDictLabel(this.categoryOptions, row.category);
     },

+ 8 - 2
src/views/live/liveWatchUser/index.vue

@@ -1,6 +1,11 @@
 <template>
-  <div class="app-container">
-    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
+  <div class="app-container live-page">
+        <div class="live-page__header">
+      <h2 class="live-page__title">观看用户</h2>
+    </div>
+
+<div class="live-panel" v-show="showSearch">
+<el-form class="live-search-form" :model="queryParams" ref="queryForm" :inline="true" label-width="68px">
       <el-form-item label="直播ID" prop="liveId">
         <el-input
           v-model="queryParams.liveId"
@@ -15,6 +20,7 @@
         <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
       </el-form-item>
     </el-form>
+    </div>
   </div>
 </template>
 

+ 11 - 3
src/views/live/words/index.vue

@@ -1,6 +1,11 @@
 <template>
-  <div class="app-container">
-    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="80px">
+  <div class="app-container live-page">
+    <div class="live-page__header">
+      <h2 class="live-page__title">敏感词</h2>
+    </div>
+
+    <div class="live-panel" v-show="showSearch">
+      <el-form class="live-search-form" :model="queryParams" ref="queryForm" :inline="true" label-width="80px">
       <el-form-item label="敏感词" prop="word">
         <el-input
           v-model="queryParams.word"
@@ -23,8 +28,10 @@
         <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
       </el-form-item>
     </el-form>
+    </div>
 
-    <el-row :gutter="10" class="mb8">
+    <div class="live-panel live-panel--table">
+<el-row :gutter="10" class="mb8">
       <el-col :span="1.5">
         <el-button
           type="primary"
@@ -107,6 +114,7 @@
       :limit.sync="queryParams.pageSize"
       @pagination="getList"
     />
+    </div>
 
     <!-- 添加或修改直播间敏感词过滤对话框 -->
     <el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>

+ 12 - 2
src/views/store/components/productAttrValueSelect.vue

@@ -56,9 +56,16 @@
 </template>
 
 <script>
-import { getStoreProductAttrValueList } from "@/api/store/storeProduct";
+import { getStoreProductAttrValueList, getOnSaleProductAttrValueList } from "@/api/store/storeProduct";
 export default {
   name: "selectProduct",
+  props: {
+    /** 是否只查未下架商品规格,套餐选品默认 true */
+    onlyOnSale: {
+      type: Boolean,
+      default: false
+    }
+  },
   data() {
     return {
       loading: true,
@@ -91,10 +98,13 @@ export default {
     },
     getList(){
       this.loading = true;
-      getStoreProductAttrValueList(this.queryParams).then(response => {
+      const requestApi = this.onlyOnSale ? getOnSaleProductAttrValueList : getStoreProductAttrValueList;
+      requestApi(this.queryParams).then(response => {
         this.list = response.rows;
         this.total = response.total;
         this.loading = false;
+      }).catch(() => {
+        this.loading = false;
       });
     }
   }

+ 1 - 1
src/views/store/storeProductPackage/index.vue

@@ -351,7 +351,7 @@
     </el-dialog>
 
     <el-dialog :title="product.title" v-if="product.open"  :visible.sync="product.open" width="1000px" append-to-body>
-        <product-attr-value-select  ref="product"  @selectProduct="selectProduct" />
+        <product-attr-value-select  ref="product" :only-on-sale="true" @selectProduct="selectProduct" />
     </el-dialog>
   </div>
 </template>