|
|
@@ -1,1444 +1,659 @@
|
|
|
<template>
|
|
|
- <div class="app-container">
|
|
|
- <div class="title">近期直播</div>
|
|
|
- <div class="live-container">
|
|
|
- <div class="live-card" v-for="live in displayLives" :key="live.liveId">
|
|
|
- <!-- 直播状态 -->
|
|
|
- <div class="status" :class="getStatusClass(live.status)">
|
|
|
- {{ getStatusText(live.status) }}
|
|
|
- </div>
|
|
|
- <!-- 直播信息 -->
|
|
|
- <div class="content">
|
|
|
- <img :src="live.liveImgUrl" alt="直播封面" class="cover-image" />
|
|
|
- <div class="info">
|
|
|
- <h3 class="live-title">{{ live.liveName }}</h3>
|
|
|
- <p class="time">开播时间: {{ live.startTime }}</p>
|
|
|
+ <div class="el-container-md">
|
|
|
+ <!-- 数据统计指标展示区域 -->
|
|
|
+ <el-card class="statistics-card" shadow="never">
|
|
|
+ <el-row :gutter="20">
|
|
|
+ <el-col :span="4">
|
|
|
+ <div class="statistics-item">
|
|
|
+ <div class="statistics-title">累计观看人数</div>
|
|
|
+ <div class="statistics-value">{{ statisticsData.totalViewers || 0 }}</div>
|
|
|
</div>
|
|
|
- </div>
|
|
|
-
|
|
|
- <!-- 直播数据 -->
|
|
|
- <div class="stats">
|
|
|
- <div class="stat-item">
|
|
|
- <p class="label">直播间浏览量</p>
|
|
|
- <p class="value">{{ live.pageViews }}</p>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="4">
|
|
|
+ <div class="statistics-item">
|
|
|
+ <div class="statistics-title">
|
|
|
+ 累计完课人数
|
|
|
+ <el-popover placement="top" width="200" trigger="click">
|
|
|
+ <div>
|
|
|
+ <el-input-number v-model="completeWatchTime" :min="0" :precision="0" placeholder="看课时长(分钟)" style="width: 100%;"></el-input-number>
|
|
|
+ <el-button type="primary" size="mini" style="width: 100%; margin-top: 10px;" @click="updateCompleteWatchTime('total')">确定</el-button>
|
|
|
+ </div>
|
|
|
+ <el-button slot="reference" size="mini" type="text" icon="el-icon-setting" style="margin-left: 5px;">设置</el-button>
|
|
|
+ </el-popover>
|
|
|
+ </div>
|
|
|
+ <div class="statistics-value">{{ statisticsData.totalCompletedCourses || 0 }}</div>
|
|
|
</div>
|
|
|
- <div class="stat-item">
|
|
|
- <p class="label">直播间访客数</p>
|
|
|
- <p class="value">{{ live.uniqueVisitors }}</p>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="4">
|
|
|
+ <div class="statistics-item">
|
|
|
+ <div class="statistics-title">直播观看人数</div>
|
|
|
+ <div class="statistics-value">{{ statisticsData.liveViewers || 0 }}</div>
|
|
|
</div>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- <!-- 直播趋势统计 -->
|
|
|
- <div class="trend-section">
|
|
|
- <div class="trend-header">
|
|
|
- <h3>直播趋势</h3>
|
|
|
- <div class="filter-container">
|
|
|
- <!-- 时间范围选择(下拉框) -->
|
|
|
- <span class="filter-label">时间筛选:</span>
|
|
|
- <el-select v-model="selectedTimeRange" placeholder="选择时间范围" @change="changeTimeRange" style="width: 180px">
|
|
|
- <el-option label="自然天" value="day"></el-option>
|
|
|
- <el-option label="自然周" value="week"></el-option>
|
|
|
- <el-option label="自然月" value="month"></el-option>
|
|
|
- </el-select>
|
|
|
-
|
|
|
- <!-- 日期选择器 -->
|
|
|
- <!-- 选择日期 -->
|
|
|
- <el-date-picker
|
|
|
- ref="datePickerRef"
|
|
|
- v-model="selectedDate"
|
|
|
- :type="datePickerType"
|
|
|
- :format="selectedTimeRange === 'week' ? weekRange : dateFormat"
|
|
|
- :value-format="valueFormat"
|
|
|
- placeholder="选择日期"
|
|
|
- style="width: 280px"
|
|
|
- @change="changeDate"
|
|
|
- @blur="setDefaultDate"
|
|
|
- />
|
|
|
- </div>
|
|
|
- </div>
|
|
|
-
|
|
|
- <div class="trend-cards">
|
|
|
- <div class="trend-card"
|
|
|
- :class="{ 'active': selectedMetric === 'page_views' }"
|
|
|
- @click="changeMetric('page_views')">
|
|
|
- <p class="trend-title">
|
|
|
- 浏览量
|
|
|
- <el-tooltip class="item" effect="dark" content="观看页面的总浏览量" placement="top">
|
|
|
- <i class="el-icon-info"></i>
|
|
|
- </el-tooltip>
|
|
|
- </p>
|
|
|
- <p class="trend-value">{{ trendData.views || 0 }}</p>
|
|
|
- <p class="trend-tip">{{ changeLabel }}: {{ trendData.viewsChange }}%</p>
|
|
|
-
|
|
|
- </div>
|
|
|
-
|
|
|
- <div class="trend-card"
|
|
|
- :class="{ 'active': selectedMetric === 'unique_visitors' }"
|
|
|
- @click="changeMetric('unique_visitors')">
|
|
|
- <p class="trend-title">
|
|
|
- 访客数
|
|
|
- <el-tooltip class="item" effect="dark" content="进入直播间的访客数" placement="top">
|
|
|
- <i class="el-icon-info"></i>
|
|
|
- </el-tooltip>
|
|
|
- </p>
|
|
|
- <p class="trend-value">{{ trendData.visitors|| 0 }}</p>
|
|
|
- <p class="trend-tip">{{ changeLabel }}: {{ trendData.visitorsChange }}%</p>
|
|
|
-
|
|
|
- </div>
|
|
|
-
|
|
|
- <div class="trend-card"
|
|
|
- :class="{ 'active': selectedMetric === 'streams' }"
|
|
|
- @click="changeMetric('streams')">
|
|
|
- <p class="trend-title">
|
|
|
- 创建直播数
|
|
|
- <el-tooltip class="item" effect="dark" content="创建的直播间数量" placement="top">
|
|
|
- <i class="el-icon-info"></i>
|
|
|
- </el-tooltip>
|
|
|
- </p>
|
|
|
- <p class="trend-value">{{ trendData.streams|| 0 }}</p>
|
|
|
- <p class="trend-tip">{{ changeLabel }}: {{ trendData.streamsChange }}%</p>
|
|
|
-
|
|
|
- </div>
|
|
|
-
|
|
|
- <div class="trend-card"
|
|
|
- :class="{ 'active': selectedMetric === 'total_views' }"
|
|
|
- @click="changeMetric('total_views')">
|
|
|
- <p class="trend-title">
|
|
|
- 累计观看人次
|
|
|
- <el-tooltip class="item" effect="dark" content="直播间被观看的总人次" placement="top">
|
|
|
- <i class="el-icon-info"></i>
|
|
|
- </el-tooltip>
|
|
|
- </p>
|
|
|
- <p class="trend-value">{{ trendData.pv|| 0 }}</p>
|
|
|
- <p class="trend-tip">{{ changeLabel }}: {{ trendData.pvChange }}%</p>
|
|
|
-
|
|
|
- </div>
|
|
|
-
|
|
|
- <div class="trend-card"
|
|
|
- :class="{ 'active': selectedMetric === 'unique_viewers' }"
|
|
|
- @click="changeMetric('unique_viewers')">
|
|
|
- <p class="trend-title">
|
|
|
- 累计观看人数
|
|
|
- <el-tooltip class="item" effect="dark" content="去重后的观看人数" placement="top">
|
|
|
- <i class="el-icon-info"></i>
|
|
|
- </el-tooltip>
|
|
|
- </p>
|
|
|
- <p class="trend-value">{{ trendData.uv|| 0 }}</p>
|
|
|
- <p class="trend-tip">{{ changeLabel }}: {{ trendData.uvChange }}%</p>
|
|
|
-
|
|
|
- </div>
|
|
|
- </div>
|
|
|
-
|
|
|
-
|
|
|
- <!-- 直播趋势折线图 -->
|
|
|
- <div id="liveChart" class="chart"></div>
|
|
|
- </div>
|
|
|
- <!-- 直播TOP10排行榜 -->
|
|
|
- <div class="top10-section">
|
|
|
- <h3>直播TOP10排行榜</h3>
|
|
|
- <div class="ranking-container">
|
|
|
- <!-- 红榜 -->
|
|
|
- <div class="ranking-section red-rank">
|
|
|
- <span class="rank-title">红榜</span>
|
|
|
- <div class="rank-filters">
|
|
|
- <button
|
|
|
- v-for="item in redRankTypes"
|
|
|
- :key="item.value"
|
|
|
- :class="{ active: selectedRank === item.value }"
|
|
|
- @click="selectRank(item.value)"
|
|
|
- >
|
|
|
- {{ item.label }}
|
|
|
- </button>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="4">
|
|
|
+ <div class="statistics-item">
|
|
|
+ <div class="statistics-title">
|
|
|
+ 直播完课人数
|
|
|
+ <el-popover placement="top" width="200" trigger="click">
|
|
|
+ <div>
|
|
|
+ <el-input-number v-model="liveCompleteWatchTime" :min="0" :precision="0" placeholder="看课时长(分钟)" style="width: 100%;"></el-input-number>
|
|
|
+ <el-button type="primary" size="mini" style="width: 100%; margin-top: 10px;" @click="updateCompleteWatchTime('live')">确定</el-button>
|
|
|
+ </div>
|
|
|
+ <el-button slot="reference" size="mini" type="text" icon="el-icon-setting" style="margin-left: 5px;">设置</el-button>
|
|
|
+ </el-popover>
|
|
|
</div>
|
|
|
+ <div class="statistics-value">{{ statisticsData.liveCompletedCourses || 0 }}</div>
|
|
|
</div>
|
|
|
-
|
|
|
- <!-- 黑榜 -->
|
|
|
- <div class="ranking-section black-rank">
|
|
|
- <div class="rank-filters">
|
|
|
- <button
|
|
|
- v-for="item in blackRankTypes"
|
|
|
- :key="item.value"
|
|
|
- :class="{ active: selectedRank === item.value }"
|
|
|
- @click="selectRank(item.value)"
|
|
|
- >
|
|
|
- {{ item.label }}
|
|
|
- </button>
|
|
|
- </div>
|
|
|
- <span class="rank-title">黑榜</span>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="4">
|
|
|
+ <div class="statistics-item">
|
|
|
+ <div class="statistics-title">回放观看人数</div>
|
|
|
+ <div class="statistics-value">{{ statisticsData.playbackViewers || 0 }}</div>
|
|
|
</div>
|
|
|
- </div>
|
|
|
- <el-table :data="top10List" border style="width: 100%">
|
|
|
- <el-table-column prop="rank" label="排名" width="80"></el-table-column>
|
|
|
- <el-table-column prop="liveName" label="直播名称">
|
|
|
- <template #default="scope">
|
|
|
- <div class="live-name">
|
|
|
- <img :src="scope.row.liveImgUrl" class="live-cover" alt="封面">
|
|
|
- <span class="live-title">{{ scope.row.liveName }}</span>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="4">
|
|
|
+ <div class="statistics-item">
|
|
|
+ <div class="statistics-title">
|
|
|
+ 回放完课人数
|
|
|
+ <el-popover placement="top" width="200" trigger="click">
|
|
|
+ <div>
|
|
|
+ <el-input-number v-model="replayCompleteWatchTime" :min="0" :precision="0" placeholder="看课时长(分钟)" style="width: 100%;"></el-input-number>
|
|
|
+ <el-button type="primary" size="mini" style="width: 100%; margin-top: 10px;" @click="updateCompleteWatchTime('replay')">确定</el-button>
|
|
|
+ </div>
|
|
|
+ <el-button slot="reference" size="mini" type="text" icon="el-icon-setting" style="margin-left: 5px;">设置</el-button>
|
|
|
+ </el-popover>
|
|
|
</div>
|
|
|
- </template>
|
|
|
- </el-table-column>
|
|
|
- <el-table-column prop="pageViews" label="直播间浏览量(PV)"></el-table-column>
|
|
|
- <el-table-column prop="uniqueVisitors" label="直播间访客数(UV)"></el-table-column>
|
|
|
- <el-table-column prop="totalViews" label="累计观看人次(PV)"></el-table-column>
|
|
|
- <el-table-column prop="uniqueViewers" label="累计观看人数(UV)"></el-table-column>
|
|
|
- <!--<el-table-column prop="avgTime" label="人均观看时长"></el-table-column>-->
|
|
|
- <el-table-column prop="peakConcurrentViewers" label="最高在线人数"></el-table-column>
|
|
|
- </el-table>
|
|
|
-
|
|
|
+ <div class="statistics-value">{{ statisticsData.playbackCompletedCourses || 0 }}</div>
|
|
|
+ </div>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+ <el-row :gutter="20" style="margin-top: 20px;">
|
|
|
+ <el-col :span="4">
|
|
|
+ <div class="statistics-item">
|
|
|
+ <div class="statistics-title">直播峰值</div>
|
|
|
+ <div class="statistics-value">{{ statisticsData.livePeak || 0 }}</div>
|
|
|
+ </div>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="4">
|
|
|
+ <div class="statistics-item">
|
|
|
+ <div class="statistics-title">直播平均时长</div>
|
|
|
+ <div class="statistics-value">{{ formatDuration(statisticsData.liveAvgDuration) }}</div>
|
|
|
+ </div>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="4">
|
|
|
+ <div class="statistics-item">
|
|
|
+ <div class="statistics-title">回放平均时长</div>
|
|
|
+ <div class="statistics-value">{{ formatDuration(statisticsData.playbackAvgDuration) }}</div>
|
|
|
+ </div>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="4">
|
|
|
+ <div class="statistics-item">
|
|
|
+ <div class="statistics-title">GMV</div>
|
|
|
+ <div class="statistics-value">¥{{ statisticsData.gmv || 0 }}</div>
|
|
|
+ </div>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="4">
|
|
|
+ <div class="statistics-item">
|
|
|
+ <div class="statistics-title">付费人数</div>
|
|
|
+ <div class="statistics-value">{{ statisticsData.paidUsers || 0 }}</div>
|
|
|
+ </div>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="4">
|
|
|
+ <div class="statistics-item">
|
|
|
+ <div class="statistics-title">付费单数</div>
|
|
|
+ <div class="statistics-value">{{ statisticsData.paidOrders || 0 }}</div>
|
|
|
+ </div>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+ <el-row :gutter="20" style="margin-top: 20px;">
|
|
|
+ <el-col :span="4">
|
|
|
+ <div class="statistics-item">
|
|
|
+ <div class="statistics-title">销量统计</div>
|
|
|
+ <div class="statistics-value">{{ statisticsData.salesCount || 0 }}</div>
|
|
|
+ </div>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+ </el-card>
|
|
|
+
|
|
|
+ <!-- 筛选条件区域 -->
|
|
|
+ <el-form :model="queryParams" class="live-data-css" ref="queryForm" :inline="true" v-show="showSearch" label-width="100px">
|
|
|
+ <el-form-item label="直播名称" prop="liveName">
|
|
|
+ <el-input
|
|
|
+ v-model="queryParams.liveName"
|
|
|
+ placeholder="请输入直播名称"
|
|
|
+ clearable
|
|
|
+ size="small"
|
|
|
+ @keyup.enter.native="handleQuery"
|
|
|
+ />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="观看类型" prop="watchType">
|
|
|
+ <el-select v-model="queryParams.watchType" placeholder="请选择观看类型" clearable size="small">
|
|
|
+ <el-option label="直播" value="live"></el-option>
|
|
|
+ <el-option label="回放" value="replay"></el-option>
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="完课状态" prop="completeStatus">
|
|
|
+ <el-select v-model="queryParams.completeStatus" placeholder="请选择完课状态" clearable size="small">
|
|
|
+ <el-option label="已完课" value="1"></el-option>
|
|
|
+ <el-option label="未完课" value="0"></el-option>
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="付费状态" prop="payStatus">
|
|
|
+ <el-select v-model="queryParams.payStatus" placeholder="请选择付费状态" clearable size="small">
|
|
|
+ <el-option label="已付费" value="1"></el-option>
|
|
|
+ <el-option label="未付费" value="0"></el-option>
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="观看时长" prop="watchDuration">
|
|
|
+ <el-input-number
|
|
|
+ v-model="queryParams.watchDuration"
|
|
|
+ :min="0"
|
|
|
+ :precision="0"
|
|
|
+ placeholder="观看时长(分钟)"
|
|
|
+ size="small"
|
|
|
+ ></el-input-number>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="时间范围" prop="dateRange">
|
|
|
+ <el-date-picker
|
|
|
+ v-model="dateRange"
|
|
|
+ type="datetimerange"
|
|
|
+ range-separator="至"
|
|
|
+ start-placeholder="开始日期"
|
|
|
+ end-placeholder="结束日期"
|
|
|
+ size="small"
|
|
|
+ value-format="yyyy-MM-dd HH:mm:ss"
|
|
|
+ ></el-date-picker>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item>
|
|
|
+ <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
|
|
|
+ <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
|
|
|
+ </el-form-item>
|
|
|
+ </el-form>
|
|
|
+
|
|
|
+ <!-- 操作工具栏 -->
|
|
|
+ <div class="selection-toolbar">
|
|
|
+ <el-checkbox :indeterminate="isIndeterminate" v-model="allChecked" @change="toggleSelectAll">
|
|
|
+ {{ multipleSelection.length > 0 ? `已选 ${multipleSelection.length} 条` : '选中本页' }}
|
|
|
+ </el-checkbox>
|
|
|
+<!-- <el-button plain size="mini" @click="handleAutoTag">自动打标签</el-button>-->
|
|
|
+<!-- <el-button plain size="mini" @click="handleAutoRemark">自动备注</el-button>-->
|
|
|
+ <el-button plain type="primary" size="mini" icon="el-icon-download" :loading="exportLoading" @click="handleExport">导出</el-button>
|
|
|
</div>
|
|
|
-<!-- <div class="student-section">-->
|
|
|
-<!-- <h3>直播间学员</h3>-->
|
|
|
-<!-- <el-form :inline="true" v-show="showAdvancedSearch" label-width="100px">-->
|
|
|
-<!-- <!– 直播列表弹框 –>-->
|
|
|
-<!-- <el-dialog title="选择直播" :visible.sync="dialogVisible" width="70%">-->
|
|
|
-<!-- <el-row :gutter="20">-->
|
|
|
-<!-- <el-col :span="8" :sm="12" :md="8">-->
|
|
|
-<!-- <el-form-item label="直播名称:">-->
|
|
|
-<!-- <el-input v-model="liveFiltersParam.liveName" placeholder="请输入直播名称"></el-input>-->
|
|
|
-<!-- </el-form-item>-->
|
|
|
-<!-- </el-col>-->
|
|
|
-<!-- <el-col :span="8" :sm="12" :md="8">-->
|
|
|
-<!-- <el-form-item label="直播状态:">-->
|
|
|
-<!-- <el-select v-model="liveFiltersParam.status" placeholder="请选择直播状态" clearable>-->
|
|
|
-<!-- <el-option label="全部" value=""></el-option>-->
|
|
|
-<!-- <el-option label="已结束" value="3"></el-option>-->
|
|
|
-<!-- <el-option label="进行中" value="2"></el-option>-->
|
|
|
-<!-- <el-option label="未开始" value="1"></el-option>-->
|
|
|
-<!-- </el-select>-->
|
|
|
-<!-- </el-form-item>-->
|
|
|
-<!-- </el-col>-->
|
|
|
-<!-- <el-col :span="8" :sm="12" :md="8">-->
|
|
|
-<!-- <el-form-item label="直播时间:">-->
|
|
|
-<!-- <el-date-picker-->
|
|
|
-<!-- v-model="liveDateRange"-->
|
|
|
-<!-- type="daterange"-->
|
|
|
-<!-- value-format="yyyy-MM-dd"-->
|
|
|
-<!-- range-separator="至"-->
|
|
|
-<!-- start-placeholder="开始日期"-->
|
|
|
-<!-- end-placeholder="结束日期"-->
|
|
|
-<!-- clearable-->
|
|
|
-<!-- style="width: 250px"-->
|
|
|
-<!-- @change="handleLiveDateChange"-->
|
|
|
-<!-- ></el-date-picker>-->
|
|
|
-<!-- </el-form-item>-->
|
|
|
-<!-- </el-col>-->
|
|
|
-<!-- <!– 查询 & 重置 按钮 –>-->
|
|
|
-<!-- <el-col :span="4" class="button-group">-->
|
|
|
-<!-- <el-button type="primary" @click="getLive">查询</el-button>-->
|
|
|
-<!-- <el-button @click="resetLiveFilters">重置</el-button>-->
|
|
|
-<!-- </el-col>-->
|
|
|
-<!-- </el-row>-->
|
|
|
|
|
|
-<!-- <!– 直播列表 –>-->
|
|
|
-<!-- <el-table :data="liveList" border @selection-change="handleSelectionChange">-->
|
|
|
-<!-- <el-table-column type="selection" width="50" align="center" />-->
|
|
|
-<!-- <!– 直播信息列 –>-->
|
|
|
-<!-- <el-table-column label="直播名称" min-width="250">-->
|
|
|
-<!-- <template slot-scope="{ row }">-->
|
|
|
-<!-- <div class="live-info">-->
|
|
|
-<!-- <!– 直播封面图 –>-->
|
|
|
-<!-- <img :src="row.liveImgUrl" class="live-cover" />-->
|
|
|
-<!-- <!– 直播名称 + 时间 –>-->
|
|
|
-<!-- <div class="live-text">-->
|
|
|
-<!-- <div type="text"class="live-name">{{ row.liveName }}</div>-->
|
|
|
-<!-- <div class="live-time">{{ row.startTime }}-{{row.finishTime}}</div>-->
|
|
|
-<!-- </div>-->
|
|
|
-<!-- </div>-->
|
|
|
-<!-- </template>-->
|
|
|
-<!-- </el-table-column>-->
|
|
|
-
|
|
|
-<!-- <!– 直播状态 –>-->
|
|
|
-<!-- <el-table-column label="直播状态" min-width="120">-->
|
|
|
-<!-- <template slot-scope="{ row }">-->
|
|
|
-<!-- <div class="status-container">-->
|
|
|
-<!-- <span :class="['status-dot', getStatusClass(row.status)]"></span>-->
|
|
|
-<!-- <span>{{ getStatusText(row.status) }}</span>-->
|
|
|
-<!-- </div>-->
|
|
|
-<!-- </template>-->
|
|
|
-<!-- </el-table-column>-->
|
|
|
-
|
|
|
-<!-- <!– 讲师 –>-->
|
|
|
-<!-- <el-table-column label="讲师" prop="teacher" min-width="120"></el-table-column>-->
|
|
|
-<!-- </el-table>-->
|
|
|
-<!-- <pagination-->
|
|
|
-<!-- v-show="liveTotal>0"-->
|
|
|
-<!-- :total="liveTotal"-->
|
|
|
-<!-- :page.sync="liveFiltersParam.pageNum"-->
|
|
|
-<!-- :limit.sync="liveFiltersParam.pageSize"-->
|
|
|
-<!-- @pagination="getLive"-->
|
|
|
-<!-- />-->
|
|
|
-<!-- <!– 确认按钮 –>-->
|
|
|
-<!-- <span slot="footer" class="dialog-footer">-->
|
|
|
-<!-- <el-button @click="dialogVisible = false">取消</el-button>-->
|
|
|
-<!-- <el-button type="primary" @click="confirmSelection">确认</el-button>-->
|
|
|
-<!-- </span>-->
|
|
|
-<!-- </el-dialog>-->
|
|
|
-<!-- <!– 基础筛选项:始终显示 –>-->
|
|
|
-<!-- <el-row :gutter="20">-->
|
|
|
-<!-- <el-col :span="8" :sm="12" :md="8">-->
|
|
|
-<!-- <el-form-item label="直播名称:" class="el-form-item-ellipsis">-->
|
|
|
-<!-- <el-input-->
|
|
|
-<!-- v-model="filters.liveName"-->
|
|
|
-<!-- placeholder="请选择直播"-->
|
|
|
-<!-- @focus="openDialog"-->
|
|
|
-<!-- type="text"-->
|
|
|
-<!-- readonly-->
|
|
|
-<!-- >-->
|
|
|
-<!-- <template slot="prepend">-->
|
|
|
-<!-- <div v-if="filters.liveNames.length > 0" class="selected-lives">-->
|
|
|
-<!-- <el-tag-->
|
|
|
-<!-- v-for="(name, index) in filters.liveNames"-->
|
|
|
-<!-- :key="index"-->
|
|
|
-<!-- closable-->
|
|
|
-<!-- @close="removeLive(index)"-->
|
|
|
-<!-- class="live-name-tag"-->
|
|
|
-<!-- >-->
|
|
|
-<!-- {{ name }}-->
|
|
|
-<!-- </el-tag>-->
|
|
|
-<!-- </div>-->
|
|
|
-<!-- </template>-->
|
|
|
-<!-- </el-input>-->
|
|
|
-<!-- </el-form-item>-->
|
|
|
-<!-- </el-col>-->
|
|
|
-<!-- <el-col :span="8" :sm="12" :md="8">-->
|
|
|
-<!-- <el-form-item label="用户名:" class="el-form-item-ellipsis">-->
|
|
|
-<!-- <el-input v-model="filters.userName" placeholder="请输入用户名"></el-input>-->
|
|
|
-<!-- </el-form-item>-->
|
|
|
-<!-- </el-col>-->
|
|
|
-<!-- <el-col :span="8" :sm="12" :md="8">-->
|
|
|
-<!-- <el-form-item label="手机号:" class="el-form-item-ellipsis">-->
|
|
|
-<!-- <el-input v-model="filters.userPhone" placeholder="请输入手机号"></el-input>-->
|
|
|
-<!-- </el-form-item>-->
|
|
|
-<!-- </el-col>-->
|
|
|
-<!-- </el-row>-->
|
|
|
-<!-- <!– 高级筛选项 –>-->
|
|
|
-<!-- <el-row :gutter="24" v-show="showAllFilters">-->
|
|
|
-<!-- <el-col :span="8" :sm="12" :md="8" >-->
|
|
|
-<!-- <el-form-item label="用户创建时间:">-->
|
|
|
-<!-- <el-date-picker-->
|
|
|
-<!-- v-model="dateRange"-->
|
|
|
-<!-- type="daterange"-->
|
|
|
-<!-- range-separator="至"-->
|
|
|
-<!-- start-placeholder="开始日期"-->
|
|
|
-<!-- end-placeholder="结束日期"-->
|
|
|
-<!-- value-format="yyyy-MM-dd"-->
|
|
|
-<!-- @change="handleDateChange"-->
|
|
|
-<!-- class="full-width-picker"-->
|
|
|
-<!-- />-->
|
|
|
-<!-- </el-form-item>-->
|
|
|
-<!-- </el-col>-->
|
|
|
-<!-- <el-col :span="8" :sm="12" :md="8">-->
|
|
|
-<!-- <el-form-item label="分享次数:" class="el-form-item-ellipsis">-->
|
|
|
-<!-- <el-input v-model="filters.shareCount" placeholder="请输入分享次数"></el-input>-->
|
|
|
-<!-- </el-form-item>-->
|
|
|
-<!-- </el-col>-->
|
|
|
-<!-- <el-col :span="8" :sm="12" :md="8">-->
|
|
|
-<!-- <el-form-item label="所属部门:" prop="deptId">-->
|
|
|
-<!-- <treeselect style="width:220px" v-model="filters.deptId" :options="deptOptions" :show-count="true" placeholder="请选择所属部门" />-->
|
|
|
-<!-- </el-form-item>-->
|
|
|
-<!-- </el-col>-->
|
|
|
-<!-- </el-row>-->
|
|
|
-<!-- <el-row :gutter="20" v-show="showAllFilters">-->
|
|
|
-<!-- <el-col :span="8" :sm="12" :md="8"> <!– 每列 span 总和控制在 24 内 –>-->
|
|
|
-<!-- <el-form-item label="跟进人:" class="el-form-item-ellipsis">-->
|
|
|
-<!-- <el-input v-model="filters.companyUserName" placeholder="请输入跟进人"></el-input>-->
|
|
|
-<!-- </el-form-item>-->
|
|
|
-<!-- </el-col>-->
|
|
|
-<!-- <el-col :span="8" :sm="12" :md="8">-->
|
|
|
-<!-- <el-form-item label="购买商品数:" class="el-form-item-ellipsis">-->
|
|
|
-<!-- <el-input v-model="filters.goodsCount" placeholder="请输入购买商品数"></el-input>-->
|
|
|
-<!-- </el-form-item>-->
|
|
|
-<!-- </el-col>-->
|
|
|
-<!-- <el-col :span="8" :sm="12" :md="8">-->
|
|
|
-<!-- <el-form-item label="提问数:" class="flex-container">-->
|
|
|
-<!-- <el-row>-->
|
|
|
-<!-- <el-col :span="8" :sm="12" :md="8">-->
|
|
|
-<!-- <el-input-number v-model="filters.minQuestionCount" placeholder="最小提问数" :min="0" style="width: 100%"></el-input-number>-->
|
|
|
-<!-- </el-col>-->
|
|
|
-<!-- <el-col :span="2" class="text-center">至</el-col>-->
|
|
|
-<!-- <el-col :span="8" :sm="12" :md="8">-->
|
|
|
-<!-- <el-input-number v-model="filters.maxQuestionCount" placeholder="最大提问数" :min="0" style="width: 100%"></el-input-number>-->
|
|
|
-<!-- </el-col>-->
|
|
|
-<!-- </el-row>-->
|
|
|
-<!-- </el-form-item>-->
|
|
|
-<!-- </el-col>-->
|
|
|
-<!-- </el-row>-->
|
|
|
-
|
|
|
-<!-- <!– 按钮组:始终显示 –>-->
|
|
|
-<!-- <el-row>-->
|
|
|
-<!-- <el-col :span="24" class="button-group">-->
|
|
|
-<!-- <el-button type="primary" @click="getStudentData" class="custom-button">查询</el-button>-->
|
|
|
-<!-- <el-button @click="resetFilters" class="custom-button">重置</el-button>-->
|
|
|
-<!-- <el-button type="text" @click="toggleAllFilters" class="custom-button toggle-button">-->
|
|
|
-<!-- {{ showAllFilters ? '收起' : '展开' }}-->
|
|
|
-<!-- <i :class="showAllFilters ? 'el-icon-arrow-up' : 'el-icon-arrow-down'"></i>-->
|
|
|
-<!-- </el-button>-->
|
|
|
-<!-- </el-col>-->
|
|
|
-<!-- </el-row>-->
|
|
|
-<!-- </el-form>-->
|
|
|
-<!-- <!–<div class="column-button-container">-->
|
|
|
-<!-- <el-button-->
|
|
|
-<!-- type="primary"-->
|
|
|
-<!-- @click="showColumnSettings = true"-->
|
|
|
-<!-- class="custom-column-button"-->
|
|
|
-<!-- >-->
|
|
|
-<!-- 自定义列-->
|
|
|
-<!-- </el-button>-->
|
|
|
-<!-- </div>–>-->
|
|
|
-<!-- <!– 自定义列弹窗 –>-->
|
|
|
-<!-- <el-drawer-->
|
|
|
-<!-- title="自定义列设置"-->
|
|
|
-<!-- :visible.sync="showColumnSettings"-->
|
|
|
-<!-- :before-close="handleClose"-->
|
|
|
-<!-- size="25%"-->
|
|
|
-<!-- >-->
|
|
|
-<!-- <!– 抽屉内容区域 –>-->
|
|
|
-<!-- <!–<div style="display: flex; flex-direction: column; height: 100%;">-->
|
|
|
-<!-- <!– 可拖拽的列设置区域 –>-->
|
|
|
-<!-- <draggable-->
|
|
|
-<!-- v-model="columnOrder"-->
|
|
|
-<!-- group="columns"-->
|
|
|
-<!-- style="flex: 1; overflow-y: auto;"-->
|
|
|
-<!-- >-->
|
|
|
-<!-- <div v-for="col in columnOrder" :key="col.dataIndex" class="column-item">-->
|
|
|
-<!-- <el-checkbox v-model="col.status" true-label="ENABLE" false-label="DISABLE">-->
|
|
|
-<!-- {{ col.title }}-->
|
|
|
-<!-- </el-checkbox>-->
|
|
|
-<!-- <i class="el-icon-rank drag-handle"></i>-->
|
|
|
-<!-- </div>-->
|
|
|
-<!-- </draggable>-->
|
|
|
-
|
|
|
-<!-- <!– 底部按钮区域 –>-->
|
|
|
-<!-- <div style="padding: 12px; text-align: right; border-top: 2px solid;">-->
|
|
|
-<!-- <el-button @click="showColumnSettings = false" style="color: black;">取消</el-button>-->
|
|
|
-<!-- <el-button-->
|
|
|
-<!-- type="primary"-->
|
|
|
-<!-- @click="saveColumnsConfig"-->
|
|
|
-<!-- style="color: black; background-color: #409EFF; border-color: #409EFF;"-->
|
|
|
-<!-- >-->
|
|
|
-<!-- 确定-->
|
|
|
-<!-- </el-button>-->
|
|
|
-<!-- </div>-->
|
|
|
-<!-- </div>–>-->
|
|
|
-<!-- </el-drawer>-->
|
|
|
-
|
|
|
-
|
|
|
-<!-- <div style="overflow-x: auto; white-space: nowrap;">-->
|
|
|
-<!-- <el-table :data="liveStudentList" border style="min-width: 1200px;">-->
|
|
|
-<!-- <!– 固定列,内容不换行 –>-->
|
|
|
-<!-- <el-table-column prop="userName" label="客户名" fixed :style="{whiteSpace: 'nowrap',textOverflow: 'ellipsis',overflow: 'hidden',textAlign: 'center'}"></el-table-column>-->
|
|
|
-<!-- <el-table-column prop="liveName" label="直播间" fixed :show-overflow-tooltip="true" width=151px :min-width="'直播间'.length * 12 + 30":style="{whiteSpace: 'nowrap',textOverflow: 'ellipsis',overflow: 'hidden',textAlign: 'center'}"></el-table-column>-->
|
|
|
-<!-- <el-table-column prop="companyUserName" label="跟进人" fixed class="no-wrap-column"></el-table-column>-->
|
|
|
-<!-- <el-table-column prop="deptName" label="归属部门" width="150" show-overflow-tooltip/>-->
|
|
|
-<!-- <el-table-column prop="userCreateTime" label="客户创建时间" width="150" show-overflow-tooltip/>-->
|
|
|
-<!-- <el-table-column prop="userPhone" label="联系方式" width="150" show-overflow-tooltip/>-->
|
|
|
-<!-- <el-table-column prop="goodsCount" label="去下单实物商品" width="150" show-overflow-tooltip/>-->
|
|
|
-<!-- <el-table-column prop="shareCount" label="分享直播间次数" width="150" show-overflow-tooltip/>-->
|
|
|
-<!-- <el-table-column prop="questionCount" label="答题次数" width="150" show-overflow-tooltip/>-->
|
|
|
-
|
|
|
-<!-- <!– 动态列 –>-->
|
|
|
-<!-- <!–<el-table-column-->
|
|
|
-<!-- v-for="col in filteredColumns"-->
|
|
|
-<!-- :key="col.dataIndex"-->
|
|
|
-<!-- :prop="col.dataIndex"-->
|
|
|
-<!-- :label="col.title"-->
|
|
|
-<!-- :width="col.width"-->
|
|
|
-<!-- :min-width="col.title.length * 12 + 30"-->
|
|
|
-<!-- :show-overflow-tooltip="true">-->
|
|
|
-<!-- </el-table-column>–>-->
|
|
|
-
|
|
|
-<!-- </el-table>-->
|
|
|
-<!-- <pagination-->
|
|
|
-<!-- v-show="liveStudentTotal>0"-->
|
|
|
-<!-- :total="liveStudentTotal"-->
|
|
|
-<!-- :page.sync="filters.pageNum"-->
|
|
|
-<!-- :limit.sync="filters.pageSize"-->
|
|
|
-<!-- @pagination="getStudentData"-->
|
|
|
-<!-- />-->
|
|
|
-<!-- </div>-->
|
|
|
-<!-- </div>-->
|
|
|
+ <!-- 数据表格 -->
|
|
|
+ <el-table
|
|
|
+ ref="dataTable"
|
|
|
+ :data="dataList"
|
|
|
+ style="width: 100%"
|
|
|
+ v-loading="loading"
|
|
|
+ @selection-change="handleSelectionChange"
|
|
|
+ >
|
|
|
+ <el-table-column type="selection" width="55"></el-table-column>
|
|
|
+ <el-table-column type="index" label="序号" width="55" align="center"></el-table-column>
|
|
|
+ <el-table-column prop="liveName" label="直播间名称" min-width="160" show-overflow-tooltip></el-table-column>
|
|
|
+ <el-table-column prop="liveType" label="直播类型" width="100" align="center">
|
|
|
+ <template slot-scope="scope">
|
|
|
+ <el-tag type="danger" v-if="scope.row.liveType == 1">直播</el-tag>
|
|
|
+ <el-tag type="success" v-else-if="scope.row.liveType == 2">录播</el-tag>
|
|
|
+ <el-tag v-else-if="scope.row.liveType == 3">直播回放</el-tag>
|
|
|
+ <span v-else>-</span>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column prop="status" label="状态" width="100" align="center">
|
|
|
+ <template slot-scope="scope">
|
|
|
+ <el-tag type="info" v-if="scope.row.status == 1">未开始</el-tag>
|
|
|
+ <el-tag type="warning" v-else-if="scope.row.status == 2">进行中</el-tag>
|
|
|
+ <el-tag type="success" v-else-if="scope.row.status == 3">已结束</el-tag>
|
|
|
+ <span v-else>-</span>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column prop="startTime" label="开始时间" width="180" align="center"></el-table-column>
|
|
|
+ <el-table-column prop="finishTime" label="结束时间" width="180" align="center"></el-table-column>
|
|
|
+ <el-table-column prop="totalViewers" label="累计观看" width="110" align="center"></el-table-column>
|
|
|
+ <el-table-column prop="liveViewers" label="直播观看" width="110" align="center"></el-table-column>
|
|
|
+ <el-table-column prop="playbackViewers" label="回放观看" width="110" align="center"></el-table-column>
|
|
|
+ <el-table-column prop="liveAvgDuration" label="直播平均时长" width="140" align="center">
|
|
|
+ <template slot-scope="scope">{{ formatDuration(scope.row.liveAvgDuration) }}</template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column prop="playbackAvgDuration" label="回放平均时长" width="140" align="center">
|
|
|
+ <template slot-scope="scope">{{ formatDuration(scope.row.playbackAvgDuration) }}</template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column prop="totalCompletedCourses" label="累计完课" width="110" align="center"></el-table-column>
|
|
|
+ <el-table-column prop="liveCompletedCourses" label="直播完课" width="110" align="center"></el-table-column>
|
|
|
+ <el-table-column prop="playbackCompletedCourses" label="回放完课" width="110" align="center"></el-table-column>
|
|
|
+ <el-table-column prop="gmv" label="GMV" width="120" align="center">
|
|
|
+ <template slot-scope="scope">¥{{ scope.row.gmv || 0 }}</template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column prop="paidUsers" label="付费人数" width="100" align="center"></el-table-column>
|
|
|
+ <el-table-column prop="paidOrders" label="付费单数" width="100" align="center"></el-table-column>
|
|
|
+ <el-table-column prop="salesCount" label="销量统计" width="100" align="center"></el-table-column>
|
|
|
+ <el-table-column label="操作" width="120" fixed="right">
|
|
|
+ <template slot-scope="scope">
|
|
|
+ <el-button type="text" size="small" @click="handleViewDetail(scope.row)">查看详情</el-button>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ </el-table>
|
|
|
+
|
|
|
+ <!-- 分页组件 -->
|
|
|
+ <pagination
|
|
|
+ v-show="total > 0"
|
|
|
+ :total="total"
|
|
|
+ :page.sync="queryParams.pageNum"
|
|
|
+ :limit.sync="queryParams.pageSize"
|
|
|
+ @pagination="getList"
|
|
|
+ style="margin-top: 20px;"
|
|
|
+ />
|
|
|
+
|
|
|
+ <!-- 自动打标签弹窗 -->
|
|
|
+ <el-dialog
|
|
|
+ title="自动打标签"
|
|
|
+ :visible.sync="tagDialogVisible"
|
|
|
+ width="600px"
|
|
|
+ :close-on-click-modal="false"
|
|
|
+ >
|
|
|
+ <el-form :model="tagForm" label-width="120px">
|
|
|
+ <el-form-item label="打标签规则">
|
|
|
+ <el-checkbox-group v-model="tagForm.rules">
|
|
|
+ <el-checkbox label="liveComplete">直播完课</el-checkbox>
|
|
|
+ <el-checkbox label="replayComplete">回放完课</el-checkbox>
|
|
|
+ <el-checkbox label="pay">付费行为</el-checkbox>
|
|
|
+ </el-checkbox-group>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="标签名称">
|
|
|
+ <el-input v-model="tagForm.tagName" placeholder="请输入标签名称"></el-input>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="备注格式">
|
|
|
+ <el-radio-group v-model="tagForm.remarkFormat">
|
|
|
+ <el-radio label="time">时间—行为(如:2024-01-01—直播完课)</el-radio>
|
|
|
+ <el-radio label="simple">仅行为(如:直播完课)</el-radio>
|
|
|
+ </el-radio-group>
|
|
|
+ </el-form-item>
|
|
|
+ </el-form>
|
|
|
+ <div slot="footer" class="dialog-footer">
|
|
|
+ <el-button @click="tagDialogVisible = false">取 消</el-button>
|
|
|
+ <el-button type="primary" @click="confirmAutoTag">确 定</el-button>
|
|
|
+ </div>
|
|
|
+ </el-dialog>
|
|
|
+
|
|
|
+ <!-- 自动备注弹窗 -->
|
|
|
+ <el-dialog
|
|
|
+ title="自动备注"
|
|
|
+ :visible.sync="remarkDialogVisible"
|
|
|
+ width="600px"
|
|
|
+ :close-on-click-modal="false"
|
|
|
+ >
|
|
|
+ <el-form :model="remarkForm" label-width="120px">
|
|
|
+ <el-form-item label="备注规则">
|
|
|
+ <el-checkbox-group v-model="remarkForm.rules">
|
|
|
+ <el-checkbox label="liveComplete">直播完课</el-checkbox>
|
|
|
+ <el-checkbox label="replayComplete">回放完课</el-checkbox>
|
|
|
+ <el-checkbox label="pay">付费行为</el-checkbox>
|
|
|
+ </el-checkbox-group>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="备注格式">
|
|
|
+ <el-radio-group v-model="remarkForm.remarkFormat">
|
|
|
+ <el-radio label="time">时间—行为(如:2024-01-01—直播完课)</el-radio>
|
|
|
+ <el-radio label="simple">仅行为(如:直播完课)</el-radio>
|
|
|
+ </el-radio-group>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="备注位置">
|
|
|
+ <el-radio-group v-model="remarkForm.position">
|
|
|
+ <el-radio :label="1">添加在最前面</el-radio>
|
|
|
+ <el-radio :label="2">添加在最后面</el-radio>
|
|
|
+ <el-radio :label="3">替换所有备注</el-radio>
|
|
|
+ </el-radio-group>
|
|
|
+ </el-form-item>
|
|
|
+ </el-form>
|
|
|
+ <div slot="footer" class="dialog-footer">
|
|
|
+ <el-button @click="remarkDialogVisible = false">取 消</el-button>
|
|
|
+ <el-button type="primary" @click="confirmAutoRemark">确 定</el-button>
|
|
|
+ </div>
|
|
|
+ </el-dialog>
|
|
|
</div>
|
|
|
</template>
|
|
|
|
|
|
-
|
|
|
<script>
|
|
|
- import * as echarts from "echarts";
|
|
|
- import Editor from '@/components/Editor/wang';
|
|
|
- import {recentLive,liveTop,getTrendData,columns,updateColumns,queryStudentData} from "@/api/live/liveData";
|
|
|
- import {selectLiveToStudent} from "@/api/live/live";
|
|
|
- import { treeselect } from "@/api/company/companyDept";
|
|
|
- import Treeselect from "@riophae/vue-treeselect";
|
|
|
- import "@riophae/vue-treeselect/dist/vue-treeselect.css";
|
|
|
- import draggable from "vuedraggable";
|
|
|
- export default {
|
|
|
-
|
|
|
- name: "LiveData",
|
|
|
- components: { Editor,draggable,Treeselect },
|
|
|
- data() {
|
|
|
- return {
|
|
|
- // 部门树选项
|
|
|
- deptOptions: [],
|
|
|
- liveDateRange:"",
|
|
|
- liveFiltersParam:{
|
|
|
- liveName:"",
|
|
|
- status:"",
|
|
|
- startTime:"",
|
|
|
- finishTime:"",
|
|
|
- pageNum: 1,
|
|
|
- pageSize: 10,
|
|
|
- },
|
|
|
- liveList: [],
|
|
|
- dialogVisible: false,
|
|
|
- span:8,
|
|
|
- showAllFilters: true, // 默认收起
|
|
|
- showAdvancedSearch: true, // 控制表单的显示
|
|
|
- // 直播间总条数
|
|
|
- liveTotal: 0,
|
|
|
- //学员数据总条数
|
|
|
- liveStudentTotal:0,
|
|
|
- dateRange: [],//存选择的日期范围
|
|
|
- filters: {
|
|
|
- minQuestionCount: 0, // 大于提问数
|
|
|
- maxQuestionCount: 0, // 小于提问数
|
|
|
- liveNames: [], // 存放已选的直播名称
|
|
|
- liveIds: [],
|
|
|
- liveName: '',
|
|
|
- userName: '',
|
|
|
- userPhone: '',
|
|
|
- userCreateTime: '',
|
|
|
- startTime: '', // 开始时间
|
|
|
- finishTime: '',
|
|
|
- shareCount: '',
|
|
|
- deptName: '',
|
|
|
- deptId:null,
|
|
|
- companyUserName: '',
|
|
|
- goodsCount: '',
|
|
|
- questionCount: '',
|
|
|
- pageNum: 1,
|
|
|
- pageSize: 2
|
|
|
- },
|
|
|
- selectedLives: [], // 临时存储选中的直播数据
|
|
|
- showColumnSettings: false, // 控制自定义列弹窗
|
|
|
- columnOrder: [],
|
|
|
- tempOrderedColumns : [], // 用于渲染表格的列顺序
|
|
|
- selectedColumns: [], // 用于控制哪些列被选中
|
|
|
- liveStudentList:[],
|
|
|
- top10List: [],
|
|
|
- selectedRank: 'highFlow',
|
|
|
- redRankTypes: [
|
|
|
- { label: '高流量', value: 'highFlow' },
|
|
|
- { label: '高观看', value: 'highView' },
|
|
|
- /*{ label: '高时长', value: 'highTime' },*/
|
|
|
- ],
|
|
|
- blackRankTypes: [
|
|
|
- { label: '低观看', value: 'lowView' },
|
|
|
- { label: '低流量', value: 'lowFlow' },
|
|
|
- ],
|
|
|
- selectedTimeRange: 'day', // 默认选择自然天
|
|
|
- selectedDate: null, // 用户选择的日期
|
|
|
- datePickerType: 'date', // 默认日期选择类型
|
|
|
- weekRange: 'yyyy-ww', // 周选择的日期格式
|
|
|
- queryParams: {
|
|
|
- type: 'day', // 用来存储时间筛选方式(如 day, week, month)
|
|
|
- date: new Date().toISOString().split("T")[0], // 用来存储用户选择的日期默认今天
|
|
|
- category:"page_views"
|
|
|
- },
|
|
|
- chart: null,
|
|
|
- trendData: {}, // 直播趋势数据
|
|
|
- selectedMetric: "page_views", // 默认选中浏览量
|
|
|
- lives: [],
|
|
|
- };
|
|
|
+import { listLiveData, exportLiveData, autoTagAndRemark, dashboardData } from "@/api/live/liveData";
|
|
|
+import { batchUpdateExternalContactNotes } from "@/api/qw/externalContact";
|
|
|
+import { addTag } from "@/api/qw/externalContact";
|
|
|
+
|
|
|
+export default {
|
|
|
+ name: "LiveData",
|
|
|
+ data() {
|
|
|
+ return {
|
|
|
+ liveId: '',
|
|
|
+ loading: true,
|
|
|
+ exportLoading: false,
|
|
|
+ showSearch: true,
|
|
|
+ dataList: [],
|
|
|
+ total: 0,
|
|
|
+ multipleSelection: [],
|
|
|
+ allChecked: false,
|
|
|
+ isIndeterminate: false,
|
|
|
+ dateRange: [],
|
|
|
+ // 统计数据
|
|
|
+ statisticsData: {
|
|
|
+ gmv: 0,
|
|
|
+ liveAvgDuration: 0,
|
|
|
+ liveCompletedCourses: 0,
|
|
|
+ livePeak: 0,
|
|
|
+ liveViewers: 0,
|
|
|
+ paidOrders: 0,
|
|
|
+ paidUsers: 0,
|
|
|
+ playbackAvgDuration: 0,
|
|
|
+ playbackCompletedCourses: 0,
|
|
|
+ playbackViewers: 0,
|
|
|
+ salesCount: 0,
|
|
|
+ totalCompletedCourses: 0,
|
|
|
+ totalViewers: 1
|
|
|
+ },
|
|
|
+ // 完课时长设置
|
|
|
+ completeWatchTime: 0,
|
|
|
+ liveCompleteWatchTime: 0,
|
|
|
+ replayCompleteWatchTime: 0,
|
|
|
+ // 查询参数
|
|
|
+ queryParams: {
|
|
|
+ pageNum: 1,
|
|
|
+ pageSize: 10,
|
|
|
+ liveId: null,
|
|
|
+ liveName: null,
|
|
|
+ watchType: null,
|
|
|
+ completeStatus: null,
|
|
|
+ payStatus: null,
|
|
|
+ watchDuration: null,
|
|
|
+ startTime: null,
|
|
|
+ endTime: null
|
|
|
+ },
|
|
|
+ // 打标签弹窗
|
|
|
+ tagDialogVisible: false,
|
|
|
+ tagForm: {
|
|
|
+ rules: [],
|
|
|
+ tagName: '',
|
|
|
+ remarkFormat: 'time'
|
|
|
+ },
|
|
|
+ // 备注弹窗
|
|
|
+ remarkDialogVisible: false,
|
|
|
+ remarkForm: {
|
|
|
+ rules: [],
|
|
|
+ remarkFormat: 'time',
|
|
|
+ position: 1
|
|
|
+ }
|
|
|
+ };
|
|
|
+ },
|
|
|
+ created() {
|
|
|
+ this.liveId = this.$route.query.liveId;
|
|
|
+ this.queryParams.liveId = this.liveId;
|
|
|
+
|
|
|
+ this.getList();
|
|
|
+ },
|
|
|
+ methods: {
|
|
|
+ /** 获取统计数据 */
|
|
|
+ getStatistics() {
|
|
|
+ if (!this.liveId) return;
|
|
|
+ dashboardData(this.liveId).then(response => {
|
|
|
+ if (response.code === 200) {
|
|
|
+ this.statisticsData = response.data || {};
|
|
|
+ }
|
|
|
+ });
|
|
|
},
|
|
|
- computed: {
|
|
|
-
|
|
|
- filteredColumns() {
|
|
|
- return this.columnOrder.filter(col => col.status === "ENABLE");
|
|
|
- },
|
|
|
- changeLabel() {
|
|
|
- switch (this.selectedTimeRange) {
|
|
|
- case 'day': return '比前一天';
|
|
|
- case 'week': return '比上周';
|
|
|
- case 'month': return '比上月';
|
|
|
- default: return '变化';
|
|
|
+ /** 获取列表数据 */
|
|
|
+ getList() {
|
|
|
+ this.loading = true;
|
|
|
+ // 处理时间范围
|
|
|
+ if (this.dateRange && this.dateRange.length === 2) {
|
|
|
+ this.queryParams.startTime = this.dateRange[0];
|
|
|
+ this.queryParams.endTime = this.dateRange[1];
|
|
|
+ } else {
|
|
|
+ this.queryParams.startTime = null;
|
|
|
+ this.queryParams.endTime = null;
|
|
|
+ }
|
|
|
+ listLiveData(this.queryParams).then(response => {
|
|
|
+ if (response.code === 200) {
|
|
|
+ this.statisticsData = response.data || {};
|
|
|
+ this.dataList = response.list || [];
|
|
|
+ this.total = response.total || 0;
|
|
|
}
|
|
|
- },
|
|
|
- displayLives() {
|
|
|
- return this.lives.slice(0, 4); // 最多只显示前 4 条数据
|
|
|
- },
|
|
|
-
|
|
|
- dateFormat() {
|
|
|
- return this.selectedTimeRange === "day"
|
|
|
- ? "yyyy-MM-dd"
|
|
|
- : this.selectedTimeRange === "week"
|
|
|
- ? "yyyy-MM-dd 至 yyyy-MM-dd"
|
|
|
- : "yyyy-MM-dd";
|
|
|
- },
|
|
|
- valueFormat() {
|
|
|
- return this.selectedTimeRange === "day"
|
|
|
- ? "yyyy-MM-dd"
|
|
|
- : this.selectedTimeRange === "week"
|
|
|
- ? "yyyy-MM-dd"
|
|
|
- : "yyyy-MM-dd";
|
|
|
- },
|
|
|
+ this.loading = false;
|
|
|
+ }).catch(() => {
|
|
|
+ this.loading = false;
|
|
|
+ });
|
|
|
},
|
|
|
- created() {
|
|
|
- this.getRecentLive();
|
|
|
- this.getLiveTop();
|
|
|
- this.getTrendData();
|
|
|
- //this.getColumns();
|
|
|
- this.getTreeselect();
|
|
|
-
|
|
|
+ /** 搜索按钮操作 */
|
|
|
+ handleQuery() {
|
|
|
+ this.queryParams.pageNum = 1;
|
|
|
+ this.getList();
|
|
|
},
|
|
|
- mounted() {
|
|
|
- this.selectedColumns = this.columnOrder.map((col) => col.prop); // 默认全选
|
|
|
- this.selectedMetric = this.trendData[0]; // 默认选中第一个
|
|
|
- this.initChart(); // 初始化折线图
|
|
|
- if (!this.selectedMetric) {
|
|
|
- this.selectedMetric = "page_views";
|
|
|
- };
|
|
|
- this.updateChart();
|
|
|
+ /** 重置按钮操作 */
|
|
|
+ resetQuery() {
|
|
|
+ this.dateRange = [];
|
|
|
+ this.$refs.queryForm.resetFields();
|
|
|
+ this.handleQuery();
|
|
|
},
|
|
|
- watch: {
|
|
|
- // 如果 columnOrder 更新,确保 orderedColumns 跟随更新
|
|
|
- columnOrder(newOrder) {
|
|
|
- this.tempOrderedColumns = [...newOrder];
|
|
|
- },
|
|
|
+ /** 全选或取消全选 */
|
|
|
+ toggleSelectAll(val) {
|
|
|
+ if (val) {
|
|
|
+ this.toggleSelection(this.dataList);
|
|
|
+ } else {
|
|
|
+ this.toggleSelection();
|
|
|
+ }
|
|
|
},
|
|
|
-
|
|
|
- methods: {
|
|
|
- handleLiveDateChange(value) {
|
|
|
- if (value) {
|
|
|
- this.liveFiltersParam.startTime = value[0];
|
|
|
- this.liveFiltersParam.finishTime = value[1];
|
|
|
- } else {
|
|
|
- this.liveFiltersParam.startTime = null;
|
|
|
- this.liveFiltersParam.finishTime = null;
|
|
|
- }
|
|
|
- console.log("直播时间范围:", this.liveFiltersParam.startTime, this.liveFiltersParam.finishTime);
|
|
|
- },
|
|
|
- handleDateChange(value) {
|
|
|
- if (value) {
|
|
|
- this.filters.startTime = value[0];
|
|
|
- this.filters.finishTime = value[1];
|
|
|
- } else {
|
|
|
- this.filters.startTime = '';
|
|
|
- this.filters.finishTime = '';
|
|
|
- }
|
|
|
- console.log("用户创建时间范围:", this.filters.startTime, this.filters.finishTime);
|
|
|
- },
|
|
|
- getTreeselect() {
|
|
|
- treeselect().then((response) => {
|
|
|
- this.deptOptions = response.data;
|
|
|
- console.log(this.deptOptions)
|
|
|
+ toggleSelection(rows) {
|
|
|
+ if (rows) {
|
|
|
+ rows.forEach(row => {
|
|
|
+ this.$refs.dataTable.toggleRowSelection(row);
|
|
|
});
|
|
|
- },
|
|
|
- getLive(){
|
|
|
- selectLiveToStudent(this.liveFiltersParam).then(response => {
|
|
|
- this.liveList = response.rows;
|
|
|
- this.liveTotal = response.total;
|
|
|
- });
|
|
|
- },
|
|
|
- resetLiveFilters(){
|
|
|
- this.liveDateRange="",
|
|
|
- this.liveFiltersParam = {
|
|
|
- liveName:"",
|
|
|
- status:"",
|
|
|
- startTime:"",
|
|
|
- finishTime:""
|
|
|
- }
|
|
|
- },
|
|
|
- // 确认选择
|
|
|
- confirmSelection() {
|
|
|
- this.filters.liveNames = this.selectedLives.map(item => item.liveName);
|
|
|
- this.filters.liveIds = this.selectedLives.map(item => item.liveId);
|
|
|
- console.log(this.filters.liveIds)
|
|
|
- this.dialogVisible = false; // 关闭弹框
|
|
|
- },
|
|
|
- // 移除已选直播
|
|
|
- removeLive(index) {
|
|
|
- this.filters.liveNames.splice(index, 1);
|
|
|
- this.filters.liveIds.splice(index, 1);
|
|
|
- },
|
|
|
- // 处理表格选中
|
|
|
- handleSelectionChange(selectedRows) {
|
|
|
- this.selectedLives = selectedRows; // 存储选中的直播数据
|
|
|
- },
|
|
|
-
|
|
|
- openDialog() {
|
|
|
- this.getLive();
|
|
|
- this.dialogVisible = true;
|
|
|
- },
|
|
|
- toggleAllFilters() {
|
|
|
- this.showAllFilters = !this.showAllFilters;
|
|
|
- },
|
|
|
- getStudentData() {
|
|
|
- console.log("查询条件:", this.filters);
|
|
|
- queryStudentData(this.filters).then(response => {
|
|
|
- console.log(response)
|
|
|
- this.liveStudentList = response.rows;
|
|
|
- this.liveStudentTotal = response.total;
|
|
|
- console.log(response.total)
|
|
|
- });
|
|
|
- },
|
|
|
- resetFilters() {
|
|
|
- this.filters = {
|
|
|
- liveName: "",
|
|
|
- liveNames:[],
|
|
|
- liveIds:[],
|
|
|
- phoneNumber: "",
|
|
|
- wechatNickname: "",
|
|
|
- customerId: "",
|
|
|
- visitTime: [],
|
|
|
- isWinner: "",
|
|
|
- department: "",
|
|
|
- customerTag: "",
|
|
|
- };
|
|
|
- },
|
|
|
- handleClose(done) {
|
|
|
- this.$confirm('确认关闭?')
|
|
|
- .then(_ => {
|
|
|
- done();
|
|
|
- })
|
|
|
- .catch(_ => {});
|
|
|
- },
|
|
|
- saveColumnsConfig() {
|
|
|
- this.tempOrderedColumns = [...this.columnOrder]; // 更新 orderedColumns
|
|
|
- console.log(this.tempOrderedColumns)
|
|
|
- updateColumns(this.tempOrderedColumns).then(response=>{
|
|
|
- //this.getColumns()
|
|
|
- location.reload()
|
|
|
- this.showColumnSettings = false;
|
|
|
- })
|
|
|
- },
|
|
|
- /*getColumns(){
|
|
|
- this.loading = true;
|
|
|
- columns().then(response => {
|
|
|
- this.columnOrder = response.data;
|
|
|
- this.selectedColumns = response.data.map((col) => col.prop);
|
|
|
- console.log(this.columnOrder)
|
|
|
- this.loading = false;
|
|
|
- });
|
|
|
- },*/
|
|
|
- getTrendData(){
|
|
|
- getTrendData(this.queryParams).then(response=>{
|
|
|
- this.trendData = response.data
|
|
|
- this.updateChart()
|
|
|
- })
|
|
|
- },
|
|
|
- // 更新折线图
|
|
|
- getMetricTitle(metricKey) {
|
|
|
- const titles = {
|
|
|
- page_views: "浏览量",
|
|
|
- unique_visitors: "访客数",
|
|
|
- streams: "创建直播数",
|
|
|
- total_views: "累计观看人次",
|
|
|
- unique_viewers: "累计观看人数"
|
|
|
- };
|
|
|
- return titles [metricKey] || "未知指标";
|
|
|
- },
|
|
|
-
|
|
|
- // 点击卡片切换指标
|
|
|
- changeMetric(metric) {
|
|
|
- this.selectedMetric = metric;
|
|
|
- // 更新 queryParams 中的 category
|
|
|
- this.queryParams.category = metric; // 默认值为 prevViews
|
|
|
- this.updateChart(); // 更新图表
|
|
|
- this.getTrendData(); // 重新请求数据
|
|
|
- },
|
|
|
- getLiveTop() {
|
|
|
-
|
|
|
- liveTop({ rankType: this.selectedRank })
|
|
|
- .then(response => {
|
|
|
-
|
|
|
- if (response.data) {
|
|
|
- // 遍历数据并添加排名字段
|
|
|
- this.top10List = response.data.map((item, index) => ({
|
|
|
- ...item,
|
|
|
- rank: index + 1 // 排名从 1 开始
|
|
|
- }));
|
|
|
- } else {
|
|
|
- this.top10List = [];
|
|
|
- }
|
|
|
- })
|
|
|
- .catch(error => {
|
|
|
- });
|
|
|
- },
|
|
|
- getStatusText(status) {
|
|
|
- if (status === 1) return "待直播";
|
|
|
- if (status === 2) return "直播中";
|
|
|
- return "已结束";
|
|
|
- },
|
|
|
- getStatusClass(status) {
|
|
|
- if (status === 1) return "status-upcoming";
|
|
|
- if (status === 2) return "status-live";
|
|
|
- return "status-ended";
|
|
|
- },
|
|
|
- getRecentLive(){
|
|
|
- this.loading = true;
|
|
|
- recentLive().then(response => {
|
|
|
- this.lives = response.data;
|
|
|
- this.loading = false;
|
|
|
- });
|
|
|
- },
|
|
|
-
|
|
|
- getWeekRange(selectedWeek) {
|
|
|
- let date = new Date(selectedWeek);
|
|
|
-
|
|
|
- if (isNaN(date.getTime())) {
|
|
|
- return "日期错误";
|
|
|
+ } else {
|
|
|
+ this.$refs.dataTable.clearSelection();
|
|
|
+ }
|
|
|
+ },
|
|
|
+ /** 多选框选中数据 */
|
|
|
+ handleSelectionChange(val) {
|
|
|
+ this.multipleSelection = val;
|
|
|
+ this.allChecked = val.length === this.dataList.length;
|
|
|
+ this.isIndeterminate = val.length > 0 && val.length < this.dataList.length;
|
|
|
+ },
|
|
|
+ /** 导出按钮操作 */
|
|
|
+ handleExport() {
|
|
|
+ if (this.dateRange && this.dateRange.length === 2) {
|
|
|
+ this.queryParams.startTime = this.dateRange[0];
|
|
|
+ this.queryParams.endTime = this.dateRange[1];
|
|
|
+ } else {
|
|
|
+ this.queryParams.startTime = null;
|
|
|
+ this.queryParams.endTime = null;
|
|
|
+ }
|
|
|
+ this.$confirm('是否确认导出所有直播数据?', "警告", {
|
|
|
+ confirmButtonText: "确定",
|
|
|
+ cancelButtonText: "取消",
|
|
|
+ type: "warning"
|
|
|
+ }).then(() => {
|
|
|
+ this.exportLoading = true;
|
|
|
+ return exportLiveData(this.queryParams);
|
|
|
+ }).then(response => {
|
|
|
+ if (response.code === 200) {
|
|
|
+ this.download(response.msg);
|
|
|
}
|
|
|
-
|
|
|
- let dayOfWeek = date.getDay(); // 0(星期天)- 6(星期六)
|
|
|
-
|
|
|
- // 计算周一
|
|
|
- let startDate = new Date(date); // 创建一个新的 Date 实例
|
|
|
- startDate.setDate(date.getDate() - dayOfWeek + (dayOfWeek === 0 ? -6 : 1));
|
|
|
-
|
|
|
- // 计算周日(确保 `endDate` 是新实例)
|
|
|
- let endDate = new Date(startDate.getTime()); // 复制 `startDate`
|
|
|
- endDate.setDate(startDate.getDate() + 6);
|
|
|
-
|
|
|
-
|
|
|
- return `${this.formatDate(startDate)} 至 ${this.formatDate(endDate)}`;
|
|
|
- },
|
|
|
- changeDate(value) {
|
|
|
- if (this.selectedTimeRange === 'week' && value) {
|
|
|
- this.weekRange = this.getWeekRange(value);
|
|
|
- console.log("周"+this.weekRange)
|
|
|
- } else {
|
|
|
- this.weekRange = ''; // 非周选择时清空周范围
|
|
|
+ this.exportLoading = false;
|
|
|
+ }).catch(() => {
|
|
|
+ this.exportLoading = false;
|
|
|
+ });
|
|
|
+ },
|
|
|
+ /** 自动打标签 */
|
|
|
+ // handleAutoTag() {
|
|
|
+ // if (this.multipleSelection.length === 0) {
|
|
|
+ // this.$message.warning('请选择要打标签的数据');
|
|
|
+ // return;
|
|
|
+ // }
|
|
|
+ // this.tagDialogVisible = true;
|
|
|
+ // },
|
|
|
+ /** 确认自动打标签 */
|
|
|
+ confirmAutoTag() {
|
|
|
+ if (this.tagForm.rules.length === 0) {
|
|
|
+ this.$message.warning('请选择打标签规则');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ const userIds = this.multipleSelection.map(item => item.userId).filter(id => id);
|
|
|
+ if (userIds.length === 0) {
|
|
|
+ this.$message.warning('所选数据中没有有效的用户ID');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ const data = {
|
|
|
+ userIds: userIds,
|
|
|
+ rules: this.tagForm.rules,
|
|
|
+ tagName: this.tagForm.tagName,
|
|
|
+ remarkFormat: this.tagForm.remarkFormat,
|
|
|
+ liveId: this.liveId
|
|
|
+ };
|
|
|
+ autoTagAndRemark(data).then(response => {
|
|
|
+ if (response.code === 200) {
|
|
|
+ this.$message.success('打标签成功');
|
|
|
+ this.tagDialogVisible = false;
|
|
|
+ this.tagForm = {
|
|
|
+ rules: [],
|
|
|
+ tagName: '',
|
|
|
+ remarkFormat: 'time'
|
|
|
+ };
|
|
|
+ this.getList();
|
|
|
}
|
|
|
-
|
|
|
- // 更新 queryParams.date
|
|
|
- if (value) {
|
|
|
- this.queryParams.date = this.formatDate(value);
|
|
|
+ });
|
|
|
+ },
|
|
|
+ /** 自动备注 */
|
|
|
+ // handleAutoRemark() {
|
|
|
+ // if (this.multipleSelection.length === 0) {
|
|
|
+ // this.$message.warning('请选择要备注的数据');
|
|
|
+ // return;
|
|
|
+ // }
|
|
|
+ // this.remarkDialogVisible = true;
|
|
|
+ // },
|
|
|
+ /** 确认自动备注 */
|
|
|
+ confirmAutoRemark() {
|
|
|
+ if (this.remarkForm.rules.length === 0) {
|
|
|
+ this.$message.warning('请选择备注规则');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ const userIds = this.multipleSelection.map(item => item.userId).filter(id => id);
|
|
|
+ if (userIds.length === 0) {
|
|
|
+ this.$message.warning('所选数据中没有有效的用户ID');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ // 生成备注内容
|
|
|
+ let notes = '';
|
|
|
+ const now = new Date();
|
|
|
+ const dateStr = now.getFullYear() + '-' +
|
|
|
+ String(now.getMonth() + 1).padStart(2, '0') + '-' +
|
|
|
+ String(now.getDate()).padStart(2, '0');
|
|
|
+
|
|
|
+ this.remarkForm.rules.forEach((rule, index) => {
|
|
|
+ let ruleText = '';
|
|
|
+ if (rule === 'liveComplete') {
|
|
|
+ ruleText = '直播完课';
|
|
|
+ } else if (rule === 'replayComplete') {
|
|
|
+ ruleText = '回放完课';
|
|
|
+ } else if (rule === 'pay') {
|
|
|
+ ruleText = '付费行为';
|
|
|
}
|
|
|
- this.getTrendData();
|
|
|
- },
|
|
|
- formatDate(date) {
|
|
|
- let date_ = new Date(date);
|
|
|
- console.log(date_)
|
|
|
- const year = date_.getFullYear();
|
|
|
- const month = String(date_.getMonth() + 1).padStart(2, '0');
|
|
|
- const day = String(date_.getDate()).padStart(2, '0');
|
|
|
- return `${year}-${month}-${day}`;
|
|
|
- },
|
|
|
|
|
|
- // 获取一周的起始日期(周一)
|
|
|
- getStartOfWeek(date) {
|
|
|
- let parsedDate = new Date(date);
|
|
|
- if (isNaN(parsedDate.getTime())) {
|
|
|
- console.error("无效的日期:", date);
|
|
|
- return null;
|
|
|
+ if (this.remarkForm.remarkFormat === 'time') {
|
|
|
+ notes += (index > 0 ? ';' : '') + dateStr + '—' + ruleText;
|
|
|
+ } else {
|
|
|
+ notes += (index > 0 ? ';' : '') + ruleText;
|
|
|
}
|
|
|
+ });
|
|
|
+
|
|
|
+ const data = {
|
|
|
+ userIds: userIds,
|
|
|
+ notes: notes,
|
|
|
+ type: this.remarkForm.position,
|
|
|
+ nameType: 3, // 不添加客户名称
|
|
|
+ filter: false
|
|
|
+ };
|
|
|
|
|
|
- const day = parsedDate.getDay();
|
|
|
- const diff = parsedDate.getDate() - day + (day === 0 ? -6 : 1); // 调整到周一
|
|
|
- return new Date(parsedDate.setDate(diff));
|
|
|
- },
|
|
|
-
|
|
|
- getEndOfWeek(date) {
|
|
|
- const startOfWeek = this.getStartOfWeek(date);
|
|
|
- if (!startOfWeek) return null;
|
|
|
-
|
|
|
- let endOfWeek = new Date(startOfWeek);
|
|
|
- endOfWeek.setDate(startOfWeek.getDate() + 6); // 调整到周日
|
|
|
- return endOfWeek;
|
|
|
- },
|
|
|
- computedChange(item) {
|
|
|
- switch (this.selectedTimeRange) {
|
|
|
- case 'day': return item.dailyChange;
|
|
|
- case 'week': return item.weeklyChange;
|
|
|
- case 'month': return item.monthlyChange;
|
|
|
- default: return 0;
|
|
|
- }
|
|
|
- },
|
|
|
- selectRank(type) {
|
|
|
- this.selectedRank = type; // 只允许一个按钮被选中
|
|
|
- this.getLiveTop(); // 重新获取排行榜数据
|
|
|
- },
|
|
|
- // 切换时间范围
|
|
|
- changeTimeRange() {
|
|
|
- // 根据选中的时间范围修改 datePicker 类型
|
|
|
- if (this.selectedTimeRange === 'day') {
|
|
|
- this.datePickerType = 'date'; // 自然天
|
|
|
- this.weekRange = ''; // 清空周范围
|
|
|
- } else if (this.selectedTimeRange === 'month') {
|
|
|
- this.datePickerType = 'month'; // 自然月
|
|
|
- this.weekRange = ''; // 清空周范围
|
|
|
- } else if (this.selectedTimeRange === 'week') {
|
|
|
- this.datePickerType = 'week'; // 自然周
|
|
|
+ batchUpdateExternalContactNotes(data).then(response => {
|
|
|
+ if (response.code === 200) {
|
|
|
+ this.$message.success('备注成功');
|
|
|
+ this.remarkDialogVisible = false;
|
|
|
+ this.remarkForm = {
|
|
|
+ rules: [],
|
|
|
+ remarkFormat: 'time',
|
|
|
+ position: 1
|
|
|
+ };
|
|
|
+ this.getList();
|
|
|
}
|
|
|
- // 立即弹出日期选择器
|
|
|
- this.$nextTick(() => {
|
|
|
- this.$refs.datePickerRef.focus();
|
|
|
- });
|
|
|
- // 重置已选日期
|
|
|
- this.queryParams.type = this.selectedTimeRange;
|
|
|
- this.selectedDate = null;
|
|
|
- this.queryParams.date = ''; // 清空 queryParams 中的日期
|
|
|
- },
|
|
|
- setDefaultDate() {
|
|
|
- // 如果用户没有选择日期,则使用当前日期
|
|
|
- if (!this.selectedDate) {
|
|
|
- this.selectedDate = this.formatDate(new Date());
|
|
|
- this.queryParams.date = this.selectedDate;
|
|
|
- }
|
|
|
- },
|
|
|
- // 切换日期时更新数据
|
|
|
- updateTrendData() {
|
|
|
- // 这里可以加接口请求,获取新的数据
|
|
|
- this.getTrendData()
|
|
|
- this.updateChart();
|
|
|
- },
|
|
|
- initChart() {
|
|
|
- let chartDom = document.getElementById("liveChart");
|
|
|
- if (!chartDom) return;
|
|
|
- this.chart = echarts.init(chartDom);
|
|
|
- this.updateChart();
|
|
|
- },
|
|
|
- // 更新折线图
|
|
|
- updateChart() {
|
|
|
- if (!this.chart) return;
|
|
|
-
|
|
|
- // 获取当前选中的指标对应的数据
|
|
|
- let metricKey = this.selectedMetric;
|
|
|
- let metricTitle = this.getMetricTitle(metricKey); // 获取指标标题
|
|
|
- let chartData = this.trendData.data || [];
|
|
|
- let dates = this.trendData.dates || []; // 确保 dates 不是 null
|
|
|
- let options = {
|
|
|
- tooltip: {
|
|
|
- trigger: "axis",
|
|
|
- backgroundColor: "rgba(0, 0, 0, 0.7)",
|
|
|
- textStyle: { color: "#fff" },
|
|
|
- formatter: function (params) {
|
|
|
- let date = params[0].name; // x 轴的日期
|
|
|
- let value = params[0].value; // 对应的数值
|
|
|
- return `${date}<br/>${params[0].seriesName}: ${value}`;
|
|
|
- }
|
|
|
- },
|
|
|
- grid: {
|
|
|
- left: "5%", right: "5%", bottom: "10%", top: "10%", containLabel: true
|
|
|
- },
|
|
|
- xAxis: {
|
|
|
- type: "category",
|
|
|
- data: this.trendData.dates || [],
|
|
|
- axisLine: {lineStyle: {color: "#ddd"}}
|
|
|
- },
|
|
|
- yAxis: {
|
|
|
- type: "value",
|
|
|
- splitLine: {lineStyle: {type: "dashed", color: "#ddd"}}
|
|
|
- },
|
|
|
- series: [
|
|
|
- {
|
|
|
- name: metricTitle,
|
|
|
- data: chartData,
|
|
|
- type: "line",
|
|
|
- smooth: true,
|
|
|
- symbol: "circle",
|
|
|
- symbolSize: 8,
|
|
|
- itemStyle: {
|
|
|
- color: "#007BFF",
|
|
|
- borderColor: "#fff",
|
|
|
- borderWidth: 2
|
|
|
- },
|
|
|
- lineStyle: {
|
|
|
- width: 3,
|
|
|
- color: "#007BFF"
|
|
|
- },
|
|
|
- areaStyle: {
|
|
|
- color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
|
|
- {offset: 0, color: "rgba(0, 123, 255, 0.5)"},
|
|
|
- {offset: 1, color: "rgba(0, 123, 255, 0)"}
|
|
|
- ])
|
|
|
- },
|
|
|
- emphasis: {
|
|
|
- focus: "series",
|
|
|
- label: {
|
|
|
- show: true,
|
|
|
- formatter: "{c}",
|
|
|
- fontSize: 14,
|
|
|
- color: "#333",
|
|
|
- backgroundColor: "#fff",
|
|
|
- padding: [4, 6]
|
|
|
- }
|
|
|
- },
|
|
|
- animationDuration: 1200,
|
|
|
- animationEasing: "quadraticOut"
|
|
|
- }
|
|
|
- ]
|
|
|
- };
|
|
|
-
|
|
|
- this.chart.setOption(options);
|
|
|
- },
|
|
|
+ });
|
|
|
+ },
|
|
|
+ /** 查看详情 */
|
|
|
+ handleViewDetail(row) {
|
|
|
+ // 可以跳转到详情页面或打开详情弹窗
|
|
|
+ this.$message.info('查看详情功能待实现');
|
|
|
+ },
|
|
|
+ /** 格式化时长 */
|
|
|
+ formatDuration(seconds) {
|
|
|
+ if (!seconds) return '00:00:00';
|
|
|
+ const hours = Math.floor(seconds / 3600);
|
|
|
+ const minutes = Math.floor((seconds % 3600) / 60);
|
|
|
+ const secs = seconds % 60;
|
|
|
+ return `${String(hours).padStart(2, '0')}:${String(minutes).padStart(2, '0')}:${String(secs).padStart(2, '0')}`;
|
|
|
+ },
|
|
|
+ /** 更新完课时长设置 */
|
|
|
+ updateCompleteWatchTime(type) {
|
|
|
+ // 这里可以调用API保存设置
|
|
|
+ let watchTime = 0;
|
|
|
+ if (type === 'total') {
|
|
|
+ watchTime = this.completeWatchTime;
|
|
|
+ } else if (type === 'live') {
|
|
|
+ watchTime = this.liveCompleteWatchTime;
|
|
|
+ } else if (type === 'replay') {
|
|
|
+ watchTime = this.replayCompleteWatchTime;
|
|
|
+ }
|
|
|
+ // 更新查询参数并重新获取数据
|
|
|
+ this.queryParams.completeWatchTime = watchTime;
|
|
|
+ // this.getStatistics();
|
|
|
+ this.getList();
|
|
|
+ this.$message.success('设置成功');
|
|
|
}
|
|
|
- };
|
|
|
+ }
|
|
|
+};
|
|
|
</script>
|
|
|
|
|
|
<style scoped>
|
|
|
- .app-container {
|
|
|
- padding: 20px;
|
|
|
- }
|
|
|
-
|
|
|
- .title {
|
|
|
- font-size: 18px;
|
|
|
- font-weight: bold;
|
|
|
- margin-bottom: 10px;
|
|
|
- }
|
|
|
- .live-container {
|
|
|
- display: flex;
|
|
|
- gap: 15px; /* 控制卡片之间的间距 */
|
|
|
- overflow-x: auto; /* 允许横向滚动 */
|
|
|
- padding-bottom: 10px;
|
|
|
- }
|
|
|
-
|
|
|
- /* 卡片样式 */
|
|
|
- .live-card {
|
|
|
- width: 380px;
|
|
|
- height: 225px;
|
|
|
- background: rgb(250,250,250);
|
|
|
- border-radius: 10px;
|
|
|
- padding: 15px;
|
|
|
- box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
|
|
|
- display: flex;
|
|
|
- flex-direction: column;
|
|
|
- align-items: flex-start;
|
|
|
- }
|
|
|
-
|
|
|
- /* 直播状态 */
|
|
|
- .status {
|
|
|
- background: #d3d3d3;
|
|
|
- color: #333;
|
|
|
- font-size: 14px;
|
|
|
- padding: 4px 8px;
|
|
|
- border-radius: 5px;
|
|
|
- display: inline-block;
|
|
|
- }
|
|
|
-
|
|
|
- /* 不同状态颜色 */
|
|
|
- .status-upcoming {
|
|
|
- background: #FFC107; /* 黄色 */
|
|
|
- color: #333;
|
|
|
- }
|
|
|
-
|
|
|
- .status-live {
|
|
|
- background: #28A745; /* 绿色 */
|
|
|
- color: white;
|
|
|
- }
|
|
|
-
|
|
|
- .status-ended {
|
|
|
- background: #D3D3D3; /* 灰色 */
|
|
|
- color: #333;
|
|
|
- }
|
|
|
- /* 直播信息部分 */
|
|
|
- .content {
|
|
|
- background: rgb(250,250,250);
|
|
|
- display: flex;
|
|
|
- gap: 10px;
|
|
|
- align-items: center;
|
|
|
- width: 90%;
|
|
|
- margin-top: 0px;
|
|
|
- }
|
|
|
-
|
|
|
- /* 直播封面图片 */
|
|
|
- .cover-image {
|
|
|
- width: 70px;
|
|
|
- height: 70px;
|
|
|
- border-radius: 10px;
|
|
|
- object-fit: cover;
|
|
|
- }
|
|
|
-
|
|
|
- /* 直播文本信息 */
|
|
|
- .info {
|
|
|
- flex: 1;
|
|
|
- }
|
|
|
-
|
|
|
- .live-title {
|
|
|
- font-size: 16px;
|
|
|
- font-weight: bold;
|
|
|
- white-space: nowrap;
|
|
|
- overflow: hidden;
|
|
|
- text-overflow: ellipsis;
|
|
|
- max-width: 250px; /* 防止标题过长 */
|
|
|
- }
|
|
|
-
|
|
|
- .time {
|
|
|
- font-size: 14px;
|
|
|
- color: #666;
|
|
|
- }
|
|
|
-
|
|
|
- /* 直播数据部分 */
|
|
|
- .stats {
|
|
|
- display: flex;
|
|
|
- justify-content: space-between;
|
|
|
- width: 100%;
|
|
|
- flex-shrink: 0;
|
|
|
- margin-top: 10px;
|
|
|
- }
|
|
|
-
|
|
|
- .stat-item {
|
|
|
- text-align: center;
|
|
|
- flex: 1;
|
|
|
- }
|
|
|
-
|
|
|
- .label {
|
|
|
- font-size: 14px;
|
|
|
- color: #888;
|
|
|
- }
|
|
|
-
|
|
|
- .value {
|
|
|
- font-size: 18px;
|
|
|
- font-weight: bold;
|
|
|
- color: #333;
|
|
|
- }
|
|
|
- /* 直播趋势 */
|
|
|
- .trend-section {
|
|
|
- margin-top: 30px;
|
|
|
- }
|
|
|
- .trend-header {
|
|
|
- display: flex;
|
|
|
- justify-content: space-between;
|
|
|
- align-items: center;
|
|
|
- }
|
|
|
- .trend-cards {
|
|
|
- display: flex;
|
|
|
- gap: 15px;
|
|
|
- }
|
|
|
- .trend-card {
|
|
|
- width: 280px;
|
|
|
- hight: 100px;
|
|
|
- border: 1px solid #ddd;
|
|
|
- border-radius: 8px;
|
|
|
- padding: 16px;
|
|
|
- transition: all 0.3s;
|
|
|
- position: relative;
|
|
|
- }
|
|
|
-
|
|
|
- .trend-card.active {
|
|
|
- border-color: #007bff; /* 选中时边框变蓝 */
|
|
|
- box-shadow: 0px 0px 10px rgba(0, 123, 255, 0.3);
|
|
|
- }
|
|
|
-
|
|
|
- .trend-card.active::after {
|
|
|
- content: "✔";
|
|
|
- position: absolute;
|
|
|
- bottom: 8px;
|
|
|
- right: 8px;
|
|
|
- color: white;
|
|
|
- background: #007bff;
|
|
|
- border-radius: 50%;
|
|
|
- width: 20px;
|
|
|
- height: 20px;
|
|
|
- display: flex;
|
|
|
- justify-content: center;
|
|
|
- align-items: center;
|
|
|
- font-size: 14px;
|
|
|
- font-weight: bold;
|
|
|
- }
|
|
|
- .trend-value {
|
|
|
- font-size: 24px;
|
|
|
- font-weight: bold;
|
|
|
- }
|
|
|
-
|
|
|
- /* 折线图 */
|
|
|
- .chart {
|
|
|
- width: 100%;
|
|
|
- height: 300px;
|
|
|
- margin-top: 20px;
|
|
|
- }
|
|
|
- .top10-section {
|
|
|
- margin-top: 20px;
|
|
|
- background: #fff;
|
|
|
- padding: 16px;
|
|
|
- border-radius: 8px;
|
|
|
- box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
|
|
- }
|
|
|
- .live-name {
|
|
|
- display: flex;
|
|
|
- align-items: center;
|
|
|
- }
|
|
|
-
|
|
|
- .live-cover {
|
|
|
- width: 80px; /* 直播封面宽度 */
|
|
|
- height: 45px; /* 直播封面高度 */
|
|
|
- border-radius: 4px;
|
|
|
- margin-right: 10px;
|
|
|
- object-fit: cover;
|
|
|
- }
|
|
|
-
|
|
|
- .live-title {
|
|
|
- font-size: 14px;
|
|
|
- color: #333;
|
|
|
- white-space: nowrap;
|
|
|
- overflow: hidden;
|
|
|
- text-overflow: ellipsis;
|
|
|
- }
|
|
|
- .ranking-tabs {
|
|
|
- display: flex;
|
|
|
- justify-content: space-between;
|
|
|
- align-items: center;
|
|
|
- padding: 10px 20px;
|
|
|
- }
|
|
|
-
|
|
|
- .ranking-container {
|
|
|
- display: flex;
|
|
|
- justify-content: space-between;
|
|
|
- align-items: center;
|
|
|
- margin: 10px 0;
|
|
|
- border-radius: 8px;
|
|
|
- overflow: hidden;
|
|
|
- }
|
|
|
-
|
|
|
- .ranking-section {
|
|
|
- display: flex;
|
|
|
- align-items: center;
|
|
|
- width: 50%;
|
|
|
- padding: 10px 15px;
|
|
|
- }
|
|
|
-
|
|
|
- .red-rank {
|
|
|
- background: linear-gradient(90deg, #ff6b6b, #ff8e8e);
|
|
|
- justify-content: flex-start;
|
|
|
- }
|
|
|
-
|
|
|
- .black-rank {
|
|
|
- background: linear-gradient(90deg, #4a4a4a, #6b6b6b);
|
|
|
- justify-content: flex-end;
|
|
|
- }
|
|
|
-
|
|
|
- .rank-title {
|
|
|
- font-size: 18px;
|
|
|
- font-weight: bold;
|
|
|
- color: white;
|
|
|
- margin: 0 15px;
|
|
|
- }
|
|
|
-
|
|
|
- .rank-filters {
|
|
|
- display: flex;
|
|
|
- }
|
|
|
-
|
|
|
- button {
|
|
|
- background: rgba(255, 255, 255, 0.2);
|
|
|
- border: 1px solid rgba(255, 255, 255, 0.5);
|
|
|
- color: white;
|
|
|
- font-size: 14px;
|
|
|
- padding: 5px 12px;
|
|
|
- margin: 0 5px;
|
|
|
- border-radius: 15px;
|
|
|
- cursor: pointer;
|
|
|
- transition: all 0.3s ease;
|
|
|
- }
|
|
|
-
|
|
|
- button.active {
|
|
|
- background: white;
|
|
|
- color: #ff4d4f;
|
|
|
- }
|
|
|
- .filter-container {
|
|
|
- display: flex;
|
|
|
- align-items: center;
|
|
|
- gap: 8px; /* 控制两个组件的间距 */
|
|
|
- }
|
|
|
-
|
|
|
- .filter-label {
|
|
|
- font-weight: bold;
|
|
|
- white-space: nowrap; /* 防止换行 */
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- .student-section {
|
|
|
- margin-top: 20px;
|
|
|
- background: #fff;
|
|
|
- padding: 16px;
|
|
|
- border-radius: 8px;
|
|
|
- box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
|
|
- }
|
|
|
- .column-item {
|
|
|
- display: flex;
|
|
|
- align-items: center;
|
|
|
- justify-content: space-between;
|
|
|
- padding: 8px;
|
|
|
- border: 1px solid #ddd;
|
|
|
- background: #f9f9f9;
|
|
|
- margin-bottom: 5px;
|
|
|
- cursor: grab;
|
|
|
- }
|
|
|
-
|
|
|
- .drag-handle {
|
|
|
- cursor: grab;
|
|
|
- }
|
|
|
- /* 自定义列按钮的样式 */
|
|
|
- .column-button-container {
|
|
|
- text-align: right; /* 使按钮右对齐 */
|
|
|
- margin-bottom: 10px; /* 为按钮和表格之间增加间距 */
|
|
|
- }
|
|
|
-
|
|
|
- .custom-column-button {
|
|
|
- background-color: #409EFF;
|
|
|
- color: black;
|
|
|
- padding: 10px 20px;
|
|
|
- border-radius: 5px;
|
|
|
- margin-right: 10px; /* 如果有多个按钮,确保按钮之间有间距 */
|
|
|
- }
|
|
|
-
|
|
|
- /* 表格区域的样式 */
|
|
|
- .student-section {
|
|
|
- padding: 20px;
|
|
|
- }
|
|
|
-
|
|
|
- .el-table {
|
|
|
- margin-top: 20px; /* 为表格添加顶部间距 */
|
|
|
- }
|
|
|
-
|
|
|
- .el-table-column {
|
|
|
- /* 让列文本超出时显示省略号 */
|
|
|
- white-space: nowrap;
|
|
|
- text-overflow: ellipsis;
|
|
|
- overflow: hidden;
|
|
|
- }
|
|
|
-
|
|
|
- .el-table-column:hover {
|
|
|
- cursor: pointer;
|
|
|
- }
|
|
|
- .no-wrap-column .cell {
|
|
|
- white-space: nowrap;
|
|
|
- text-overflow: ellipsis;
|
|
|
- overflow: hidden;
|
|
|
- text-align: center; /* 使列内容居中 */
|
|
|
- }
|
|
|
- .advanced-search {
|
|
|
- margin-bottom: 10px;
|
|
|
- padding: 15px;
|
|
|
- }
|
|
|
- .button-group {
|
|
|
- display: flex;
|
|
|
- justify-content: flex-end;
|
|
|
- width: 100%;
|
|
|
- }
|
|
|
-
|
|
|
- /* 让按钮默认显示 */
|
|
|
- .el-button {
|
|
|
- opacity: 1; /* 确保默认状态可见 */
|
|
|
- color: #606266; /* 默认文字颜色 */
|
|
|
- border-color: #dcdfe6; /* 默认边框颜色 */
|
|
|
- background-color: #f5f7fa; /* 默认背景色 */
|
|
|
- transition: all 0.3s; /* 添加动画过渡 */
|
|
|
- }
|
|
|
-
|
|
|
- /* 鼠标悬停时高亮 */
|
|
|
- .el-button:hover {
|
|
|
- color: #409eff !important; /* 文字变蓝 */
|
|
|
- border-color: #409eff !important; /* 边框变蓝 */
|
|
|
- background-color: #ecf5ff !important; /* 背景变浅蓝 */
|
|
|
- }
|
|
|
-
|
|
|
- /* “展开/收起” 按钮特殊处理 */
|
|
|
- .el-button[type="text"] {
|
|
|
- background: none;
|
|
|
- border: none;
|
|
|
- color: #606266;
|
|
|
- }
|
|
|
-
|
|
|
- /* 鼠标悬停时高亮 */
|
|
|
- .el-button[type="text"]:hover {
|
|
|
- color: #409eff !important;
|
|
|
- }
|
|
|
- .el-form-item-ellipsis .el-input__inner,
|
|
|
- .el-form-item-ellipsis .el-select .el-input__inner {
|
|
|
- text-align: center; /* 输入框内容居中对齐 */
|
|
|
- }
|
|
|
-
|
|
|
- /* 控制 label 长度过长时显示省略号 */
|
|
|
- .el-form-item-ellipsis .el-form-item__label {
|
|
|
- white-space: nowrap;
|
|
|
- overflow: hidden;
|
|
|
- text-overflow: ellipsis;
|
|
|
- width: 100%; /* 确保标签可以适配宽度 */
|
|
|
- text-align: right;
|
|
|
- vertical-align: middle;
|
|
|
- float: left;
|
|
|
- font-size: 14px;
|
|
|
- color: #606266;
|
|
|
- line-height: 40px;
|
|
|
- padding: 0 10px 0 0;
|
|
|
- -webkit-box-sizing: border-box;
|
|
|
- box-sizing: border-box;
|
|
|
- cursor: pointer;
|
|
|
- }
|
|
|
-
|
|
|
- /* 直播信息整体布局 */
|
|
|
- .live-info {
|
|
|
- display: flex;
|
|
|
- align-items: center;
|
|
|
- }
|
|
|
-
|
|
|
- /* 直播封面图 */
|
|
|
- .live-cover {
|
|
|
- width: 80px;
|
|
|
- height: 80px;
|
|
|
- object-fit: cover;
|
|
|
- border-radius: 6px;
|
|
|
- margin-right: 10px;
|
|
|
- }
|
|
|
-
|
|
|
- /* 直播名称 + 时间 */
|
|
|
- .live-text {
|
|
|
- display: flex;
|
|
|
- flex-direction: column;
|
|
|
- justify-content: center;
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- /* 直播时间 */
|
|
|
- .live-time {
|
|
|
- font-size: 12px;
|
|
|
- color: #888;
|
|
|
- margin-top: 4px;
|
|
|
- }
|
|
|
-
|
|
|
- /* 直播状态 */
|
|
|
- .status-container {
|
|
|
- display: flex;
|
|
|
- align-items: center;
|
|
|
- }
|
|
|
-
|
|
|
- /* 状态圆点 */
|
|
|
- .status-dot {
|
|
|
- width: 8px;
|
|
|
- height: 8px;
|
|
|
- border-radius: 50%;
|
|
|
- display: inline-block;
|
|
|
- margin-right: 6px;
|
|
|
- }
|
|
|
- .live-name-input .el-input__inner {
|
|
|
- white-space: normal; /* 允许文本换行 */
|
|
|
- word-wrap: break-word; /* 长文本自动换行 */
|
|
|
- padding-left: 10px;
|
|
|
- }
|
|
|
-
|
|
|
- .live-name-tag {
|
|
|
- margin-right: 5px;
|
|
|
- display: inline-block; /* 确保标签显示为块级元素 */
|
|
|
- }
|
|
|
- .flex-container {
|
|
|
- display: flex;
|
|
|
- align-items: center;
|
|
|
- gap: 10px; /* 调整 label 与输入框间距 */
|
|
|
- width: 90%;
|
|
|
- }
|
|
|
-
|
|
|
- .ellipsis-label .el-form-item__label {
|
|
|
- white-space: nowrap;
|
|
|
- overflow: hidden;
|
|
|
- text-overflow: ellipsis;
|
|
|
- max-width: 80px; /* 你可以调整这个值 */
|
|
|
- display: block;
|
|
|
- padding: 0 10px 0 0;
|
|
|
- }
|
|
|
-
|
|
|
- .full-width-picker {
|
|
|
- flex: 1; /* 让日期选择器填充剩余空间 */
|
|
|
- }
|
|
|
-
|
|
|
+.statistics-card {
|
|
|
+ margin-bottom: 20px;
|
|
|
+}
|
|
|
+
|
|
|
+.statistics-item {
|
|
|
+ text-align: center;
|
|
|
+ padding: 15px;
|
|
|
+ background: #f5f7fa;
|
|
|
+ border-radius: 4px;
|
|
|
+}
|
|
|
+
|
|
|
+.statistics-title {
|
|
|
+ font-size: 14px;
|
|
|
+ color: #606266;
|
|
|
+ margin-bottom: 10px;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+}
|
|
|
+
|
|
|
+.statistics-value {
|
|
|
+ font-size: 24px;
|
|
|
+ font-weight: bold;
|
|
|
+ color: #303133;
|
|
|
+}
|
|
|
+
|
|
|
+.selection-toolbar {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ margin-bottom: 10px;
|
|
|
+ padding-left: 10px;
|
|
|
+}
|
|
|
+
|
|
|
+.selection-toolbar .el-checkbox {
|
|
|
+ margin-right: 10px;
|
|
|
+}
|
|
|
+
|
|
|
+.live-data-css {
|
|
|
+ padding-left: 10px;
|
|
|
+ padding-top: 30px;
|
|
|
+}
|
|
|
</style>
|
|
|
-
|