123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709 |
- <template>
- <div class="statistics-dashboard">
- <!-- 数据概览 (Data Overview) -->
- <el-card class="overview-section" shadow="never">
- <div slot="header" class="header">
- <span>数据概览</span>
- <el-dropdown class="dropdown-menu" trigger="click">
- <span class="el-dropdown-link">
- 部门方案查看 <i class="el-icon-arrow-down el-icon--right"></i>
- </span>
- <el-dropdown-menu slot="dropdown">
- <el-dropdown-item>全部部门</el-dropdown-item>
- <el-dropdown-item>销售部</el-dropdown-item>
- <el-dropdown-item>市场部</el-dropdown-item>
- </el-dropdown-menu>
- </el-dropdown>
- </div>
- <el-row :gutter="20">
- <el-col :span="3">
- <div class="data-card">
- <div class="card-title">
- <i class="el-icon-user-solid"></i>
- 分公司数量
- </div>
- <div class="card-value highlight">{{dealderCount}}</div>
- </div>
- </el-col>
- <el-col :span="3">
- <div class="data-card">
- <div class="card-title">
- <i class="el-icon-user"></i>
- 销售数量
- </div>
- <div class="card-value highlight">{{groupMgrCount}}</div>
- </div>
- </el-col>
- <el-col :span="3">
- <div class="data-card">
- <div class="card-title">
- <i class="el-icon-shopping-cart-full"></i>
- 会员数量
- </div>
- <div class="card-value highlight">{{memberCount}}</div>
- <div class="card-badge">
- <i class="el-icon-camera"></i>
- </div>
- </div>
- </el-col>
- <el-col :span="3">
- <div class="data-card">
- <div class="card-title">
- <i class="el-icon-money"></i>
- 可用余额
- </div>
- <div class="card-value highlight">143,650.07</div>
- </div>
- </el-col>
- <el-col :span="3">
- <div class="data-card">
- <div class="card-title">
- <span>今日消耗</span>
- </div>
- <div class="card-value highlight">1,093.70</div>
- <div class="card-sub">
- <span>昨日消耗(元)</span>
- <span class="sub-value">1952.8</span>
- </div>
- <el-progress :percentage="74" :show-text="false" color="#409EFF"></el-progress>
- <div class="card-desc">预测不足74天</div>
- </div>
- </el-col>
- <el-col :span="3">
- <div class="data-card">
- <div class="card-title">
- <span class="cdn-label">CDN</span>
- 今日
- </div>
- <div class="card-value highlight">1.79T</div>
- <div class="card-sub">
- <span>本月</span>
- <span class="sub-value">18.45T</span>
- </div>
- </div>
- </el-col>
- <el-col :span="3">
- <div class="data-card">
- <div class="card-title">
- <i class="el-icon-message"></i>
- 短信剩余条数
- </div>
- <div class="card-value highlight">{{smsRemainCount}}</div>
- </div>
- </el-col>
- <el-col :span="3">
- <div class="data-card">
- <div class="card-title">
- 平台今日看课人数
- </div>
- <div class="card-value highlight">{{todayWatchUserCount}}</div>
- <div class="card-sub">
- <span>配额上限</span>
- <span class="sub-value">{{todayWatchUserCount}}/{{versionLimit}}</span>
- </div>
- <el-progress :percentage="70" :show-text="false" color="#409EFF"></el-progress>
- </div>
- </el-col>
- </el-row>
- </el-card>
- <!-- 分析概览 (Analysis Overview) -->
- <el-card class="analysis-section" shadow="never">
- <div slot="header" class="header">
- <span>分析概览</span>
- <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">
- <el-button size="small" plain icon="el-icon-refresh">手动刷新</el-button>
- <el-button size="small" plain>自动刷新</el-button>
- <el-button size="small" type="primary">刷新</el-button>
- </div>
- </div>
- <el-row :gutter="20">
- <el-col :span="6">
- <div class="analysis-card">
- <div class="card-icon"><i class="el-icon-monitor"></i></div>
- <div class="card-content">
- <div class="card-row">
- <span>观看人数</span>
- <span class="highlight">{{watchUserCount}}</span>
- </div>
- <div class="card-row">
- <span>完播人数</span>
- <span class="highlight">{{completedUserCount}}</span>
- </div>
- <div class="card-row">
- <span>完播率</span>
- <span class="highlight">{{completedRate}}%</span>
- </div>
- </div>
- </div>
- </el-col>
- <el-col :span="6">
- <div class="analysis-card">
- <div class="card-icon"><i class="el-icon-video-play"></i></div>
- <div class="card-content">
- <div class="card-row">
- <span>观看次数</span>
- <span class="highlight">{{watchCount}}</span>
- </div>
- <div class="card-row">
- <span>完播次数</span>
- <span class="highlight">{{completedCount}}</span>
- </div>
- <div class="card-row">
- <span>视频完播率</span>
- <span class="highlight">{{watchRate}}</span>
- </div>
- </div>
- </div>
- </el-col>
- <el-col :span="6">
- <div class="analysis-card">
- <div class="card-icon"><i class="el-icon-headset"></i></div>
- <div class="card-content">
- <div class="card-row">
- <span>答题人数</span>
- <span class="highlight">{{answerMemberCount}}</span>
- </div>
- <div class="card-row">
- <span>正确人数</span>
- <span class="highlight">{{correctUserCount}}</span>
- </div>
- <div class="card-row">
- <span>正确率</span>
- <span class="highlight">{{correctRate}}%</span>
- </div>
- </div>
- </div>
- </el-col>
- <el-col :span="6">
- <div class="analysis-card">
- <div class="card-icon"><i class="el-icon-present"></i></div>
- <div class="card-content">
- <div class="card-row">
- <span>答题红包个数</span>
- <span class="highlight">{{rewardCount}}</span>
- </div>
- <div class="card-row">
- <span>答题红包金额(元)</span>
- <span class="highlight">{{rewardMoney}}</span>
- </div>
- </div>
- </div>
- </el-col>
- </el-row>
- </el-card>
- <!-- 图表区域 (Charts Area) -->
- <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>
- <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>经销商会员观看TOP10</span>
- <div class="legend">
- <el-radio-group v-model="viewerType" size="small">
- <el-radio-button label="viewers">按观看人数</el-radio-button>
- <el-radio-button label="completed">按完播人数</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="dealerChart" class="chart-container"></div>
- </el-card>
- </el-col>
- </el-row>
- </div>
- </template>
- <script>
- import * as echarts from 'echarts'
- import {analysisPreview, authorizationInfo, dealerAggregated, smsBalance} from "@/api/statistics/statistics";
- import dayjs from 'dayjs';
- export default {
- name: 'StatisticsDashboard',
- data() {
- return {
- smsRemainCount: 0,
- viewerType: 'viewers',
- viewerChart: null,
- dealerChart: null,
- // 分公司数量
- dealderCount: 0,
- // 销售数量
- groupMgrCount: 0,
- // 会员总数量
- memberCount: 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
- }
- },
- mounted() {
- this.$nextTick(() => {
- this.initViewerChart()
- this.initDealerChart()
- // 监听窗口大小变化,重新渲染图表
- window.addEventListener('resize', () => {
- this.viewerChart && this.viewerChart.resize()
- this.dealerChart && this.dealerChart.resize()
- })
- })
- },
- created() {
- dealerAggregated().then(res=>{
- if(res.code === 200){
- this.dealderCount = res.data.dealderCount;
- this.groupMgrCount = res.data.groupMgrCount;
- this.memberCount = res.data.memberCount;
- this.normalNum = res.data.normalNum;
- this.blackNum = res.data.blackNum;
- }
- })
- let param = {
- startTime: '',
- endTime: ''
- };
- // 获取当前日期时间
- 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;
- }
- })
- smsBalance().then(res=>{
- if(res.code === 200){
- this.smsRemainCount = res.data;
- }
- })
- authorizationInfo().then(res=>{
- if(res.code === 200){
- this.todayWatchUserCount = res.data.todayWatchUserCount;
- this.versionLimit = res.data.versionLimit;
- }
- })
- },
- methods: {
- formatDate(date) {
- return dayjs(date).format('YYYY-MM-DD');
- },
- // 分析概览
- handleAnalysis(){
- let param = {
- startTime: '',
- endTime: ''
- };
- // 获取当前日期时间
- const today = dayjs();
- if (this.queryTime === '今日') {
- param.startTime = this.formatDate(today);
- param.endTime = this.formatDate(today);
- } else if (this.queryTime === '昨日') {
- const yesterday = today.subtract(1, 'day');
- param.startTime = this.formatDate(yesterday);
- param.endTime = this.formatDate(yesterday);
- } else if (this.queryTime === '本周') {
- param.startTime = this.formatDate(today.startOf('week'));
- param.endTime = this.formatDate(today.endOf('week'));
- } else if (this.queryTime === '本月') {
- param.startTime = this.formatDate(today.startOf('month'));
- param.endTime = this.formatDate(today.endOf('month'));
- } else if (this.queryTime === '上月') {
- const lastMonth = today.subtract(1, 'month');
- param.startTime = this.formatDate(lastMonth.startOf('month'));
- param.endTime = this.formatDate(lastMonth.endOf('month'));
- } else {
- // 可以添加一个默认处理或错误提示
- console.warn(`未知的 queryTime: ${this.queryTime}, 默认使用今日`);
- 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;
- }
- })
- },
- initViewerChart() {
- this.viewerChart = echarts.init(this.$refs.viewerChart)
- const option = {
- 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: [50, 20, 40, 120, 700, 800, 900, 780, 700, 700, 680, 580, 550, 450, 400, 320, 170, 0, 0, 0, 0, 0, 0, 0],
- itemStyle: {
- color: '#409EFF'
- }
- },
- {
- name: '完播人数',
- type: 'bar',
- data: [30, 10, 20, 300, 600, 700, 780, 700, 650, 650, 650, 520, 450, 400, 330, 120, 100, 0, 0, 0, 0, 0, 0, 0],
- itemStyle: {
- color: '#67C23A'
- }
- }
- ]
- }
- this.viewerChart.setOption(option)
- },
- initDealerChart() {
- this.dealerChart = echarts.init(this.$refs.dealerChart)
- const option = {
- tooltip: {
- trigger: 'axis',
- axisPointer: {
- type: 'shadow'
- }
- },
- grid: {
- left: '3%',
- right: '4%',
- bottom: '3%',
- containLabel: true
- },
- xAxis: {
- type: 'value'
- },
- yAxis: {
- type: 'category',
- data: ['七彩公司', '二级经销商A', '内蒙七彩', '呼和浩特分公司', '呼和浩特经销商', '海南经销', '重庆市分公司', '东方分销商', '银田分公司', '重庆一分销']
- },
- series: [
- {
- name: '观看人数',
- type: 'bar',
- data: [2780, 1050, 650, 600, 500, 350, 260, 230, 180, 120],
- itemStyle: {
- color: '#409EFF'
- }
- }
- ]
- }
- this.dealerChart.setOption(option)
- }
- },
- watch: {
- viewerType(newVal) {
- if (newVal === 'viewers') {
- this.dealerChart.setOption({
- series: [{
- data: [2780, 1050, 650, 600, 500, 350, 260, 230, 180, 120]
- }]
- })
- } else {
- this.dealerChart.setOption({
- series: [{
- name: '完播人数',
- data: [2500, 980, 620, 580, 480, 320, 240, 210, 160, 100]
- }]
- })
- }
- }
- },
- beforeDestroy() {
- window.removeEventListener('resize', this.resizeHandler)
- this.viewerChart && this.viewerChart.dispose()
- this.dealerChart && this.dealerChart.dispose()
- }
- }
- </script>
- <style scoped>
- ::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: 100px;
- display: flex;
- flex-direction: column;
- position: relative;
- }
- .card-title {
- color: #606266;
- font-size: 14px;
- margin-bottom: 10px;
- }
- .card-value {
- font-size: 24px;
- font-weight: bold;
- margin-top: auto;
- }
- .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 {
- background-color: #fff;
- border-radius: 4px;
- padding: 20px;
- display: flex;
- box-shadow: 0 0 10px rgba(0, 0, 0, 0.05);
- }
- .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 {
- flex: 1;
- }
- .card-row {
- display: flex;
- justify-content: space-between;
- margin-bottom: 10px;
- }
- .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%;
- }
- </style>
|