Browse Source

Merge remote-tracking branch 'origin/master'

yfh 3 ngày trước cách đây
mục cha
commit
c1b94ab61a

+ 86 - 0
src/api/qw/applyIpad.js

@@ -0,0 +1,86 @@
+import request from '@/utils/request'
+
+// 查询分配记录列表
+export function listRecords(query) {
+  return request({
+    url: '/qw/records/list',
+    method: 'get',
+    params: query
+  })
+}
+
+// 查询分配记录详细
+export function getRecords(id) {
+  return request({
+    url: '/qw/records/' + id,
+    method: 'get'
+  })
+}
+
+// 新增分配记录
+export function addRecords(data) {
+  return request({
+    url: '/qw/records',
+    method: 'post',
+    data: data
+  })
+}
+
+// 修改分配记录
+export function updateRecords(data) {
+  return request({
+    url: '/qw/records',
+    method: 'put',
+    data: data
+  })
+}
+
+// 删除分配记录
+export function delRecords(id) {
+  return request({
+    url: '/qw/records/' + id,
+    method: 'delete'
+  })
+}
+
+// 导出分配记录
+export function exportRecords(query) {
+  return request({
+    url: '/qw/records/export',
+    method: 'get',
+    params: query
+  })
+}
+
+export function apply(applyCount) {
+  return request({
+    url: '/qw/records/apply',
+    method: 'get',
+    params: applyCount
+  })
+}
+// 新增分配记录
+//@PostMapping("/batchUpdate")
+// public AjaxResult batchUpdate(@RequestBody Long[] ids)
+//后端接收结构
+export function batchUpdate(data) {
+  return request({
+    url: '/qw/records/batchUpdate',
+    method: 'post',
+    data: data
+  })
+}
+// 获取服务器信息
+export function getServerInfo(id) {
+  return request({
+    url: '/qw/records/server/' + id,
+    method: 'get'
+  })
+}
+export function release(data) {
+  return request({
+    url: '/qw/records/release',
+    method: 'post',
+    data: data
+  })
+}

+ 9 - 0
src/api/qw/qwIpadServer.js

@@ -50,4 +50,13 @@ export function exportQwIpadServer(query) {
     method: 'get',
     params: query
   })
+}
+
+// 新增ipad服务器
+export function release(data) {
+  return request({
+    url: '/qw/records/release',
+    method: 'post',
+    data: data
+  })
 }

+ 2313 - 0
src/views/components/index/statisticsDashboard.vue

@@ -0,0 +1,2313 @@
+<template>
+  <div class="statistics-dashboard">
+    <!-- 数据概览 (Data Overview) -->
+    <el-card class="overview-section" shadow="never">
+      <el-row :gutter="20">
+        <el-col :xs="24" :sm="24" :md="16" :lg="16" :xl="16" class="companybox">
+          <img src="@/assets/images/topbg.png" alt="" class="topimg">
+          <img src="@/assets/images/topbg.png" alt="" class="bottomimg">
+          <div class="companyboxtitle">
+            企业数据
+          </div>
+          <div class="companyflex">
+            <div class="topbg companycard cardafter">
+              <div class="card-title1">
+                <img src="@/assets/images/tab_company.png" alt="" class="icon-img">
+                分公司数量
+              </div>
+
+              <div class="card-value highlight1">
+                <count-to :start-val="0" :end-val="dealderCount" :duration="3600"
+                          class="card-panel-num companynumber" />
+              </div>
+            </div>
+            <div class="companycard cardafter">
+              <div class="card-title1">
+                <img src="@/assets/images/salesperson.png" alt="" class="icon-img">
+                销售数量
+              </div>
+              <div class="card-value highlight1">
+                <count-to :start-val="0" :end-val="groupMgrCount" :duration="3600"
+                          class="card-panel-num companynumber" />
+              </div>
+            </div>
+            <div class="companycard cardafter">
+              <div class="card-title1">
+                <img src="@/assets/images/member.png" alt="" class="icon-img">
+                会员数量
+              </div>
+              <div class="card-value highlight1">
+                <count-to :start-val="0" :end-val="memberCount" :duration="3600" class="card-panel-num companynumber" />
+                <span class="highlight-today-add companyadd">+{{todayIncreaseUserNum}}</span>
+              </div>
+
+            </div>
+            <div class="cardafter companycard">
+              <div class="card-title1">
+                <img src="@/assets/images/tab_enterprise.png" alt="" class="icon-img">
+                企微数量
+              </div>
+              <div class="card-value highlight1">
+                <count-to :start-val="0" :end-val="qwMemberNum" :duration="3600" class="card-panel-num companynumber" />
+              </div>
+            </div>
+            <div class="botttombg companycard">
+              <div class="card-title1">
+                <svg-icon icon-class="phone" />
+                pad使用情况
+              </div>
+              <div class="card-value highlight1">
+                <count-to :start-val="0" :end-val="padUsedNum" :duration="3600" class="card-panel-num companynumber" />
+                /
+                <template v-if="typeof padTotalNum === 'number'">
+                  <count-to :start-val="0" :end-val="padTotalNum" :duration="1800" class="card-panel-num companynumber" />
+                </template>
+                <template v-else>
+                  <span class="card-panel-num companynumber">{{ padTotalNum }}</span>
+                </template>
+              </div>
+            </div>
+          </div>
+        </el-col>
+
+
+        <el-col :xs="24" :sm="24" :md="8" :lg="8" :xl="8" class="propertyboxtitle">
+          <div class="property_title">
+            资产概览
+          </div>
+          <div class="propertyboxflex">
+            <div class="property-card propertyline">
+              <div class="property-title">
+                <i class="el-icon-money"></i>
+                企业资产(元)
+              </div>
+              <div class="card-value highlight">
+                <count-to :start-val="0" :end-val="balance" :duration="3600" class="card-panel-num" />
+              </div>
+            </div>
+            <div class="property-card propertyline">
+              <div class="property-title">
+                <i class="el-icon-money"></i>
+                润天余额(元)
+              </div>
+              <div class="card-value highlight">
+                <count-to :start-val="0" :end-val="runTianBalance" :duration="3600" class="card-panel-num" />
+              </div>
+            </div>
+            <div class="property-card">
+              <div class="property-title">
+                <span>今日消耗 (元)</span>
+              </div>
+              <div class="card-value highlight" style="color: rgba(32, 33, 36, 1);margin-top: 10px;">
+                <count-to :start-val="0" :end-val="todayComsumption" :duration="3600" class="card-panel-num" />
+              </div>
+              <div class="card-compare">
+                较昨日 <span>+1</span>
+              </div>
+            </div>
+          </div>
+
+        </el-col>
+      </el-row>
+
+      <el-row :gutter="20">
+        <el-col :xs="24" :sm="24" :md="16" :lg="16" :xl="16">
+          <div class="operatetitle">
+            经营数据
+          </div>
+          <div class="operatetitle-col">
+            <div class="operatetitle-card">
+              <div class="card-title">
+                <i class="el-icon-shopping-cart-full"></i>
+                收款总数
+              </div>
+              <div class="operate-value highlight">
+                <count-to :start-val="0" :end-val="recvTotalNum" :duration="3600" class="card-panel-num" />
+                <div class="yesterdaybox">
+                  较昨日 <span class="highlight-today-add2">+{{recvTodayNum}}</span>
+                </div>
+              </div>
+              <div class="card-badge">
+              </div>
+            </div>
+            <div class="operatetitle-card">
+              <div class="card-title">
+                <i class="el-icon-shopping-cart-full"></i>
+                订单总数
+              </div>
+              <div class="operate-value highlight">
+                <count-to :start-val="0" :end-val="orderTotalNum" :duration="3600" class="card-panel-num" />
+                <div class="yesterdaybox">
+                  较昨日 <span class="highlight-today-add2">+{{todayOrderNum}}</span>
+                </div>
+
+              </div>
+              <div class="card-badge">
+              </div>
+
+            </div>
+            <div class="operatetitle-card">
+              <div class="card-title">
+                平台今日看课人数
+              </div>
+              <div class="operate-value highlight">
+                <count-to :start-val="0" :end-val="todayWatchUserCount" :duration="3600" class="card-panel-num" />
+              </div>
+              <div class="card-sub">
+                <span>配额上限</span>
+                <span class="sub-value">
+                  <count-to :start-val="0" :end-val="todayWatchUserCount" :duration="3600" class="card-panel-num"
+                            style="color: rgba(49, 185, 154, 1);" />
+                  /
+                  <count-to :start-val="0" :end-val="versionLimit" :duration="3600" class="card-panel-num" />
+                </span>
+              </div>
+              <el-progress :percentage="versionLimitPercent" :show-text="false"
+                           color="#409EFF"></el-progress>
+            </div>
+            <div class="operatetitle-card">
+              <div class="card-title">
+                <i class="el-icon-shopping-cart-full"></i>
+                商品总数
+              </div>
+              <div class="operate-value highlight">
+                <count-to :start-val="0" :end-val="goodsTotalNum" :duration="3600" class="card-panel-num" />
+                <div class="yesterdaybox">
+                  较昨日 <span class="highlight-today-add2">+{{todayGoodsNum}}</span>
+                </div>
+
+              </div>
+              <div class="card-badge">
+              </div>
+            </div>
+          </div>
+
+        </el-col>
+
+
+        <el-col :xs="24" :sm="24" :md="8" :lg="8" :xl="8" style="padding-left: 15px;">
+
+          <div class="internetbox">
+            <div class="internet-cardtop">
+              <div class="cardinnerbox">
+                <div class="cardtopimg">
+                  <img src="@/assets/images/liuliang.png" alt=""><span>剩余流量</span>
+                </div>
+                <div class="cardtopnumber">
+                  <span>{{formatBytes(this.trafficCount)}}</span>
+                </div>
+              </div>
+              <div class="progress">
+                <el-progress :percentage="90" :show-text="false" define-back-color="#000">
+
+                </el-progress>
+              </div>
+              <div class="cardinnerbox2">
+                <div>
+                  今日消耗 <span>{{formatBytes(this.todayTraffic)}}</span>
+                </div>
+                <div>
+                  本月 <span>{{formatBytes(this.thisMonthTraffic)}}</span>
+                </div>
+              </div>
+            </div>
+
+            <div class="internetbox-messge">
+              <div class="internet-card">
+                <img src="@/assets/images/message.png" alt="">
+
+                <span class="internet-title">
+                  短信剩余条数 (条)
+                </span>
+              </div>
+              <div class="internet-number">
+                0
+              </div>
+            </div>
+          </div>
+        </el-col>
+      </el-row>
+    </el-card>
+    <!-- 分析概览 (Analysis Overview) -->
+    <div class="analysis-section" shadow="never">
+      <div slot="header" class="header">
+        <div>分析概览</div>
+        <div class="tab-group">
+          <el-radio-group v-model="queryTime" size="medium" @change="handleAnalysis">
+            <el-radio-button label="今日"></el-radio-button>
+            <el-radio-button label="昨日"></el-radio-button>
+            <el-radio-button label="本周"></el-radio-button>
+            <el-radio-button label="本月"></el-radio-button>
+            <el-radio-button label="上月"></el-radio-button>
+          </el-radio-group>
+        </div>
+        <div class="action-group">
+          <div v-if="this.$store.state.user.medicalMallConfig.statics">
+            <!-- 选择部门 -->
+            <el-select v-model="deptId" placeholder="请选择部门" size="small" @change="handleDeptChange" style="width: 150px">
+              <el-option
+                v-for="company in deptOptions"
+                :key="company.deptId"
+                :label="company.deptName"
+                :value="company.deptId"
+              />
+            </el-select>
+            <!-- 选择销售公司 -->
+            <el-select  v-model="companyId" placeholder="请选择销售公司" size="small" clearable @change="handleCompanyChange" style="width: 180px" >
+              <el-option
+                v-for="company in companyOptions"
+                :key="company.companyId"
+                :label="company.companyName"
+                :value="company.companyId"
+              />
+            </el-select>
+          </div>
+          <el-radio-group v-model="userTypeText" @change="handleUserType">
+            <el-radio-button label="会员"></el-radio-button>
+            <el-radio-button label="企微"></el-radio-button>
+          </el-radio-group>
+
+          <el-dropdown @command="handleAutoRefresh" trigger="click">
+            <el-button size="small" plain>
+              自动刷新
+              <i class="el-icon-arrow-down el-icon--right"></i>
+            </el-button>
+            <el-dropdown-menu slot="dropdown">
+              <el-dropdown-item :command="0" :class="{ 'is-active': !autoRefreshInterval }">关闭</el-dropdown-item>
+              <el-dropdown-item :command="5" :class="{ 'is-active': autoRefreshInterval === 5 }">5分钟</el-dropdown-item>
+              <el-dropdown-item :command="10"
+                                :class="{ 'is-active': autoRefreshInterval === 10 }">10分钟</el-dropdown-item>
+              <el-dropdown-item :command="15"
+                                :class="{ 'is-active': autoRefreshInterval === 15 }">15分钟</el-dropdown-item>
+            </el-dropdown-menu>
+          </el-dropdown>
+          <el-button size="small" plain icon="el-icon-refresh" type="primary" @click="manualRefresh">手动刷新</el-button>
+        </div>
+      </div>
+    </div>
+    <div>
+      <el-row :gutter="20">
+        <el-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12" style="position: relative">
+          <div class="analysis-card-check" :class="selectedDiv===0?'analysis-card-check-selected color':''"
+               @click="handleToggleDiv(0)">
+            <div class="analysis-card">
+              <img class="card-icon" src="@/assets/images/cishu_views.png"></img>
+              <div class="card-content">
+                <div class="card-row">
+                  <span>观看人数</span>
+                  <span class="highlight">
+                    <count-to :start-val="0" :end-val="watchUserCount" :duration="3600" class="card-panel-num" />
+                  </span>
+                </div>
+                <div class="card-row">
+                  <span>完播人数</span>
+                  <span class="highlight">
+                    <count-to :start-val="0" :end-val="completedUserCount" :duration="3600" class="card-panel-num" />
+                  </span>
+                </div>
+                <div class="card-row">
+                  <span>完播率</span>
+                  <span class="highlight">{{completedRate}}%</span>
+                </div>
+              </div>
+            </div>
+            <div class="analysis-card">
+              <img class="card-icon" src="@/assets/images/number_views.png"></img>
+              <div class="card-content">
+                <div class="card-row">
+                  <span>观看次数</span>
+                  <span class="highlight-red">
+                    <count-to :start-val="0" :end-val="watchCount" :duration="3600" class="card-panel-num" /></span>
+                </div>
+                <div class="card-row">
+                  <span>完播次数</span>
+                  <span class="highlight-red">
+                    <count-to :start-val="0" :end-val="completedCount" :duration="3600" class="card-panel-num" />
+                  </span>
+                </div>
+                <div class="card-row">
+                  <span>视频完播率</span>
+                  <span class="highlight-red">{{watchRate}}%</span>
+                </div>
+              </div>
+            </div>
+          </div>
+        </el-col>
+
+        <el-col :xs="24" :sm="12" :md="6" :lg="6" :xl="6" style="position: relative">
+          <div class="analysis-card-check" :class="selectedDiv===1?'analysis-card-check-selected color':''"
+               @click="handleToggleDiv(1)">
+            <div class="analysis-card">
+              <img class="card-icon" src="@/assets/images/renshu_views.png"></img>
+              <div class="card-content">
+                <div class="card-row">
+                  <span>答题人数</span>
+                  <span class="highlight-black">
+                    <count-to :start-val="0" :end-val="answerMemberCount" :duration="3600" class="card-panel-num" />
+                  </span>
+                </div>
+                <div class="card-row">
+                  <span>正确人数</span>
+                  <span class="highlight-black">
+                    <count-to :start-val="0" :end-val="correctUserCount" :duration="3600" class="card-panel-num" />
+                  </span>
+                </div>
+                <div class="card-row">
+                  <span>正确率</span>
+                  <span class="highlight-black">{{correctRate}}%</span>
+                </div>
+              </div>
+            </div>
+          </div>
+        </el-col>
+
+        <el-col :xs="24" :sm="12" :md="6" :lg="6" :xl="6"  style="position: relative">
+          <div class="analysis-card-check" :class="selectedDiv===2?'analysis-card-check-selected color':''"
+               @click="handleToggleDiv(2)">
+            <div class="analysis-card">
+              <img class="card-icon" src="@/assets/images/hongbao_views.png"></img>
+              <div class="card-content">
+                <div class="card-row">
+                  <span>答题红包个数</span>
+                  <span class="highlight-black">
+                    <count-to :start-val="0" :end-val="rewardCount" :duration="3600" class="card-panel-num" />
+                  </span>
+                </div>
+                <div class="card-row">
+                  <span>答题红包金额(元)</span>
+                  <span class="highlight-black">
+                    <count-to :start-val="0" :end-val="rewardMoney" :duration="3600" class="card-panel-num" /></span>
+                </div>
+              </div>
+            </div>
+          </div>
+        </el-col>
+      </el-row>
+    </div>
+
+    <!-- 图表区域 (Charts Area) -->
+    <transition name="fade">
+      <el-row :gutter="20" class="charts-section" v-show="selectedDiv===0">
+        <el-col :span="12">
+          <el-card shadow="never">
+            <div slot="header" class="chart-header">
+              <span>会员观看、完播人数趋势图</span>
+              <div class="legend">
+                <div class="legend-item">
+                  <span class="dot viewer-dot"></span>
+                  <span>观看人数</span>
+                </div>
+                <div class="legend-item">
+                  <span class="dot complete-dot"></span>
+                  <span>完播人数</span>
+                </div>
+              </div>
+              <!--              <el-button size="small" plain class="view-more">平台每日统计 <i class="el-icon-arrow-right"></i></el-button>-->
+            </div>
+            <div ref="viewerChart" class="chart-container"></div>
+          </el-card>
+        </el-col>
+
+
+        <el-col :span="12">
+          <el-card shadow="never">
+            <div slot="header" class="chart-header">
+              <span>经销商看客统计</span>
+              <div class="legend">
+                <div class="legend-item">
+                  <span class="dot viewer-dot"></span>
+                  <span>观看人数</span>
+                </div>
+                <div class="legend-item">
+                  <span class="dot complete-dot"></span>
+                  <span>完播人数</span>
+                </div>
+              </div>
+            </div>
+            <div ref="dealerChartNew" class="chart-container"></div>
+          </el-card>
+        </el-col>
+
+        <!--        <el-col :span="12">-->
+        <!--          <el-card shadow="never">-->
+        <!--            <div slot="header" class="chart-header">-->
+        <!--              <span>经销商会员观看TOP10</span>-->
+        <!--              <div class="legend">-->
+        <!--                <el-radio-group v-model="viewerType" size="small" @change="handleDealerChartData">-->
+        <!--                  <el-radio-button label="0">按观看人数</el-radio-button>-->
+        <!--                  <el-radio-button label="1">按完播人数</el-radio-button>-->
+        <!--                </el-radio-group>-->
+        <!--              </div>-->
+        <!--              &lt;!&ndash;              <el-button size="small" plain class="view-more">经销商统计 <i class="el-icon-arrow-right"></i></el-button>&ndash;&gt;-->
+        <!--            </div>-->
+        <!--            <div ref="dealerChart" class="chart-container"></div>-->
+        <!--          </el-card>-->
+        <!--        </el-col>-->
+      </el-row>
+    </transition>
+    <transition name="fade">
+      <el-row :gutter="20" class="charts-section" v-show="selectedDiv===1">
+        <el-card shadow="never">
+          <div slot="header" class="chart-header">
+            <span>课程观看TOP10</span>
+            <div class="legend">
+              <el-radio-group v-model="viewerType" size="small" @change="handleCourseWatchChart">
+                <el-radio-button label="0">按观看人数</el-radio-button>
+                <el-radio-button label="1">按完播人数</el-radio-button>
+                <el-radio-button label="2">按答题人数</el-radio-button>
+                <el-radio-button label="3">按正确人数</el-radio-button>
+              </el-radio-group>
+            </div>
+            <div class="legend">
+              <el-radio-group v-model="delerSort" @change="handleCourseWatchChart">
+                <el-radio label="DESC">前10名</el-radio>
+                <el-radio label="ASC">倒数10名</el-radio>
+              </el-radio-group>
+            </div>
+            <div class="legend">
+              <div class="legend-item">
+                <span class="dot viewer-dot"></span>
+                <span>观看人数</span>
+              </div>
+              <div class="legend-item">
+                <span class="dot complete-dot"></span>
+                <span>完播人数</span>
+              </div>
+              <div class="legend-item">
+                <span class="dot" style="background-color: #E6A23C"></span>
+                <span>答题人数</span>
+              </div>
+              <div class="legend-item">
+                <span class="dot" style="background-color: #F56C6C"></span>
+                <span>正确人数</span>
+              </div>
+            </div>
+            <!--            <el-button size="small" plain class="view-more">经销商统计 <i class="el-icon-arrow-right"></i></el-button>-->
+          </div>
+          <div ref="courseWatchChart" class="chart-container"></div>
+        </el-card>
+      </el-row>
+    </transition>
+
+    <transition name="fade">
+      <el-row :gutter="20" class="charts-section" v-show="selectedDiv===2">
+        <el-col :span="12">
+          <el-card shadow="never">
+            <div slot="header" class="chart-header">
+              <span>答题红包金额TOP10</span>
+              <div class="legend">
+                <el-radio-group v-model="dataType" size="small" @change="handleAnswerRedPackViewerChart">
+                  <el-radio-button label="0">按经销商排行</el-radio-button>
+                  <el-radio-button label="1">按课程排行</el-radio-button>
+                </el-radio-group>
+              </div>
+              <!--              <el-button size="small" plain class="view-more">红包记录 <i class="el-icon-arrow-right"></i></el-button>-->
+            </div>
+            <div ref="answerRedPackViewerChart" class="chart-container"></div>
+          </el-card>
+        </el-col>
+        <el-col :span="12">
+          <el-card shadow="never">
+            <div slot="header" class="chart-header">
+              <span>答题红包金额趋势图</span>
+              <div class="legend">
+                <div class="legend-item">
+                  <span class="dot viewer-dot"></span>
+                  <span>答题红包金额</span>
+                </div>
+              </div>
+              <!--              <el-button size="small" plain class="view-more">红包记录 <i class="el-icon-arrow-right"></i></el-button>-->
+            </div>
+            <div ref="answerRedPackMoneyViewerChart" class="chart-container"></div>
+          </el-card>
+        </el-col>
+      </el-row>
+    </transition>
+    <el-row :gutter="20" class="charts-section">
+      <el-col :span="12">
+        <el-card shadow="never">
+          <div slot="header" class="chart-header">
+            <span>本月订单数</span>
+            <div class="legend">
+              <div class="legend-item">
+                <span class="dot viewer-dot"></span>
+                <span>订单数</span>
+              </div>
+              <div class="legend-item">
+                <span class="dot complete-dot"></span>
+                <span>订单金额</span>
+              </div>
+            </div>
+          </div>
+          <div ref="viewerOrderChart" class="chart-container"></div>
+        </el-card>
+      </el-col>
+      <el-col :span="12">
+        <el-card shadow="never">
+          <div slot="header" class="chart-header">
+            <span>本月收款数</span>
+            <div class="legend">
+              <div class="legend-item">
+                <span class="dot viewer-dot"></span>
+                <span>收款数</span>
+              </div>
+              <div class="legend-item">
+                <span class="dot complete-dot"></span>
+                <span>收款金额</span>
+              </div>
+            </div>
+          </div>
+          <div ref="viewerReceiveChart" class="chart-container"></div>
+        </el-card>
+      </el-col>
+    </el-row>
+    <br/>
+  </div>
+</template>
+
+<script>
+import * as echarts from 'echarts'
+import CountTo from "vue-count-to";
+import {
+  analysisPreview,
+  authorizationInfo,
+  dealerAggregated, deaMemberTopTen, rechargeComsumption, rewardMoneyTopTen, rewardMoneyTrend,
+  smsBalance, thisMonthOrderCount, thisMonthRecvCount, trafficLog,
+  watchCourseTopTen, watchEndPlayTrend,getWatchCourseStatisticsData
+} from "@/api/statistics/statistics";
+import dayjs from 'dayjs';
+import { listDept } from '@/api/system/dept'
+import { listCompany } from '@/api/his/company'
+
+
+const viewCharOption = {
+  tooltip: {
+    trigger: 'axis',
+    axisPointer: {
+      type: 'shadow'
+    }
+  },
+  grid: {
+    left: '3%',
+    right: '4%',
+    bottom: '3%',
+    containLabel: true
+  },
+  xAxis: {
+    type: 'category',
+    data: ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '20', '21', '22', '23']
+  },
+  yAxis: {
+    type: 'value'
+  },
+  series: [
+    {
+      name: '观看人数',
+      type: 'bar',
+      data: [],
+      itemStyle: {
+        color: '#409EFF'
+      }
+    },
+    {
+      name: '完播人数',
+      type: 'bar',
+      data: [],
+      itemStyle: {
+        color: '#67C23A'
+      }
+    }
+  ]
+}
+
+const dealerOptionNew = {
+  tooltip: {
+    trigger: 'axis',
+    axisPointer: {
+      type: 'shadow'
+    }
+  },
+  grid: {
+    left: '3%',
+    right: '4%',
+    bottom: '3%',
+    containLabel: true
+  },
+  xAxis: {
+    type: 'category',
+    axisLabel: {
+      rotate: 30, // 设置标签倾斜45度
+      // fontSize: 12, // 减小字体大小
+      interval: 0, // 显示所有标签
+      // 可选:限制标签宽度并截断
+      width: 80,
+      overflow: 'truncate',
+      // 可选:设置标签的对齐方式
+      margin: 20,
+      fontWeight: 'bold' // 设置字体加粗
+    }
+  },
+  yAxis: {
+    type: 'value'
+  },
+  series: [
+    {
+      name: '观看人数',
+      type: 'bar',
+      data: [],
+      itemStyle: {
+        color: '#409EFF'
+      }
+    },
+    {
+      name: '完播人数',
+      type: 'bar',
+      data: [],
+      itemStyle: {
+        color: '#67C23A'
+      }
+    }
+  ]
+}
+
+const thisMonthOrderCountOption = {
+  tooltip: {
+    trigger: 'axis',
+    axisPointer: {
+      type: 'shadow'
+    }
+  },
+  grid: {
+    left: '3%',
+    right: '4%',
+    bottom: '3%',
+    containLabel: true
+  },
+  xAxis: {
+    type: 'category',
+    data: ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '20', '21', '22', '23']
+  },
+  yAxis: {
+    type: 'value'
+  },
+  series: [
+    {
+      name: '订单数',
+      type: 'line',
+      data: [],
+      itemStyle: {
+        color: '#409EFF'
+      }
+    },
+    {
+      name: '订单金额',
+      type: 'line',
+      data: [],
+      itemStyle: {
+        color: '#67C23A'
+      }
+    }
+  ]
+}
+
+const thisMonthRecvCountOption = {
+  tooltip: {
+    trigger: 'axis',
+    axisPointer: {
+      type: 'shadow'
+    }
+  },
+  grid: {
+    left: '3%',
+    right: '4%',
+    bottom: '3%',
+    containLabel: true
+  },
+  xAxis: {
+    type: 'category',
+    data: ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '20', '21', '22', '23']
+  },
+  yAxis: {
+    type: 'value'
+  },
+  series: [
+    {
+      name: '收款数',
+      type: 'line',
+      data: [],
+      itemStyle: {
+        color: '#409EFF'
+      }
+    },
+    {
+      name: '收款金额',
+      type: 'line',
+      data: [],
+      itemStyle: {
+        color: '#67C23A'
+      }
+    }
+  ]
+}
+const dealerOption = {
+  tooltip: {
+    trigger: 'axis',
+    axisPointer: {
+      type: 'shadow'
+    }
+  },
+  grid: {
+    left: '3%',
+    right: '4%',
+    bottom: '3%',
+    containLabel: true
+  },
+  xAxis: {
+    type: 'value'
+  },
+  yAxis: {
+    type: 'category',
+    data: []
+  },
+  series: [
+    {
+      name: '观看人数',
+      type: 'bar',
+      data: [],
+      itemStyle: {
+        color: '#409EFF'
+      }
+    }
+  ]
+}
+
+const courseWatchOption = {
+  tooltip: {
+    trigger: 'axis',
+    axisPointer: {
+      type: 'shadow'
+    }
+  },
+  grid: {
+    left: '3%',
+    right: '4%',
+    bottom: '8%',
+    top: '3%',
+    containLabel: true
+  },
+  xAxis: {
+    type: 'category',
+    data: [],
+    axisLabel: {
+      interval: 0,
+      rotate: 30,
+      fontSize: 10,
+      width: 100,
+      overflow: 'truncate'
+    }
+  },
+  yAxis: {
+    type: 'value',
+    splitLine: {
+      lineStyle: {
+        type: 'dashed'
+      }
+    }
+  },
+  series: [
+    {
+      name: '观看人数',
+      type: 'bar',
+      data: [],
+      itemStyle: {
+        color: '#409EFF'
+      }
+    },
+    {
+      name: '完播人数',
+      type: 'bar',
+      data: [],
+      itemStyle: {
+        color: '#67C23A'
+      }
+    },
+    {
+      name: '答题人数',
+      type: 'bar',
+      data: [],
+      itemStyle: {
+        color: '#E6A23C'
+      }
+    },
+    {
+      name: '正确人数',
+      type: 'bar',
+      data: [],
+      itemStyle: {
+        color: '#F56C6C'
+      }
+    }
+  ]
+}
+
+const lineChartOption = {
+  tooltip: {
+    trigger: 'axis',
+    axisPointer: {
+      type: 'cross' // 改为 'cross' 更适合折线图
+    }
+  },
+  grid: {
+    left: '3%',
+    right: '4%',
+    bottom: '8%', // 如果x轴标签旋转,可能需要更大的 bottom
+    top: '5%',    // 增加一点顶部空间给可能的 Y 轴名称
+    containLabel: true
+  },
+  xAxis: {
+    type: 'time', // X轴类型改为 'time'
+    // data: [], // 时间轴不需要单独设置 data,数据在 series 中提供
+    axisLabel: {
+      // interval: 0, // 时间轴通常自动处理间隔,可以先移除或注释掉
+      rotate: 30,   // 保留旋转,如果标签可能重叠
+      fontSize: 10,
+      // width: 100, // width 和 overflow 对于时间轴可能行为不同,按需调整
+      // overflow: 'truncate',
+      formatter: null // ECharts 会自动格式化时间,如需特定格式可用 function 或字符串模板
+    }
+  },
+  yAxis: {
+    type: 'value',
+    name: '金额 (元)', // 添加 Y 轴名称
+    nameLocation: 'end', // 名称位置
+    nameTextStyle: {
+      align: 'right',
+      padding: [0, 10, 0, 0] // 调整名称与轴线的距离
+    },
+    splitLine: {
+      lineStyle: {
+        type: 'dashed'
+      }
+    },
+    axisLabel: {
+      formatter: '{value} 元' // 可选:给 Y 轴刻度添加单位
+    }
+  },
+  series: [
+    {
+      name: '答题红包金额',
+      type: 'line', // 系列类型改为 'line'
+      data: [
+      ],
+      itemStyle: { // 控制数据点(标记)的样式
+        color: '#409EFF'
+      },
+      lineStyle: { // 控制线的样式
+        color: '#409EFF'
+      },
+      smooth: false, // 是否平滑曲线,可设为 true
+      symbol: 'circle', // 数据点标记形状,'emptyCircle', 'rect', 'roundRect', 'triangle', 'diamond', 'pin', 'arrow', 'none'
+      symbolSize: 4   // 数据点标记大小
+    }
+  ]
+};
+
+
+const redPackageOption = {
+  tooltip: {
+    trigger: 'axis',
+    axisPointer: {
+      type: 'shadow'
+    }
+  },
+  grid: {
+    left: '3%',
+    right: '4%',
+    bottom: '8%',
+    top: '3%',
+    containLabel: true
+  },
+  xAxis: {
+    type: 'category',
+    data: [],
+    axisLabel: {
+      interval: 0,
+      rotate: 30,
+      fontSize: 10,
+      width: 100,
+      overflow: 'truncate'
+    }
+  },
+  yAxis: {
+    type: 'value',
+    splitLine: {
+      lineStyle: {
+        type: 'dashed'
+      }
+    }
+  },
+  series: [
+    {
+      name: '答题红包金额',
+      type: 'bar',
+      data: [],
+      itemStyle: {
+        color: '#409EFF'
+      }
+    }
+  ]
+}
+export default {
+  name: 'StatisticsDashboard',
+  components: {CountTo},
+  data() {
+    return {
+      deptInitOptions:[],
+      deptOptions:[],
+      intiDeptId:this.$store.state.user.user.deptId,
+      deptId:this.$store.state.user.user.deptId,
+      staticParam : {companyId:null,deptId:this.$store.state.user.user.deptId},
+      companyIntiOptions:[],
+      companyOptions:[],
+      companyId:null,
+      percentage: 0,
+      // 预测message
+      remainMessage: '',
+      // 当天使用流量
+      todayTraffic: 0,
+      trafficCount: 0,
+      // 当月使用流量
+      thisMonthTraffic: 0,
+      dataType: '0',
+      delerSort: 'DESC',
+      smsRemainCount: 0,
+      viewerType: '0',
+      viewerChart: null,
+      dealerChartNew: null,
+      userTypeText: process.env.VUE_APP_COURSE_DEFAULT==1?"会员":"企微",
+      userType: process.env.VUE_APP_COURSE_DEFAULT,
+      dealerChart: null,
+      // 分公司数量
+      dealderCount: 0,
+      // 销售数量
+      groupMgrCount: 0,
+      // 会员总数量
+      memberCount: 0,
+      // 企微数量
+      qwMemberNum: 0,
+      // pad使用情况
+      padTotalNum: 0,
+      // pad使用情况
+      padUsedNum: 0,
+      // 正常会员数量
+      normalNum: 0,
+      // 黑名单会员数量
+      blackNum: 0,
+      // 观看人数
+      watchUserCount: 0,
+      // 完播人数
+      completedUserCount: 0,
+      // 完播率
+      completedRate: 0,
+      // 观看次数
+      watchCount:0,
+      // 完播次数
+      completedCount: 0,
+      // 视频完播率
+      watchRate: 0,
+      // 答题人数
+      answerMemberCount: 0,
+      // 正确人数
+      correctUserCount: 0,
+      correctRate: 0.0,
+      // 答题红包个数
+      rewardCount: 0,
+      // 答题红包金额
+      rewardMoney: 0.0,
+      queryTime: '今日',
+      todayWatchUserCount: 0,
+      versionLimit: 0,
+      versionLimitPercent : 0.0,
+      /// 选中的分析概览
+      selectedDiv: 0,
+      filterType: 0,
+      answerRedPackViewerChart: null,
+      answerRedPackMoneyViewerChart: null,
+      todayComsumption: 0,
+      yesterdayComsumption: 0,
+      balance: 0,
+      runTianBalance: 0,
+      autoRefreshInterval: null,
+      // 今日新增用户数
+      todayIncreaseUserNum: 0,
+      // 订单总数
+      orderTotalNum: 0,
+      // 今日新增订单数
+      todayOrderNum: 0,
+      // 收款总数
+      recvTotalNum: 0,
+      // 今日收款总数
+      recvTodayNum: 0,
+      // 商品总数
+      goodsTotalNum: 0,
+      // 今日商品总数
+      todayGoodsNum: 0
+    }
+  },
+  mounted() {
+    this.$nextTick(() => {
+      this.initViewerChart();
+      this.initDealerChartNew();
+      this.initDealerChart();
+      this.initCourseWatchChart();
+      this.initAnswerRedPackViewerChart();
+      this.initAnswerRedPackMoneyViewerChart();
+      this.initThisMonthOrderChart();
+      this.initThisMonthRecvChart();
+
+
+      // 监听窗口大小变化,重新渲染图表
+      window.addEventListener('resize', () => {
+        this.viewerChart && this.viewerChart.resize()
+        this.dealerChart && this.dealerChart.resize()
+        this.dealerChartNew && this.dealerChartNew.resize()
+      })
+    })
+  },
+  created() {
+    this.refresh();
+    listDept().then(res => {
+      this.deptInitOptions = res.data;
+      listCompany().then(res => {
+        this.companyIntiOptions = res.rows;
+        this.getDeptOptions(this.intiDeptId);
+        this.getCompanyOptions(this.intiDeptId);
+      });
+    });
+  },
+  methods: {
+    getDeptOptions(deptId) {
+      const deptInitOptions = this.deptInitOptions;
+      // 部门本身节点
+      let deptNode = deptInitOptions.filter(item => item.deptId === deptId);
+
+      // 递归查找所有子节点
+      function findChildren(parentId) {
+        //部门的子部门
+        let deptChildren = deptInitOptions.filter(item => item.parentId === parentId);
+        //添加子部门
+        deptChildren.forEach(child => {
+          deptNode.push(child);
+          findChildren(child.deptId); // 递归查找子节点的子节点
+        });
+      }
+
+      // 从目标节点开始查找子节点
+      findChildren(deptId);
+      this.deptOptions = deptNode;
+    },
+    getCompanyOptions(deptId) {
+      this.companyId = null;
+      //修改选择后清空查询参数
+      this.staticParam.companyId = null;
+      this.staticParam.deptId = deptId;
+      const deptInitOptions = this.deptInitOptions;
+      const companyInitOptions = this.companyIntiOptions;
+      // 部门下的公司
+      let companyNode = companyInitOptions.filter(item => item.deptId === deptId);
+
+      // 递归查找所有子节点
+      function findChildren(parentId) {
+        //部门的子部门
+        let deptChildren = deptInitOptions.filter(item => item.parentId === parentId);
+        //添加子部门
+        deptChildren.forEach(child => {
+          //子部门下的销售公司
+          let companyChildren = companyInitOptions.filter(item => item.deptId === child.deptId);
+          companyChildren.forEach(companyChild => {
+            companyNode.push(companyChild);
+          })
+          findChildren(child.deptId); // 递归查找子节点的子节点
+        });
+      }
+
+      // 从目标节点开始查找子节点
+      findChildren(deptId);
+      this.companyOptions = companyNode;
+    },
+    //首页统计选择部门、销售公司
+    handleDeptChange() {
+      this.getCompanyOptions(this.deptId);
+      this.refresh();
+    },
+    handleCompanyChange() {
+      this.staticParam.companyId = this.companyId;
+      this.refresh();
+    },
+    handleUserType() {
+      if (this.userTypeText === '会员') {
+        this.userType = 1
+      } else {
+        this.userType = 2
+      }
+
+      this.refresh()
+    },
+    /**
+     * 计算余额预计可持续的天数
+     * @param {number} balance - 当前账户余额
+     * @param {number} runTianBalance - 润天账户余额
+     * @param {number} todayConsumption - 今日消耗金额
+     * @param {number} yesterdayConsumption - 昨日消耗金额
+     * @return {Object} 包含天数和进度百分比的对象
+     */
+    calculateRemainingDays(balance, todayConsumption, yesterdayConsumption) {
+      // 如果今日和昨日消耗都为0,则无法预测(避免除以0)
+      if (todayConsumption === 0 && yesterdayConsumption === 0) {
+        return {
+          days: Infinity,
+          percentage: 0,
+          message: '暂无消耗数据'
+        };
+      }
+
+      // 计算每日平均消耗量
+      const avgDailyConsumption = (todayConsumption + yesterdayConsumption) / 2;
+
+      // 如果平均消耗为0,则无法预测
+      if (avgDailyConsumption === 0) {
+        return {
+          days: Infinity,
+          percentage: 0,
+          message: '暂无消耗数据'
+        };
+      }
+
+      // 计算剩余天数(向下取整)
+      const remainingDays = Math.floor(balance / avgDailyConsumption);
+
+      // 计算进度条百分比,最大为100
+      // 这里假设100天是满值,可以根据需要调整
+      const maxDays = 100;
+      const percentage = Math.min(100, Math.max(0, Math.round((remainingDays / maxDays) * 100)));
+
+      let message = '';
+      if (remainingDays > 365) {
+        message = '预测余额充足';
+      } else {
+        message = `预测不足${remainingDays}天`;
+      }
+
+      return {
+        days: remainingDays,
+        percentage: 100 - percentage,
+        message: message
+      };
+    },
+    /**
+     * 将字节数转换为合适的单位表示(Byte、KB、MB、GB、TB)
+     * @param {number} bytes - 字节数
+     * @param {number} [decimals=2] - 小数点后保留的位数
+     * @returns {string} 格式化后的字符串,包含数值和单位
+     */
+    formatBytes(bytes, decimals = 2) {
+      const isNegative = bytes < 0;  // 判断是否为负数
+      bytes = Math.abs(bytes);  // 获取绝对值
+
+      if (bytes === 0) return '0 Byte';
+
+      const k = 1024;
+      const sizes = ['Byte', 'KB', 'MB', 'GB', 'TB'];
+
+      // 计算合适的单位级别
+      let i = Math.floor(Math.log(bytes) / Math.log(k));
+
+      // 转换为对应单位的值
+      const value = bytes / Math.pow(k, i);
+
+
+      if(this.deptId !== 1 ||  this.companyId !== null){
+        i += 1;
+      }
+      // 格式化为指定小数位的字符串
+      const result = parseFloat(value.toFixed(decimals)) + ' ' + sizes[Math.min(i, sizes.length - 1)];
+
+      // 如果是负数,返回带负号的值
+      return isNegative ? `-${result}` : result;
+    },
+    // 手动刷新
+    manualRefresh() {
+      this.refresh();
+    },
+    // 处理自动刷新选项
+    handleAutoRefresh(command) {
+      // 清除之前的定时器
+      if (this.timer) {
+        clearInterval(this.timer);
+        this.timer = null;
+      }
+
+      // 设置新的刷新间隔
+      this.autoRefreshInterval = parseInt(command);
+
+      // 如果间隔大于0,设置新的定时器
+      if (this.autoRefreshInterval > 0) {
+        this.timer = setInterval(() => {
+          this.refresh();
+        }, this.autoRefreshInterval * 60 * 1000); // 转换为毫秒
+
+        this.$message.success(`已设置${this.autoRefreshInterval}分钟自动刷新`);
+      } else {
+        this.$message.info('已关闭自动刷新');
+      }
+    },
+    refresh() {
+      rechargeComsumption(this.staticParam).then(res => {
+        console.log(res);
+        if (res.code === 200) {
+          this.balance = res.data.balance;
+          this.runTianBalance = res.data.runTianBalance;
+          this.todayComsumption = res.data.todayComsumption;
+          this.yesterdayComsumption = res.data.yesterdayComsumption;
+          let calculateRemainingDays1 = this.calculateRemainingDays(this.balance, this.todayComsumption, this.yesterdayComsumption);
+          this.percentage = calculateRemainingDays1.percentage;
+          this.remainMessage = calculateRemainingDays1.message;
+        }
+      });
+
+      trafficLog(this.staticParam).then(res => {
+        if (res.code === 200) {
+          this.todayTraffic = res.data.today;
+          this.thisMonthTraffic = res.data.thisMonth;
+          this.trafficCount = res.data.traffic;
+        }
+      })
+
+      dealerAggregated(this.staticParam).then(res => {
+        if (res.code === 200) {
+          this.dealderCount = res.data.dealderCount ?? 0;
+          this.groupMgrCount = res.data.groupMgrCount ?? 0;
+          this.memberCount = res.data.memberCount ?? 0;
+          this.qwMemberNum = res.data.qwMemberNum ?? 0;
+          const totalNum = res.data.padTotalNum;
+          //-1 不限 null 未设置
+          if(totalNum != null && totalNum !== -1 && totalNum > 0){
+            this.padTotalNum = totalNum;
+          }else{
+            this.padTotalNum = '不限';
+          }
+          this.padUsedNum = res.data.padUsedNum ?? 0;
+          this.padInfo = res.data.padInfo;
+          this.normalNum = res.data.normalNum ?? 0;
+          this.blackNum = res.data.blackNum ?? 0;
+          this.todayIncreaseUserNum = res.data.todayIncreaseUserNum ?? 0;
+          this.orderTotalNum = res.data.orderTotalNum ?? 0;
+          this.todayOrderNum = res.data.todayOrderNum ?? 0;
+          this.recvTotalNum = res.data.recvTotalNum ?? 0;
+          this.recvTodayNum = res.data.recvTodayNum ?? 0;
+          this.goodsTotalNum = res.data.goodsTotalNum ?? 0;
+          this.todayGoodsNum = res.data.todayGoodsNum ?? 0;
+        }
+      })
+      let param = this.getParam();
+
+      // 获取当前日期时间
+      const today = dayjs();
+      param.startTime = this.formatDate(today);
+      param.endTime = this.formatDate(today);
+      analysisPreview(param).then(res => {
+        if (res.code === 200) {
+          this.watchUserCount = res.data.watchUserCount;
+          this.completedUserCount = res.data.completedUserCount;
+          this.completedRate = res.data.completedRate;
+          this.watchCount = res.data.watchCount;
+          this.completedCount = res.data.completedCount;
+          this.answerMemberCount = res.data.answerMemberCount;
+          this.correctUserCount = res.data.correctUserCount;
+          this.correctRate = res.data.correctRate;
+          this.rewardCount = res.data.rewardCount;
+          this.rewardMoney = res.data.rewardMoney;
+          this.watchRate = res.data.watchRate;
+        }
+      })
+      smsBalance(this.staticParam).then(res => {
+        if (res.code === 200) {
+          if (res.data == null) {
+            this.smsRemainCount = 0;
+          } else {
+            this.smsRemainCount = res.data;
+          }
+        }
+      })
+      authorizationInfo(this.staticParam).then(res => {
+        if (res.code === 200 && res.data != null) {
+          this.todayWatchUserCount = res.data.todayWatchUserCount;
+          this.versionLimit = res.data.versionLimit;
+          if(this.versionLimit){
+            this.versionLimitPercent = this.todayWatchUserCount/this.versionLimit;
+          }
+        }
+      })
+
+      this.handleCourseWatchChart()
+      this.handleViewChartData()
+      this.handleDealerChartDataNew()
+
+      // 经销商会员观看TOP10
+      this.handleDealerChartData()
+
+      this.handleAnswerRedPackViewerChart()
+
+      this.handleAnswerRedPackMoneyViewerChart()
+
+      this.handleThisMonthRecvCount();
+      this.handleThisMonthOrderCount();
+
+    },
+    /**
+     * 将数字添加千位分隔符
+     * @param {number|string} num - 需要格式化的数字
+     * @return {string} 添加千位分隔符后的字符串
+     */
+    formatNumberWithCommas(num) {
+      if (num === null || num === undefined || isNaN(Number(num))) {
+        return '0';
+      }
+
+      const numStr = String(num);
+
+      // 处理负数
+      const isNegative = numStr.startsWith('-');
+      const absNumStr = isNegative ? numStr.slice(1) : numStr;
+
+      // 分离整数部分和小数部分
+      const parts = absNumStr.split('.');
+      const integerPart = parts[0];
+      const decimalPart = parts.length > 1 ? '.' + parts[1] : '';
+
+      const formattedInteger = integerPart.replace(/\B(?=(\d{3})+(?!\d))/g, ',');
+
+      return (isNegative ? '-' : '') + formattedInteger + decimalPart;
+    },
+    handleToggleDiv(selected) {
+      this.selectedDiv = selected;
+
+      if (selected === 1) {
+        this.$nextTick(() => {
+          if (this.courseWatchChart) {
+            this.courseWatchChart.resize();
+          } else {
+          }
+        });
+      } else if (selected === 0) {
+        this.$nextTick(() => {
+          if (this.viewerChart) this.viewerChart.resize();
+          if (this.dealerChart) this.dealerChart.resize();
+        });
+      } else if (selected === 2) {
+        this.$nextTick(() => {
+          if (this.answerRedPackViewerChart) this.answerRedPackViewerChart.resize();
+          if (this.answerRedPackMoneyViewerChart) this.answerRedPackMoneyViewerChart.resize();
+        });
+      }
+      if (this.selectedDiv === 0) {
+        this.handleViewChartData()
+        this.handleDealerChartData()
+        this.handleDealerChartDataNew()
+      } else if (this.selectedDiv === 1) {
+        this.handleCourseWatchChart()
+      } else if (this.selectedDiv === 2) {
+        this.handleAnswerRedPackViewerChart()
+        this.handleAnswerRedPackMoneyViewerChart()
+      }
+    },
+    formatDate(date) {
+      return dayjs(date).format('YYYY-MM-DD');
+    },
+
+    getParam() {
+      let param = {
+        startTime: '',
+        endTime: '',
+        userType: this.userType,
+        companyId: this.companyId,
+        deptId: this.deptId
+      };
+      // 获取当前日期时间
+      const today = dayjs();
+
+      let type = 0;
+      if (this.queryTime === '今日') {
+        param.startTime = this.formatDate(today);
+        param.endTime = this.formatDate(today);
+        type = 0;
+      } else if (this.queryTime === '昨日') {
+        const yesterday = today.subtract(1, 'day');
+        param.startTime = this.formatDate(yesterday);
+        param.endTime = this.formatDate(yesterday);
+        type = 1;
+      } else if (this.queryTime === '本周') {
+        param.startTime = this.formatDate(today.startOf('week'));
+        param.endTime = this.formatDate(today.endOf('week'));
+        type = 2;
+      } else if (this.queryTime === '本月') {
+        param.startTime = this.formatDate(today.startOf('month'));
+        param.endTime = this.formatDate(today.endOf('month'));
+        type = 3;
+      } else if (this.queryTime === '上月') {
+        const lastMonth = today.subtract(1, 'month');
+        param.startTime = this.formatDate(lastMonth.startOf('month'));
+        param.endTime = this.formatDate(lastMonth.endOf('month'));
+        type = 4;
+      } else {
+        console.warn(`未知的 queryTime: ${this.queryTime}, 默认使用今日`);
+        param.startTime = this.formatDate(today);
+        param.endTime = this.formatDate(today);
+      }
+      param.type = type;
+      param.sort = this.delerSort;
+      return param;
+    },
+    // 分析概览
+    handleAnalysis(e) {
+
+      let param = this.getParam();
+      analysisPreview(param).then(res => {
+        if (res.code === 200) {
+          this.watchUserCount = res.data.watchUserCount;
+          this.completedUserCount = res.data.completedUserCount;
+          this.completedRate = res.data.completedRate;
+          this.watchCount = res.data.watchCount;
+          this.completedCount = res.data.completedCount;
+          this.answerMemberCount = res.data.answerMemberCount;
+          this.correctUserCount = res.data.correctUserCount;
+          this.correctRate = res.data.correctRate;
+          this.rewardCount = res.data.rewardCount;
+          this.rewardMoney = res.data.rewardMoney;
+          this.watchRate = res.data.watchRate;
+        }
+      })
+
+      if (this.selectedDiv === 0) {
+        this.handleViewChartData()
+        this.handleDealerChartData()
+        this.handleDealerChartDataNew()
+      } else if (this.selectedDiv === 1) {
+        this.handleCourseWatchChart()
+      } else if (this.selectedDiv === 2) {
+        this.handleAnswerRedPackViewerChart()
+        this.handleAnswerRedPackMoneyViewerChart()
+      }
+    },
+    handleAnswerRedPackViewerChart() {
+      let param = this.getParam();
+      param = { ...param, statisticalType: this.viewerType, dataType: this.dataType };
+      rewardMoneyTopTen(param).then(res => {
+        if (res.code === 200) {
+          let data = res.data;
+          let companyNameList = data.map(e => e.companyName)
+          let courseNameList = data.map(e => e.courseName)
+          let rewardMoneyList = data.map(e => e.rewardMoney)
+          if (this.dataType === '0') {
+            redPackageOption.xAxis.data = companyNameList;
+          } else {
+            redPackageOption.xAxis.data = courseNameList;
+          }
+          redPackageOption.series[0].data = rewardMoneyList;
+
+          this.answerRedPackViewerChart.setOption(redPackageOption)
+        }
+      })
+    },
+    handleAnswerRedPackMoneyViewerChart() {
+      let param = this.getParam();
+      param = { ...param, statisticalType: this.viewerType, dataType: this.dataType };
+      rewardMoneyTrend(param).then(res => {
+        if (res.code === 200) {
+          let data = res.data;
+          let option = data.map(e => [e.x, e.rewardMoney])
+          lineChartOption.series[0].data = option;
+
+          this.answerRedPackMoneyViewerChart.setOption(lineChartOption)
+        }
+      })
+    },
+    handleCourseWatchChart() {
+      let param = this.getParam();
+      param = { ...param, statisticalType: this.viewerType };
+      watchCourseTopTen(param).then(res => {
+        if (res.code === 200) {
+          let data = res.data;
+          let watchUserCountList = data.map(e => e.watchUserCount);
+          let completedUserCountList = data.map(e => e.completedUserCount);
+          let answerUserCountList = data.map(e => e.answerUserCount);
+          let correctUserCountList = data.map(e => e.correctUserCount);
+          let courseNameList = data.map(e => e.courseName);
+          courseWatchOption.xAxis.data = courseNameList;
+          courseWatchOption.series[0].data = watchUserCountList;
+          courseWatchOption.series[1].data = completedUserCountList;
+          courseWatchOption.series[2].data = answerUserCountList;
+          courseWatchOption.series[3].data = correctUserCountList;
+          this.courseWatchChart.setOption(courseWatchOption)
+        }
+      })
+    },
+    handleDealerChartData() {
+      let param = this.getParam();
+
+      // 经销商会员观看TOP10
+      deaMemberTopTen({ ...param, statisticalType: this.viewerType }).then(res => {
+        if (res.code === 200) {
+          let data = res.data;
+          let companyNameList = data.map(e => e.companyName);
+          let watchUserList = data.map(e => e.watchUserCount);
+          dealerOption.yAxis.data = companyNameList;
+          dealerOption.series[0].data = watchUserList;
+
+          this.dealerChart.setOption(dealerOption)
+        }
+      })
+
+    },
+    handleThisMonthOrderCount() {
+      thisMonthOrderCount(this.staticParam).then(res => {
+        if (res.code === 200) {
+          let dates = res.dates;
+          let orderCount = res.orderCount;
+          let payPrice = res.payPrice;
+
+          thisMonthOrderCountOption.series[0].data = orderCount;
+          thisMonthOrderCountOption.series[1].data = payPrice;
+          thisMonthOrderCountOption.xAxis.data = dates;
+
+          this.thisMonthOrderChart.setOption(thisMonthOrderCountOption)
+        }
+      })
+    },
+    handleThisMonthRecvCount() {
+      thisMonthRecvCount(this.staticParam).then(res => {
+        if (res.code === 200) {
+          let dates = res.dates;
+          let orderCount = res.orderCount;
+          let payMoney = res.payMoney;
+
+          thisMonthRecvCountOption.series[0].data = orderCount;
+          thisMonthRecvCountOption.series[1].data = payMoney;
+          thisMonthRecvCountOption.xAxis.data = dates;
+          this.thisMonthRecvChart.setOption(thisMonthRecvCountOption)
+        }
+      })
+    },
+    handleViewChartData() {
+      let param = this.getParam();
+
+      watchEndPlayTrend({ ...param }).then(res => {
+        if (res.code === 200) {
+          let data = res.data;
+          let watchUserCountList = data.map(e => e.watchUserCount);
+          let completedUserCountList = data.map(e => e.completedUserCount);
+          let xAxis = data.map(e => e.x);
+          viewCharOption.series[0].data = watchUserCountList;
+          viewCharOption.series[1].data = completedUserCountList;
+          viewCharOption.xAxis.data = xAxis;
+
+          this.viewerChart.setOption(viewCharOption);
+        }
+      })
+
+    },
+    handleDealerChartDataNew() {
+      let param = this.getParam();
+
+      getWatchCourseStatisticsData({ ...param }).then(res => {
+        if (res.code === 200) {
+          console.log(res.data);
+          // 根据实际数据结构调整
+          let data = res.data;
+          let watchUserCountList = data.map(e => e.watchCount);     // 观看次数
+          let completedUserCountList = data.map(e => e.finishCount); // 完播次数
+          let xAxis = data.map(e => e.companyName);                 // X轴使用公司名称
+
+          // 更新图表配置
+          dealerOptionNew.series[0].data = watchUserCountList;
+          dealerOptionNew.series[1].data = completedUserCountList;
+          dealerOptionNew.xAxis.data = xAxis;
+
+          this.dealerChartNew.setOption(dealerOptionNew);
+
+        }
+      })
+
+    },
+    initThisMonthOrderChart() {
+      this.thisMonthOrderChart = echarts.init(this.$refs.viewerOrderChart)
+      this.thisMonthOrderChart.setOption(thisMonthOrderCountOption)
+    },
+    initThisMonthRecvChart() {
+
+      this.thisMonthRecvChart = echarts.init(this.$refs.viewerReceiveChart)
+      this.thisMonthRecvChart.setOption(thisMonthOrderCountOption)
+    },
+    initViewerChart() {
+      this.viewerChart = echarts.init(this.$refs.viewerChart)
+      this.viewerChart.setOption(viewCharOption)
+    },
+    initDealerChartNew() {
+      this.dealerChartNew = echarts.init(this.$refs.dealerChartNew)
+      this.dealerChartNew.setOption(dealerOptionNew)
+    },
+    initDealerChart() {
+      this.dealerChart = echarts.init(this.$refs.dealerChart)
+
+      this.dealerChart.setOption(dealerOption)
+    },
+    initCourseWatchChart() {
+      this.courseWatchChart = echarts.init(this.$refs.courseWatchChart)
+
+      this.courseWatchChart.setOption(courseWatchOption)
+    },
+    initAnswerRedPackViewerChart() {
+      this.answerRedPackViewerChart = echarts.init(this.$refs.answerRedPackViewerChart)
+
+      this.answerRedPackViewerChart.setOption(redPackageOption)
+    },
+    initAnswerRedPackMoneyViewerChart() {
+      this.answerRedPackMoneyViewerChart = echarts.init(this.$refs.answerRedPackMoneyViewerChart)
+
+      this.answerRedPackMoneyViewerChart.setOption(lineChartOption)
+    }
+  },
+
+  beforeDestroy() {
+    // 组件销毁时清除定时器
+    if (this.timer) {
+      clearInterval(this.timer);
+      this.timer = null;
+    }
+    // window.removeEventListener('resize', this.resizeHandler)
+    this.viewerChart && this.viewerChart.dispose()
+    this.dealerChart && this.dealerChart.dispose()
+    this.dealerChartNew && this.dealerChartNew.dispose()
+  }
+}
+</script>
+
+<style scoped>
+.highlight-today-add {
+  color: green;
+  font-size: 17px;
+  font-weight: normal;
+}
+
+.highlight-today-add2 {
+  color: rgba(49, 185, 154, 1);
+  font-size: 14px;
+  font-weight: normal;
+  transform: scale(.9);
+}
+
+.action-group .el-button + .el-button,
+.action-group .el-dropdown {
+  margin-left: 10px;
+}
+
+.is-active {
+  color: #409EFF;
+  font-weight: bold;
+}
+
+::v-deep .el-radio-button__inner:hover {
+  color: #409EFF;
+  /* 鼠标悬浮时的文字颜色,可以根据需要调整 */
+}
+
+::v-deep .el-radio-button.is-active .el-radio-button__inner {
+  background-color: #409EFF;
+  /* 选中时的背景色 */
+  border-color: #409EFF;
+  /* 选中时的边框色 */
+  color: #FFFFFF;
+  /* 选中时的文字颜色 (通常是白色) */
+  box-shadow: -1px 0 0 0 #409EFF;
+  /* 处理按钮间的连接缝隙 */
+}
+
+/* 如果需要,也可以修改非选中状态下的聚焦(focus)或悬浮(hover)样式 */
+/* 例如,让非选中按钮悬浮时边框和文字也变蓝 */
+::v-deep .el-radio-button:not(.is-active) .el-radio-button__inner:hover {
+  color: #409EFF;
+  /* border-color: #b3d8ff;  Element UI 默认悬浮边框色,可以按需修改 */
+}
+
+/* 聚焦时的外框,如果需要的话 */
+::v-deep .el-radio-button:focus:not(.is-checked) .el-radio-button__inner {
+  /* border-color: #409EFF; */
+  /* Element UI 默认的 focus 颜色通常关联主题色 */
+  /* box-shadow: 0 0 2px 2px rgba(64, 158, 255, 0.2); */
+  /* 示例 focus 光晕 */
+}
+
+.statistics-dashboard {
+  padding: 20px;
+  background-color: #f5f7fa;
+}
+
+.overview-section,
+.analysis-section {
+  margin-bottom: 20px;
+  border-radius: 4px;
+}
+
+.header {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  font-size: 16px;
+  font-weight: 500;
+}
+
+.data-card {
+  background-color: #fff;
+  border-radius: 4px;
+  padding: 15px;
+  height: 120px;
+  display: flex;
+  flex-direction: column;
+  position: relative;
+  transition: background-color 0.3s ease-in-out;
+}
+
+.data-card:hover {
+  border: 1px solid #4592ff;
+  background-color: #e7f1ff;
+}
+
+.card-title {
+  color: #606266;
+  font-size: 14px;
+  margin-bottom: 10px;
+}
+
+.card-title1 {
+  color: white;
+  font-size: 14px;
+  margin-bottom: 10px;
+}
+
+.card-value {
+  font-size: 24px;
+  font-weight: bold;
+  margin-top: 20px;
+}
+
+.highlight {
+  color: #409EFF;
+}
+
+.card-sub {
+  display: flex;
+  justify-content: space-between;
+  font-size: 12px;
+  color: #909399;
+  margin-top: 5px;
+}
+
+.card-desc {
+  font-size: 12px;
+  color: #909399;
+  margin-top: 5px;
+}
+
+.card-badge {
+  position: absolute;
+  top: 15px;
+  right: 15px;
+  background: #f0f9eb;
+  color: #67c23a;
+  padding: 2px 5px;
+  border-radius: 4px;
+}
+
+.cdn-label {
+  background-color: #409EFF;
+  color: white;
+  padding: 2px 5px;
+  border-radius: 4px;
+  margin-right: 5px;
+  font-size: 12px;
+}
+
+.tab-group {
+  display: flex;
+  gap: 10px;
+}
+
+.action-group {
+  display: flex;
+  gap: 10px;
+}
+
+.analysis-card {
+  border-radius: 4px;
+  padding: 20px;
+  display: flex;
+  align-items: center;
+}
+
+.card-icon {
+  width: 50px;
+  height: 50px;
+  background-color: rgba(64, 158, 255, 0.1);
+  border-radius: 8px;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  font-size: 24px;
+  color: #409EFF;
+  margin-right: 20px;
+}
+
+.card-content {
+  display: flex;
+}
+
+.card-row {
+  display: flex;
+  justify-content: center;
+  justify-items: center;
+  flex-direction: column;
+  padding: 10px;
+
+  .highlight {
+    text-align: center;
+    margin-top: 1em;
+
+    font-family: BebasNeue;
+    color: #1677ff;
+    font-size: 21px;
+    line-height: 42px;
+    font-weight: 600;
+    margin-top: 8px;
+  }
+
+  font-size: 15px;
+  color: #000;
+
+}
+
+.charts-section {
+  margin-top: 20px;
+}
+
+.chart-header {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+}
+
+.view-more {
+  font-size: 12px;
+}
+
+.legend {
+  display: flex;
+  gap: 15px;
+}
+
+.legend-item {
+  display: flex;
+  align-items: center;
+  font-size: 12px;
+}
+
+.dot {
+  width: 10px;
+  height: 10px;
+  border-radius: 50%;
+  margin-right: 5px;
+}
+
+.viewer-dot {
+  background-color: #409EFF;
+}
+
+.complete-dot {
+  background-color: #67C23A;
+}
+
+.chart-container {
+  height: 350px;
+  width: 100%;
+}
+
+.analysis-card-check {
+  display: flex;
+  flex-direction: row;
+  border: 1px solid transparent;
+  background-color: #fff;
+  border-radius: 4px;
+}
+
+.analysis-card-check:hover {
+  cursor: pointer;
+}
+
+.analysis-card-check-selected:after {
+  content: "";
+  display: block;
+  border-width: 15px;
+  position: absolute;
+  bottom: -30px;
+  left: 50%;
+  margin-left: -32px;
+  border-style: solid dashed dashed solid;
+  border-color: #4592FF transparent transparent transparent;
+  font-size: 0;
+  line-height: 0;
+  z-index: 1;
+}
+
+.analysis-card-check-selected:before {
+  content: "";
+  display: block;
+  border-width: 15px;
+  position: absolute;
+  bottom: -30px;
+  left: 50%;
+  margin-left: -32px;
+  border-style: solid dashed dashed solid;
+  border-color: #4592FF transparent transparent transparent;
+  font-size: 0;
+  line-height: 0;
+  z-index: 1;
+}
+
+.analysis-card-check-selected {
+  border: 1px solid #4592FF;
+  background-color: #e7f1ff;
+}
+
+.color {
+  position: relative;
+  border: 1px solid #4592FF;
+  background-color: #e7f1ff;
+}
+
+.color:after {
+  bottom: -27px;
+  border-color: #E7F1FF transparent transparent transparent;
+}
+
+.companybox {
+  color: white;
+  background-color: #006CFF;
+  padding: 10px 10px 40px 10px;
+  box-sizing: border-box;
+  position: relative;
+  border-radius: 6px;
+
+  .topimg {
+    width: 100px;
+    height: 80px;
+    position: absolute;
+    top: 0px;
+    left: 0;
+  }
+
+  .bottomimg {
+    width: 100px;
+    height: 80px;
+    position: absolute;
+    bottom: -10px;
+    right: 0;
+    transform: rotate(180deg);
+  }
+
+  .companyboxtitle {
+    height: 30px;
+    margin-bottom: 20px;
+    font-weight: 600;
+  }
+
+  .companyflex {
+    display: flex;
+    justify-content: space-around;
+  }
+}
+
+
+.companynumber {
+  color: white;
+}
+
+.companycard {
+
+  width: 25%;
+}
+
+.companyadd {
+  color: white;
+}
+
+.cardafter {
+  position: relative;
+
+}
+
+.cardafter::after {
+  content: "";
+  height: 60px;
+  border-right: 1px solid rgba(255, 255, 255, 0.20);
+  position: absolute;
+  right: 30px;
+  top: 0px;
+}
+
+.highlight1 {
+  margin-top: 10px;
+  margin-left: 20px;
+}
+
+
+.propertyboxtitle {
+  background-image: url(~@/assets/images/zcgl_bg.png);
+  height: 100px;
+}
+
+.propertyboxflex {
+  display: flex;
+  background-color: white;
+  justify-content: space-between;
+}
+
+.property-card {
+  width: 48%;
+  border-radius: 4px;
+  padding: 15px;
+  height: 100px;
+  display: flex;
+  flex-direction: column;
+  position: relative;
+  transition: background-color 0.3s ease-in-out;
+
+}
+
+.property_title {
+  height: 40px;
+  line-height: 40px;
+  color: rgba(32, 33, 36, 1);
+  box-sizing: border-box;
+  padding-left: 10px;
+  font-weight: 600;
+}
+
+.card-compare {
+  margin-top: 5px;
+  font-size: 14px;
+  color: rgba(92, 95, 102, 1);
+
+  span {
+    font-size: 13px;
+    transform: scale(.8);
+    color: rgba(49, 185, 154, 1);
+  }
+}
+
+.propertyline {
+  position: relative;
+}
+
+.propertyline::after {
+  position: absolute;
+  content: "";
+  height: 80px;
+  border-right: 1px solid rgba(237, 239, 242, 1);
+  right: 0;
+}
+
+.operatetitle {
+  font-weight: 600;
+  height: 50px;
+  line-height: 50px;
+}
+
+.operatetitle-col {
+  display: flex;
+  justify-content: space-between;
+  padding-bottom: 20px;
+  padding-right: 5px;
+
+  .operatetitle-card {
+    width: 24%;
+    border: 1px solid transparent;
+    background-color: rgba(245, 247, 250, 1);
+    border-radius: 4px;
+    padding: 15px;
+    display: flex;
+    flex-direction: column;
+    position: relative;
+    transition: background-color 0.3s ease-in-out;
+
+    .operate-value {
+      font-size: 24px;
+      font-weight: bold;
+    }
+  }
+}
+
+.yesterdaybox {
+  font-size: 14px;
+  color: rgba(92, 95, 102, 1);
+  font-weight: 200;
+  margin-top: 10px;
+}
+
+.internetbox {
+  background: linear-gradient(to right, rgba(255, 99, 0, 1), rgba(255, 159, 1, 1));
+  border-radius: 6px;
+  padding: 2px 5px 15px 5px;
+  width: 100%;
+
+  .internet-cardtop {
+    background-color: white;
+    border-radius: 3px;
+    margin-top: 5px;
+    justify-content: space-between;
+    padding: 15px;
+    box-sizing: border-box;
+    color: rgba(32, 33, 36, 1);
+
+    .cardinnerbox {
+      display: flex;
+      justify-content: space-between;
+
+      .cardtopimg {
+        display: flex;
+        align-items: center;
+        font-size: 15px;
+
+        img {
+          height: 18px;
+          width: 18px;
+        }
+      }
+
+      .cardtopnumber {
+        font-size: 25px;
+        color: rgba(32, 33, 36, 1);
+        font-weight: bold;
+      }
+
+    }
+
+    .cardinnerbox2 {
+      display: flex;
+      justify-content: space-between;
+      font-size: 14px;
+      color: rgba(92, 95, 102, 1);
+
+
+    }
+
+  }
+
+  .internetbox-messge {
+    color: white;
+    display: flex;
+    justify-content: space-between;
+    padding: 15px 10px 10px 10px;
+
+    .internet-card {
+      display: flex;
+      font-size: 14px;
+      align-items: center;
+
+      img {
+        height: 18px;
+        width: 18px;
+      }
+
+      span {
+        padding-left: 5px;
+      }
+    }
+
+    .internet-number {
+      font-size: 25px;
+
+    }
+  }
+
+  .internetbox {
+    display: flex;
+    color: white;
+
+    .internet-card {
+      width: 100%;
+      display: flex;
+      color: white;
+
+    }
+  }
+
+  .internet-title {
+    font-size: 14px;
+  }
+
+}
+
+.company {
+  background-color: #006CFF;
+}
+
+.highlight-red {
+  text-align: center;
+  margin-top: 1em;
+  font-family: BebasNeue;
+  color: rgba(255, 82, 82, 1);
+  font-size: 21px;
+  line-height: 42px;
+  font-weight: 600;
+  margin-top: 8px;
+}
+
+.highlight-black {
+  text-align: center;
+  margin-top: 1em;
+  font-family: BebasNeue;
+  color: rgba(32, 33, 36, 1);
+  font-size: 21px;
+  line-height: 42px;
+  font-weight: 600;
+  margin-top: 8px;
+
+}
+
+.operatetitle-card:hover {
+  border: 1px solid #4592ff;
+  background-color: #e7f1ff;
+}
+
+.icon-img {
+  height: 14px;
+  width: 14px;
+}
+
+.progress {
+  transform: rotate(180deg);
+  margin-top: 20px;
+}
+
+.progress ::v-deep .el-progress-bar__outer {
+  background-color: rgba(247, 152, 11, 0.2);
+}
+
+::v-deep .el-progress {
+
+  .el-progress-bar {
+    .el-progress-bar__inner {
+      background: linear-gradient(to right, #FF6300, #FF9F01)
+    }
+  }
+}
+</style>

+ 72 - 0
src/views/components/index/welcomePage.vue

@@ -0,0 +1,72 @@
+<template>
+  <div class="app-container welcome-container">
+    <div class="welcome-content">
+      <div class="time-display">{{ currentTime }}</div>
+      <div class="system-name">{{ systemName }}</div>
+    </div>
+  </div>
+</template>
+
+<script>
+export default {
+  name: 'Welcome',
+  data() {
+    return {
+      currentTime: '',
+      systemName: process.env.VUE_APP_TITLE,
+      timer: null
+    }
+  },
+  mounted() {
+    this.updateTime()
+    this.timer = setInterval(this.updateTime, 1000)
+  },
+  beforeDestroy() {
+    if (this.timer) {
+      clearInterval(this.timer)
+    }
+  },
+  methods: {
+    updateTime() {
+      const now = new Date()
+      this.currentTime = now.toLocaleString('zh-CN', {
+        year: 'numeric',
+        month: '2-digit',
+        day: '2-digit',
+        hour: '2-digit',
+        minute: '2-digit',
+        second: '2-digit',
+        weekday: 'long'
+      })
+    }
+  }
+}
+</script>
+
+<style scoped>
+.welcome-container {
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  height: calc(100vh - 84px); /* 减去头部和padding的高度 */
+  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+}
+
+.welcome-content {
+  text-align: center;
+  color: white;
+}
+
+.time-display {
+  font-size: 36px;
+  font-weight: 300;
+  margin-bottom: 20px;
+  text-shadow: 0 2px 10px rgba(0, 0, 0, 0.3);
+}
+
+.system-name {
+  font-size: 48px;
+  font-weight: bold;
+  text-shadow: 0 2px 15px rgba(0, 0, 0, 0.4);
+}
+</style>

+ 3 - 1
src/views/hisStore/menu/index.vue

@@ -207,7 +207,9 @@ export default {
         {"dictLabel": "养生有道",
           "dictValue": "2"},
         {"dictLabel": "个人中心",
-          "dictValue": "3"}
+          "dictValue": "3"},
+        {"dictLabel": "主菜单-热销榜",
+          "dictValue": "4"}
       ],
       statusOptions:[],
       imageArr:[],

+ 13 - 7
src/views/hisStore/storeOrder/index.vue

@@ -92,6 +92,16 @@
         />
       </el-form-item>
 
+      <el-form-item label="产品名称" prop="productName">
+        <el-input
+          v-model="queryParams.productName"
+          placeholder="请输入产品名称"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+
       <el-form-item label="手机号" prop="userPhone">
         <el-input
           v-model="queryParams.userPhone"
@@ -511,7 +521,7 @@
               <el-tag prop="orderMedium" v-for="(item, index) in orderMediumOptions"    v-if="scope.row.orderMedium==item.dictValue">{{item.dictLabel}}</el-tag>
           </template>
       </el-table-column>
-      <el-table-column label="订单产品" align="center" width="200px">
+      <el-table-column label="产品名称" align="center" width="200px">
         <template slot-scope="scope">
           <div v-if="scope.row.items && scope.row.items.length > 0">
             <el-tag
@@ -668,7 +678,7 @@
                     </el-popover>
                   </template>
                 </el-table-column>
-                <el-table-column label="品名称" show-overflow-tooltip align="center" prop="productName" />
+                <el-table-column label="品名称" show-overflow-tooltip align="center" prop="productName" />
                 <el-table-column label="商品规格" align="center" prop="sku" />
                 <el-table-column label="库存" align="center" prop="stock" />
                 <el-table-column label="单价" align="center" prop="price" />
@@ -1230,7 +1240,7 @@ export default {
         isSysDel: null,
         deptId:null,
         isUpload:null,
-
+        productName:null,
       },
       // 表单参数
       form: {
@@ -1444,10 +1454,6 @@ export default {
 
     },
 
-    // 删除订单号
-    removeOrderCode(index) {
-      this.queryParams.orderCodes.splice(index, 1)
-    },
     // 清空所有标签
     clearAllTags() {
       this.$confirm('确认清空所有订单号吗?', '提示', {

+ 44 - 2277
src/views/index.vue

@@ -1,2313 +1,80 @@
 <template>
-  <div class="statistics-dashboard">
-    <!-- 数据概览 (Data Overview) -->
-    <el-card class="overview-section" shadow="never">
-      <el-row :gutter="20">
-        <el-col :xs="24" :sm="24" :md="16" :lg="16" :xl="16" class="companybox">
-          <img src="../assets/images/topbg.png" alt="" class="topimg">
-          <img src="../assets/images/topbg.png" alt="" class="bottomimg">
-          <div class="companyboxtitle">
-            企业数据
-          </div>
-          <div class="companyflex">
-            <div class="topbg companycard cardafter">
-              <div class="card-title1">
-                <img src="../assets/images/tab_company.png" alt="" class="icon-img">
-                分公司数量
-              </div>
-
-              <div class="card-value highlight1">
-                <count-to :start-val="0" :end-val="dealderCount" :duration="3600"
-                          class="card-panel-num companynumber" />
-              </div>
-            </div>
-            <div class="companycard cardafter">
-              <div class="card-title1">
-                <img src="../assets/images/salesperson.png" alt="" class="icon-img">
-                销售数量
-              </div>
-              <div class="card-value highlight1">
-                <count-to :start-val="0" :end-val="groupMgrCount" :duration="3600"
-                          class="card-panel-num companynumber" />
-              </div>
-            </div>
-            <div class="companycard cardafter">
-              <div class="card-title1">
-                <img src="../assets/images/member.png" alt="" class="icon-img">
-                会员数量
-              </div>
-              <div class="card-value highlight1">
-                <count-to :start-val="0" :end-val="memberCount" :duration="3600" class="card-panel-num companynumber" />
-                <span class="highlight-today-add companyadd">+{{todayIncreaseUserNum}}</span>
-              </div>
-
-            </div>
-            <div class="cardafter companycard">
-              <div class="card-title1">
-                <img src="../assets/images/tab_enterprise.png" alt="" class="icon-img">
-                企微数量
-              </div>
-              <div class="card-value highlight1">
-                <count-to :start-val="0" :end-val="qwMemberNum" :duration="3600" class="card-panel-num companynumber" />
-              </div>
-            </div>
-            <div class="botttombg companycard">
-              <div class="card-title1">
-                <svg-icon icon-class="phone" />
-                pad使用情况
-              </div>
-              <div class="card-value highlight1">
-                <count-to :start-val="0" :end-val="padUsedNum" :duration="3600" class="card-panel-num companynumber" />
-                /
-                <template v-if="typeof padTotalNum === 'number'">
-                  <count-to :start-val="0" :end-val="padTotalNum" :duration="1800" class="card-panel-num companynumber" />
-                </template>
-                <template v-else>
-                  <span class="card-panel-num companynumber">{{ padTotalNum }}</span>
-                </template>
-              </div>
-            </div>
-          </div>
-        </el-col>
-
-
-        <el-col :xs="24" :sm="24" :md="8" :lg="8" :xl="8" class="propertyboxtitle">
-          <div class="property_title">
-            资产概览
-          </div>
-          <div class="propertyboxflex">
-            <div class="property-card propertyline">
-              <div class="property-title">
-                <i class="el-icon-money"></i>
-                企业资产(元)
-              </div>
-              <div class="card-value highlight">
-                <count-to :start-val="0" :end-val="balance" :duration="3600" class="card-panel-num" />
-              </div>
-            </div>
-            <div class="property-card propertyline">
-              <div class="property-title">
-                <i class="el-icon-money"></i>
-                润天余额(元)
-              </div>
-              <div class="card-value highlight">
-                <count-to :start-val="0" :end-val="runTianBalance" :duration="3600" class="card-panel-num" />
-              </div>
-            </div>
-            <div class="property-card">
-              <div class="property-title">
-                <span>今日消耗 (元)</span>
-              </div>
-              <div class="card-value highlight" style="color: rgba(32, 33, 36, 1);margin-top: 10px;">
-                <count-to :start-val="0" :end-val="todayComsumption" :duration="3600" class="card-panel-num" />
-              </div>
-              <div class="card-compare">
-                较昨日 <span>+1</span>
-              </div>
-            </div>
-          </div>
-
-        </el-col>
-      </el-row>
-
-      <el-row :gutter="20">
-        <el-col :xs="24" :sm="24" :md="16" :lg="16" :xl="16">
-          <div class="operatetitle">
-            经营数据
-          </div>
-          <div class="operatetitle-col">
-            <div class="operatetitle-card">
-              <div class="card-title">
-                <i class="el-icon-shopping-cart-full"></i>
-                收款总数
-              </div>
-              <div class="operate-value highlight">
-                <count-to :start-val="0" :end-val="recvTotalNum" :duration="3600" class="card-panel-num" />
-                <div class="yesterdaybox">
-                  较昨日 <span class="highlight-today-add2">+{{recvTodayNum}}</span>
-                </div>
-              </div>
-              <div class="card-badge">
-              </div>
-            </div>
-            <div class="operatetitle-card">
-              <div class="card-title">
-                <i class="el-icon-shopping-cart-full"></i>
-                订单总数
-              </div>
-              <div class="operate-value highlight">
-                <count-to :start-val="0" :end-val="orderTotalNum" :duration="3600" class="card-panel-num" />
-                <div class="yesterdaybox">
-                  较昨日 <span class="highlight-today-add2">+{{todayOrderNum}}</span>
-                </div>
-
-              </div>
-              <div class="card-badge">
-              </div>
-
-            </div>
-            <div class="operatetitle-card">
-              <div class="card-title">
-                平台今日看课人数
-              </div>
-              <div class="operate-value highlight">
-                <count-to :start-val="0" :end-val="todayWatchUserCount" :duration="3600" class="card-panel-num" />
-              </div>
-              <div class="card-sub">
-                <span>配额上限</span>
-                <span class="sub-value">
-                  <count-to :start-val="0" :end-val="todayWatchUserCount" :duration="3600" class="card-panel-num"
-                            style="color: rgba(49, 185, 154, 1);" />
-                  /
-                  <count-to :start-val="0" :end-val="versionLimit" :duration="3600" class="card-panel-num" />
-                </span>
-              </div>
-              <el-progress :percentage="versionLimitPercent" :show-text="false"
-                           color="#409EFF"></el-progress>
-            </div>
-            <div class="operatetitle-card">
-              <div class="card-title">
-                <i class="el-icon-shopping-cart-full"></i>
-                商品总数
-              </div>
-              <div class="operate-value highlight">
-                <count-to :start-val="0" :end-val="goodsTotalNum" :duration="3600" class="card-panel-num" />
-                <div class="yesterdaybox">
-                  较昨日 <span class="highlight-today-add2">+{{todayGoodsNum}}</span>
-                </div>
-
-              </div>
-              <div class="card-badge">
-              </div>
-            </div>
-          </div>
-
-        </el-col>
-
-
-        <el-col :xs="24" :sm="24" :md="8" :lg="8" :xl="8" style="padding-left: 15px;">
-
-          <div class="internetbox">
-            <div class="internet-cardtop">
-              <div class="cardinnerbox">
-                <div class="cardtopimg">
-                  <img src="../assets/images/liuliang.png" alt=""><span>剩余流量</span>
-                </div>
-                <div class="cardtopnumber">
-                  <span>{{formatBytes(this.trafficCount)}}</span>
-                </div>
-              </div>
-              <div class="progress">
-                <el-progress :percentage="90" :show-text="false" define-back-color="#000">
-
-                </el-progress>
-              </div>
-              <div class="cardinnerbox2">
-                <div>
-                  今日消耗 <span>{{formatBytes(this.todayTraffic)}}</span>
-                </div>
-                <div>
-                  本月 <span>{{formatBytes(this.thisMonthTraffic)}}</span>
-                </div>
-              </div>
-            </div>
-
-            <div class="internetbox-messge">
-              <div class="internet-card">
-                <img src="../assets/images/message.png" alt="">
-
-                <span class="internet-title">
-                  短信剩余条数 (条)
-                </span>
-              </div>
-              <div class="internet-number">
-                0
-              </div>
-            </div>
-          </div>
-        </el-col>
-      </el-row>
-    </el-card>
-    <!-- 分析概览 (Analysis Overview) -->
-    <div class="analysis-section" shadow="never">
-      <div slot="header" class="header">
-        <div>分析概览</div>
-        <div class="tab-group">
-          <el-radio-group v-model="queryTime" size="medium" @change="handleAnalysis">
-            <el-radio-button label="今日"></el-radio-button>
-            <el-radio-button label="昨日"></el-radio-button>
-            <el-radio-button label="本周"></el-radio-button>
-            <el-radio-button label="本月"></el-radio-button>
-            <el-radio-button label="上月"></el-radio-button>
-          </el-radio-group>
-        </div>
-        <div class="action-group">
-          <div v-if="this.$store.state.user.medicalMallConfig.statics">
-            <!-- 选择部门 -->
-            <el-select v-model="deptId" placeholder="请选择部门" size="small" @change="handleDeptChange" style="width: 150px">
-              <el-option
-                v-for="company in deptOptions"
-                :key="company.deptId"
-                :label="company.deptName"
-                :value="company.deptId"
-              />
-            </el-select>
-            <!-- 选择销售公司 -->
-            <el-select  v-model="companyId" placeholder="请选择销售公司" size="small" clearable @change="handleCompanyChange" style="width: 180px" >
-              <el-option
-                v-for="company in companyOptions"
-                :key="company.companyId"
-                :label="company.companyName"
-                :value="company.companyId"
-              />
-            </el-select>
-          </div>
-          <el-radio-group v-model="userTypeText" @change="handleUserType">
-            <el-radio-button label="会员"></el-radio-button>
-            <el-radio-button label="企微"></el-radio-button>
-          </el-radio-group>
-
-          <el-dropdown @command="handleAutoRefresh" trigger="click">
-            <el-button size="small" plain>
-              自动刷新
-              <i class="el-icon-arrow-down el-icon--right"></i>
-            </el-button>
-            <el-dropdown-menu slot="dropdown">
-              <el-dropdown-item :command="0" :class="{ 'is-active': !autoRefreshInterval }">关闭</el-dropdown-item>
-              <el-dropdown-item :command="5" :class="{ 'is-active': autoRefreshInterval === 5 }">5分钟</el-dropdown-item>
-              <el-dropdown-item :command="10"
-                                :class="{ 'is-active': autoRefreshInterval === 10 }">10分钟</el-dropdown-item>
-              <el-dropdown-item :command="15"
-                                :class="{ 'is-active': autoRefreshInterval === 15 }">15分钟</el-dropdown-item>
-            </el-dropdown-menu>
-          </el-dropdown>
-          <el-button size="small" plain icon="el-icon-refresh" type="primary" @click="manualRefresh">手动刷新</el-button>
-        </div>
-      </div>
+  <div class="app-container">
+    <!-- 有权限时显示统计仪表板 -->
+    <statistics-dashboard v-if="hasDashboardPermission && !loading" />
+
+    <!-- 无权限时显示欢迎页面 -->
+    <welcome-page v-else-if="!hasDashboardPermission && !loading" />
+
+    <!-- 加载状态 -->
+    <div v-else class="loading-container">
+      <el-skeleton animated>
+        <template #template>
+          <el-skeleton-item variant="image" style="width: 100%; height: 400px;" />
+        </template>
+      </el-skeleton>
     </div>
-    <div>
-      <el-row :gutter="20">
-        <el-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12" style="position: relative">
-          <div class="analysis-card-check" :class="selectedDiv===0?'analysis-card-check-selected color':''"
-               @click="handleToggleDiv(0)">
-            <div class="analysis-card">
-              <img class="card-icon" src="../assets/images/cishu_views.png"></img>
-              <div class="card-content">
-                <div class="card-row">
-                  <span>观看人数</span>
-                  <span class="highlight">
-                    <count-to :start-val="0" :end-val="watchUserCount" :duration="3600" class="card-panel-num" />
-                  </span>
-                </div>
-                <div class="card-row">
-                  <span>完播人数</span>
-                  <span class="highlight">
-                    <count-to :start-val="0" :end-val="completedUserCount" :duration="3600" class="card-panel-num" />
-                  </span>
-                </div>
-                <div class="card-row">
-                  <span>完播率</span>
-                  <span class="highlight">{{completedRate}}%</span>
-                </div>
-              </div>
-            </div>
-            <div class="analysis-card">
-              <img class="card-icon" src="../assets/images/number_views.png"></img>
-              <div class="card-content">
-                <div class="card-row">
-                  <span>观看次数</span>
-                  <span class="highlight-red">
-                    <count-to :start-val="0" :end-val="watchCount" :duration="3600" class="card-panel-num" /></span>
-                </div>
-                <div class="card-row">
-                  <span>完播次数</span>
-                  <span class="highlight-red">
-                    <count-to :start-val="0" :end-val="completedCount" :duration="3600" class="card-panel-num" />
-                  </span>
-                </div>
-                <div class="card-row">
-                  <span>视频完播率</span>
-                  <span class="highlight-red">{{watchRate}}%</span>
-                </div>
-              </div>
-            </div>
-          </div>
-        </el-col>
-
-        <el-col :xs="24" :sm="12" :md="6" :lg="6" :xl="6" style="position: relative">
-          <div class="analysis-card-check" :class="selectedDiv===1?'analysis-card-check-selected color':''"
-               @click="handleToggleDiv(1)">
-            <div class="analysis-card">
-              <img class="card-icon" src="../assets/images/renshu_views.png"></img>
-              <div class="card-content">
-                <div class="card-row">
-                  <span>答题人数</span>
-                  <span class="highlight-black">
-                    <count-to :start-val="0" :end-val="answerMemberCount" :duration="3600" class="card-panel-num" />
-                  </span>
-                </div>
-                <div class="card-row">
-                  <span>正确人数</span>
-                  <span class="highlight-black">
-                    <count-to :start-val="0" :end-val="correctUserCount" :duration="3600" class="card-panel-num" />
-                  </span>
-                </div>
-                <div class="card-row">
-                  <span>正确率</span>
-                  <span class="highlight-black">{{correctRate}}%</span>
-                </div>
-              </div>
-            </div>
-          </div>
-        </el-col>
-
-        <el-col :xs="24" :sm="12" :md="6" :lg="6" :xl="6"  style="position: relative">
-          <div class="analysis-card-check" :class="selectedDiv===2?'analysis-card-check-selected color':''"
-               @click="handleToggleDiv(2)">
-            <div class="analysis-card">
-              <img class="card-icon" src="../assets/images/hongbao_views.png"></img>
-              <div class="card-content">
-                <div class="card-row">
-                  <span>答题红包个数</span>
-                  <span class="highlight-black">
-                    <count-to :start-val="0" :end-val="rewardCount" :duration="3600" class="card-panel-num" />
-                  </span>
-                </div>
-                <div class="card-row">
-                  <span>答题红包金额(元)</span>
-                  <span class="highlight-black">
-                    <count-to :start-val="0" :end-val="rewardMoney" :duration="3600" class="card-panel-num" /></span>
-                </div>
-              </div>
-            </div>
-          </div>
-        </el-col>
-      </el-row>
-    </div>
-
-    <!-- 图表区域 (Charts Area) -->
-    <transition name="fade">
-      <el-row :gutter="20" class="charts-section" v-show="selectedDiv===0">
-        <el-col :span="12">
-          <el-card shadow="never">
-            <div slot="header" class="chart-header">
-              <span>会员观看、完播人数趋势图</span>
-              <div class="legend">
-                <div class="legend-item">
-                  <span class="dot viewer-dot"></span>
-                  <span>观看人数</span>
-                </div>
-                <div class="legend-item">
-                  <span class="dot complete-dot"></span>
-                  <span>完播人数</span>
-                </div>
-              </div>
-              <!--              <el-button size="small" plain class="view-more">平台每日统计 <i class="el-icon-arrow-right"></i></el-button>-->
-            </div>
-            <div ref="viewerChart" class="chart-container"></div>
-          </el-card>
-        </el-col>
-
-
-        <el-col :span="12">
-          <el-card shadow="never">
-            <div slot="header" class="chart-header">
-              <span>经销商看客统计</span>
-              <div class="legend">
-                <div class="legend-item">
-                  <span class="dot viewer-dot"></span>
-                  <span>观看人数</span>
-                </div>
-                <div class="legend-item">
-                  <span class="dot complete-dot"></span>
-                  <span>完播人数</span>
-                </div>
-              </div>
-            </div>
-            <div ref="dealerChartNew" class="chart-container"></div>
-          </el-card>
-        </el-col>
-
-<!--        <el-col :span="12">-->
-<!--          <el-card shadow="never">-->
-<!--            <div slot="header" class="chart-header">-->
-<!--              <span>经销商会员观看TOP10</span>-->
-<!--              <div class="legend">-->
-<!--                <el-radio-group v-model="viewerType" size="small" @change="handleDealerChartData">-->
-<!--                  <el-radio-button label="0">按观看人数</el-radio-button>-->
-<!--                  <el-radio-button label="1">按完播人数</el-radio-button>-->
-<!--                </el-radio-group>-->
-<!--              </div>-->
-<!--              &lt;!&ndash;              <el-button size="small" plain class="view-more">经销商统计 <i class="el-icon-arrow-right"></i></el-button>&ndash;&gt;-->
-<!--            </div>-->
-<!--            <div ref="dealerChart" class="chart-container"></div>-->
-<!--          </el-card>-->
-<!--        </el-col>-->
-      </el-row>
-    </transition>
-    <transition name="fade">
-      <el-row :gutter="20" class="charts-section" v-show="selectedDiv===1">
-        <el-card shadow="never">
-          <div slot="header" class="chart-header">
-            <span>课程观看TOP10</span>
-            <div class="legend">
-              <el-radio-group v-model="viewerType" size="small" @change="handleCourseWatchChart">
-                <el-radio-button label="0">按观看人数</el-radio-button>
-                <el-radio-button label="1">按完播人数</el-radio-button>
-                <el-radio-button label="2">按答题人数</el-radio-button>
-                <el-radio-button label="3">按正确人数</el-radio-button>
-              </el-radio-group>
-            </div>
-            <div class="legend">
-              <el-radio-group v-model="delerSort" @change="handleCourseWatchChart">
-                <el-radio label="DESC">前10名</el-radio>
-                <el-radio label="ASC">倒数10名</el-radio>
-              </el-radio-group>
-            </div>
-            <div class="legend">
-              <div class="legend-item">
-                <span class="dot viewer-dot"></span>
-                <span>观看人数</span>
-              </div>
-              <div class="legend-item">
-                <span class="dot complete-dot"></span>
-                <span>完播人数</span>
-              </div>
-              <div class="legend-item">
-                <span class="dot" style="background-color: #E6A23C"></span>
-                <span>答题人数</span>
-              </div>
-              <div class="legend-item">
-                <span class="dot" style="background-color: #F56C6C"></span>
-                <span>正确人数</span>
-              </div>
-            </div>
-            <!--            <el-button size="small" plain class="view-more">经销商统计 <i class="el-icon-arrow-right"></i></el-button>-->
-          </div>
-          <div ref="courseWatchChart" class="chart-container"></div>
-        </el-card>
-      </el-row>
-    </transition>
-
-    <transition name="fade">
-      <el-row :gutter="20" class="charts-section" v-show="selectedDiv===2">
-        <el-col :span="12">
-          <el-card shadow="never">
-            <div slot="header" class="chart-header">
-              <span>答题红包金额TOP10</span>
-              <div class="legend">
-                <el-radio-group v-model="dataType" size="small" @change="handleAnswerRedPackViewerChart">
-                  <el-radio-button label="0">按经销商排行</el-radio-button>
-                  <el-radio-button label="1">按课程排行</el-radio-button>
-                </el-radio-group>
-              </div>
-              <!--              <el-button size="small" plain class="view-more">红包记录 <i class="el-icon-arrow-right"></i></el-button>-->
-            </div>
-            <div ref="answerRedPackViewerChart" class="chart-container"></div>
-          </el-card>
-        </el-col>
-        <el-col :span="12">
-          <el-card shadow="never">
-            <div slot="header" class="chart-header">
-              <span>答题红包金额趋势图</span>
-              <div class="legend">
-                <div class="legend-item">
-                  <span class="dot viewer-dot"></span>
-                  <span>答题红包金额</span>
-                </div>
-              </div>
-              <!--              <el-button size="small" plain class="view-more">红包记录 <i class="el-icon-arrow-right"></i></el-button>-->
-            </div>
-            <div ref="answerRedPackMoneyViewerChart" class="chart-container"></div>
-          </el-card>
-        </el-col>
-      </el-row>
-    </transition>
-    <el-row :gutter="20" class="charts-section">
-      <el-col :span="12">
-        <el-card shadow="never">
-          <div slot="header" class="chart-header">
-            <span>本月订单数</span>
-            <div class="legend">
-              <div class="legend-item">
-                <span class="dot viewer-dot"></span>
-                <span>订单数</span>
-              </div>
-              <div class="legend-item">
-                <span class="dot complete-dot"></span>
-                <span>订单金额</span>
-              </div>
-            </div>
-          </div>
-          <div ref="viewerOrderChart" class="chart-container"></div>
-        </el-card>
-      </el-col>
-      <el-col :span="12">
-        <el-card shadow="never">
-          <div slot="header" class="chart-header">
-            <span>本月收款数</span>
-            <div class="legend">
-              <div class="legend-item">
-                <span class="dot viewer-dot"></span>
-                <span>收款数</span>
-              </div>
-              <div class="legend-item">
-                <span class="dot complete-dot"></span>
-                <span>收款金额</span>
-              </div>
-            </div>
-          </div>
-          <div ref="viewerReceiveChart" class="chart-container"></div>
-        </el-card>
-      </el-col>
-    </el-row>
-    <br/>
   </div>
 </template>
 
 <script>
-import * as echarts from 'echarts'
-import CountTo from "vue-count-to";
-import {
-  analysisPreview,
-  authorizationInfo,
-  dealerAggregated, deaMemberTopTen, rechargeComsumption, rewardMoneyTopTen, rewardMoneyTrend,
-  smsBalance, thisMonthOrderCount, thisMonthRecvCount, trafficLog,
-  watchCourseTopTen, watchEndPlayTrend,getWatchCourseStatisticsData
-} from "@/api/statistics/statistics";
-import dayjs from 'dayjs';
-import { listDept } from '@/api/system/dept'
-import { listCompany } from '@/api/his/company'
-
-
-const viewCharOption = {
-  tooltip: {
-    trigger: 'axis',
-    axisPointer: {
-      type: 'shadow'
-    }
-  },
-  grid: {
-    left: '3%',
-    right: '4%',
-    bottom: '3%',
-    containLabel: true
-  },
-  xAxis: {
-    type: 'category',
-    data: ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '20', '21', '22', '23']
-  },
-  yAxis: {
-    type: 'value'
-  },
-  series: [
-    {
-      name: '观看人数',
-      type: 'bar',
-      data: [],
-      itemStyle: {
-        color: '#409EFF'
-      }
-    },
-    {
-      name: '完播人数',
-      type: 'bar',
-      data: [],
-      itemStyle: {
-        color: '#67C23A'
-      }
-    }
-  ]
-}
-
-const dealerOptionNew = {
-  tooltip: {
-    trigger: 'axis',
-    axisPointer: {
-      type: 'shadow'
-    }
-  },
-  grid: {
-    left: '3%',
-    right: '4%',
-    bottom: '3%',
-    containLabel: true
-  },
-  xAxis: {
-    type: 'category',
-    axisLabel: {
-      rotate: 30, // 设置标签倾斜45度
-      // fontSize: 12, // 减小字体大小
-      interval: 0, // 显示所有标签
-      // 可选:限制标签宽度并截断
-      width: 80,
-      overflow: 'truncate',
-      // 可选:设置标签的对齐方式
-      margin: 20,
-      fontWeight: 'bold' // 设置字体加粗
-    }
-  },
-  yAxis: {
-    type: 'value'
-  },
-  series: [
-    {
-      name: '观看人数',
-      type: 'bar',
-      data: [],
-      itemStyle: {
-        color: '#409EFF'
-      }
-    },
-    {
-      name: '完播人数',
-      type: 'bar',
-      data: [],
-      itemStyle: {
-        color: '#67C23A'
-      }
-    }
-  ]
-}
-
-const thisMonthOrderCountOption = {
-  tooltip: {
-    trigger: 'axis',
-    axisPointer: {
-      type: 'shadow'
-    }
-  },
-  grid: {
-    left: '3%',
-    right: '4%',
-    bottom: '3%',
-    containLabel: true
-  },
-  xAxis: {
-    type: 'category',
-    data: ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '20', '21', '22', '23']
-  },
-  yAxis: {
-    type: 'value'
-  },
-  series: [
-    {
-      name: '订单数',
-      type: 'line',
-      data: [],
-      itemStyle: {
-        color: '#409EFF'
-      }
-    },
-    {
-      name: '订单金额',
-      type: 'line',
-      data: [],
-      itemStyle: {
-        color: '#67C23A'
-      }
-    }
-  ]
-}
-
-const thisMonthRecvCountOption = {
-  tooltip: {
-    trigger: 'axis',
-    axisPointer: {
-      type: 'shadow'
-    }
-  },
-  grid: {
-    left: '3%',
-    right: '4%',
-    bottom: '3%',
-    containLabel: true
-  },
-  xAxis: {
-    type: 'category',
-    data: ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '20', '21', '22', '23']
-  },
-  yAxis: {
-    type: 'value'
-  },
-  series: [
-    {
-      name: '收款数',
-      type: 'line',
-      data: [],
-      itemStyle: {
-        color: '#409EFF'
-      }
-    },
-    {
-      name: '收款金额',
-      type: 'line',
-      data: [],
-      itemStyle: {
-        color: '#67C23A'
-      }
-    }
-  ]
-}
-const dealerOption = {
-  tooltip: {
-    trigger: 'axis',
-    axisPointer: {
-      type: 'shadow'
-    }
-  },
-  grid: {
-    left: '3%',
-    right: '4%',
-    bottom: '3%',
-    containLabel: true
-  },
-  xAxis: {
-    type: 'value'
-  },
-  yAxis: {
-    type: 'category',
-    data: []
-  },
-  series: [
-    {
-      name: '观看人数',
-      type: 'bar',
-      data: [],
-      itemStyle: {
-        color: '#409EFF'
-      }
-    }
-  ]
-}
-
-const courseWatchOption = {
-  tooltip: {
-    trigger: 'axis',
-    axisPointer: {
-      type: 'shadow'
-    }
-  },
-  grid: {
-    left: '3%',
-    right: '4%',
-    bottom: '8%',
-    top: '3%',
-    containLabel: true
-  },
-  xAxis: {
-    type: 'category',
-    data: [],
-    axisLabel: {
-      interval: 0,
-      rotate: 30,
-      fontSize: 10,
-      width: 100,
-      overflow: 'truncate'
-    }
-  },
-  yAxis: {
-    type: 'value',
-    splitLine: {
-      lineStyle: {
-        type: 'dashed'
-      }
-    }
-  },
-  series: [
-    {
-      name: '观看人数',
-      type: 'bar',
-      data: [],
-      itemStyle: {
-        color: '#409EFF'
-      }
-    },
-    {
-      name: '完播人数',
-      type: 'bar',
-      data: [],
-      itemStyle: {
-        color: '#67C23A'
-      }
-    },
-    {
-      name: '答题人数',
-      type: 'bar',
-      data: [],
-      itemStyle: {
-        color: '#E6A23C'
-      }
-    },
-    {
-      name: '正确人数',
-      type: 'bar',
-      data: [],
-      itemStyle: {
-        color: '#F56C6C'
-      }
-    }
-  ]
-}
-
-const lineChartOption = {
-  tooltip: {
-    trigger: 'axis',
-    axisPointer: {
-      type: 'cross' // 改为 'cross' 更适合折线图
-    }
-  },
-  grid: {
-    left: '3%',
-    right: '4%',
-    bottom: '8%', // 如果x轴标签旋转,可能需要更大的 bottom
-    top: '5%',    // 增加一点顶部空间给可能的 Y 轴名称
-    containLabel: true
-  },
-  xAxis: {
-    type: 'time', // X轴类型改为 'time'
-    // data: [], // 时间轴不需要单独设置 data,数据在 series 中提供
-    axisLabel: {
-      // interval: 0, // 时间轴通常自动处理间隔,可以先移除或注释掉
-      rotate: 30,   // 保留旋转,如果标签可能重叠
-      fontSize: 10,
-      // width: 100, // width 和 overflow 对于时间轴可能行为不同,按需调整
-      // overflow: 'truncate',
-      formatter: null // ECharts 会自动格式化时间,如需特定格式可用 function 或字符串模板
-    }
-  },
-  yAxis: {
-    type: 'value',
-    name: '金额 (元)', // 添加 Y 轴名称
-    nameLocation: 'end', // 名称位置
-    nameTextStyle: {
-      align: 'right',
-      padding: [0, 10, 0, 0] // 调整名称与轴线的距离
-    },
-    splitLine: {
-      lineStyle: {
-        type: 'dashed'
-      }
-    },
-    axisLabel: {
-      formatter: '{value} 元' // 可选:给 Y 轴刻度添加单位
-    }
-  },
-  series: [
-    {
-      name: '答题红包金额',
-      type: 'line', // 系列类型改为 'line'
-      data: [
-      ],
-      itemStyle: { // 控制数据点(标记)的样式
-        color: '#409EFF'
-      },
-      lineStyle: { // 控制线的样式
-        color: '#409EFF'
-      },
-      smooth: false, // 是否平滑曲线,可设为 true
-      symbol: 'circle', // 数据点标记形状,'emptyCircle', 'rect', 'roundRect', 'triangle', 'diamond', 'pin', 'arrow', 'none'
-      symbolSize: 4   // 数据点标记大小
-    }
-  ]
-};
+// 异步加载组件
+const StatisticsDashboard = () => import('./components/index/statisticsDashboard')
+const WelcomePage = () => import('./components/index/welcomePage')
 
-
-const redPackageOption = {
-  tooltip: {
-    trigger: 'axis',
-    axisPointer: {
-      type: 'shadow'
-    }
-  },
-  grid: {
-    left: '3%',
-    right: '4%',
-    bottom: '8%',
-    top: '3%',
-    containLabel: true
-  },
-  xAxis: {
-    type: 'category',
-    data: [],
-    axisLabel: {
-      interval: 0,
-      rotate: 30,
-      fontSize: 10,
-      width: 100,
-      overflow: 'truncate'
-    }
-  },
-  yAxis: {
-    type: 'value',
-    splitLine: {
-      lineStyle: {
-        type: 'dashed'
-      }
-    }
-  },
-  series: [
-    {
-      name: '答题红包金额',
-      type: 'bar',
-      data: [],
-      itemStyle: {
-        color: '#409EFF'
-      }
-    }
-  ]
-}
 export default {
-  name: 'StatisticsDashboard',
-  components: {CountTo},
+  name: 'Index',
+  components: {
+    StatisticsDashboard,
+    WelcomePage
+  },
   data() {
     return {
-      deptInitOptions:[],
-      deptOptions:[],
-      intiDeptId:this.$store.state.user.user.deptId,
-      deptId:this.$store.state.user.user.deptId,
-      staticParam : {companyId:null,deptId:this.$store.state.user.user.deptId},
-      companyIntiOptions:[],
-      companyOptions:[],
-      companyId:null,
-      percentage: 0,
-      // 预测message
-      remainMessage: '',
-      // 当天使用流量
-      todayTraffic: 0,
-      trafficCount: 0,
-      // 当月使用流量
-      thisMonthTraffic: 0,
-      dataType: '0',
-      delerSort: 'DESC',
-      smsRemainCount: 0,
-      viewerType: '0',
-      viewerChart: null,
-      dealerChartNew: null,
-      userTypeText: process.env.VUE_APP_COURSE_DEFAULT==1?"会员":"企微",
-      userType: process.env.VUE_APP_COURSE_DEFAULT,
-      dealerChart: null,
-      // 分公司数量
-      dealderCount: 0,
-      // 销售数量
-      groupMgrCount: 0,
-      // 会员总数量
-      memberCount: 0,
-      // 企微数量
-      qwMemberNum: 0,
-      // pad使用情况
-      padTotalNum: 0,
-      // pad使用情况
-      padUsedNum: 0,
-      // 正常会员数量
-      normalNum: 0,
-      // 黑名单会员数量
-      blackNum: 0,
-      // 观看人数
-      watchUserCount: 0,
-      // 完播人数
-      completedUserCount: 0,
-      // 完播率
-      completedRate: 0,
-      // 观看次数
-      watchCount:0,
-      // 完播次数
-      completedCount: 0,
-      // 视频完播率
-      watchRate: 0,
-      // 答题人数
-      answerMemberCount: 0,
-      // 正确人数
-      correctUserCount: 0,
-      correctRate: 0.0,
-      // 答题红包个数
-      rewardCount: 0,
-      // 答题红包金额
-      rewardMoney: 0.0,
-      queryTime: '今日',
-      todayWatchUserCount: 0,
-      versionLimit: 0,
-      versionLimitPercent : 0.0,
-      /// 选中的分析概览
-      selectedDiv: 0,
-      filterType: 0,
-      answerRedPackViewerChart: null,
-      answerRedPackMoneyViewerChart: null,
-      todayComsumption: 0,
-      yesterdayComsumption: 0,
-      balance: 0,
-      runTianBalance: 0,
-      autoRefreshInterval: null,
-      // 今日新增用户数
-      todayIncreaseUserNum: 0,
-      // 订单总数
-      orderTotalNum: 0,
-      // 今日新增订单数
-      todayOrderNum: 0,
-      // 收款总数
-      recvTotalNum: 0,
-      // 今日收款总数
-      recvTodayNum: 0,
-      // 商品总数
-      goodsTotalNum: 0,
-      // 今日商品总数
-      todayGoodsNum: 0
+      hasDashboardPermission: false,
+      loading: true
     }
   },
-  mounted() {
-    this.$nextTick(() => {
-      this.initViewerChart();
-      this.initDealerChartNew();
-      this.initDealerChart();
-      this.initCourseWatchChart();
-      this.initAnswerRedPackViewerChart();
-      this.initAnswerRedPackMoneyViewerChart();
-      this.initThisMonthOrderChart();
-      this.initThisMonthRecvChart();
-
-
-      // 监听窗口大小变化,重新渲染图表
-      window.addEventListener('resize', () => {
-        this.viewerChart && this.viewerChart.resize()
-        this.dealerChart && this.dealerChart.resize()
-        this.dealerChartNew && this.dealerChartNew.resize()
-      })
-    })
-  },
   created() {
-    this.refresh();
-    listDept().then(res => {
-      this.deptInitOptions = res.data;
-      listCompany().then(res => {
-        this.companyIntiOptions = res.rows;
-        this.getDeptOptions(this.intiDeptId);
-        this.getCompanyOptions(this.intiDeptId);
-      });
-    });
+    this.checkDashboardPermission()
   },
   methods: {
-    getDeptOptions(deptId) {
-      const deptInitOptions = this.deptInitOptions;
-      // 部门本身节点
-      let deptNode = deptInitOptions.filter(item => item.deptId === deptId);
-
-      // 递归查找所有子节点
-      function findChildren(parentId) {
-        //部门的子部门
-        let deptChildren = deptInitOptions.filter(item => item.parentId === parentId);
-        //添加子部门
-        deptChildren.forEach(child => {
-          deptNode.push(child);
-          findChildren(child.deptId); // 递归查找子节点的子节点
-        });
-      }
-
-      // 从目标节点开始查找子节点
-      findChildren(deptId);
-      this.deptOptions = deptNode;
-    },
-    getCompanyOptions(deptId) {
-      this.companyId = null;
-      //修改选择后清空查询参数
-      this.staticParam.companyId = null;
-      this.staticParam.deptId = deptId;
-      const deptInitOptions = this.deptInitOptions;
-      const companyInitOptions = this.companyIntiOptions;
-      // 部门下的公司
-      let companyNode = companyInitOptions.filter(item => item.deptId === deptId);
+    checkDashboardPermission() {
 
-      // 递归查找所有子节点
-      function findChildren(parentId) {
-        //部门的子部门
-        let deptChildren = deptInitOptions.filter(item => item.parentId === parentId);
-        //添加子部门
-        deptChildren.forEach(child => {
-          //子部门下的销售公司
-          let companyChildren = companyInitOptions.filter(item => item.deptId === child.deptId);
-          companyChildren.forEach(companyChild => {
-            companyNode.push(companyChild);
-          })
-          findChildren(child.deptId); // 递归查找子节点的子节点
-        });
+      // 方式2: 检查用户权限
+      if (this.hasPermi(['his:index'])) {
+        this.hasDashboardPermission = true
       }
-
-      // 从目标节点开始查找子节点
-      findChildren(deptId);
-      this.companyOptions = companyNode;
-    },
-    //首页统计选择部门、销售公司
-    handleDeptChange() {
-      this.getCompanyOptions(this.deptId);
-      this.refresh();
-    },
-    handleCompanyChange() {
-      this.staticParam.companyId = this.companyId;
-      this.refresh();
-    },
-    handleUserType() {
-      if (this.userTypeText === '会员') {
-        this.userType = 1
-      } else {
-        this.userType = 2
+      // 方式3: 检查用户角色
+      else if (this.hasRole(['admin'])) {
+        this.hasDashboardPermission = true
       }
 
-      this.refresh()
+      this.loading = false
     },
-    /**
-     * 计算余额预计可持续的天数
-     * @param {number} balance - 当前账户余额
-     * @param {number} runTianBalance - 润天账户余额
-     * @param {number} todayConsumption - 今日消耗金额
-     * @param {number} yesterdayConsumption - 昨日消耗金额
-     * @return {Object} 包含天数和进度百分比的对象
-     */
-    calculateRemainingDays(balance, todayConsumption, yesterdayConsumption) {
-      // 如果今日和昨日消耗都为0,则无法预测(避免除以0)
-      if (todayConsumption === 0 && yesterdayConsumption === 0) {
-        return {
-          days: Infinity,
-          percentage: 0,
-          message: '暂无消耗数据'
-        };
-      }
-
-      // 计算每日平均消耗量
-      const avgDailyConsumption = (todayConsumption + yesterdayConsumption) / 2;
-
-      // 如果平均消耗为0,则无法预测
-      if (avgDailyConsumption === 0) {
-        return {
-          days: Infinity,
-          percentage: 0,
-          message: '暂无消耗数据'
-        };
-      }
-
-      // 计算剩余天数(向下取整)
-      const remainingDays = Math.floor(balance / avgDailyConsumption);
-
-      // 计算进度条百分比,最大为100
-      // 这里假设100天是满值,可以根据需要调整
-      const maxDays = 100;
-      const percentage = Math.min(100, Math.max(0, Math.round((remainingDays / maxDays) * 100)));
-
-      let message = '';
-      if (remainingDays > 365) {
-        message = '预测余额充足';
-      } else {
-        message = `预测不足${remainingDays}天`;
-      }
-
-      return {
-        days: remainingDays,
-        percentage: 100 - percentage,
-        message: message
-      };
-    },
-    /**
-     * 将字节数转换为合适的单位表示(Byte、KB、MB、GB、TB)
-     * @param {number} bytes - 字节数
-     * @param {number} [decimals=2] - 小数点后保留的位数
-     * @returns {string} 格式化后的字符串,包含数值和单位
-     */
-    formatBytes(bytes, decimals = 2) {
-      const isNegative = bytes < 0;  // 判断是否为负数
-      bytes = Math.abs(bytes);  // 获取绝对值
-
-      if (bytes === 0) return '0 Byte';
-
-      const k = 1024;
-      const sizes = ['Byte', 'KB', 'MB', 'GB', 'TB'];
-
-      // 计算合适的单位级别
-      let i = Math.floor(Math.log(bytes) / Math.log(k));
-
-      // 转换为对应单位的值
-      const value = bytes / Math.pow(k, i);
-
-
-      if(this.deptId !== 1 ||  this.companyId !== null){
-        i += 1;
-      }
-      // 格式化为指定小数位的字符串
-      const result = parseFloat(value.toFixed(decimals)) + ' ' + sizes[Math.min(i, sizes.length - 1)];
-
-      // 如果是负数,返回带负号的值
-      return isNegative ? `-${result}` : result;
-    },
-    // 手动刷新
-    manualRefresh() {
-      this.refresh();
-    },
-    // 处理自动刷新选项
-    handleAutoRefresh(command) {
-      // 清除之前的定时器
-      if (this.timer) {
-        clearInterval(this.timer);
-        this.timer = null;
-      }
-
-      // 设置新的刷新间隔
-      this.autoRefreshInterval = parseInt(command);
-
-      // 如果间隔大于0,设置新的定时器
-      if (this.autoRefreshInterval > 0) {
-        this.timer = setInterval(() => {
-          this.refresh();
-        }, this.autoRefreshInterval * 60 * 1000); // 转换为毫秒
-
-        this.$message.success(`已设置${this.autoRefreshInterval}分钟自动刷新`);
-      } else {
-        this.$message.info('已关闭自动刷新');
-      }
-    },
-    refresh() {
-      rechargeComsumption(this.staticParam).then(res => {
-        console.log(res);
-        if (res.code === 200) {
-          this.balance = res.data.balance;
-          this.runTianBalance = res.data.runTianBalance;
-          this.todayComsumption = res.data.todayComsumption;
-          this.yesterdayComsumption = res.data.yesterdayComsumption;
-          let calculateRemainingDays1 = this.calculateRemainingDays(this.balance, this.todayComsumption, this.yesterdayComsumption);
-          this.percentage = calculateRemainingDays1.percentage;
-          this.remainMessage = calculateRemainingDays1.message;
-        }
-      });
-
-      trafficLog(this.staticParam).then(res => {
-        if (res.code === 200) {
-          this.todayTraffic = res.data.today;
-          this.thisMonthTraffic = res.data.thisMonth;
-          this.trafficCount = res.data.traffic;
-        }
-      })
-
-      dealerAggregated(this.staticParam).then(res => {
-        if (res.code === 200) {
-          this.dealderCount = res.data.dealderCount ?? 0;
-          this.groupMgrCount = res.data.groupMgrCount ?? 0;
-          this.memberCount = res.data.memberCount ?? 0;
-          this.qwMemberNum = res.data.qwMemberNum ?? 0;
-          const totalNum = res.data.padTotalNum;
-          //-1 不限 null 未设置
-          if(totalNum != null && totalNum !== -1 && totalNum > 0){
-            this.padTotalNum = totalNum;
-          }else{
-            this.padTotalNum = '不限';
-          }
-          this.padUsedNum = res.data.padUsedNum ?? 0;
-          this.padInfo = res.data.padInfo;
-          this.normalNum = res.data.normalNum ?? 0;
-          this.blackNum = res.data.blackNum ?? 0;
-          this.todayIncreaseUserNum = res.data.todayIncreaseUserNum ?? 0;
-          this.orderTotalNum = res.data.orderTotalNum ?? 0;
-          this.todayOrderNum = res.data.todayOrderNum ?? 0;
-          this.recvTotalNum = res.data.recvTotalNum ?? 0;
-          this.recvTodayNum = res.data.recvTodayNum ?? 0;
-          this.goodsTotalNum = res.data.goodsTotalNum ?? 0;
-          this.todayGoodsNum = res.data.todayGoodsNum ?? 0;
-        }
-      })
-      let param = this.getParam();
 
-      // 获取当前日期时间
-      const today = dayjs();
-      param.startTime = this.formatDate(today);
-      param.endTime = this.formatDate(today);
-      analysisPreview(param).then(res => {
-        if (res.code === 200) {
-          this.watchUserCount = res.data.watchUserCount;
-          this.completedUserCount = res.data.completedUserCount;
-          this.completedRate = res.data.completedRate;
-          this.watchCount = res.data.watchCount;
-          this.completedCount = res.data.completedCount;
-          this.answerMemberCount = res.data.answerMemberCount;
-          this.correctUserCount = res.data.correctUserCount;
-          this.correctRate = res.data.correctRate;
-          this.rewardCount = res.data.rewardCount;
-          this.rewardMoney = res.data.rewardMoney;
-          this.watchRate = res.data.watchRate;
-        }
-      })
-      smsBalance(this.staticParam).then(res => {
-        if (res.code === 200) {
-          if (res.data == null) {
-            this.smsRemainCount = 0;
-          } else {
-            this.smsRemainCount = res.data;
-          }
-        }
-      })
-      authorizationInfo(this.staticParam).then(res => {
-        if (res.code === 200 && res.data != null) {
-          this.todayWatchUserCount = res.data.todayWatchUserCount;
-          this.versionLimit = res.data.versionLimit;
-          if(this.versionLimit){
-            this.versionLimitPercent = this.todayWatchUserCount/this.versionLimit;
-          }
-        }
-      })
-
-      this.handleCourseWatchChart()
-      this.handleViewChartData()
-      this.handleDealerChartDataNew()
-
-      // 经销商会员观看TOP10
-      this.handleDealerChartData()
-
-      this.handleAnswerRedPackViewerChart()
-
-      this.handleAnswerRedPackMoneyViewerChart()
-
-      this.handleThisMonthRecvCount();
-      this.handleThisMonthOrderCount();
-
-    },
-    /**
-     * 将数字添加千位分隔符
-     * @param {number|string} num - 需要格式化的数字
-     * @return {string} 添加千位分隔符后的字符串
-     */
-    formatNumberWithCommas(num) {
-      if (num === null || num === undefined || isNaN(Number(num))) {
-        return '0';
-      }
-
-      const numStr = String(num);
-
-      // 处理负数
-      const isNegative = numStr.startsWith('-');
-      const absNumStr = isNegative ? numStr.slice(1) : numStr;
-
-      // 分离整数部分和小数部分
-      const parts = absNumStr.split('.');
-      const integerPart = parts[0];
-      const decimalPart = parts.length > 1 ? '.' + parts[1] : '';
-
-      const formattedInteger = integerPart.replace(/\B(?=(\d{3})+(?!\d))/g, ',');
-
-      return (isNegative ? '-' : '') + formattedInteger + decimalPart;
-    },
-    handleToggleDiv(selected) {
-      this.selectedDiv = selected;
-
-      if (selected === 1) {
-        this.$nextTick(() => {
-          if (this.courseWatchChart) {
-            this.courseWatchChart.resize();
-          } else {
-          }
-        });
-      } else if (selected === 0) {
-        this.$nextTick(() => {
-          if (this.viewerChart) this.viewerChart.resize();
-          if (this.dealerChart) this.dealerChart.resize();
-        });
-      } else if (selected === 2) {
-        this.$nextTick(() => {
-          if (this.answerRedPackViewerChart) this.answerRedPackViewerChart.resize();
-          if (this.answerRedPackMoneyViewerChart) this.answerRedPackMoneyViewerChart.resize();
-        });
-      }
-      if (this.selectedDiv === 0) {
-        this.handleViewChartData()
-        this.handleDealerChartData()
-        this.handleDealerChartDataNew()
-      } else if (this.selectedDiv === 1) {
-        this.handleCourseWatchChart()
-      } else if (this.selectedDiv === 2) {
-        this.handleAnswerRedPackViewerChart()
-        this.handleAnswerRedPackMoneyViewerChart()
-      }
-    },
-    formatDate(date) {
-      return dayjs(date).format('YYYY-MM-DD');
-    },
-
-    getParam() {
-      let param = {
-        startTime: '',
-        endTime: '',
-        userType: this.userType,
-        companyId: this.companyId,
-        deptId: this.deptId
-      };
-      // 获取当前日期时间
-      const today = dayjs();
-
-      let type = 0;
-      if (this.queryTime === '今日') {
-        param.startTime = this.formatDate(today);
-        param.endTime = this.formatDate(today);
-        type = 0;
-      } else if (this.queryTime === '昨日') {
-        const yesterday = today.subtract(1, 'day');
-        param.startTime = this.formatDate(yesterday);
-        param.endTime = this.formatDate(yesterday);
-        type = 1;
-      } else if (this.queryTime === '本周') {
-        param.startTime = this.formatDate(today.startOf('week'));
-        param.endTime = this.formatDate(today.endOf('week'));
-        type = 2;
-      } else if (this.queryTime === '本月') {
-        param.startTime = this.formatDate(today.startOf('month'));
-        param.endTime = this.formatDate(today.endOf('month'));
-        type = 3;
-      } else if (this.queryTime === '上月') {
-        const lastMonth = today.subtract(1, 'month');
-        param.startTime = this.formatDate(lastMonth.startOf('month'));
-        param.endTime = this.formatDate(lastMonth.endOf('month'));
-        type = 4;
-      } else {
-        console.warn(`未知的 queryTime: ${this.queryTime}, 默认使用今日`);
-        param.startTime = this.formatDate(today);
-        param.endTime = this.formatDate(today);
-      }
-      param.type = type;
-      param.sort = this.delerSort;
-      return param;
-    },
-    // 分析概览
-    handleAnalysis(e) {
-
-      let param = this.getParam();
-      analysisPreview(param).then(res => {
-        if (res.code === 200) {
-          this.watchUserCount = res.data.watchUserCount;
-          this.completedUserCount = res.data.completedUserCount;
-          this.completedRate = res.data.completedRate;
-          this.watchCount = res.data.watchCount;
-          this.completedCount = res.data.completedCount;
-          this.answerMemberCount = res.data.answerMemberCount;
-          this.correctUserCount = res.data.correctUserCount;
-          this.correctRate = res.data.correctRate;
-          this.rewardCount = res.data.rewardCount;
-          this.rewardMoney = res.data.rewardMoney;
-          this.watchRate = res.data.watchRate;
-        }
-      })
-
-      if (this.selectedDiv === 0) {
-        this.handleViewChartData()
-        this.handleDealerChartData()
-        this.handleDealerChartDataNew()
-      } else if (this.selectedDiv === 1) {
-        this.handleCourseWatchChart()
-      } else if (this.selectedDiv === 2) {
-        this.handleAnswerRedPackViewerChart()
-        this.handleAnswerRedPackMoneyViewerChart()
-      }
-    },
-    handleAnswerRedPackViewerChart() {
-      let param = this.getParam();
-      param = { ...param, statisticalType: this.viewerType, dataType: this.dataType };
-      rewardMoneyTopTen(param).then(res => {
-        if (res.code === 200) {
-          let data = res.data;
-          let companyNameList = data.map(e => e.companyName)
-          let courseNameList = data.map(e => e.courseName)
-          let rewardMoneyList = data.map(e => e.rewardMoney)
-          if (this.dataType === '0') {
-            redPackageOption.xAxis.data = companyNameList;
-          } else {
-            redPackageOption.xAxis.data = courseNameList;
-          }
-          redPackageOption.series[0].data = rewardMoneyList;
-
-          this.answerRedPackViewerChart.setOption(redPackageOption)
-        }
-      })
-    },
-    handleAnswerRedPackMoneyViewerChart() {
-      let param = this.getParam();
-      param = { ...param, statisticalType: this.viewerType, dataType: this.dataType };
-      rewardMoneyTrend(param).then(res => {
-        if (res.code === 200) {
-          let data = res.data;
-          let option = data.map(e => [e.x, e.rewardMoney])
-          lineChartOption.series[0].data = option;
-
-          this.answerRedPackMoneyViewerChart.setOption(lineChartOption)
-        }
-      })
-    },
-    handleCourseWatchChart() {
-      let param = this.getParam();
-      param = { ...param, statisticalType: this.viewerType };
-      watchCourseTopTen(param).then(res => {
-        if (res.code === 200) {
-          let data = res.data;
-          let watchUserCountList = data.map(e => e.watchUserCount);
-          let completedUserCountList = data.map(e => e.completedUserCount);
-          let answerUserCountList = data.map(e => e.answerUserCount);
-          let correctUserCountList = data.map(e => e.correctUserCount);
-          let courseNameList = data.map(e => e.courseName);
-          courseWatchOption.xAxis.data = courseNameList;
-          courseWatchOption.series[0].data = watchUserCountList;
-          courseWatchOption.series[1].data = completedUserCountList;
-          courseWatchOption.series[2].data = answerUserCountList;
-          courseWatchOption.series[3].data = correctUserCountList;
-          this.courseWatchChart.setOption(courseWatchOption)
-        }
+    // 使用若依的权限检查方法
+    hasPermi(permissions) {
+      return this.$store.state.user.permissions.some(permission => {
+        return permissions.includes(permission)
       })
     },
-    handleDealerChartData() {
-      let param = this.getParam();
-
-      // 经销商会员观看TOP10
-      deaMemberTopTen({ ...param, statisticalType: this.viewerType }).then(res => {
-        if (res.code === 200) {
-          let data = res.data;
-          let companyNameList = data.map(e => e.companyName);
-          let watchUserList = data.map(e => e.watchUserCount);
-          dealerOption.yAxis.data = companyNameList;
-          dealerOption.series[0].data = watchUserList;
 
-          this.dealerChart.setOption(dealerOption)
-        }
+    // 使用若依的角色检查方法
+    hasRole(roles) {
+      return this.$store.state.user.roles.some(role => {
+        return roles.includes(role)
       })
-
-    },
-    handleThisMonthOrderCount() {
-      thisMonthOrderCount(this.staticParam).then(res => {
-        if (res.code === 200) {
-          let dates = res.dates;
-          let orderCount = res.orderCount;
-          let payPrice = res.payPrice;
-
-          thisMonthOrderCountOption.series[0].data = orderCount;
-          thisMonthOrderCountOption.series[1].data = payPrice;
-          thisMonthOrderCountOption.xAxis.data = dates;
-
-          this.thisMonthOrderChart.setOption(thisMonthOrderCountOption)
-        }
-      })
-    },
-    handleThisMonthRecvCount() {
-      thisMonthRecvCount(this.staticParam).then(res => {
-        if (res.code === 200) {
-          let dates = res.dates;
-          let orderCount = res.orderCount;
-          let payMoney = res.payMoney;
-
-          thisMonthRecvCountOption.series[0].data = orderCount;
-          thisMonthRecvCountOption.series[1].data = payMoney;
-          thisMonthRecvCountOption.xAxis.data = dates;
-          this.thisMonthRecvChart.setOption(thisMonthRecvCountOption)
-        }
-      })
-    },
-    handleViewChartData() {
-      let param = this.getParam();
-
-      watchEndPlayTrend({ ...param }).then(res => {
-        if (res.code === 200) {
-          let data = res.data;
-          let watchUserCountList = data.map(e => e.watchUserCount);
-          let completedUserCountList = data.map(e => e.completedUserCount);
-          let xAxis = data.map(e => e.x);
-          viewCharOption.series[0].data = watchUserCountList;
-          viewCharOption.series[1].data = completedUserCountList;
-          viewCharOption.xAxis.data = xAxis;
-
-          this.viewerChart.setOption(viewCharOption);
-        }
-      })
-
-    },
-    handleDealerChartDataNew() {
-      let param = this.getParam();
-
-      getWatchCourseStatisticsData({ ...param }).then(res => {
-        if (res.code === 200) {
-          console.log(res.data);
-          // 根据实际数据结构调整
-          let data = res.data;
-          let watchUserCountList = data.map(e => e.watchCount);     // 观看次数
-          let completedUserCountList = data.map(e => e.finishCount); // 完播次数
-          let xAxis = data.map(e => e.companyName);                 // X轴使用公司名称
-
-          // 更新图表配置
-          dealerOptionNew.series[0].data = watchUserCountList;
-          dealerOptionNew.series[1].data = completedUserCountList;
-          dealerOptionNew.xAxis.data = xAxis;
-
-          this.dealerChartNew.setOption(dealerOptionNew);
-
-        }
-      })
-
-    },
-    initThisMonthOrderChart() {
-      this.thisMonthOrderChart = echarts.init(this.$refs.viewerOrderChart)
-      this.thisMonthOrderChart.setOption(thisMonthOrderCountOption)
-    },
-    initThisMonthRecvChart() {
-
-      this.thisMonthRecvChart = echarts.init(this.$refs.viewerReceiveChart)
-      this.thisMonthRecvChart.setOption(thisMonthOrderCountOption)
-    },
-    initViewerChart() {
-      this.viewerChart = echarts.init(this.$refs.viewerChart)
-      this.viewerChart.setOption(viewCharOption)
-    },
-    initDealerChartNew() {
-      this.dealerChartNew = echarts.init(this.$refs.dealerChartNew)
-      this.dealerChartNew.setOption(dealerOptionNew)
-    },
-    initDealerChart() {
-      this.dealerChart = echarts.init(this.$refs.dealerChart)
-
-      this.dealerChart.setOption(dealerOption)
-    },
-    initCourseWatchChart() {
-      this.courseWatchChart = echarts.init(this.$refs.courseWatchChart)
-
-      this.courseWatchChart.setOption(courseWatchOption)
-    },
-    initAnswerRedPackViewerChart() {
-      this.answerRedPackViewerChart = echarts.init(this.$refs.answerRedPackViewerChart)
-
-      this.answerRedPackViewerChart.setOption(redPackageOption)
-    },
-    initAnswerRedPackMoneyViewerChart() {
-      this.answerRedPackMoneyViewerChart = echarts.init(this.$refs.answerRedPackMoneyViewerChart)
-
-      this.answerRedPackMoneyViewerChart.setOption(lineChartOption)
     }
-  },
-
-  beforeDestroy() {
-    // 组件销毁时清除定时器
-    if (this.timer) {
-      clearInterval(this.timer);
-      this.timer = null;
-    }
-    // window.removeEventListener('resize', this.resizeHandler)
-    this.viewerChart && this.viewerChart.dispose()
-    this.dealerChart && this.dealerChart.dispose()
-    this.dealerChartNew && this.dealerChartNew.dispose()
   }
 }
 </script>
 
 <style scoped>
-.highlight-today-add {
-  color: green;
-  font-size: 17px;
-  font-weight: normal;
-}
-
-.highlight-today-add2 {
-  color: rgba(49, 185, 154, 1);
-  font-size: 14px;
-  font-weight: normal;
-  transform: scale(.9);
-}
-
-.action-group .el-button + .el-button,
-.action-group .el-dropdown {
-  margin-left: 10px;
-}
-
-.is-active {
-  color: #409EFF;
-  font-weight: bold;
-}
-
-::v-deep .el-radio-button__inner:hover {
-  color: #409EFF;
-  /* 鼠标悬浮时的文字颜色,可以根据需要调整 */
+.app-container {
+  min-height: calc(100vh - 84px);
 }
 
-::v-deep .el-radio-button.is-active .el-radio-button__inner {
-  background-color: #409EFF;
-  /* 选中时的背景色 */
-  border-color: #409EFF;
-  /* 选中时的边框色 */
-  color: #FFFFFF;
-  /* 选中时的文字颜色 (通常是白色) */
-  box-shadow: -1px 0 0 0 #409EFF;
-  /* 处理按钮间的连接缝隙 */
-}
-
-/* 如果需要,也可以修改非选中状态下的聚焦(focus)或悬浮(hover)样式 */
-/* 例如,让非选中按钮悬浮时边框和文字也变蓝 */
-::v-deep .el-radio-button:not(.is-active) .el-radio-button__inner:hover {
-  color: #409EFF;
-  /* border-color: #b3d8ff;  Element UI 默认悬浮边框色,可以按需修改 */
-}
-
-/* 聚焦时的外框,如果需要的话 */
-::v-deep .el-radio-button:focus:not(.is-checked) .el-radio-button__inner {
-  /* border-color: #409EFF; */
-  /* Element UI 默认的 focus 颜色通常关联主题色 */
-  /* box-shadow: 0 0 2px 2px rgba(64, 158, 255, 0.2); */
-  /* 示例 focus 光晕 */
-}
-
-.statistics-dashboard {
+.loading-container {
   padding: 20px;
-  background-color: #f5f7fa;
-}
-
-.overview-section,
-.analysis-section {
-  margin-bottom: 20px;
-  border-radius: 4px;
-}
-
-.header {
-  display: flex;
-  justify-content: space-between;
-  align-items: center;
-  font-size: 16px;
-  font-weight: 500;
-}
-
-.data-card {
-  background-color: #fff;
-  border-radius: 4px;
-  padding: 15px;
-  height: 120px;
-  display: flex;
-  flex-direction: column;
-  position: relative;
-  transition: background-color 0.3s ease-in-out;
-}
-
-.data-card:hover {
-  border: 1px solid #4592ff;
-  background-color: #e7f1ff;
-}
-
-.card-title {
-  color: #606266;
-  font-size: 14px;
-  margin-bottom: 10px;
-}
-
-.card-title1 {
-  color: white;
-  font-size: 14px;
-  margin-bottom: 10px;
-}
-
-.card-value {
-  font-size: 24px;
-  font-weight: bold;
-  margin-top: 20px;
-}
-
-.highlight {
-  color: #409EFF;
-}
-
-.card-sub {
-  display: flex;
-  justify-content: space-between;
-  font-size: 12px;
-  color: #909399;
-  margin-top: 5px;
-}
-
-.card-desc {
-  font-size: 12px;
-  color: #909399;
-  margin-top: 5px;
-}
-
-.card-badge {
-  position: absolute;
-  top: 15px;
-  right: 15px;
-  background: #f0f9eb;
-  color: #67c23a;
-  padding: 2px 5px;
-  border-radius: 4px;
-}
-
-.cdn-label {
-  background-color: #409EFF;
-  color: white;
-  padding: 2px 5px;
-  border-radius: 4px;
-  margin-right: 5px;
-  font-size: 12px;
-}
-
-.tab-group {
-  display: flex;
-  gap: 10px;
-}
-
-.action-group {
-  display: flex;
-  gap: 10px;
-}
-
-.analysis-card {
-  border-radius: 4px;
-  padding: 20px;
-  display: flex;
-  align-items: center;
-}
-
-.card-icon {
-  width: 50px;
-  height: 50px;
-  background-color: rgba(64, 158, 255, 0.1);
-  border-radius: 8px;
-  display: flex;
-  justify-content: center;
-  align-items: center;
-  font-size: 24px;
-  color: #409EFF;
-  margin-right: 20px;
-}
-
-.card-content {
-  display: flex;
-}
-
-.card-row {
-  display: flex;
-  justify-content: center;
-  justify-items: center;
-  flex-direction: column;
-  padding: 10px;
-
-  .highlight {
-    text-align: center;
-    margin-top: 1em;
-
-    font-family: BebasNeue;
-    color: #1677ff;
-    font-size: 21px;
-    line-height: 42px;
-    font-weight: 600;
-    margin-top: 8px;
-  }
-
-  font-size: 15px;
-  color: #000;
-
-}
-
-.charts-section {
-  margin-top: 20px;
-}
-
-.chart-header {
-  display: flex;
-  justify-content: space-between;
-  align-items: center;
-}
-
-.view-more {
-  font-size: 12px;
-}
-
-.legend {
-  display: flex;
-  gap: 15px;
-}
-
-.legend-item {
-  display: flex;
-  align-items: center;
-  font-size: 12px;
-}
-
-.dot {
-  width: 10px;
-  height: 10px;
-  border-radius: 50%;
-  margin-right: 5px;
-}
-
-.viewer-dot {
-  background-color: #409EFF;
-}
-
-.complete-dot {
-  background-color: #67C23A;
-}
-
-.chart-container {
-  height: 350px;
-  width: 100%;
-}
-
-.analysis-card-check {
-  display: flex;
-  flex-direction: row;
-  border: 1px solid transparent;
-  background-color: #fff;
-  border-radius: 4px;
-}
-
-.analysis-card-check:hover {
-  cursor: pointer;
-}
-
-.analysis-card-check-selected:after {
-  content: "";
-  display: block;
-  border-width: 15px;
-  position: absolute;
-  bottom: -30px;
-  left: 50%;
-  margin-left: -32px;
-  border-style: solid dashed dashed solid;
-  border-color: #4592FF transparent transparent transparent;
-  font-size: 0;
-  line-height: 0;
-  z-index: 1;
-}
-
-.analysis-card-check-selected:before {
-  content: "";
-  display: block;
-  border-width: 15px;
-  position: absolute;
-  bottom: -30px;
-  left: 50%;
-  margin-left: -32px;
-  border-style: solid dashed dashed solid;
-  border-color: #4592FF transparent transparent transparent;
-  font-size: 0;
-  line-height: 0;
-  z-index: 1;
-}
-
-.analysis-card-check-selected {
-  border: 1px solid #4592FF;
-  background-color: #e7f1ff;
-}
-
-.color {
-  position: relative;
-  border: 1px solid #4592FF;
-  background-color: #e7f1ff;
-}
-
-.color:after {
-  bottom: -27px;
-  border-color: #E7F1FF transparent transparent transparent;
-}
-
-.companybox {
-  color: white;
-  background-color: #006CFF;
-  padding: 10px 10px 40px 10px;
-  box-sizing: border-box;
-  position: relative;
-  border-radius: 6px;
-
-  .topimg {
-    width: 100px;
-    height: 80px;
-    position: absolute;
-    top: 0px;
-    left: 0;
-  }
-
-  .bottomimg {
-    width: 100px;
-    height: 80px;
-    position: absolute;
-    bottom: -10px;
-    right: 0;
-    transform: rotate(180deg);
-  }
-
-  .companyboxtitle {
-    height: 30px;
-    margin-bottom: 20px;
-    font-weight: 600;
-  }
-
-  .companyflex {
-    display: flex;
-    justify-content: space-around;
-  }
-}
-
-
-.companynumber {
-  color: white;
-}
-
-.companycard {
-
-  width: 25%;
-}
-
-.companyadd {
-  color: white;
-}
-
-.cardafter {
-  position: relative;
-
-}
-
-.cardafter::after {
-  content: "";
-  height: 60px;
-  border-right: 1px solid rgba(255, 255, 255, 0.20);
-  position: absolute;
-  right: 30px;
-  top: 0px;
-}
-
-.highlight1 {
-  margin-top: 10px;
-  margin-left: 20px;
-}
-
-
-.propertyboxtitle {
-  background-image: url(../assets/images/zcgl_bg.png);
-  height: 100px;
-}
-
-.propertyboxflex {
-  display: flex;
-  background-color: white;
-  justify-content: space-between;
-}
-
-.property-card {
-  width: 48%;
-  border-radius: 4px;
-  padding: 15px;
-  height: 100px;
-  display: flex;
-  flex-direction: column;
-  position: relative;
-  transition: background-color 0.3s ease-in-out;
-
-}
-
-.property_title {
-  height: 40px;
-  line-height: 40px;
-  color: rgba(32, 33, 36, 1);
-  box-sizing: border-box;
-  padding-left: 10px;
-  font-weight: 600;
-}
-
-.card-compare {
-  margin-top: 5px;
-  font-size: 14px;
-  color: rgba(92, 95, 102, 1);
-
-  span {
-    font-size: 13px;
-    transform: scale(.8);
-    color: rgba(49, 185, 154, 1);
-  }
-}
-
-.propertyline {
-  position: relative;
-}
-
-.propertyline::after {
-  position: absolute;
-  content: "";
-  height: 80px;
-  border-right: 1px solid rgba(237, 239, 242, 1);
-  right: 0;
-}
-
-.operatetitle {
-  font-weight: 600;
-  height: 50px;
-  line-height: 50px;
-}
-
-.operatetitle-col {
-  display: flex;
-  justify-content: space-between;
-  padding-bottom: 20px;
-  padding-right: 5px;
-
-  .operatetitle-card {
-    width: 24%;
-    border: 1px solid transparent;
-    background-color: rgba(245, 247, 250, 1);
-    border-radius: 4px;
-    padding: 15px;
-    display: flex;
-    flex-direction: column;
-    position: relative;
-    transition: background-color 0.3s ease-in-out;
-
-    .operate-value {
-      font-size: 24px;
-      font-weight: bold;
-    }
-  }
-}
-
-.yesterdaybox {
-  font-size: 14px;
-  color: rgba(92, 95, 102, 1);
-  font-weight: 200;
-  margin-top: 10px;
-}
-
-.internetbox {
-  background: linear-gradient(to right, rgba(255, 99, 0, 1), rgba(255, 159, 1, 1));
-  border-radius: 6px;
-  padding: 2px 5px 15px 5px;
-  width: 100%;
-
-  .internet-cardtop {
-    background-color: white;
-    border-radius: 3px;
-    margin-top: 5px;
-    justify-content: space-between;
-    padding: 15px;
-    box-sizing: border-box;
-    color: rgba(32, 33, 36, 1);
-
-    .cardinnerbox {
-      display: flex;
-      justify-content: space-between;
-
-      .cardtopimg {
-        display: flex;
-        align-items: center;
-        font-size: 15px;
-
-        img {
-          height: 18px;
-          width: 18px;
-        }
-      }
-
-      .cardtopnumber {
-        font-size: 25px;
-        color: rgba(32, 33, 36, 1);
-        font-weight: bold;
-      }
-
-    }
-
-    .cardinnerbox2 {
-      display: flex;
-      justify-content: space-between;
-      font-size: 14px;
-      color: rgba(92, 95, 102, 1);
-
-
-    }
-
-  }
-
-  .internetbox-messge {
-    color: white;
-    display: flex;
-    justify-content: space-between;
-    padding: 15px 10px 10px 10px;
-
-    .internet-card {
-      display: flex;
-      font-size: 14px;
-      align-items: center;
-
-      img {
-        height: 18px;
-        width: 18px;
-      }
-
-      span {
-        padding-left: 5px;
-      }
-    }
-
-    .internet-number {
-      font-size: 25px;
-
-    }
-  }
-
-  .internetbox {
-    display: flex;
-    color: white;
-
-    .internet-card {
-      width: 100%;
-      display: flex;
-      color: white;
-
-    }
-  }
-
-  .internet-title {
-    font-size: 14px;
-  }
-
-}
-
-.company {
-  background-color: #006CFF;
-}
-
-.highlight-red {
-  text-align: center;
-  margin-top: 1em;
-  font-family: BebasNeue;
-  color: rgba(255, 82, 82, 1);
-  font-size: 21px;
-  line-height: 42px;
-  font-weight: 600;
-  margin-top: 8px;
-}
-
-.highlight-black {
-  text-align: center;
-  margin-top: 1em;
-  font-family: BebasNeue;
-  color: rgba(32, 33, 36, 1);
-  font-size: 21px;
-  line-height: 42px;
-  font-weight: 600;
-  margin-top: 8px;
-
-}
-
-.operatetitle-card:hover {
-  border: 1px solid #4592ff;
-  background-color: #e7f1ff;
-}
-
-.icon-img {
-  height: 14px;
-  width: 14px;
-}
-
-.progress {
-  transform: rotate(180deg);
-  margin-top: 20px;
-}
-
-.progress ::v-deep .el-progress-bar__outer {
-  background-color: rgba(247, 152, 11, 0.2);
-}
-
-::v-deep .el-progress {
-
-  .el-progress-bar {
-    .el-progress-bar__inner {
-      background: linear-gradient(to right, #FF6300, #FF9F01)
-    }
-  }
 }
 </style>

+ 38 - 1
src/views/live/liveConsole/LiveConsole.vue

@@ -137,6 +137,7 @@
                     <a style="cursor: pointer;color: #ff0000;padding: 8px 8px 0 0;font-size: 12px;" @click="blockUser(m)">拉黑</a>
                     <a v-if="m.singleVisible === 1" style="cursor: pointer;color: #ff0000;padding: 8px 8px 0 0;font-size: 12px;" @click="singleVisible(m)">解除用户自见</a>
                     <a v-else style="cursor: pointer;color: #ff0000;padding: 8px 8px 0 0;font-size: 12px;" @click="singleVisible(m)">用户自见</a>
+                    <a style="cursor: pointer;color: #ff0000;padding: 8px 8px 0 0;font-size: 12px;" @click="deleteMsg(m)">删除</a>
                   </el-col>
                 </el-row>
               </el-col>
@@ -249,7 +250,7 @@
 <script>
 import LivePlayer from './LivePlayer.vue';
 import {blockUser, changeUserStatus, getLiveUserTotals, dashBoardWatchUserList} from '@/api/live/liveWatchUser'
-import { listLiveSingleMsg } from '@/api/live/liveMsg'
+import { listLiveSingleMsg,delLiveMsg } from '@/api/live/liveMsg'
 import { getLive } from '@/api/live/live'
 import { consoleList } from '@/api/live/task'
 import ScreenScale from './ScreenScale.vue'; // 路径根据实际位置调整
@@ -445,6 +446,42 @@ export default {
       }
       this.socket.send(JSON.stringify(msg))
     },
+    deleteMsg(m){
+      // 1. 弹出确认对话框
+      this.$confirm('此操作将永久删除该消息, 是否继续?', '提示', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning'
+      }).then(() => {
+        delLiveMsg(m.msgId).then(res => {
+          if (res.code === 200) {
+            const index = this.msgList.findIndex(item => item.msgId === m.msgId);
+            if (index !== -1) {
+              this.msgList.splice(index, 1);
+              console.log(`消息 ${m.msgId} 已在本地删除。`);
+            }
+            let msg = {
+              liveId: this.liveId,
+              userId: m.userId,
+              msg: m.msgId, // 关键:将消息ID发送给后台
+              cmd: 'deleteMsg',
+            };
+            this.socket.send(JSON.stringify(msg));
+            // 可以在这里给用户一个删除成功的提示
+            this.$message({
+              type: 'success',
+              message: '消息删除成功!'
+            });
+          }
+        })
+      }).catch(() => {
+        // 3. 用户点击“取消”或关闭对话框后的回调
+        this.$message({
+          type: 'info',
+          message: '已取消删除'
+        });
+      });
+    },
     globalVisibleChange( val){
       // 消息自可见
       let msg = {

+ 13 - 2
src/views/live/liveOrder/index.vue

@@ -45,6 +45,16 @@
         />
       </el-form-item>
 
+      <el-form-item label="收货人姓名" prop="userName">
+        <el-input
+          v-model="queryParams.userName"
+          placeholder="请输入收货人姓名"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+
       <el-form-item label="商品规格" prop="productSpec">
         <el-input
           v-model="queryParams.productSpec"
@@ -183,7 +193,7 @@
           <span v-else>-</span>
         </template>
       </el-table-column>
-      <el-table-column label="绑定销售昵称" align="center" prop="companyUserName" />
+      <el-table-column label="绑定销售昵称" align="center" prop="companyUserNickName" />
       <el-table-column label="客户编码" align="center" prop="userId" />
       <el-table-column label="会员等级" align="center" prop="userLevel">
       </el-table-column>
@@ -196,7 +206,8 @@
       <el-table-column label="客户ID" align="center" prop="userId" />
       <el-table-column label="客户昵称" align="center" prop="nickName" />
       <el-table-column label="客户绑定手机号" align="center" prop="userBindPhone" />
-      <el-table-column label="收货手机号" align="center" prop="userPhone" />
+      <el-table-column label="收货人电话" align="center" prop="userPhone" />
+      <el-table-column label="收货人姓名" align="center" prop="userName" />
       <el-table-column label="累计成交笔数" align="center" prop="totalOrderCount" />
       <el-table-column label="累计成交总额" align="center" prop="totalOrderAmount" />
       <el-table-column label="最新绑定时间" align="center" prop="latestBindTime" width="180">

+ 1388 - 0
src/views/qw/applyIpad/index.vue

@@ -0,0 +1,1388 @@
+<template>
+  <div class="app-container">
+    <!-- 数据统计卡片 -->
+    <div class="statistics-cards">
+      <el-row :gutter="20">
+        <el-col :span="6">
+          <el-card class="stat-card" shadow="hover">
+            <div class="stat-content">
+              <div class="stat-icon pending">
+                <i class="el-icon-time"></i>
+              </div>
+              <div class="stat-info">
+                <div class="stat-value">{{ statistics.pending }}</div>
+                <div class="stat-label">待审核</div>
+              </div>
+            </div>
+          </el-card>
+        </el-col>
+        <el-col :span="6">
+          <el-card class="stat-card" shadow="hover">
+            <div class="stat-content">
+              <div class="stat-icon approved">
+                <i class="el-icon-check"></i>
+              </div>
+              <div class="stat-info">
+                <div class="stat-value">{{ statistics.approved }}</div>
+                <div class="stat-label">已通过</div>
+              </div>
+            </div>
+          </el-card>
+        </el-col>
+        <el-col :span="6">
+          <el-card class="stat-card" shadow="hover">
+            <div class="stat-content">
+              <div class="stat-icon rejected">
+                <i class="el-icon-close"></i>
+              </div>
+              <div class="stat-info">
+                <div class="stat-value">{{ statistics.rejected }}</div>
+                <div class="stat-label">已拒绝</div>
+              </div>
+            </div>
+          </el-card>
+        </el-col>
+        <el-col :span="6">
+          <el-card class="stat-card" shadow="hover">
+            <div class="stat-content">
+              <div class="stat-icon total">
+                <i class="el-icon-data-line"></i>
+              </div>
+              <div class="stat-info">
+                <div class="stat-value">{{ statistics.total }}</div>
+                <div class="stat-label">总申请数</div>
+              </div>
+            </div>
+          </el-card>
+        </el-col>
+      </el-row>
+    </div>
+
+    <!-- 搜索表单 -->
+    <el-card class="search-card" shadow="hover">
+      <div class="search-header">
+        <div class="search-title">
+          <i class="el-icon-search"></i>
+          <span>筛选条件</span>
+        </div>
+        <el-button type="text" @click="toggleSearch">
+          {{ showSearch ? '收起' : '展开' }}
+          <i :class="showSearch ? 'el-icon-arrow-up' : 'el-icon-arrow-down'"></i>
+        </el-button>
+      </div>
+
+      <el-collapse-transition>
+        <el-form v-show="showSearch" :model="queryParams" ref="queryForm" :inline="true" label-width="80px"
+          class="search-form">
+          <el-row :gutter="20">
+            <el-col :span="6">
+              <el-form-item label="公司名称" prop="companyName">
+                <el-input v-model="queryParams.companyName" placeholder="请输入公司名称" clearable
+                  prefix-icon="el-icon-office-building" class="search-input" />
+              </el-form-item>
+            </el-col>
+            <el-col :span="6">
+              <el-form-item label="提交单号" prop="applicationNumber">
+                <el-input v-model="queryParams.applicationNumber" placeholder="请输入提交单号" clearable
+                  prefix-icon="el-icon-document" class="search-input" />
+              </el-form-item>
+            </el-col>
+            <el-col :span="6">
+              <el-form-item label="申请数量" prop="applyQuantity">
+                <el-input v-model="queryParams.applyQuantity" placeholder="请输入申请数量" clearable
+                  prefix-icon="el-icon-s-data" class="search-input" />
+              </el-form-item>
+            </el-col>
+            <el-col :span="6">
+              <el-form-item label="审核状态" prop="auditStatus">
+                <el-select v-model="queryParams.auditStatus" placeholder="请选择审核状态" clearable class="search-select">
+                  <el-option label="待审核" value="0" />
+                  <el-option label="审核通过" value="1" />
+                  <el-option label="审核拒绝" value="2" />
+                  <el-option label="部分分配" value="3" />
+                </el-select>
+              </el-form-item>
+            </el-col>
+          </el-row>
+
+          <el-row :gutter="20">
+            <el-col :span="6">
+              <el-form-item label="提交时间" prop="submitTime">
+                <el-date-picker clearable v-model="queryParams.submitTime" type="datetime"
+                  value-format="yyyy-MM-dd HH:mm:ss" placeholder="选择提交时间" class="search-date">
+                </el-date-picker>
+              </el-form-item>
+            </el-col>
+            <el-col :span="6">
+              <el-form-item label="审核时间" prop="reviewTime">
+                <el-date-picker clearable v-model="queryParams.reviewTime" type="datetime"
+                  value-format="yyyy-MM-dd HH:mm:ss" placeholder="选择审核时间" class="search-date">
+                </el-date-picker>
+              </el-form-item>
+            </el-col>
+            <el-col :span="6">
+              <el-form-item label="实际分配" prop="allocatedQuantity">
+                <el-input v-model="queryParams.allocatedQuantity" placeholder="请输入实际分配数量" clearable
+                  prefix-icon="el-icon-s-marketing" class="search-input" />
+              </el-form-item>
+            </el-col>
+            <el-col :span="6">
+              <el-form-item>
+                <el-button type="primary" icon="el-icon-search" @click="handleQuery" class="search-btn">
+                  搜索
+                </el-button>
+                <el-button icon="el-icon-refresh" @click="resetQuery" class="reset-btn">
+                  重置
+                </el-button>
+              </el-form-item>
+            </el-col>
+          </el-row>
+        </el-form>
+      </el-collapse-transition>
+    </el-card>
+
+    <!-- 批量操作工具栏 -->
+    <div class="batch-toolbar" v-show="selectedRows.length > 0">
+      <el-card class="batch-card" shadow="hover">
+        <div class="batch-content">
+          <div class="batch-info">
+            <i class="el-icon-warning-outline"></i>
+            <span>已选择 {{ selectedRows.length }} 项</span>
+          </div>
+          <div class="batch-actions">
+            <el-button size="small" @click="batchApprove" v-if="canBatchApprove" type="success">
+              <i class="el-icon-check"></i> 批量通过
+            </el-button>
+            <el-button size="small" @click="batchReject" v-if="canBatchReject" type="danger">
+              <i class="el-icon-close"></i> 批量拒绝
+            </el-button>
+            <el-button size="small" @click="handleBatchSync" type="warning">
+              <i class="el-icon-refresh"></i> 批量同步
+            </el-button>
+            <el-button size="small" @click="clearSelection">
+              <i class="el-icon-close"></i> 清空选择
+            </el-button>
+          </div>
+        </div>
+      </el-card>
+    </div>
+
+    <!-- 操作按钮区域 -->
+    <el-card class="action-card" shadow="never">
+      <div class="action-bar">
+        <div class="action-group">
+          <el-button type="primary" icon="el-icon-plus" @click="handleAdd" class="action-btn"
+            >
+            申请iPad
+          </el-button>
+
+          <el-button type="success" icon="el-icon-edit" @click="handleUpdate" :disabled="single" class="action-btn"
+            >
+            修改
+          </el-button>
+
+          <el-dropdown @command="handleBatchCommand" class="action-dropdown">
+            <el-button type="warning" class="action-btn">
+              批量操作<i class="el-icon-arrow-down el-icon--right"></i>
+            </el-button>
+            <el-dropdown-menu slot="dropdown">
+              <el-dropdown-item command="sync">
+                批量同步
+              </el-dropdown-item>
+              <el-dropdown-item command="export">
+                导出数据
+              </el-dropdown-item>
+            </el-dropdown-menu>
+          </el-dropdown>
+        </div>
+
+        <div class="action-right">
+          <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
+        </div>
+      </div>
+    </el-card>
+
+    <!-- 数据表格 -->
+    <el-card class="table-card" shadow="hover">
+      <el-table border v-loading="loading" :data="recordsList" @selection-change="handleSelectionChange"
+        class="modern-table" :header-cell-class-name="headerCellClass" :cell-class-name="cellClass"
+        @row-click="handleRowClick">
+
+        <el-table-column type="selection" width="55" align="center" />
+        <el-table-column label="公司名称" align="center" prop="companyName" min-width="150">
+          <template slot-scope="scope">
+            <div class="company-name">
+              <i class="el-icon-office-building"></i>
+              {{ scope.row.companyName }}
+            </div>
+          </template>
+        </el-table-column>
+        <el-table-column label="申请数量" align="center" prop="applyQuantity" width="100">
+          <template slot-scope="scope">
+            <el-tag type="info" effect="plain">{{ scope.row.applyQuantity }}</el-tag>
+          </template>
+        </el-table-column>
+        <el-table-column label="实际分配数量" align="center" prop="allocatedQuantity" width="120">
+          <template slot-scope="scope">
+            <el-tag type="success" effect="plain">{{ scope.row.allocatedQuantity || 0 }}</el-tag>
+          </template>
+        </el-table-column>
+        <el-table-column label="提交时间" align="center" prop="submitTime" width="180">
+          <template slot-scope="scope">
+            <div class="time-info">
+              <i class="el-icon-time"></i>
+              <span>{{ parseTime(scope.row.submitTime) }}</span>
+            </div>
+          </template>
+        </el-table-column>
+        <el-table-column label="审核时间" align="center" prop="reviewTime" width="180">
+          <template slot-scope="scope">
+            <div class="time-info" v-if="scope.row.reviewTime">
+              <i class="el-icon-check"></i>
+              <span>{{ parseTime(scope.row.reviewTime) }}</span>
+            </div>
+            <span v-else class="no-data">-</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="审核状态" align="center" prop="auditStatus" width="120">
+          <template slot-scope="scope">
+            <div class="status-wrapper">
+              <el-tag :class="getStatusClass(scope.row.auditStatus)"
+                :effect="scope.row.auditStatus === 1 ? 'dark' : 'light'">
+                <i :class="getStatusIcon(scope.row.auditStatus)"></i>
+                {{ getStatusText(scope.row.auditStatus) }}
+              </el-tag>
+            </div>
+          </template>
+        </el-table-column>
+        <el-table-column label="拒绝原因" align="center" prop="rejectionReason" min-width="150">
+          <template slot-scope="scope">
+            <el-tooltip :content="scope.row.rejectionReason" placement="top" v-if="scope.row.rejectionReason">
+              <span class="rejection-reason">{{ scope.row.rejectionReason }}</span>
+            </el-tooltip>
+            <span v-else class="no-data">-</span>
+          </template>
+        </el-table-column>
+
+        <el-table-column label="操作" align="center" width="180" fixed="right">
+          <template slot-scope="scope">
+            <div class="action-buttons">
+              <el-tooltip content="同步数据" placement="top">
+                <el-button type="text" icon="el-icon-refresh" class="action-icon-btn sync-btn"
+                  @click="handleSingleSync(scope.row)" />
+              </el-tooltip>
+
+              <el-tooltip content="释放管理" placement="top"
+                v-if="scope.row.auditStatus == 1 || scope.row.auditStatus == 3">
+                <el-button type="text" icon="el-icon-unlock" class="action-icon-btn release-btn"
+                  @click="handleRelease(scope.row)" />
+              </el-tooltip>
+            </div>
+          </template>
+        </el-table-column>
+      </el-table>
+
+      <!-- 分页 -->
+      <pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize"
+        @pagination="getList" class="pagination-wrapper" />
+    </el-card>
+
+    <!-- 申请iPad对话框 -->
+    <el-dialog :title="title" :visible.sync="open" width="600px" :close-on-click-modal="false"
+      custom-class="modern-dialog" append-to-body>
+      <div class="dialog-content">
+        <div class="form-header">
+          <i class="el-icon-mobile-phone"></i>
+          <span>请填写申请信息</span>
+        </div>
+
+        <el-form ref="form" :model="form" :rules="rules" label-width="100px" class="modern-form">
+          <el-form-item label="申请地区" prop="area">
+            <el-input v-model="form.area" placeholder="请输入申请地区,如:广州、上海、重庆、北京等"
+              prefix-icon="el-icon-location" class="area-input" />
+            <div class="form-tip">
+              <i class="el-icon-info"></i>
+              请填写申请iPad使用的地区
+            </div>
+          </el-form-item>
+          <el-form-item label="申请数量" prop="applyQuantity">
+            <el-input-number v-model="form.applyQuantity" :min="1" :max="100" controls-position="right"
+              class="number-input" placeholder="请输入申请数量" />
+            <div class="form-tip">
+              <i class="el-icon-info"></i>
+              建议根据实际需求申请合理数量
+            </div>
+          </el-form-item>
+        </el-form>
+      </div>
+
+      <div slot="footer" class="dialog-footer">
+        <el-button @click="cancel" class="cancel-btn">取 消</el-button>
+        <el-button type="primary" @click="submitForm" :loading="submitting" class="confirm-btn">
+          {{ submitting ? '提交中...' : '确定申请' }}
+        </el-button>
+      </div>
+    </el-dialog>
+
+    <!-- 释放信息抽屉 -->
+    <el-drawer title="服务器分配信息" :visible.sync="releaseOpen" direction="rtl" size="70%" :before-close="handleDrawerClose"
+      custom-class="modern-drawer">
+      <div class="drawer-content">
+        <el-table :data="serverInfoList" border v-loading="releaseLoading" class="server-table">
+          <el-table-column label="访问地址" align="center" prop="accessUrl" min-width="200">
+            <template slot-scope="scope">
+              <a :href="scope.row.accessUrl" target="_blank" class="access-url">
+                <i class="el-icon-link"></i>
+                {{ scope.row.accessUrl }}
+              </a>
+            </template>
+          </el-table-column>
+          <el-table-column label="已分配座位数" align="center" prop="allocatedSeats" width="120">
+            <template slot-scope="scope">
+              <el-tag type="primary">{{ scope.row.allocatedSeats }}</el-tag>
+            </template>
+          </el-table-column>
+          <el-table-column label="分配时间" align="center" prop="allocatedTime" width="180">
+            <template slot-scope="scope">
+              <span>{{ parseTime(scope.row.allocatedTime) }}</span>
+            </template>
+          </el-table-column>
+          <el-table-column label="分配状态" align="center" prop="allocationStatus" width="100">
+            <template slot-scope="scope">
+              <el-tag :type="scope.row.allocationStatus === 1 ? 'success' : 'info'">
+                {{ scope.row.allocationStatus === 1 ? '有效' : '已释放' }}
+              </el-tag>
+            </template>
+          </el-table-column>
+          <el-table-column label="IP地址" align="center" prop="ipAddress" width="120" />
+          <el-table-column label="端口" align="center" prop="port" width="80" />
+          <el-table-column label="地区" align="center" prop="region" width="100" />
+          <el-table-column label="创建时间" align="center" prop="createdTime" width="180">
+            <template slot-scope="scope">
+              <span>{{ parseTime(scope.row.createdTime) }}</span>
+            </template>
+          </el-table-column>
+          <el-table-column label="操作" align="center" width="120">
+            <template slot-scope="scope">
+              <el-button size="mini" type="primary" @click="handleReleaseSeats(scope.row)"
+                :disabled="scope.row.allocationStatus !== 1" class="release-seats-btn">
+                释放座位
+              </el-button>
+            </template>
+          </el-table-column>
+        </el-table>
+      </div>
+    </el-drawer>
+
+    <!-- 释放座位数量输入弹窗 -->
+    <el-dialog title="释放座位" :visible.sync="releaseSeatsOpen" width="400px" custom-class="release-dialog" append-to-body>
+      <el-form :model="releaseForm" :rules="releaseRules" ref="releaseForm" label-width="100px">
+        <el-form-item label="释放数量" prop="releaseCount">
+          <el-input-number v-model="releaseForm.releaseCount" :min="1" :max="releaseForm.maxSeats" placeholder="请输入释放数量"
+            controls-position="right" style="width: 100%" />
+          <div class="form-tip">
+            <i class="el-icon-info"></i>
+            最大可释放数量:{{ releaseForm.maxSeats }}
+          </div>
+        </el-form-item>
+      </el-form>
+      <div slot="footer" class="dialog-footer">
+        <el-button @click="releaseSeatsOpen = false">取 消</el-button>
+        <el-button type="primary" @click="submitReleaseSeats()">确 定</el-button>
+      </div>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import { listRecords, getServerInfo, getRecords, delRecords, release, updateRecords, exportRecords, apply, batchUpdate } from "@/api/qw/applyIpad";
+
+export default {
+  name: "Records",
+  data() {
+    return {
+      // 遮罩层
+      loading: true,
+      // 导出遮罩层
+      exportLoading: false,
+      // 提交状态
+      submitting: false,
+      // 选中数组
+      ids: [],
+      // 选中的行数据
+      selectedRows: [],
+      // 非单个禁用
+      single: true,
+      // 非多个禁用
+      multiple: true,
+      // 显示搜索条件
+      showSearch: true,
+      // 总条数
+      total: 0,
+      // 分配记录表格数据
+      recordsList: [],
+      // 弹出层标题
+      title: "",
+      // 是否显示弹出层
+      open: false,
+      // 查询参数
+      queryParams: {
+        pageNum: 1,
+        pageSize: 10,
+        companyId: null,
+        companyName: null,
+        applicationNumber: null,
+        applyQuantity: null,
+        allocatedQuantity: null,
+        submitTime: null,
+        reviewTime: null,
+        auditStatus: null,
+        rejectionReason: null,
+        createdTime: null,
+        updatedTime: null
+      },
+      // 表单参数
+      form: {},
+      // 表单校验
+      rules: {
+        area: [
+          { required: true, message: "申请地区不能为空", trigger: "blur" }
+        ],
+        applyQuantity: [
+          { required: true, message: "申请数量不能为空", trigger: "blur" },
+          { pattern: /^[1-9]\d*$/, message: "申请数量必须为正整数", trigger: "blur" }
+        ]
+      },
+      // 释放抽屉显示状态
+      releaseOpen: false,
+      // 释放信息加载状态
+      releaseLoading: false,
+      // 服务器信息列表
+      serverInfoList: [],
+      // 释放座位弹窗显示状态
+      releaseSeatsOpen: false,
+      // 释放座位表单
+      releaseForm: {
+        releaseCount: null,
+        maxSeats: 0,
+        currentServer: null
+      },
+      // 释放座位表单校验规则
+      releaseRules: {
+        releaseCount: [
+          { required: true, message: "释放数量不能为空", trigger: "blur" },
+          { type: 'number', min: 1, message: "释放数量必须大于0", trigger: "blur" },
+          {
+            validator: (rule, value, callback) => {
+              if (value > this.releaseForm.maxSeats) {
+                callback(new Error(`释放数量不能超过${this.releaseForm.maxSeats}`));
+              } else {
+                callback();
+              }
+            },
+            trigger: "blur"
+          }
+        ]
+      },
+      // 当前选中的申请记录ID
+      currentRecordId: null,
+      // 统计数据
+      statistics: {
+        pending: 0,
+        approved: 0,
+        rejected: 0,
+        total: 0
+      }
+    };
+  },
+  computed: {
+    // 是否可以批量审核通过
+    canBatchApprove() {
+      return this.selectedRows.some(row => row.auditStatus === 0);
+    },
+    // 是否可以批量审核拒绝
+    canBatchReject() {
+      return this.selectedRows.some(row => row.auditStatus === 0);
+    }
+  },
+  created() {
+    this.getList();
+    this.calculateStatistics();
+    // 添加键盘事件监听
+    document.addEventListener('keydown', this.handleKeydown);
+  },
+  beforeDestroy() {
+    document.removeEventListener('keydown', this.handleKeydown);
+  },
+  methods: {
+    /** 查询分配记录列表 */
+    getList() {
+      this.loading = true;
+      listRecords(this.queryParams).then(response => {
+        this.recordsList = response.rows;
+        this.total = response.total;
+        this.loading = false;
+        this.calculateStatistics();
+      });
+    },
+
+    /** 计算统计数据 */
+    calculateStatistics() {
+      this.statistics = {
+        pending: this.recordsList.filter(item => item.auditStatus === 0).length,
+        approved: this.recordsList.filter(item => item.auditStatus === 1).length,
+        rejected: this.recordsList.filter(item => item.auditStatus === 2).length,
+        total: this.recordsList.length
+      };
+    },
+
+    /** 键盘事件处理 */
+    handleKeydown(e) {
+      // Ctrl + N: 新建申请
+      if (e.ctrlKey && e.key === 'n') {
+        e.preventDefault();
+        this.handleAdd();
+      }
+      // Ctrl + E: 导出数据
+      if (e.ctrlKey && e.key === 'e') {
+        e.preventDefault();
+        this.handleExport();
+      }
+      // F5: 刷新数据
+      if (e.key === 'F5') {
+        e.preventDefault();
+        this.getList();
+      }
+    },
+
+    /** 切换搜索显示 */
+    toggleSearch() {
+      this.showSearch = !this.showSearch;
+    },
+
+    /** 表格头部样式 */
+    headerCellClass({ row, column, rowIndex, columnIndex }) {
+      return 'modern-header';
+    },
+
+    /** 表格单元格样式 */
+    cellClass({ row, column, rowIndex, columnIndex }) {
+      return 'modern-cell';
+    },
+
+    /** 行点击事件 */
+    handleRowClick(row, column, event) {
+      // 可以添加行点击后的处理逻辑
+    },
+
+    /** 获取状态样式类 */
+    getStatusClass(status) {
+      const statusMap = {
+        0: 'status-pending',
+        1: 'status-approved',
+        2: 'status-rejected',
+        3: 'status-partial'
+      };
+      return statusMap[status] || '';
+    },
+
+    /** 获取状态图标 */
+    getStatusIcon(status) {
+      const iconMap = {
+        0: 'el-icon-time',
+        1: 'el-icon-check',
+        2: 'el-icon-close',
+        3: 'el-icon-warning'
+      };
+      return iconMap[status] || 'el-icon-question';
+    },
+
+    /** 获取状态文本 */
+    getStatusText(status) {
+      const textMap = {
+        0: '待审核',
+        1: '审核通过',
+        2: '审核拒绝',
+        3: '部分分配'
+      };
+      return textMap[status] || '-';
+    },
+
+    /** 批量命令处理 */
+    handleBatchCommand(command) {
+      if (command === 'sync') {
+        this.handleBatchSync();
+      } else if (command === 'export') {
+        this.handleExport();
+      }
+    },
+
+    /** 批量审核通过 */
+    batchApprove() {
+      const pendingRows = this.selectedRows.filter(row => row.auditStatus === 0);
+      if (pendingRows.length === 0) {
+        this.msgWarning('没有可审核通过的记录');
+        return;
+      }
+
+      this.$confirm(`确认审核通过选中的 ${pendingRows.length} 条记录?`, '批量审核', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning'
+      }).then(() => {
+        // 这里调用批量审核通过的API
+        this.msgSuccess('批量审核通过成功');
+        this.getList();
+      });
+    },
+
+    /** 批量审核拒绝 */
+    batchReject() {
+      const pendingRows = this.selectedRows.filter(row => row.auditStatus === 0);
+      if (pendingRows.length === 0) {
+        this.msgWarning('没有可审核拒绝的记录');
+        return;
+      }
+
+      this.$confirm(`确认审核拒绝选中的 ${pendingRows.length} 条记录?`, '批量审核', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning'
+      }).then(() => {
+        // 这里调用批量审核拒绝的API
+        this.msgSuccess('批量审核拒绝成功');
+        this.getList();
+      });
+    },
+
+    /** 清空选择 */
+    clearSelection() {
+      this.$refs.multipleTable && this.$refs.multipleTable.clearSelection();
+      this.selectedRows = [];
+      this.ids = [];
+    },
+
+    // 取消按钮
+    cancel() {
+      this.open = false;
+      this.reset();
+    },
+    // 表单重置
+    reset() {
+      this.form = {
+        area: null,
+        applyQuantity: null
+      };
+      this.resetForm("form");
+    },
+    /** 搜索按钮操作 */
+    handleQuery() {
+      this.queryParams.pageNum = 1;
+      this.getList();
+    },
+    /** 重置按钮操作 */
+    resetQuery() {
+      this.resetForm("queryForm");
+      this.handleQuery();
+    },
+    // 多选框选中数据
+    handleSelectionChange(selection) {
+      this.ids = selection.map(item => item.id)
+      this.selectedRows = selection;
+      this.single = selection.length !== 1
+      this.multiple = !selection.length
+    },
+    /** 新增按钮操作 */
+    handleAdd() {
+      this.reset();
+      this.open = true;
+      this.title = "申请iPad";
+    },
+    /** 修改按钮操作 */
+    handleUpdate(row) {
+      this.reset();
+      const id = row.id || this.ids
+      getRecords(id).then(response => {
+        this.form = response.data;
+        this.open = true;
+        this.title = "修改分配记录";
+      });
+    },
+    /** 提交按钮 */
+    submitForm() {
+      this.$refs["form"].validate(valid => {
+        if (valid) {
+          this.submitting = true;
+          // 调用apply接口,传递申请数量和地区作为参数对象
+          apply({
+            applyCount: this.form.applyQuantity,
+            area: this.form.area
+          }).then(response => {
+            this.msgSuccess("申请成功");
+            this.open = false;
+            this.getList();
+          }).catch(() => {
+            // this.msgError("申请失败");
+          }).finally(() => {
+            this.submitting = false;
+          });
+        }
+      });
+    },
+    /** 删除按钮操作 */
+    handleDelete(row) {
+      const ids = row.id || this.ids;
+      this.$confirm('是否确认删除分配记录编号为"' + ids + '"的数据项?', "警告", {
+        confirmButtonText: "确定",
+        cancelButtonText: "取消",
+        type: "warning"
+      }).then(function () {
+        return delRecords(ids);
+      }).then(() => {
+        this.getList();
+        this.msgSuccess("删除成功");
+      }).catch(() => { });
+    },
+    /** 导出按钮操作 */
+    handleExport() {
+      const queryParams = this.queryParams;
+      this.$confirm('是否确认导出所有分配记录数据项?', "警告", {
+        confirmButtonText: "确定",
+        cancelButtonText: "取消",
+        type: "warning"
+      }).then(() => {
+        this.exportLoading = true;
+        return exportRecords(queryParams);
+      }).then(response => {
+        this.download(response.msg);
+        this.exportLoading = false;
+      }).catch(() => { });
+    },
+    /** 单个同步按钮操作 */
+    handleSingleSync(row) {
+      const id = row.id;
+      this.$confirm('是否确认同步编号为"' + id + '"的数据项?', "警告", {
+        confirmButtonText: "确定",
+        cancelButtonText: "取消",
+        type: "warning"
+      }).then(() => {
+        return batchUpdate([id]);
+      }).then(() => {
+        this.getList();
+        this.msgSuccess("同步成功");
+      }).catch(() => { });
+    },
+    /** 批量同步按钮操作 */
+    handleBatchSync() {
+      const ids = this.ids;
+      this.$confirm('是否确认同步选中的' + ids.length + '条数据项?', "警告", {
+        confirmButtonText: "确定",
+        cancelButtonText: "取消",
+        type: "warning"
+      }).then(() => {
+        return batchUpdate(ids);
+      }).then(() => {
+        this.getList();
+        this.msgSuccess("批量同步成功");
+      }).catch(() => { });
+    },
+    /** 释放按钮操作 */
+    handleRelease(row) {
+      this.releaseLoading = true;
+      this.releaseOpen = true;
+      this.serverInfoList = [];
+      this.currentRecordId = row.id;
+
+      // 调用getServerInfo接口获取服务器信息
+      getServerInfo(row.id).then(response => {
+        this.serverInfoList = response.data || [];
+        this.releaseLoading = false;
+      }).catch(error => {
+        this.releaseLoading = false;
+        this.msgError("获取服务器信息失败");
+      });
+    },
+
+    /** 抽屉关闭处理 */
+    handleDrawerClose(done) {
+      this.releaseOpen = false;
+      done();
+    },
+
+    /** 释放座位按钮操作 */
+    handleReleaseSeats(server) {
+      this.releaseForm = {
+        releaseCount: null,
+        maxSeats: server.allocatedSeats || 0,
+        currentServer: server
+      };
+
+      this.releaseSeatsOpen = true;
+      this.$nextTick(() => {
+        this.$refs.releaseForm && this.$refs.releaseForm.clearValidate();
+      });
+    },
+
+    /** 提交释放座位 */
+    submitReleaseSeats() {
+      this.$refs.releaseForm.validate(valid => {
+        if (valid) {
+          // 根据后端ServerParam类结构组装参数
+          const params = {
+            ipadServerId: this.releaseForm.currentServer.ipadRecordId, // 服务器ID
+            recordId: this.currentRecordId,                 // 记录ID
+            releaseCount: this.releaseForm.releaseCount,    // 释放数量
+            ipAddress: this.releaseForm.currentServer.ipAddress, // iPad地址
+            port: this.releaseForm.currentServer.port       // 端口号
+          };
+
+          // 调用release接口
+          release(params).then(response => {
+            this.msgSuccess("释放成功");
+            this.releaseSeatsOpen = false;
+            // 重新加载服务器信息
+            this.handleRelease({ id: this.currentRecordId });
+          }).catch(error => {
+            this.msgError("释放失败");
+          });
+        }
+      });
+    },
+
+    /** 重置释放座位表单 */
+    resetReleaseForm() {
+      this.releaseForm = {
+        releaseCount: null,
+        maxSeats: 0,
+        currentServer: null
+      };
+      this.$refs.releaseForm && this.$refs.releaseForm.clearValidate();
+    }
+  }
+};
+</script>
+
+<style lang="scss" scoped>
+.app-container {
+  padding: 20px;
+  background: #f0f2f5;
+  min-height: calc(100vh - 84px);
+}
+
+// 统计卡片样式
+.statistics-cards {
+  margin-bottom: 20px;
+
+  .stat-card {
+    border-radius: 12px;
+    border: none;
+    transition: all 0.3s ease;
+
+    &:hover {
+      transform: translateY(-5px);
+      box-shadow: 0 8px 25px rgba(0, 0, 0, 0.15);
+    }
+
+    .stat-content {
+      display: flex;
+      align-items: center;
+      padding: 10px;
+
+      .stat-icon {
+        width: 60px;
+        height: 60px;
+        border-radius: 12px;
+        display: flex;
+        align-items: center;
+        justify-content: center;
+        margin-right: 15px;
+        font-size: 24px;
+        color: white;
+
+        &.pending {
+          background: #409eff;
+        }
+
+        &.approved {
+          background: #67c23a;
+        }
+
+        &.rejected {
+          background: #f56c6c;
+        }
+
+        &.total {
+          background: #909399;
+        }
+      }
+
+      .stat-info {
+        flex: 1;
+
+        .stat-value {
+          font-size: 28px;
+          font-weight: bold;
+          color: #2c3e50;
+          line-height: 1;
+        }
+
+        .stat-label {
+          font-size: 14px;
+          color: #7f8c8d;
+          margin-top: 5px;
+        }
+      }
+    }
+  }
+}
+
+// 搜索卡片样式
+.search-card {
+  border-radius: 12px;
+  margin-bottom: 20px;
+  border: none;
+  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
+  transition: all 0.3s ease;
+
+  &:hover {
+    box-shadow: 0 4px 20px rgba(0, 0, 0, 0.12);
+  }
+
+  .search-header {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    padding-bottom: 15px;
+    border-bottom: 1px solid #ebeef5;
+    margin-bottom: 20px;
+
+    .search-title {
+      display: flex;
+      align-items: center;
+      font-size: 16px;
+      font-weight: 600;
+      color: #2c3e50;
+
+      i {
+        margin-right: 8px;
+        color: #409eff;
+      }
+    }
+  }
+
+  .search-form {
+
+    .search-input,
+    .search-select,
+    .search-date {
+      width: 100%;
+    }
+
+    .search-btn,
+    .reset-btn {
+      border-radius: 8px;
+      font-weight: 500;
+      transition: all 0.3s ease;
+    }
+
+    .search-btn:hover {
+      background-color: #66b1ff;
+    }
+
+    .reset-btn:hover {
+      color: #409eff;
+      border-color: #c6e2ff;
+      background-color: #ecf5ff;
+    }
+  }
+}
+
+// 批量操作工具栏
+.batch-toolbar {
+  margin-bottom: 20px;
+
+  .batch-card {
+    border-radius: 12px;
+    border: none;
+    background: #fdf6ec;
+
+    .batch-content {
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+
+      .batch-info {
+        display: flex;
+        align-items: center;
+        color: #f39c12;
+        font-weight: 500;
+
+        i {
+          margin-right: 8px;
+          font-size: 18px;
+        }
+      }
+
+      .batch-actions {
+        display: flex;
+        gap: 10px;
+
+        .el-button {
+          border-radius: 6px;
+          font-weight: 500;
+        }
+      }
+    }
+  }
+}
+
+// 操作按钮区域
+.action-card {
+  border-radius: 12px;
+  margin-bottom: 20px;
+  border: none;
+  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
+
+  .action-bar {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+
+    .action-group {
+      display: flex;
+      gap: 15px;
+
+      .action-btn {
+        border-radius: 4px;
+        font-weight: 500;
+        transition: all 0.3s ease;
+
+        &:hover {
+          opacity: 0.8;
+        }
+      }
+    }
+  }
+}
+
+// 表格卡片样式
+.table-card {
+  border-radius: 12px;
+  border: none;
+  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
+  transition: all 0.3s ease;
+
+  &:hover {
+    box-shadow: 0 4px 20px rgba(0, 0, 0, 0.12);
+  }
+
+  .modern-table {
+    border-radius: 8px;
+    overflow: hidden;
+
+    ::v-deep .modern-header {
+      background: #f5f7fa;
+      color: #606266;
+      font-weight: 600;
+    }
+
+    ::v-deep .modern-cell {
+      .company-name {
+        display: flex;
+        align-items: center;
+
+        i {
+          margin-right: 5px;
+          color: #409eff;
+        }
+      }
+
+      .time-info {
+        display: flex;
+        align-items: center;
+        font-size: 12px;
+
+        i {
+          margin-right: 5px;
+          color: #909399;
+        }
+      }
+
+      .no-data {
+        color: #c0c4cc;
+      }
+
+      .rejection-reason {
+        max-width: 150px;
+        overflow: hidden;
+        text-overflow: ellipsis;
+        white-space: nowrap;
+        display: inline-block;
+      }
+    }
+
+    ::v-deep .el-table__row {
+      transition: all 0.3s ease;
+
+      &:hover {
+        background-color: #f5f7fa;
+      }
+    }
+
+    .action-buttons {
+      display: flex;
+      justify-content: center;
+      gap: 10px;
+
+      .action-icon-btn {
+        width: 32px;
+        height: 32px;
+        border-radius: 6px;
+        display: flex;
+        align-items: center;
+        justify-content: center;
+        transition: all 0.3s ease;
+
+        &:hover {
+          transform: scale(1.1);
+        }
+
+        &.sync-btn {
+          color: #409eff;
+
+          &:hover {
+            background: rgba(64, 158, 255, 0.1);
+          }
+        }
+
+        &.release-btn {
+          color: #e6a23c;
+
+          &:hover {
+            background: rgba(230, 162, 60, 0.1);
+          }
+        }
+      }
+    }
+  }
+
+  .pagination-wrapper {
+    margin-top: 20px;
+    text-align: right;
+  }
+}
+
+// 状态标签样式
+.status-wrapper {
+  .el-tag {
+    border-radius: 20px;
+    padding: 4px 12px;
+    font-weight: 500;
+    border: none;
+
+    i {
+      margin-right: 5px;
+    }
+
+    &.status-pending {
+      background-color: #e1f3d8;
+      color: #67c23a;
+      border-color: #e1f3d8;
+    }
+
+    &.status-approved {
+      background-color: #f0f9ff;
+      color: #409eff;
+      border-color: #f0f9ff;
+    }
+
+    &.status-rejected {
+      background-color: #fef0f0;
+      color: #f56c6c;
+      border-color: #fef0f0;
+    }
+
+    &.status-partial {
+      background-color: #fdf6ec;
+      color: #e6a23c;
+      border-color: #fdf6ec;
+    }
+  }
+}
+
+// 对话框样式
+::v-deep .modern-dialog {
+  border-radius: 12px;
+
+  .el-dialog__header {
+    background: #f5f7fa;
+    color: #606266;
+    border-radius: 12px 12px 0 0;
+    padding: 20px;
+
+    .el-dialog__title {
+      font-weight: 600;
+    }
+  }
+
+  .el-dialog__body {
+    padding: 30px 20px;
+  }
+
+  .dialog-content {
+    .form-header {
+      display: flex;
+      align-items: center;
+      margin-bottom: 20px;
+      padding-bottom: 15px;
+      border-bottom: 1px solid #ebeef5;
+
+      i {
+        margin-right: 10px;
+        color: #409eff;
+        font-size: 20px;
+      }
+
+      span {
+        font-size: 16px;
+        font-weight: 600;
+        color: #2c3e50;
+      }
+    }
+
+    .modern-form {
+      .area-input {
+        width: 100%;
+      }
+      
+      .number-input {
+        width: 100%;
+      }
+
+      .form-tip {
+        display: flex;
+        align-items: center;
+        color: #909399;
+        font-size: 12px;
+        margin-top: 5px;
+
+        i {
+          margin-right: 5px;
+        }
+      }
+    }
+  }
+
+  .dialog-footer {
+    text-align: right;
+    padding: 15px 20px;
+    border-top: 1px solid #ebeef5;
+
+    .cancel-btn,
+    .confirm-btn {
+      border-radius: 8px;
+      font-weight: 500;
+      transition: all 0.3s ease;
+    }
+
+    .confirm-btn:hover {
+      background-color: #66b1ff;
+    }
+  }
+}
+
+// 抽屉样式
+::v-deep .modern-drawer {
+  .el-drawer__header {
+    background: #f5f7fa;
+    color: #606266;
+    padding: 20px;
+    margin-bottom: 0;
+
+    span {
+      font-weight: 600;
+    }
+  }
+
+  .drawer-content {
+    padding: 20px;
+
+    .server-table {
+      border-radius: 8px;
+      overflow: hidden;
+
+      .access-url {
+        color: #409eff;
+        text-decoration: none;
+
+        &:hover {
+          text-decoration: underline;
+        }
+
+        i {
+          margin-right: 5px;
+        }
+      }
+
+      .release-seats-btn {
+        border-radius: 6px;
+        font-weight: 500;
+      }
+    }
+  }
+}
+
+// 释放对话框样式
+::v-deep .release-dialog {
+  border-radius: 12px;
+
+  .el-dialog__header {
+    background: #f5f7fa;
+    color: #606266;
+    border-radius: 12px 12px 0 0;
+
+    .el-dialog__title {
+      font-weight: 600;
+    }
+  }
+}
+
+// 响应式设计
+@media (max-width: 768px) {
+  .app-container {
+    padding: 10px;
+  }
+
+  .statistics-cards {
+    .el-col {
+      margin-bottom: 10px;
+    }
+  }
+
+  .search-form {
+    .el-col {
+      margin-bottom: 10px;
+    }
+  }
+
+  .action-bar {
+    flex-direction: column;
+    align-items: stretch;
+
+    .action-group {
+      margin-bottom: 10px;
+      justify-content: center;
+    }
+  }
+
+  .batch-content {
+    flex-direction: column;
+    align-items: flex-start;
+
+    .batch-actions {
+      margin-top: 10px;
+      flex-wrap: wrap;
+    }
+  }
+}
+</style>

+ 13 - 0
src/views/system/config/config.vue

@@ -730,6 +730,16 @@
       <el-tab-pane label="短信配置" name="his.sms">
 
         <el-form ref="form14" :model="form14" label-width="150px">
+          <el-form-item label="是否开启APP短信验证" label-width="160px">
+            <el-switch
+              v-model="form14.isSmsVerification"
+              active-color="#13ce66"
+              inactive-color="#ff4949"
+              active-value="1"
+              inactive-value="0"
+            >
+            </el-switch>
+          </el-form-item>
           <el-form-item label="短信服务商" prop="type">
             <el-radio-group v-model="form14.type">
               <el-radio label="rf">重庆润芳</el-radio>
@@ -2927,6 +2937,9 @@ export default {
       })
     },
     submitForm14() {
+      if (!this.form14.isSmsVerification) {
+        this.form14.isSmsVerification = 0;
+      }
       var param = { configId: this.configId, configValue: JSON.stringify(this.form14) }
       updateConfigByKey(param).then(response => {
         if (response.code === 200) {