4 changed files with 1360 additions and 874 deletions
-
71src/components/workspace/CashManagement.vue
-
770src/components/workspace/GoldGraph.vue
-
454src/components/workspace/GoldManagement.vue
-
939src/views/workspace/index.vue
@ -0,0 +1,71 @@ |
|||
<template> |
|||
<span class="text"> 现金管理 |
|||
最后更新时间:{{ |
|||
workDataUpdateTime && workDataUpdateTime !== '1970-01-01 08:00:00' ? workDataUpdateTime : '该地区暂无数据' |
|||
}}</span> |
|||
<div> |
|||
<el-card class="cash-card"> |
|||
<div>总营收:{{ cashData.totalIncome }}</div> |
|||
<div ref="chartRef" class="chart"></div> |
|||
</el-card> |
|||
</div> |
|||
</template> |
|||
|
|||
<script setup> |
|||
import * as echarts from 'echarts' |
|||
import {ref, onMounted} from 'vue' |
|||
|
|||
// 模拟数据 |
|||
const cashData = ref({ |
|||
updateTime: '2025-09-24 12:00:00', |
|||
totalIncome: 1200000, |
|||
markets: [ |
|||
{name: '北京', value: 450000}, |
|||
{name: '上海', value: 300000}, |
|||
{name: '广州', value: 200000}, |
|||
{name: '深圳', value: 150000}, |
|||
{name: '其他', value: 100000} |
|||
] |
|||
}) |
|||
|
|||
const chartRef = ref(null) |
|||
let chartInstance = null |
|||
|
|||
const renderChart = () => { |
|||
if (!chartInstance && chartRef.value) { |
|||
chartInstance = echarts.init(chartRef.value) |
|||
} |
|||
const option = { |
|||
tooltip: {trigger: 'item'}, |
|||
legend: {bottom: 10}, |
|||
series: [ |
|||
{ |
|||
type: 'pie', |
|||
radius: ['40%', '70%'], |
|||
data: cashData.value.markets |
|||
} |
|||
] |
|||
} |
|||
chartInstance.setOption(option) |
|||
} |
|||
|
|||
onMounted(() => { |
|||
renderChart() |
|||
}) |
|||
</script> |
|||
|
|||
<style scoped> |
|||
.cash-card { |
|||
width: 100%; |
|||
} |
|||
|
|||
.chart { |
|||
width: 100%; |
|||
height: 200px; |
|||
} |
|||
|
|||
.cash-title { |
|||
font-weight: bold; |
|||
font-size: 16px; |
|||
} |
|||
</style> |
@ -0,0 +1,770 @@ |
|||
<template> |
|||
<div class="graph"> |
|||
<el-card style="width:84vw;"> |
|||
<div> |
|||
<el-tabs v-model="activeTab" @tab-change="handleTabChange"> |
|||
<el-tab-pane label="金币充值" name="recharge"></el-tab-pane> |
|||
<el-tab-pane label="金币消费" name="consume"></el-tab-pane> |
|||
</el-tabs> |
|||
</div> |
|||
<div class="condition"> |
|||
<div class="stats"> |
|||
<div v-if="activeTab === 'consume'">合计:{{ sumConsume / 100 }}</div> |
|||
永久金币: {{ activeTab === 'recharge' ? sumRechargePermanent / 100 : sumConsumePermanent / 100 }} |
|||
免费金币: {{ activeTab === 'recharge' ? sumRechargeFree / 100 : sumConsumeFree / 100 }} |
|||
任务金币: {{ activeTab === 'recharge' ? sumRechargeTask / 100 : sumConsumeTask / 100 }} |
|||
</div> |
|||
<div> |
|||
<el-button @click="getYes()" size="small" :type="activeTimeRange === 'yes' ? 'primary' : ''">昨天 |
|||
</el-button> |
|||
<el-button @click="getToday()" size="small" :type="activeTimeRange === 'today' ? 'primary' : ''">今天 |
|||
</el-button> |
|||
<el-button @click="getWeek()" size="small" :type="activeTimeRange === 'week' ? 'primary' : ''">本周 |
|||
</el-button> |
|||
<el-button @click="getMonth()" size="small" :type="activeTimeRange === 'month' ? 'primary' : ''">本月 |
|||
</el-button> |
|||
<el-button @click="getYear()" size="small" :type="activeTimeRange === 'year' ? 'primary' : ''">本年 |
|||
</el-button> |
|||
</div> |
|||
<div> |
|||
<el-date-picker size="small" v-model="dateRange" type="datetimerange" range-separator="→" |
|||
start-placeholder="开始时间" end-placeholder="结束时间" format="YYYY-MM-DD HH:mm:ss" |
|||
style="width:20vw;margin-left:0.5vw;" value-format="YYYY-MM-DD HH:mm:ss" :default-time="defaultTime" |
|||
:disabled-date="disabledDate" @change="handleDatePickerChange" /> |
|||
<el-button type="primary" size="small" style="margin-left: 0.5vw" @click="getChartData">查询</el-button> |
|||
</div> |
|||
</div> |
|||
|
|||
<div class="graph-content"> |
|||
<div ref="chartRef" class="left"></div> |
|||
<div class="right"> |
|||
<el-card> |
|||
<div class="card-large">金币{{ activeTab === 'recharge' ? '充值' : '消费' }}排名</div> |
|||
<el-select v-model="selectedType" style="width: 100%; margin-bottom: 15px"> |
|||
<el-option label="全部类型" value="all"></el-option> |
|||
<el-option label="永久金币" value="permanent"></el-option> |
|||
<el-option label="免费金币" value="free"></el-option> |
|||
<el-option label="任务金币" value="task"></el-option> |
|||
</el-select> |
|||
<el-table :data="tableData" height="320px"> |
|||
<el-table-column prop="rank" label="排名" width="60" align="center"></el-table-column> |
|||
<el-table-column prop="market" label="地区" align="center"> |
|||
<template #default="scope"> |
|||
<span>{{ marketMapping[scope.row.market] || scope.row.market }}</span> |
|||
</template> |
|||
</el-table-column> |
|||
<el-table-column prop="coinAmount" label="金币数量" align="center"> |
|||
<template #default="{ row }"> |
|||
{{ row.coinAmount.toLocaleString() }} |
|||
</template> |
|||
</el-table-column> |
|||
</el-table> |
|||
</el-card> |
|||
</div> |
|||
</div> |
|||
</el-card> |
|||
</div> |
|||
</template> |
|||
|
|||
<script setup> |
|||
import * as echarts from 'echarts' |
|||
import {onMounted, onUnmounted, ref, watch} from 'vue' |
|||
import API from '@/util/http' |
|||
import {ElMessage} from 'element-plus' |
|||
import dayjs from 'dayjs'; |
|||
import utc from 'dayjs-plugin-utc' |
|||
import {marketMapping} from "@/utils/marketMap.js"; |
|||
|
|||
dayjs.extend(utc) |
|||
|
|||
const defaultTime = [ |
|||
new Date(2000, 1, 1, 0, 0, 0), |
|||
new Date(2000, 2, 1, 23, 59, 59), |
|||
] |
|||
|
|||
// 地区数据 |
|||
const markets = ref([]) |
|||
// 图表相关 |
|||
const dateRange = ref([]) |
|||
const activeTab = ref('recharge') |
|||
const selectedType = ref('all') |
|||
const tableData = ref([]) |
|||
const chartRef = ref(null) |
|||
let chartInstance = null |
|||
// 图表合计数 |
|||
const sumRechargePermanent = ref(0) |
|||
const sumRechargeFree = ref(0) |
|||
const sumRechargeTask = ref(0) |
|||
const sumConsumePermanent = ref(0) |
|||
const sumConsumeFree = ref(0) |
|||
const sumConsumeTask = ref(0) |
|||
const sumConsume = ref(0) |
|||
// 用户信息 |
|||
const adminData = ref({}) |
|||
// 卡片数据相关 |
|||
const currentGold = ref(0) |
|||
const dailyChange = ref(0) |
|||
const currentPermanent = ref(0) |
|||
const currentFree = ref(0) |
|||
const currentFreeJune = ref(0) |
|||
const currentFreeDecember = ref(0) |
|||
const currentTask = ref(0) |
|||
const yearlyRecharge = ref(0) |
|||
const yearlyMoney = ref(0) |
|||
const recharge = ref(0) |
|||
const money = ref(0) |
|||
const yearlyReduce = ref(0) |
|||
const yearlyConsume = ref(0) |
|||
const yearlyRefund = ref(0) |
|||
const dailyReduce = ref(0) |
|||
const dailyConsume = ref(0) |
|||
const dailyRefund = ref(0) |
|||
const yearlyRechargeNum = ref(0) |
|||
const sumWow = ref(0) |
|||
const sumDaily = ref(0) |
|||
const rechargeNum = ref(0) |
|||
const ydayRechargeNum = ref(0) |
|||
const firstRecharge = ref(0) |
|||
const length = ref(0) |
|||
// 加载状态 |
|||
const chartLoading = ref(true) |
|||
|
|||
const handleResize = () => { |
|||
if (chartInstance.value) { |
|||
try { |
|||
chartInstance.value.resize() |
|||
console.log('resize一下') |
|||
} catch (error) { |
|||
console.error('图表resize失败:', error) |
|||
} |
|||
} |
|||
} |
|||
// 初始化图表 |
|||
const initChart = () => { |
|||
if (!chartInstance && chartRef.value) { |
|||
chartInstance = echarts.init(chartRef.value) |
|||
window.addEventListener('resize', handleResize) |
|||
} |
|||
} |
|||
// 销毁图表 |
|||
const destroyChart = () => { |
|||
if (chartInstance.value) { |
|||
try { |
|||
chartInstance.value.dispose() |
|||
} catch (error) { |
|||
console.error('图表销毁失败:', error) |
|||
} |
|||
chartInstance.value = null |
|||
} |
|||
window.removeEventListener('resize', handleResize) |
|||
} |
|||
const formatDate = function (date) { |
|||
const year = date.getFullYear(); |
|||
const month = String(date.getMonth() + 1).padStart(2, '0'); |
|||
const day = String(date.getDate()).padStart(2, '0'); |
|||
const hours = String(date.getHours()).padStart(2, '0'); |
|||
const minutes = String(date.getMinutes()).padStart(2, '0'); |
|||
const seconds = String(date.getSeconds()).padStart(2, '0'); |
|||
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`; |
|||
} |
|||
// 昨天 |
|||
const getYes = function () { |
|||
const yesterday = dayjs().subtract(1, 'day') |
|||
const startTime = yesterday.startOf('day').format('YYYY-MM-DD HH:mm:ss') |
|||
const endTime = yesterday.endOf('day').format('YYYY-MM-DD HH:mm:ss') |
|||
dateRange.value = [startTime, endTime] |
|||
console.log('看看dateRange', dateRange.value) |
|||
activeTimeRange.value = 'yes' // 标记当前激活状态 |
|||
|
|||
getChartData() |
|||
} |
|||
// 今天 |
|||
const getToday = function () { |
|||
const today = dayjs() |
|||
const startTime = today.startOf('day').format('YYYY-MM-DD HH:mm:ss') |
|||
const endTime = today.endOf('day').format('YYYY-MM-DD HH:mm:ss') |
|||
// const endTime = today.add(1, 'day').startOf('day').format('YYYY-MM-DD HH:mm:ss') |
|||
dateRange.value = [startTime, endTime] |
|||
console.log('看看dateRange', dateRange.value) |
|||
activeTimeRange.value = 'today' // 标记当前激活状态 |
|||
|
|||
getChartData() |
|||
} |
|||
// 本周 |
|||
const getWeek = function () { |
|||
const today = dayjs(); |
|||
// 获取今天是星期几(0是周日,1是周一,...,6是周六) |
|||
const day = today.day(); |
|||
|
|||
// 计算本周一(如果今天是周一,就取今天;如果是周日,就减6天) |
|||
let monday = today.subtract(day === 0 ? 6 : day - 1, 'day'); |
|||
// 计算本周日(如果今天是周日,就取今天;否则就加(7 - day)天) |
|||
let sunday = today.add(day === 0 ? 0 : 7 - day, 'day'); |
|||
|
|||
// 设置时间为起始和结束 |
|||
const startTime = monday.startOf('day').format('YYYY-MM-DD HH:mm:ss'); |
|||
const endTime = sunday.endOf('day').format('YYYY-MM-DD HH:mm:ss'); |
|||
|
|||
dateRange.value = [startTime, endTime]; |
|||
console.log('本周时间范围(周一到周日):', dateRange.value); |
|||
activeTimeRange.value = 'week'; |
|||
|
|||
getChartData(); |
|||
}; |
|||
// 本月 |
|||
const getMonth = function () { |
|||
const today = dayjs() |
|||
const startTime = today.startOf('month').format('YYYY-MM-DD HH:mm:ss') |
|||
// const endTime = today.add(1, 'month').startOf('month').format('YYYY-MM-DD HH:mm:ss') |
|||
const endTime = today.endOf('month').format('YYYY-MM-DD HH:mm:ss') |
|||
dateRange.value = [startTime, endTime] |
|||
console.log('看看dateRange', dateRange.value) |
|||
activeTimeRange.value = 'month' // 标记当前激活状态 |
|||
|
|||
getChartData() |
|||
} |
|||
// 本年 |
|||
const getYear = function () { |
|||
const today = dayjs() |
|||
const startTime = today.startOf('year').format('YYYY-MM-DD HH:mm:ss') |
|||
const endTime = today.endOf('year').format('YYYY-MM-DD HH:mm:ss') |
|||
// const endTime = today.add(1, 'year').startOf('year').format('YYYY-MM-DD HH:mm:ss') |
|||
dateRange.value = [startTime, endTime] |
|||
console.log('看看dateRange', dateRange.value) |
|||
activeTimeRange.value = 'year' // 标记当前激活状态 |
|||
|
|||
getChartData() |
|||
} |
|||
|
|||
// 要加上所有市场的,还有额外计算的(总数 = 永久 + 6月 + 12月 + 免费 + 任务) |
|||
const processData = (data) => { |
|||
const summary = { |
|||
currentGold: 0, |
|||
dailyChange: 0, |
|||
currentPermanent: 0, |
|||
currentFreeJune: 0, |
|||
currentFreeDecember: 0, |
|||
currentTask: 0, |
|||
currentFree: 0, |
|||
recharge: 0, |
|||
money: 0, |
|||
yearlyRecharge: 0, |
|||
yearlyMoney: 0, |
|||
consumePermanent: 0, |
|||
consumeFreeJune: 0, |
|||
consumeFreeDecember: 0, |
|||
consumeTask: 0, |
|||
refundPermanent: 0, |
|||
refundFreeJune: 0, |
|||
refundFreeDecember: 0, |
|||
refundTask: 0, |
|||
dailyReduce: 0, |
|||
yearlyConsume: 0, |
|||
yearlyRefund: 0, |
|||
yearlyReduce: 0, |
|||
rechargeNum: 0, |
|||
ydayRechargeNum: 0, |
|||
firstRecharge: 0, |
|||
sumWow: 0, |
|||
sumDaily: 0, |
|||
yearlyRechargeNum: 0 |
|||
} |
|||
|
|||
// 遍历市场 |
|||
data.marketCards.forEach(market => { |
|||
for (const i in summary) { |
|||
if (market[i] !== undefined && market[i] !== null) { // 其实还应该卡一个number |
|||
summary[i] += market[i] |
|||
} |
|||
} |
|||
}) |
|||
|
|||
// wow和daily除一下 |
|||
length.value = data.markets.length |
|||
console.log(length.value) |
|||
|
|||
// 计算昨日新增消费和退款 |
|||
const yesterdayConsume = summary.consumePermanent + summary.consumeFreeJune + summary.consumeFreeDecember + summary.consumeTask |
|||
const yesterdayRefund = summary.refundPermanent + summary.refundFreeJune + summary.refundFreeDecember + summary.refundTask |
|||
|
|||
// 更新卡片数据 |
|||
currentGold.value = summary.currentGold.toFixed(2) |
|||
dailyChange.value = summary.dailyChange.toFixed(2) |
|||
currentPermanent.value = summary.currentPermanent.toFixed(2) |
|||
currentFree.value = summary.currentFree.toFixed(2) |
|||
currentFreeJune.value = summary.currentFreeJune.toFixed(2) |
|||
currentFreeDecember.value = summary.currentFreeDecember.toFixed(2) |
|||
currentTask.value = summary.currentTask.toFixed(2) |
|||
|
|||
yearlyRecharge.value = summary.yearlyRecharge.toFixed(2) |
|||
yearlyMoney.value = summary.yearlyMoney.toFixed(2) |
|||
recharge.value = summary.recharge.toFixed(2) |
|||
money.value = summary.money.toFixed(2) |
|||
|
|||
yearlyReduce.value = summary.yearlyReduce.toFixed(2) |
|||
yearlyConsume.value = summary.yearlyConsume.toFixed(2) |
|||
yearlyRefund.value = summary.yearlyRefund.toFixed(2) |
|||
dailyReduce.value = summary.dailyReduce.toFixed(2) |
|||
dailyConsume.value = yesterdayConsume.toFixed(2) |
|||
dailyRefund.value = yesterdayRefund.toFixed(2) |
|||
|
|||
yearlyRechargeNum.value = summary.yearlyRechargeNum |
|||
|
|||
// // 周同比 |
|||
// sumWow.value = (marketCards.sumWow / length.value).toFixed(2) |
|||
// // 日环比 |
|||
// sumDaily.value = (marketCards.sumDaily / length.value).toFixed(2) |
|||
|
|||
// rechargeNum.value = summary.rechargeNum |
|||
ydayRechargeNum.value = summary.ydayRechargeNum |
|||
firstRecharge.value = summary.firstRecharge |
|||
} |
|||
|
|||
//无法选择的时间 |
|||
const disabledDate = (time) => { |
|||
const limitDate = new Date(2025, 0, 1); |
|||
return time.getTime() < limitDate.getTime(); |
|||
} |
|||
|
|||
// 获取市场列表 |
|||
const getMarkets = async () => { |
|||
console.log("adminData", adminData.value.account) |
|||
try { |
|||
const response = await API({ |
|||
url: '/general/adminMarkets', |
|||
data: { |
|||
account: adminData.value.account |
|||
} |
|||
}) |
|||
if (Array.isArray(response.data)) { |
|||
// markets.value = response.data.filter(data => data !== "1") |
|||
markets.value = response.data |
|||
console.log('市场列表获取成功:', markets.value) |
|||
} else { |
|||
console.error('获取市场列表失败', response) |
|||
ElMessage.error('获取市场列表失败') |
|||
} |
|||
} catch (error) { |
|||
console.error('获取市场列表失败:', error) |
|||
ElMessage.error('获取市场列表失败') |
|||
} |
|||
} |
|||
|
|||
// 获取图表数据 |
|||
const getChartData = async () => { |
|||
try { |
|||
// 校验市场数据到底有没有 |
|||
if (!markets.value || markets.value.length === 0) { |
|||
await getMarkets() |
|||
} |
|||
// 本年 |
|||
if (!dateRange.value || dateRange.value.length === 0) { |
|||
getYear() |
|||
} |
|||
const params = { |
|||
markets: markets.value, |
|||
startDate: dateRange.value[0], |
|||
endDate: dateRange.value[1] |
|||
}; |
|||
|
|||
|
|||
const response = await API({ |
|||
url: '/workbench/getGraph', |
|||
data: params |
|||
}) |
|||
console.log('看看params', params) |
|||
if (Array.isArray(response.marketGraphs)) { |
|||
// const filteredGraphs = response.marketGraphs.filter(data => data.market !== "1"); |
|||
// 处理图表数据 |
|||
processChartData(response.marketGraphs) |
|||
// 处理排名数据 |
|||
processRankingData(response.marketGraphs) |
|||
} else { |
|||
console.error('获取图表数据失败:', response) |
|||
ElMessage.error('获取图表数据失败') |
|||
} |
|||
} catch (error) { |
|||
console.error('获取图表数据失败:', error) |
|||
ElMessage.error('获取图表数据失败') |
|||
} |
|||
} |
|||
// 处理图表数据 |
|||
const processChartData = (marketCards) => { |
|||
const chartData = { |
|||
rechargePermanent: [], |
|||
rechargeFree: [], |
|||
rechargeTask: [], |
|||
consumePermanent: [], |
|||
consumeFree: [], |
|||
consumeTask: [], |
|||
sumConsume: [] |
|||
} |
|||
// 这是图表的合计数,怎样遍历????? |
|||
const sumRechargePermanent1 = ref(0) |
|||
const sumRechargeFree1 = ref(0) |
|||
const sumRechargeTask1 = ref(0) |
|||
const sumConsumePermanent1 = ref(0) |
|||
const sumConsumeFree1 = ref(0) |
|||
const sumConsumeTask1 = ref(0) |
|||
const sumConsume1 = ref(0) |
|||
|
|||
|
|||
marketCards.forEach(market => { |
|||
chartData.rechargePermanent.push(market.sumRechargePermanent / 100 || 0) |
|||
chartData.rechargeFree.push(market.sumRechargeFree / 100 || 0) |
|||
chartData.rechargeTask.push(market.sumRechargeTask / 100 || 0) |
|||
chartData.consumePermanent.push(market.sumConsumePermanent / 100 || 0) |
|||
chartData.consumeFree.push(market.sumConsumeFree / 100 || 0) |
|||
chartData.consumeTask.push(market.sumConsumeTask / 100 || 0) |
|||
chartData.sumConsume.push(market.sumConsume / 100 || 0) |
|||
|
|||
// 合计数合计数合计数咋算 |
|||
sumRechargePermanent1.value += (market.sumRechargePermanent || 0) |
|||
sumRechargeFree1.value += (market.sumRechargeFree || 0) |
|||
//sumRechargeTask1.value += (market.sumRechargeTask || 0) |
|||
sumConsumePermanent1.value += (market.sumConsumePermanent || 0) |
|||
sumConsumeFree1.value += (market.sumConsumeFree || 0) |
|||
sumConsumeTask1.value += (market.sumConsumeTask || 0) |
|||
sumConsume1.value += (market.sumConsume || 0) |
|||
}) |
|||
sumRechargePermanent.value = sumRechargePermanent1.value |
|||
sumRechargeFree.value = sumRechargeFree1.value |
|||
sumRechargeTask.value = 0 |
|||
sumConsumePermanent.value = sumConsumePermanent1.value |
|||
sumConsumeFree.value = sumConsumeFree1.value |
|||
sumConsumeTask.value = sumConsumeTask1.value |
|||
sumConsume.value = sumConsume1.value |
|||
|
|||
updateChart(chartData) |
|||
} |
|||
|
|||
const processRankingData = (marketCards) => { |
|||
// 每个市场的总金币数 |
|||
const rankingData = marketCards.map(market => { |
|||
let coinAmount = 0; |
|||
if (activeTab.value === 'recharge') { |
|||
// 充值排名 |
|||
switch (selectedType.value) { |
|||
case 'all': |
|||
coinAmount = (market.sumRechargePermanent / 100 || 0) + (market.sumRechargeFree / 100 || 0) + (market.sumRechargeTask / 100 || 0); |
|||
break; |
|||
case 'permanent': |
|||
coinAmount = market.sumRechargePermanent / 100 || 0; |
|||
break; |
|||
case 'free': |
|||
coinAmount = market.sumRechargeFree / 100 || 0; |
|||
break; |
|||
case 'task': |
|||
coinAmount = market.sumRechargeTask / 100 || 0; |
|||
break; |
|||
} |
|||
} else { |
|||
// 消费排名 |
|||
switch (selectedType.value) { |
|||
case 'all': |
|||
coinAmount = (market.sumConsumePermanent / 100 || 0) + (market.sumConsumeFree / 100 || 0) + (market.sumConsumeTask / 100 || 0); |
|||
break; |
|||
case 'permanent': |
|||
coinAmount = market.sumConsumePermanent / 100 || 0; |
|||
break; |
|||
case 'free': |
|||
coinAmount = market.sumConsumeFree / 100 || 0; |
|||
break; |
|||
case 'task': |
|||
coinAmount = market.sumConsumeTask / 100 || 0; |
|||
break; |
|||
} |
|||
} |
|||
return { |
|||
market: market.market, |
|||
coinAmount: coinAmount |
|||
}; |
|||
}); |
|||
|
|||
// 按金币数量排序 |
|||
rankingData.sort((a, b) => b.coinAmount - a.coinAmount); |
|||
|
|||
// 排名序号 |
|||
tableData.value = rankingData.map((item, index) => ({ |
|||
rank: index + 1, |
|||
...item |
|||
})); |
|||
} |
|||
|
|||
watch(selectedType, () => { |
|||
getChartData(); |
|||
}); |
|||
// 更新图表 |
|||
const updateChart = (chartData) => { |
|||
if (!chartInstance) { |
|||
initChart() |
|||
} |
|||
chartLoading.value = true |
|||
try { |
|||
let series = [] |
|||
let legend = [] |
|||
|
|||
if (activeTab.value === 'recharge') { |
|||
series = [ |
|||
{ |
|||
name: '永久金币', |
|||
type: 'bar', |
|||
stack: 'recharge', |
|||
data: chartData.rechargePermanent, |
|||
itemStyle: { color: '#5470c6' }, |
|||
barWidth: 30 |
|||
}, |
|||
{ |
|||
name: '免费金币', |
|||
type: 'bar', |
|||
stack: 'recharge', |
|||
data: chartData.rechargeFree, |
|||
itemStyle: { color: '#91cc75' }, |
|||
barWidth: 30 |
|||
}, |
|||
{ |
|||
name: '任务金币', |
|||
type: 'bar', |
|||
stack: 'recharge', |
|||
data: chartData.rechargeTask, |
|||
itemStyle: { color: '#fac858' }, |
|||
barWidth: 30 |
|||
} |
|||
] |
|||
legend = ['永久金币', '免费金币', '任务金币'] |
|||
} else { |
|||
series = [ |
|||
{ |
|||
name: '永久金币', |
|||
type: 'bar', |
|||
stack: 'consume', |
|||
data: chartData.consumePermanent, |
|||
itemStyle: { color: '#5470c6' }, |
|||
barWidth: 30 |
|||
}, |
|||
{ |
|||
name: '免费金币', |
|||
type: 'bar', |
|||
stack: 'consume', |
|||
data: chartData.consumeFree, |
|||
itemStyle: { color: '#91cc75' }, |
|||
barWidth: 30 |
|||
}, |
|||
{ |
|||
name: '任务金币', |
|||
type: 'bar', |
|||
stack: 'consume', |
|||
data: chartData.consumeTask, |
|||
itemStyle: { color: '#fac858' }, |
|||
barWidth: 30 |
|||
} |
|||
] |
|||
legend = ['永久金币', '免费金币', '任务金币'] |
|||
} |
|||
|
|||
const option = { |
|||
tooltip: { |
|||
trigger: 'axis', |
|||
axisPointer: { |
|||
type: 'shadow' |
|||
}, |
|||
formatter: function (params) { |
|||
let result = params[0].name + '<br/>' |
|||
let total = 0; |
|||
params.forEach(param => { |
|||
result += `${param.seriesName}: ${param.value.toLocaleString()}<br/>`; |
|||
total += param.value; |
|||
}) |
|||
result += `总${activeTab.value === 'recharge' ? '充值' : '消费'}: ${total.toLocaleString()}`; |
|||
return result |
|||
} |
|||
}, |
|||
legend: { |
|||
data: legend, |
|||
bottom: 10 |
|||
}, |
|||
grid: { |
|||
left: '3%', |
|||
right: '4%', |
|||
bottom: '10%', |
|||
containLabel: true |
|||
}, |
|||
xAxis: { |
|||
type: 'category', |
|||
data: markets.value, |
|||
axisLabel: { |
|||
interval: 0, |
|||
rotate: 30 |
|||
} |
|||
}, |
|||
yAxis: { |
|||
type: 'value', |
|||
axisLabel: { |
|||
formatter: function (value) { |
|||
return value.toLocaleString() |
|||
} |
|||
} |
|||
}, |
|||
series: series, |
|||
// dataZoom: [ |
|||
// { |
|||
// type: 'slider', |
|||
// show: true, |
|||
// start: 0, |
|||
// end: 100, |
|||
// maxSpan: 100, |
|||
// minSpan: 100, |
|||
// |
|||
// height: 2, |
|||
// }, |
|||
// ] |
|||
} |
|||
|
|||
chartInstance.setOption(option) |
|||
} catch (error) { |
|||
console.error('图表更新失败:', error) |
|||
ElMessage.error('图表渲染失败') |
|||
} finally { |
|||
setTimeout(() => { |
|||
chartLoading.value = false |
|||
}, 300) |
|||
} |
|||
} |
|||
|
|||
// 处理标签切换 |
|||
const handleTabChange = () => { |
|||
getChartData() |
|||
console.log('标签切换调用图表') |
|||
} |
|||
|
|||
const getAdminData = async function () { |
|||
try { |
|||
const result = await API({ url: '/admin/userinfo', data: {} }) |
|||
adminData.value = result |
|||
console.log('用户信息', adminData.value) |
|||
} catch (error) { |
|||
console.log('请求失败', error) |
|||
} |
|||
} |
|||
// 获取卡片数据 |
|||
const getCardData = async () => { |
|||
try { |
|||
const response = await API({ url: '/workbench/getCard', data: {} }) |
|||
workDataUpdateTime.value = response.updateTime |
|||
// 周同比 |
|||
sumWow.value = response.sumWow.toFixed(2) |
|||
// 日环比 |
|||
sumDaily.value = response.sumDaily.toFixed(2) |
|||
|
|||
if (response && response.data) { |
|||
processData(response.data) |
|||
} else if (Array.isArray(response?.marketCards)) { |
|||
processData(response) |
|||
} else { |
|||
console.error('无效的API响应结构:', response) |
|||
} |
|||
} catch (error) { |
|||
console.error('获取卡片数据失败:', error) |
|||
} |
|||
} |
|||
const workDataUpdateTime = ref(null) |
|||
|
|||
// 标记当前激活的时间范围按钮 |
|||
const activeTimeRange = ref('') |
|||
// 日期选择器变化时清除按钮激活状态 |
|||
const handleDatePickerChange = () => { |
|||
activeTimeRange.value = '' |
|||
} |
|||
|
|||
onMounted(async () => { |
|||
await getAdminData() |
|||
await getCardData() |
|||
await getMarkets() |
|||
getYear() |
|||
window.addEventListener('resize', () => { |
|||
chartInstance.resize() |
|||
}) |
|||
}) |
|||
onUnmounted(() => { |
|||
destroyChart() |
|||
}) |
|||
</script> |
|||
<style scoped> |
|||
|
|||
|
|||
.graph { |
|||
.condition { |
|||
width: 100%; |
|||
height: 1%; |
|||
display: flex; |
|||
align-items: center; |
|||
|
|||
.stats { |
|||
display: flex; |
|||
align-items: center; |
|||
width: 35vw; |
|||
font-size: 15px; |
|||
} |
|||
} |
|||
|
|||
.graph-content { |
|||
flex: 1; |
|||
height: auto; |
|||
display: flex; |
|||
|
|||
.left { |
|||
width: 70%; |
|||
height: auto; |
|||
} |
|||
|
|||
.right { |
|||
flex: 1; |
|||
padding: 0.5vw 2vh; |
|||
} |
|||
} |
|||
} |
|||
|
|||
.center-card { |
|||
display: flex; |
|||
justify-content: center; |
|||
align-items: center; |
|||
} |
|||
|
|||
.margin-bottom { |
|||
margin-bottom: 0.5vh; |
|||
} |
|||
|
|||
.card-item { |
|||
width: 25%; |
|||
height: 28vh; |
|||
display: flex; |
|||
flex-direction: column; |
|||
justify-content: center; |
|||
margin-right: 0.25vw; |
|||
} |
|||
|
|||
.card-title { |
|||
font-weight: bold; |
|||
margin-bottom: 1vh; |
|||
display: flex; |
|||
justify-content: center; |
|||
align-items: center; |
|||
} |
|||
|
|||
.card-large { |
|||
font-weight: bold; |
|||
font-size: 16px; |
|||
text-align: center; |
|||
margin-bottom: 15px; |
|||
} |
|||
|
|||
@keyframes spin { |
|||
0% { |
|||
transform: rotate(0deg); |
|||
} |
|||
|
|||
100% { |
|||
transform: rotate(360deg); |
|||
} |
|||
} |
|||
</style> |
@ -0,0 +1,454 @@ |
|||
<template> |
|||
<div> |
|||
金币管理 <span class="text"> |
|||
最后更新时间:{{ |
|||
workDataUpdateTime && workDataUpdateTime !== '1970-01-01 08:00:00' ? workDataUpdateTime : '该地区暂无数据' |
|||
}} |
|||
</span> |
|||
<!-- 第一行:包含两个横向格子 --> |
|||
<el-row> |
|||
<el-col :span="12"> |
|||
<!-- 第一个卡片 --> |
|||
<el-card class="card-item"> |
|||
<template #header> |
|||
<div class="card-title">当前金币余量 |
|||
<span style="font-weight: bold">{{ |
|||
currentGold / 100 |
|||
}}</span> 较前一日 |
|||
{{ dailyChange / 100 }} |
|||
<template v-if="dailyChange > 0"> |
|||
<el-icon style="color:red"> |
|||
<ArrowUpBold/> |
|||
</el-icon> |
|||
</template> |
|||
<template v-else-if="dailyChange < 0"> |
|||
<el-icon style="color:forestgreen"> |
|||
<ArrowDownBold/> |
|||
</el-icon> |
|||
</template> |
|||
<template v-else> |
|||
<el-icon style="color:grey"> |
|||
<SemiSelect/> |
|||
</el-icon> |
|||
</template> |
|||
</div> |
|||
</template> |
|||
<div> |
|||
<el-row> |
|||
<!-- 左边文本信息 --> |
|||
<el-col :span="12"> |
|||
<div class="margin-bottom">永久金币:<b>{{ currentPermanent / 100 }}</b></div> |
|||
<div class="margin-bottom"> </div> |
|||
|
|||
<div class="margin-bottom">免费金币:{{ currentFree / 100 }}</div> |
|||
<!-- <div class="margin-bottom"> </div>--> |
|||
<!-- <div class="margin-bottom"> </div>--> |
|||
<div class="margin-bottom"> |
|||
[6月到期:{{ currentFreeJune / 100 }}] |
|||
</div> |
|||
<div class="margin-bottom"> </div> |
|||
|
|||
<div>任务金币:{{ currentTask / 100 }}</div> |
|||
</el-col> |
|||
<!-- 右边图表 --> |
|||
<el-col :span="12"> |
|||
<!-- <div ref="goldTypeChart" style="width: 100%; height: 100px;"></div>--> |
|||
<div style="width: 100%; height: 60px;"> </div> |
|||
<div class="margin-bottom"> |
|||
[12月到期:{{ currentFreeDecember / 100 }}] |
|||
</div> |
|||
</el-col> |
|||
</el-row> |
|||
</div> |
|||
|
|||
</el-card> |
|||
</el-col> |
|||
<el-col :span="12"> |
|||
<!-- 第二个卡片 --> |
|||
<el-card class="card-item"> |
|||
<div class="card-title">全年累计充值金币数{{ yearlyRecharge / 100 }}</div> |
|||
<el-row> |
|||
<el-col :span="12"> |
|||
<div class="center-card">折合新币累计金额:{{ yearlyMoney / 100 }}</div> |
|||
<div class="center-card">昨日新增金币:{{ recharge / 100 }}</div> |
|||
</el-col> |
|||
<el-col :span="12"> |
|||
<div class="center-card">其中永久金币:{{ money / 100 }}</div> |
|||
<!-- 新增的环形图容器 --> |
|||
<div ref="rechargeGoldChart" style="width: 100%; height: 150px; margin-top: 10px;"></div> |
|||
</el-col> |
|||
</el-row> |
|||
</el-card> |
|||
</el-col> |
|||
</el-row> |
|||
<!-- 第二行:包含两个横向格子 --> |
|||
<el-row> |
|||
<el-col :span="12"> |
|||
<!-- 第三个卡片 --> |
|||
<el-card class="card-item"> |
|||
<div class="card-title">全年累计消费金币数{{ yearlyReduce / 100 }}</div> |
|||
<div style="padding-left: 30%;">消耗:{{ yearlyConsume / 100 }}</div> |
|||
<div style="padding-left: 30%;">退款:{{ yearlyRefund / 100 }}</div> |
|||
<!-- 新增的两个环形图容器 --> |
|||
<div ref="consumeChart" style="width: 100%; height: 150px; margin-top: 10px;"></div> |
|||
<template #footer> |
|||
<div style="margin-bottom:0.5%;padding-left: 30%;">昨日新增消费:{{ dailyConsume / 100 }}</div> |
|||
<div style="margin-bottom:0.5%;padding-left: 30%;">昨日新增消耗:{{ dailyReduce / 100 }}</div> |
|||
<div style="margin-bottom:0.5%;padding-left: 30%;">昨日新增退款:{{ dailyRefund / 100 }}</div> |
|||
</template> |
|||
</el-card> |
|||
</el-col> |
|||
<el-col :span="12"> |
|||
<!-- 第四个卡片 --> |
|||
<el-card class="card-item"> |
|||
<el-col class="card-title">全年累计充值人头数{{ yearlyRechargeNum }}</el-col> |
|||
<el-col style="padding-left: 35%;">周同比:{{ sumWow }}% |
|||
<template v-if="sumWow > 0"> |
|||
<el-icon style="color:red"> |
|||
<ArrowUpBold/> |
|||
</el-icon> |
|||
</template> |
|||
<template v-else-if="sumWow < 0"> |
|||
<el-icon style="color:forestgreen"> |
|||
<ArrowDownBold/> |
|||
</el-icon> |
|||
</template> |
|||
<template v-else> |
|||
<el-icon style="color:grey"> |
|||
<SemiSelect/> |
|||
</el-icon> |
|||
</template> |
|||
</el-col> |
|||
<el-col style="padding-left: 35%;">日环比:{{ sumDaily }}% |
|||
<template v-if="sumDaily > 0"> |
|||
<el-icon style="color:red"> |
|||
<ArrowUpBold/> |
|||
</el-icon> |
|||
</template> |
|||
<template v-else-if="sumDaily < 0"> |
|||
<el-icon style="color:forestgreen"> |
|||
<ArrowDownBold/> |
|||
</el-icon> |
|||
</template> |
|||
<template v-else> |
|||
<el-icon style="color:grey"> |
|||
<SemiSelect/> |
|||
</el-icon> |
|||
</template> |
|||
</el-col> |
|||
<!-- 新增的环形图容器 --> |
|||
<div ref="rechargePeopleChart" style="width: 100%; height: 150px; margin-top: 10px;"></div> |
|||
<template #footer> |
|||
<el-col style="padding-left: 35%;margin-bottom:0.5%">昨日充值人数:{{ ydayRechargeNum }}</el-col> |
|||
<el-col style="padding-left: 35%;">其中首充:{{ firstRecharge }}</el-col> |
|||
</template> |
|||
</el-card> |
|||
</el-col> |
|||
</el-row> |
|||
</div> |
|||
</template> |
|||
|
|||
<script setup> |
|||
import {onMounted, ref, nextTick} from 'vue' |
|||
import * as echarts from 'echarts' |
|||
import API from '@/util/http' |
|||
import dayjs from 'dayjs'; |
|||
import utc from 'dayjs-plugin-utc' |
|||
import {ArrowDownBold, ArrowUpBold, SemiSelect} from '@element-plus/icons-vue' |
|||
|
|||
dayjs.extend(utc) |
|||
|
|||
// 用户信息 |
|||
const adminData = ref({}) |
|||
// 卡片数据相关 |
|||
const currentGold = ref(0) |
|||
const dailyChange = ref(0) |
|||
const currentPermanent = ref(0) |
|||
const currentFree = ref(0) |
|||
const currentFreeJune = ref(0) |
|||
const currentFreeDecember = ref(0) |
|||
const currentTask = ref(0) |
|||
const yearlyRecharge = ref(0) |
|||
const yearlyMoney = ref(0) |
|||
const recharge = ref(0) |
|||
const money = ref(0) |
|||
const yearlyReduce = ref(0) |
|||
const yearlyConsume = ref(0) |
|||
const yearlyRefund = ref(0) |
|||
const dailyReduce = ref(0) |
|||
const dailyConsume = ref(0) |
|||
const dailyRefund = ref(0) |
|||
const yearlyRechargeNum = ref(0) |
|||
const sumWow = ref(0) |
|||
const sumDaily = ref(0) |
|||
const rechargeNum = ref(0) |
|||
const ydayRechargeNum = ref(0) |
|||
const firstRecharge = ref(0) |
|||
const length = ref(0) |
|||
|
|||
// ECharts 实例引用 |
|||
const goldTypeChart = ref(null) |
|||
const rechargeGoldChart = ref(null) |
|||
const consumeChart = ref(null) |
|||
const rechargePeopleChart = ref(null) |
|||
|
|||
// 要加上所有市场的,还有额外计算的(总数 = 永久 + 6月 + 12月 + 免费 + 任务) |
|||
const processData = (data) => { |
|||
const summary = { |
|||
currentGold: 0, |
|||
dailyChange: 0, |
|||
currentPermanent: 0, |
|||
currentFreeJune: 0, |
|||
currentFreeDecember: 0, |
|||
currentTask: 0, |
|||
currentFree: 0, |
|||
recharge: 0, |
|||
money: 0, |
|||
yearlyRecharge: 0, |
|||
yearlyMoney: 0, |
|||
consumePermanent: 0, |
|||
consumeFreeJune: 0, |
|||
consumeFreeDecember: 0, |
|||
consumeTask: 0, |
|||
refundPermanent: 0, |
|||
refundFreeJune: 0, |
|||
refundFreeDecember: 0, |
|||
refundTask: 0, |
|||
dailyReduce: 0, |
|||
yearlyConsume: 0, |
|||
yearlyRefund: 0, |
|||
yearlyReduce: 0, |
|||
rechargeNum: 0, |
|||
ydayRechargeNum: 0, |
|||
firstRecharge: 0, |
|||
sumWow: 0, |
|||
sumDaily: 0, |
|||
yearlyRechargeNum: 0 |
|||
} |
|||
|
|||
// 遍历市场 |
|||
data.marketCards.forEach(market => { |
|||
for (const i in summary) { |
|||
if (market[i] !== undefined && market[i] !== null) { |
|||
summary[i] += market[i] |
|||
} |
|||
} |
|||
}) |
|||
|
|||
// wow和daily除一下 |
|||
length.value = data.markets.length |
|||
console.log(length.value) |
|||
|
|||
// 计算昨日新增消费和退款 |
|||
const yesterdayConsume = summary.consumePermanent + summary.consumeFreeJune + summary.consumeFreeDecember + summary.consumeTask |
|||
const yesterdayRefund = summary.refundPermanent + summary.refundFreeJune + summary.refundFreeDecember + summary.refundTask |
|||
|
|||
// 更新卡片数据 |
|||
currentGold.value = summary.currentGold.toFixed(2) |
|||
dailyChange.value = summary.dailyChange.toFixed(2) |
|||
currentPermanent.value = summary.currentPermanent.toFixed(2) |
|||
currentFree.value = summary.currentFree.toFixed(2) |
|||
currentFreeJune.value = summary.currentFreeJune.toFixed(2) |
|||
currentFreeDecember.value = summary.currentFreeDecember.toFixed(2) |
|||
currentTask.value = summary.currentTask.toFixed(2) |
|||
|
|||
yearlyRecharge.value = summary.yearlyRecharge.toFixed(2) |
|||
yearlyMoney.value = summary.yearlyMoney.toFixed(2) |
|||
recharge.value = summary.recharge.toFixed(2) |
|||
money.value = summary.money.toFixed(2) |
|||
|
|||
yearlyReduce.value = summary.yearlyReduce.toFixed(2) |
|||
yearlyConsume.value = summary.yearlyConsume.toFixed(2) |
|||
yearlyRefund.value = summary.yearlyRefund.toFixed(2) |
|||
dailyReduce.value = summary.dailyReduce.toFixed(2) |
|||
dailyConsume.value = yesterdayConsume.toFixed(2) |
|||
dailyRefund.value = yesterdayRefund.toFixed(2) |
|||
|
|||
yearlyRechargeNum.value = summary.yearlyRechargeNum |
|||
|
|||
ydayRechargeNum.value = summary.ydayRechargeNum |
|||
firstRecharge.value = summary.firstRecharge |
|||
|
|||
// 初始化图表 |
|||
nextTick(() => { |
|||
initGoldTypeChart(); |
|||
initRechargeGoldChart(); |
|||
initConsumeChart(); |
|||
initRechargePeopleChart(); |
|||
}); |
|||
} |
|||
|
|||
// 初始化金币类型南丁格尔图 |
|||
const initGoldTypeChart = () => { |
|||
const myChart = echarts.init(goldTypeChart.value); |
|||
const option = { |
|||
tooltip: { |
|||
trigger: 'item', |
|||
formatter: function (params) { |
|||
let realValue = 0 |
|||
if (params.name === '永久金币') realValue = currentPermanent.value / 100 |
|||
else if (params.name === '免费金币') realValue = (currentFreeJune.value / 100 + currentFreeDecember.value / 100) |
|||
else realValue = currentTask.value / 100 |
|||
return `${params.name}: ${realValue}` |
|||
} |
|||
}, |
|||
toolbox: { |
|||
show: true, |
|||
feature: {} |
|||
}, |
|||
series: [ |
|||
{ |
|||
name: 'Nightingale Chart', |
|||
type: 'pie', |
|||
radius: ['0%', '100%'], |
|||
center: ['50%', '50%'], |
|||
roseType: 'area', |
|||
itemStyle: { |
|||
borderRadius: 5 |
|||
}, |
|||
data: [ |
|||
{value: Math.log(currentPermanent.value / 100 + 1), name: '永久金币'}, |
|||
{value: Math.log((currentFreeJune.value / 100 + currentFreeDecember.value / 100) + 1), name: '免费金币'}, |
|||
{value: Math.log(currentTask.value / 100 + 1), name: '任务金币'} |
|||
], |
|||
labelLine: {show: false}, |
|||
label: {show: false} |
|||
} |
|||
] |
|||
}; |
|||
myChart.setOption(option); |
|||
} |
|||
|
|||
// 初始化充值金币环形图 |
|||
const initRechargeGoldChart = () => { |
|||
const myChart = echarts.init(rechargeGoldChart.value); |
|||
const option = { |
|||
series: [ |
|||
{ |
|||
type: 'pie', |
|||
radius: ['50%', '70%'], |
|||
data: [ |
|||
{value: money.value / 100, name: '永久金币'}, |
|||
{value: (recharge.value - money.value) / 100, name: '其他金币'} |
|||
], |
|||
label: { |
|||
show: true, |
|||
formatter: '{b}: {c}' |
|||
} |
|||
} |
|||
] |
|||
}; |
|||
myChart.setOption(option); |
|||
} |
|||
|
|||
// 初始化消费退款环形图 |
|||
const initConsumeChart = () => { |
|||
const myChart = echarts.init(consumeChart.value); |
|||
const option = { |
|||
series: [ |
|||
{ |
|||
type: 'pie', |
|||
radius: ['40%', '70%'], |
|||
data: [ |
|||
{value: yearlyConsume.value / 100, name: '消耗'}, |
|||
{value: yearlyRefund.value / 100, name: '退款'} |
|||
], |
|||
label: { |
|||
show: true, |
|||
formatter: '{b}: {c}' |
|||
} |
|||
} |
|||
] |
|||
}; |
|||
myChart.setOption(option); |
|||
} |
|||
|
|||
// 初始化充值人头环形图 |
|||
const initRechargePeopleChart = () => { |
|||
const myChart = echarts.init(rechargePeopleChart.value); |
|||
const option = { |
|||
series: [ |
|||
{ |
|||
type: 'pie', |
|||
radius: ['40%', '70%'], |
|||
data: [ |
|||
{value: firstRecharge.value, name: '首充'}, |
|||
{value: (ydayRechargeNum.value - firstRecharge.value), name: '非首充'} |
|||
], |
|||
label: { |
|||
show: true, |
|||
formatter: '{b}: {c}' |
|||
} |
|||
} |
|||
] |
|||
}; |
|||
myChart.setOption(option); |
|||
} |
|||
|
|||
// 获取卡片数据 |
|||
const getCardData = async () => { |
|||
try { |
|||
const response = await API({url: '/workbench/getCard', data: {}}) |
|||
workDataUpdateTime.value = response.updateTime |
|||
// 周同比 |
|||
sumWow.value = response.sumWow.toFixed(2) |
|||
// 日环比 |
|||
sumDaily.value = response.sumDaily.toFixed(2) |
|||
|
|||
if (response && response.data) { |
|||
processData(response.data) |
|||
} else if (Array.isArray(response?.marketCards)) { |
|||
processData(response) |
|||
} else { |
|||
console.error('无效的API响应结构:', response) |
|||
} |
|||
} catch (error) { |
|||
console.error('获取卡片数据失败:', error) |
|||
} |
|||
} |
|||
const workDataUpdateTime = ref(null) |
|||
|
|||
|
|||
onMounted(async () => { |
|||
await getCardData() |
|||
}) |
|||
</script> |
|||
|
|||
<style scoped> |
|||
.card { |
|||
height: 28vh; |
|||
margin-bottom: 0.5vh; |
|||
display: flex; |
|||
justify-content: center; |
|||
} |
|||
|
|||
.center-card { |
|||
display: flex; |
|||
justify-content: center; |
|||
align-items: center; |
|||
} |
|||
|
|||
|
|||
|
|||
.card-item { |
|||
height: 28vh; |
|||
} |
|||
|
|||
.card-title { |
|||
font-weight: bold; |
|||
margin-bottom: 1vh; |
|||
display: flex; |
|||
justify-content: center; |
|||
align-items: center; |
|||
} |
|||
|
|||
@keyframes spin { |
|||
0% { |
|||
transform: rotate(0deg); |
|||
} |
|||
|
|||
100% { |
|||
transform: rotate(360deg); |
|||
} |
|||
} |
|||
</style> |
@ -1,911 +1,102 @@ |
|||
<template> |
|||
<div class="top"> |
|||
<el-card style="width:10vw" class="center-card">数据总览</el-card> |
|||
<span class="text"> |
|||
最后更新时间:{{ |
|||
workDataUpdateTime && workDataUpdateTime !== '1970-01-01 08:00:00' ? workDataUpdateTime : '该地区暂无数据' |
|||
}} |
|||
</span> |
|||
</div> |
|||
|
|||
<div class="card"> |
|||
<!-- 第一个卡片 --> |
|||
<el-card class="card-item"> |
|||
<template #header> |
|||
<div class="card-title">当前金币余量</div> |
|||
<div> |
|||
<span style="font-weight: bold">{{ currentGold / 100 |
|||
}}</span> 较前一日 |
|||
{{ dailyChange / 100 }} |
|||
<template v-if="dailyChange > 0"> |
|||
<el-icon style="color:red"> |
|||
<ArrowUpBold /> |
|||
</el-icon> |
|||
</template> |
|||
<template v-else-if="dailyChange < 0"> |
|||
<el-icon style="color:forestgreen"> |
|||
<ArrowDownBold /> |
|||
</el-icon> |
|||
</template> |
|||
<template v-else> |
|||
<el-icon style="color:grey"> |
|||
<SemiSelect /> |
|||
</el-icon> |
|||
</template> |
|||
</div> |
|||
</template> |
|||
<div> |
|||
<div class="margin-bottom">永久金币:{{ currentPermanent / 100 }}</div> |
|||
<div class="margin-bottom">免费金币:{{ currentFree / 100 }}</div> |
|||
<div class="margin-bottom">[六月到期|{{ currentFreeJune / 100 }}] |
|||
[十二月到期|{{ currentFreeDecember / 100 }}] |
|||
</div> |
|||
<div>任务金币:{{ currentTask / 100 }}</div> |
|||
</div> |
|||
</el-card> |
|||
|
|||
<!-- 第二个卡片 --> |
|||
<el-card class="card-item"> |
|||
<div class="card-title">全年累计充值金币数</div> |
|||
<div class="card-title">{{ yearlyRecharge / 100 }}</div> |
|||
<div> </div> |
|||
<div class="center-card">折合新币累计金额:{{ yearlyMoney / 100 }}</div> |
|||
<template #footer> |
|||
<el-col class="margin-bottom center-card">昨日新增金币:{{ recharge / 100 }}</el-col> |
|||
<el-col class="margin-bottom center-card">其中永久金币:{{ money / 100 }}</el-col> |
|||
</template> |
|||
</el-card> |
|||
|
|||
<!-- 第三个卡片 --> |
|||
<el-card class="card-item"> |
|||
<div class="card-title">全年累计消费金币数</div> |
|||
<div class="card-title">{{ yearlyReduce / 100 }}</div> |
|||
<div style="padding-left: 30%;">消耗:{{ yearlyConsume / 100 }}</div> |
|||
<div style="padding-left: 30%;">退款:{{ yearlyRefund / 100 }}</div> |
|||
<template #footer> |
|||
<div style="margin-bottom:0.5%;padding-left: 30%;">昨日新增消费:{{ dailyConsume / 100 }}</div> |
|||
<div style="margin-bottom:0.5%;padding-left: 30%;">昨日新增消耗:{{ dailyReduce / 100 }}</div> |
|||
<div style="margin-bottom:0.5%;padding-left: 30%;">昨日新增退款:{{ dailyRefund / 100 }}</div> |
|||
</template> |
|||
</el-card> |
|||
|
|||
<!-- 第四个卡片 --> |
|||
<el-card class="card-item"> |
|||
<el-col class="card-title">全年累计充值人头数</el-col> |
|||
<el-col class="card-title">{{ yearlyRechargeNum }}</el-col> |
|||
<el-col style="padding-left: 35%;">周同比:{{ sumWow }}% |
|||
<template v-if="sumWow > 0"> |
|||
<el-icon style="color:red"> |
|||
<ArrowUpBold /> |
|||
</el-icon> |
|||
</template> |
|||
<template v-else-if="sumWow < 0"> |
|||
<el-icon style="color:forestgreen"> |
|||
<ArrowDownBold /> |
|||
</el-icon> |
|||
</template> |
|||
<template v-else> |
|||
<el-icon style="color:grey"> |
|||
<SemiSelect /> |
|||
</el-icon> |
|||
</template> |
|||
<div style="height: 100vh; overflow: hidden;"> |
|||
<el-row class="cards" > |
|||
<el-col :span="12"> |
|||
<GoldManagement :cardData="cardData" /> |
|||
</el-col> |
|||
<el-col style="padding-left: 35%;">日环比:{{ sumDaily }}% |
|||
<template v-if="sumDaily > 0"> |
|||
<el-icon style="color:red"> |
|||
<ArrowUpBold /> |
|||
</el-icon> |
|||
</template> |
|||
<template v-else-if="sumDaily < 0"> |
|||
<el-icon style="color:forestgreen"> |
|||
<ArrowDownBold /> |
|||
</el-icon> |
|||
</template> |
|||
<template v-else> |
|||
<el-icon style="color:grey"> |
|||
<SemiSelect /> |
|||
</el-icon> |
|||
</template> |
|||
<!-- 右上格子:占12列 --> |
|||
<el-col :span="12"> |
|||
<CashManagement /> |
|||
</el-col> |
|||
<template #footer> |
|||
<el-col style="padding-left: 35%;margin-bottom:0.5%">昨日充值人数:{{ ydayRechargeNum }}</el-col> |
|||
<el-col style="padding-left: 35%;">其中首充:{{ firstRecharge }}</el-col> |
|||
</template> |
|||
</el-card> |
|||
</div> |
|||
|
|||
<div class="graph"> |
|||
<el-card style="width:84vw;"> |
|||
<div> |
|||
<el-tabs v-model="activeTab" @tab-change="handleTabChange"> |
|||
<el-tab-pane label="金币充值" name="recharge"></el-tab-pane> |
|||
<el-tab-pane label="金币消费" name="consume"></el-tab-pane> |
|||
</el-tabs> |
|||
</div> |
|||
<div class="condition"> |
|||
<div class="stats"> |
|||
<div v-if="activeTab === 'consume'">合计:{{ sumConsume / 100 }}</div> |
|||
永久金币: {{ activeTab === 'recharge' ? sumRechargePermanent / 100 : sumConsumePermanent / 100 }} |
|||
免费金币: {{ activeTab === 'recharge' ? sumRechargeFree / 100 : sumConsumeFree / 100 }} |
|||
任务金币: {{ activeTab === 'recharge' ? sumRechargeTask / 100 : sumConsumeTask / 100 }} |
|||
</div> |
|||
<div> |
|||
<el-button @click="getYes()" size="small" :type="activeTimeRange === 'yes' ? 'primary' : ''">昨天 |
|||
</el-button> |
|||
<el-button @click="getToday()" size="small" :type="activeTimeRange === 'today' ? 'primary' : ''">今天 |
|||
</el-button> |
|||
<el-button @click="getWeek()" size="small" :type="activeTimeRange === 'week' ? 'primary' : ''">本周 |
|||
</el-button> |
|||
<el-button @click="getMonth()" size="small" :type="activeTimeRange === 'month' ? 'primary' : ''">本月 |
|||
</el-button> |
|||
<el-button @click="getYear()" size="small" :type="activeTimeRange === 'year' ? 'primary' : ''">本年 |
|||
</el-button> |
|||
</div> |
|||
<div> |
|||
<el-date-picker size="small" v-model="dateRange" type="datetimerange" range-separator="→" |
|||
start-placeholder="开始时间" end-placeholder="结束时间" format="YYYY-MM-DD HH:mm:ss" |
|||
style="width:20vw;margin-left:0.5vw;" value-format="YYYY-MM-DD HH:mm:ss" :default-time="defaultTime" |
|||
:disabled-date="disabledDate" @change="handleDatePickerChange" /> |
|||
<el-button type="primary" size="small" style="margin-left: 0.5vw" @click="getChartData">查询</el-button> |
|||
</div> |
|||
</div> |
|||
|
|||
<div class="graph-content"> |
|||
<div ref="chartRef" class="left"></div> |
|||
<div class="right"> |
|||
<el-card> |
|||
<div class="card-large">金币{{ activeTab === 'recharge' ? '充值' : '消费' }}排名</div> |
|||
<el-select v-model="selectedType" style="width: 100%; margin-bottom: 15px"> |
|||
<el-option label="全部类型" value="all"></el-option> |
|||
<el-option label="永久金币" value="permanent"></el-option> |
|||
<el-option label="免费金币" value="free"></el-option> |
|||
<el-option label="任务金币" value="task"></el-option> |
|||
</el-select> |
|||
<el-table :data="tableData" height="320px"> |
|||
<el-table-column prop="rank" label="排名" width="60" align="center"></el-table-column> |
|||
<el-table-column prop="market" label="地区" align="center"> |
|||
<template #default="scope"> |
|||
<span>{{ marketMapping[scope.row.market] || scope.row.market }}</span> |
|||
</template> |
|||
</el-table-column> |
|||
<el-table-column prop="coinAmount" label="金币数量" align="center"> |
|||
<template #default="{ row }"> |
|||
{{ row.coinAmount.toLocaleString() }} |
|||
</template> |
|||
</el-table-column> |
|||
</el-table> |
|||
</el-card> |
|||
</div> |
|||
</div> |
|||
</el-card> |
|||
</el-row> |
|||
<el-row class="graphs"> |
|||
<el-col :span="24"> |
|||
<GoldGraph :graphData="graphData" @tab-change="handleTabChange" /> |
|||
</el-col> |
|||
</el-row> |
|||
</div> |
|||
</template> |
|||
|
|||
<script setup> |
|||
import * as echarts from 'echarts' |
|||
import { ref, onMounted, nextTick, watch, onUnmounted } from 'vue' |
|||
import { ref, onMounted } from 'vue' |
|||
import API from '@/util/http' |
|||
import { ElMessage } from 'element-plus' |
|||
import dayjs from 'dayjs'; |
|||
import utc from 'dayjs-plugin-utc' |
|||
import weekday from 'dayjs/plugin/weekday' |
|||
dayjs.extend(utc) |
|||
import { ArrowUpBold, ArrowDownBold, SemiSelect } from '@element-plus/icons-vue' |
|||
import { marketMapping } from "@/utils/marketMap.js"; |
|||
|
|||
const defaultTime = [ |
|||
new Date(2000, 1, 1, 0, 0, 0), |
|||
new Date(2000, 2, 1, 23, 59, 59), |
|||
] |
|||
import GoldManagement from "@/components/workspace/GoldManagement.vue" |
|||
import CashManagement from "@/components/workspace/CashManagement.vue" |
|||
import GoldGraph from "@/components/workspace/GoldGraph.vue" |
|||
|
|||
// 地区数据 |
|||
const cardData = ref({}) |
|||
const graphData = ref([]) |
|||
const markets = ref([]) |
|||
// 图表相关 |
|||
const dateRange = ref([]) |
|||
const account = ref('') |
|||
const activeTab = ref('recharge') |
|||
const selectedType = ref('all') |
|||
const tableData = ref([]) |
|||
const chartRef = ref(null) |
|||
let chartInstance = null |
|||
// 图表合计数 |
|||
const sumRechargePermanent = ref(0) |
|||
const sumRechargeFree = ref(0) |
|||
const sumRechargeTask = ref(0) |
|||
const sumConsumePermanent = ref(0) |
|||
const sumConsumeFree = ref(0) |
|||
const sumConsumeTask = ref(0) |
|||
const sumConsume = ref(0) |
|||
// 用户信息 |
|||
const adminData = ref({}) |
|||
// 卡片数据相关 |
|||
const currentGold = ref(0) |
|||
const dailyChange = ref(0) |
|||
const currentPermanent = ref(0) |
|||
const currentFree = ref(0) |
|||
const currentFreeJune = ref(0) |
|||
const currentFreeDecember = ref(0) |
|||
const currentTask = ref(0) |
|||
const yearlyRecharge = ref(0) |
|||
const yearlyMoney = ref(0) |
|||
const recharge = ref(0) |
|||
const money = ref(0) |
|||
const yearlyReduce = ref(0) |
|||
const yearlyConsume = ref(0) |
|||
const yearlyRefund = ref(0) |
|||
const dailyReduce = ref(0) |
|||
const dailyConsume = ref(0) |
|||
const dailyRefund = ref(0) |
|||
const yearlyRechargeNum = ref(0) |
|||
const sumWow = ref(0) |
|||
const sumDaily = ref(0) |
|||
const rechargeNum = ref(0) |
|||
const ydayRechargeNum = ref(0) |
|||
const firstRecharge = ref(0) |
|||
const length = ref(0) |
|||
// 加载状态 |
|||
const chartLoading = ref(true) |
|||
|
|||
const handleResize = () => { |
|||
if (chartInstance.value) { |
|||
try { |
|||
chartInstance.value.resize() |
|||
console.log('resize一下') |
|||
} catch (error) { |
|||
console.error('图表resize失败:', error) |
|||
} |
|||
} |
|||
} |
|||
// 初始化图表 |
|||
const initChart = () => { |
|||
if (!chartInstance && chartRef.value) { |
|||
chartInstance = echarts.init(chartRef.value) |
|||
window.addEventListener('resize', handleResize) |
|||
} |
|||
} |
|||
// 销毁图表 |
|||
const destroyChart = () => { |
|||
if (chartInstance.value) { |
|||
try { |
|||
chartInstance.value.dispose() |
|||
} catch (error) { |
|||
console.error('图表销毁失败:', error) |
|||
} |
|||
chartInstance.value = null |
|||
} |
|||
window.removeEventListener('resize', handleResize) |
|||
} |
|||
const formatDate = function (date) { |
|||
const year = date.getFullYear(); |
|||
const month = String(date.getMonth() + 1).padStart(2, '0'); |
|||
const day = String(date.getDate()).padStart(2, '0'); |
|||
const hours = String(date.getHours()).padStart(2, '0'); |
|||
const minutes = String(date.getMinutes()).padStart(2, '0'); |
|||
const seconds = String(date.getSeconds()).padStart(2, '0'); |
|||
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`; |
|||
} |
|||
// 昨天 |
|||
const getYes = function () { |
|||
const yesterday = dayjs().subtract(1, 'day') |
|||
const startTime = yesterday.startOf('day').format('YYYY-MM-DD HH:mm:ss') |
|||
const endTime = yesterday.endOf('day').format('YYYY-MM-DD HH:mm:ss') |
|||
dateRange.value = [startTime, endTime] |
|||
console.log('看看dateRange', dateRange.value) |
|||
activeTimeRange.value = 'yes' // 标记当前激活状态 |
|||
|
|||
getChartData() |
|||
} |
|||
// 今天 |
|||
const getToday = function () { |
|||
const today = dayjs() |
|||
const startTime = today.startOf('day').format('YYYY-MM-DD HH:mm:ss') |
|||
const endTime = today.endOf('day').format('YYYY-MM-DD HH:mm:ss') |
|||
// const endTime = today.add(1, 'day').startOf('day').format('YYYY-MM-DD HH:mm:ss') |
|||
dateRange.value = [startTime, endTime] |
|||
console.log('看看dateRange', dateRange.value) |
|||
activeTimeRange.value = 'today' // 标记当前激活状态 |
|||
|
|||
getChartData() |
|||
} |
|||
// 本周 |
|||
const getWeek = function () { |
|||
const today = dayjs(); |
|||
// 获取今天是星期几(0是周日,1是周一,...,6是周六) |
|||
const day = today.day(); |
|||
|
|||
// 计算本周一(如果今天是周一,就取今天;如果是周日,就减6天) |
|||
let monday = today.subtract(day === 0 ? 6 : day - 1, 'day'); |
|||
// 计算本周日(如果今天是周日,就取今天;否则就加(7 - day)天) |
|||
let sunday = today.add(day === 0 ? 0 : 7 - day, 'day'); |
|||
|
|||
// 设置时间为起始和结束 |
|||
const startTime = monday.startOf('day').format('YYYY-MM-DD HH:mm:ss'); |
|||
const endTime = sunday.endOf('day').format('YYYY-MM-DD HH:mm:ss'); |
|||
|
|||
dateRange.value = [startTime, endTime]; |
|||
console.log('本周时间范围(周一到周日):', dateRange.value); |
|||
activeTimeRange.value = 'week'; |
|||
|
|||
getChartData(); |
|||
}; |
|||
// 本月 |
|||
const getMonth = function () { |
|||
const today = dayjs() |
|||
const startTime = today.startOf('month').format('YYYY-MM-DD HH:mm:ss') |
|||
// const endTime = today.add(1, 'month').startOf('month').format('YYYY-MM-DD HH:mm:ss') |
|||
const endTime = today.endOf('month').format('YYYY-MM-DD HH:mm:ss') |
|||
dateRange.value = [startTime, endTime] |
|||
console.log('看看dateRange', dateRange.value) |
|||
activeTimeRange.value = 'month' // 标记当前激活状态 |
|||
|
|||
getChartData() |
|||
// 👉 tab 切换时重新调图表接口 |
|||
const handleTabChange = async (tab) => { |
|||
activeTab.value = tab |
|||
await getGraphData() |
|||
} |
|||
// 本年 |
|||
const getYear = function () { |
|||
const today = dayjs() |
|||
const startTime = today.startOf('year').format('YYYY-MM-DD HH:mm:ss') |
|||
const endTime = today.endOf('year').format('YYYY-MM-DD HH:mm:ss') |
|||
// const endTime = today.add(1, 'year').startOf('year').format('YYYY-MM-DD HH:mm:ss') |
|||
dateRange.value = [startTime, endTime] |
|||
console.log('看看dateRange', dateRange.value) |
|||
activeTimeRange.value = 'year' // 标记当前激活状态 |
|||
|
|||
getChartData() |
|||
} |
|||
|
|||
// 要加上所有市场的,还有额外计算的(总数 = 永久 + 6月 + 12月 + 免费 + 任务) |
|||
const processData = (data) => { |
|||
const summary = { |
|||
currentGold: 0, |
|||
dailyChange: 0, |
|||
currentPermanent: 0, |
|||
currentFreeJune: 0, |
|||
currentFreeDecember: 0, |
|||
currentTask: 0, |
|||
currentFree: 0, |
|||
recharge: 0, |
|||
money: 0, |
|||
yearlyRecharge: 0, |
|||
yearlyMoney: 0, |
|||
consumePermanent: 0, |
|||
consumeFreeJune: 0, |
|||
consumeFreeDecember: 0, |
|||
consumeTask: 0, |
|||
refundPermanent: 0, |
|||
refundFreeJune: 0, |
|||
refundFreeDecember: 0, |
|||
refundTask: 0, |
|||
dailyReduce: 0, |
|||
yearlyConsume: 0, |
|||
yearlyRefund: 0, |
|||
yearlyReduce: 0, |
|||
rechargeNum: 0, |
|||
ydayRechargeNum: 0, |
|||
firstRecharge: 0, |
|||
sumWow: 0, |
|||
sumDaily: 0, |
|||
yearlyRechargeNum: 0 |
|||
} |
|||
|
|||
// 遍历市场 |
|||
data.marketCards.forEach(market => { |
|||
for (const i in summary) { |
|||
if (market[i] !== undefined && market[i] !== null) { // 其实还应该卡一个number |
|||
summary[i] += market[i] |
|||
} |
|||
} |
|||
}) |
|||
|
|||
// wow和daily除一下 |
|||
length.value = data.markets.length |
|||
console.log(length.value) |
|||
|
|||
// 计算昨日新增消费和退款 |
|||
const yesterdayConsume = summary.consumePermanent + summary.consumeFreeJune + summary.consumeFreeDecember + summary.consumeTask |
|||
const yesterdayRefund = summary.refundPermanent + summary.refundFreeJune + summary.refundFreeDecember + summary.refundTask |
|||
|
|||
// 更新卡片数据 |
|||
currentGold.value = summary.currentGold.toFixed(2) |
|||
dailyChange.value = summary.dailyChange.toFixed(2) |
|||
currentPermanent.value = summary.currentPermanent.toFixed(2) |
|||
currentFree.value = summary.currentFree.toFixed(2) |
|||
currentFreeJune.value = summary.currentFreeJune.toFixed(2) |
|||
currentFreeDecember.value = summary.currentFreeDecember.toFixed(2) |
|||
currentTask.value = summary.currentTask.toFixed(2) |
|||
|
|||
yearlyRecharge.value = summary.yearlyRecharge.toFixed(2) |
|||
yearlyMoney.value = summary.yearlyMoney.toFixed(2) |
|||
recharge.value = summary.recharge.toFixed(2) |
|||
money.value = summary.money.toFixed(2) |
|||
|
|||
yearlyReduce.value = summary.yearlyReduce.toFixed(2) |
|||
yearlyConsume.value = summary.yearlyConsume.toFixed(2) |
|||
yearlyRefund.value = summary.yearlyRefund.toFixed(2) |
|||
dailyReduce.value = summary.dailyReduce.toFixed(2) |
|||
dailyConsume.value = yesterdayConsume.toFixed(2) |
|||
dailyRefund.value = yesterdayRefund.toFixed(2) |
|||
|
|||
yearlyRechargeNum.value = summary.yearlyRechargeNum |
|||
|
|||
// // 周同比 |
|||
// sumWow.value = (marketCards.sumWow / length.value).toFixed(2) |
|||
// // 日环比 |
|||
// sumDaily.value = (marketCards.sumDaily / length.value).toFixed(2) |
|||
|
|||
// rechargeNum.value = summary.rechargeNum |
|||
ydayRechargeNum.value = summary.ydayRechargeNum |
|||
firstRecharge.value = summary.firstRecharge |
|||
} |
|||
|
|||
//无法选择的时间 |
|||
const disabledDate = (time) => { |
|||
const limitDate = new Date(2025, 0, 1); |
|||
return time.getTime() < limitDate.getTime(); |
|||
// 获取用户信息(拿 account) |
|||
const getUserInfo = async () => { |
|||
const res = await API({ url: '/admin/userinfo', data: {} }) |
|||
account.value = res?.account || '' |
|||
} |
|||
|
|||
// 获取市场列表 |
|||
const getMarkets = async () => { |
|||
console.log("adminData", adminData.value.account) |
|||
try { |
|||
const response = await API({ |
|||
url: '/general/adminMarkets', |
|||
data: { |
|||
account: adminData.value.account |
|||
} |
|||
}) |
|||
if (Array.isArray(response.data)) { |
|||
// markets.value = response.data.filter(data => data !== "1") |
|||
markets.value = response.data |
|||
console.log('市场列表获取成功:', markets.value) |
|||
} else { |
|||
console.error('获取市场列表失败', response) |
|||
ElMessage.error('获取市场列表失败') |
|||
} |
|||
} catch (error) { |
|||
console.error('获取市场列表失败:', error) |
|||
ElMessage.error('获取市场列表失败') |
|||
} |
|||
} |
|||
|
|||
// 获取图表数据 |
|||
const getChartData = async () => { |
|||
try { |
|||
// 校验市场数据到底有没有 |
|||
if (!markets.value || markets.value.length === 0) { |
|||
await getMarkets() |
|||
} |
|||
// 本年 |
|||
if (!dateRange.value || dateRange.value.length === 0) { |
|||
getYear() |
|||
} |
|||
const params = { |
|||
markets: markets.value, |
|||
startDate: dateRange.value[0], |
|||
endDate: dateRange.value[1] |
|||
}; |
|||
|
|||
|
|||
const response = await API({ |
|||
url: '/workbench/getGraph', |
|||
data: params |
|||
}) |
|||
console.log('看看params', params) |
|||
if (Array.isArray(response.marketGraphs)) { |
|||
// const filteredGraphs = response.marketGraphs.filter(data => data.market !== "1"); |
|||
// 处理图表数据 |
|||
processChartData(response.marketGraphs) |
|||
// 处理排名数据 |
|||
processRankingData(response.marketGraphs) |
|||
} else { |
|||
console.error('获取图表数据失败:', response) |
|||
ElMessage.error('获取图表数据失败') |
|||
} |
|||
} catch (error) { |
|||
console.error('获取图表数据失败:', error) |
|||
ElMessage.error('获取图表数据失败') |
|||
} |
|||
if (!account.value) return |
|||
const res = await API({ url: '/general/adminMarkets', data: { account: account.value } }) |
|||
markets.value = res.data |
|||
console.log('获取市场列表:res:', res) |
|||
console.log('获取市场列表:market:', markets.value) |
|||
} |
|||
// 处理图表数据 |
|||
const processChartData = (marketCards) => { |
|||
const chartData = { |
|||
rechargePermanent: [], |
|||
rechargeFree: [], |
|||
rechargeTask: [], |
|||
consumePermanent: [], |
|||
consumeFree: [], |
|||
consumeTask: [], |
|||
sumConsume: [] |
|||
} |
|||
// 这是图表的合计数,怎样遍历????? |
|||
const sumRechargePermanent1 = ref(0) |
|||
const sumRechargeFree1 = ref(0) |
|||
const sumRechargeTask1 = ref(0) |
|||
const sumConsumePermanent1 = ref(0) |
|||
const sumConsumeFree1 = ref(0) |
|||
const sumConsumeTask1 = ref(0) |
|||
const sumConsume1 = ref(0) |
|||
|
|||
|
|||
marketCards.forEach(market => { |
|||
chartData.rechargePermanent.push(market.sumRechargePermanent / 100 || 0) |
|||
chartData.rechargeFree.push(market.sumRechargeFree / 100 || 0) |
|||
chartData.rechargeTask.push(market.sumRechargeTask / 100 || 0) |
|||
chartData.consumePermanent.push(market.sumConsumePermanent / 100 || 0) |
|||
chartData.consumeFree.push(market.sumConsumeFree / 100 || 0) |
|||
chartData.consumeTask.push(market.sumConsumeTask / 100 || 0) |
|||
chartData.sumConsume.push(market.sumConsume / 100 || 0) |
|||
|
|||
// 合计数合计数合计数咋算 |
|||
sumRechargePermanent1.value += (market.sumRechargePermanent || 0) |
|||
sumRechargeFree1.value += (market.sumRechargeFree || 0) |
|||
//sumRechargeTask1.value += (market.sumRechargeTask || 0) |
|||
sumConsumePermanent1.value += (market.sumConsumePermanent || 0) |
|||
sumConsumeFree1.value += (market.sumConsumeFree || 0) |
|||
sumConsumeTask1.value += (market.sumConsumeTask || 0) |
|||
sumConsume1.value += (market.sumConsume || 0) |
|||
}) |
|||
sumRechargePermanent.value = sumRechargePermanent1.value |
|||
sumRechargeFree.value = sumRechargeFree1.value |
|||
sumRechargeTask.value = 0 |
|||
sumConsumePermanent.value = sumConsumePermanent1.value |
|||
sumConsumeFree.value = sumConsumeFree1.value |
|||
sumConsumeTask.value = sumConsumeTask1.value |
|||
sumConsume.value = sumConsume1.value |
|||
|
|||
updateChart(chartData) |
|||
} |
|||
|
|||
const processRankingData = (marketCards) => { |
|||
// 每个市场的总金币数 |
|||
const rankingData = marketCards.map(market => { |
|||
let coinAmount = 0; |
|||
if (activeTab.value === 'recharge') { |
|||
// 充值排名 |
|||
switch (selectedType.value) { |
|||
case 'all': |
|||
coinAmount = (market.sumRechargePermanent / 100 || 0) + (market.sumRechargeFree / 100 || 0) + (market.sumRechargeTask / 100 || 0); |
|||
break; |
|||
case 'permanent': |
|||
coinAmount = market.sumRechargePermanent / 100 || 0; |
|||
break; |
|||
case 'free': |
|||
coinAmount = market.sumRechargeFree / 100 || 0; |
|||
break; |
|||
case 'task': |
|||
coinAmount = market.sumRechargeTask / 100 || 0; |
|||
break; |
|||
} |
|||
} else { |
|||
// 消费排名 |
|||
switch (selectedType.value) { |
|||
case 'all': |
|||
coinAmount = (market.sumConsumePermanent / 100 || 0) + (market.sumConsumeFree / 100 || 0) + (market.sumConsumeTask / 100 || 0); |
|||
break; |
|||
case 'permanent': |
|||
coinAmount = market.sumConsumePermanent / 100 || 0; |
|||
break; |
|||
case 'free': |
|||
coinAmount = market.sumConsumeFree / 100 || 0; |
|||
break; |
|||
case 'task': |
|||
coinAmount = market.sumConsumeTask / 100 || 0; |
|||
break; |
|||
} |
|||
} |
|||
return { |
|||
market: market.market, |
|||
coinAmount: coinAmount |
|||
}; |
|||
}); |
|||
|
|||
// 按金币数量排序 |
|||
rankingData.sort((a, b) => b.coinAmount - a.coinAmount); |
|||
|
|||
// 排名序号 |
|||
tableData.value = rankingData.map((item, index) => ({ |
|||
rank: index + 1, |
|||
...item |
|||
})); |
|||
// 获取金币卡片数据 |
|||
const getCardData = async () => { |
|||
const res = await API({ url: '/workbench/getCard', data: {} }) |
|||
cardData.value = res?.data || {} |
|||
} |
|||
|
|||
watch(selectedType, () => { |
|||
getChartData(); |
|||
}); |
|||
// 更新图表 |
|||
const updateChart = (chartData) => { |
|||
if (!chartInstance) { |
|||
initChart() |
|||
} |
|||
chartLoading.value = true |
|||
try { |
|||
let series = [] |
|||
let legend = [] |
|||
|
|||
if (activeTab.value === 'recharge') { |
|||
series = [ |
|||
{ |
|||
name: '永久金币', |
|||
type: 'bar', |
|||
stack: 'recharge', |
|||
data: chartData.rechargePermanent, |
|||
itemStyle: { color: '#5470c6' }, |
|||
barWidth: 30 |
|||
}, |
|||
{ |
|||
name: '免费金币', |
|||
type: 'bar', |
|||
stack: 'recharge', |
|||
data: chartData.rechargeFree, |
|||
itemStyle: { color: '#91cc75' }, |
|||
barWidth: 30 |
|||
}, |
|||
{ |
|||
name: '任务金币', |
|||
type: 'bar', |
|||
stack: 'recharge', |
|||
data: chartData.rechargeTask, |
|||
itemStyle: { color: '#fac858' }, |
|||
barWidth: 30 |
|||
} |
|||
] |
|||
legend = ['永久金币', '免费金币', '任务金币'] |
|||
} else { |
|||
series = [ |
|||
{ |
|||
name: '永久金币', |
|||
type: 'bar', |
|||
stack: 'consume', |
|||
data: chartData.consumePermanent, |
|||
itemStyle: { color: '#5470c6' }, |
|||
barWidth: 30 |
|||
}, |
|||
{ |
|||
name: '免费金币', |
|||
type: 'bar', |
|||
stack: 'consume', |
|||
data: chartData.consumeFree, |
|||
itemStyle: { color: '#91cc75' }, |
|||
barWidth: 30 |
|||
}, |
|||
{ |
|||
name: '任务金币', |
|||
type: 'bar', |
|||
stack: 'consume', |
|||
data: chartData.consumeTask, |
|||
itemStyle: { color: '#fac858' }, |
|||
barWidth: 30 |
|||
} |
|||
] |
|||
legend = ['永久金币', '免费金币', '任务金币'] |
|||
} |
|||
// 获取金币图表数据 |
|||
const getGraphData = async () => { |
|||
if (!markets.value.length) return |
|||
|
|||
const option = { |
|||
tooltip: { |
|||
trigger: 'axis', |
|||
axisPointer: { |
|||
type: 'shadow' |
|||
}, |
|||
formatter: function (params) { |
|||
let result = params[0].name + '<br/>' |
|||
let total = 0; |
|||
params.forEach(param => { |
|||
result += `${param.seriesName}: ${param.value.toLocaleString()}<br/>`; |
|||
total += param.value; |
|||
}) |
|||
result += `总${activeTab.value === 'recharge' ? '充值' : '消费'}: ${total.toLocaleString()}`; |
|||
return result |
|||
} |
|||
}, |
|||
legend: { |
|||
data: legend, |
|||
bottom: 10 |
|||
}, |
|||
grid: { |
|||
left: '3%', |
|||
right: '4%', |
|||
bottom: '10%', |
|||
containLabel: true |
|||
}, |
|||
xAxis: { |
|||
type: 'category', |
|||
data: markets.value, |
|||
axisLabel: { |
|||
interval: 0, |
|||
rotate: 30 |
|||
} |
|||
}, |
|||
yAxis: { |
|||
type: 'value', |
|||
axisLabel: { |
|||
formatter: function (value) { |
|||
return value.toLocaleString() |
|||
} |
|||
} |
|||
}, |
|||
series: series, |
|||
// dataZoom: [ |
|||
// { |
|||
// type: 'slider', |
|||
// show: true, |
|||
// start: 0, |
|||
// end: 100, |
|||
// maxSpan: 100, |
|||
// minSpan: 100, |
|||
// |
|||
// height: 2, |
|||
// }, |
|||
// ] |
|||
const params = { |
|||
markets: markets.value, |
|||
startDate: '2025-01-01 00:00:00', |
|||
endDate: '2025-12-31 23:59:59' |
|||
} |
|||
|
|||
chartInstance.setOption(option) |
|||
} catch (error) { |
|||
console.error('图表更新失败:', error) |
|||
ElMessage.error('图表渲染失败') |
|||
} finally { |
|||
setTimeout(() => { |
|||
chartLoading.value = false |
|||
}, 300) |
|||
} |
|||
} |
|||
const res = await API({ url: '/workbench/getGraph', data: params }) |
|||
if (!res?.marketGraphs) return |
|||
|
|||
// 处理标签切换 |
|||
const handleTabChange = () => { |
|||
getChartData() |
|||
console.log('标签切换调用图表') |
|||
graphData.value = res.marketGraphs.map(m => ({ |
|||
market: m.market, |
|||
permanent: activeTab.value === 'recharge' |
|||
? m.sumRechargePermanent / 100 |
|||
: m.sumConsumePermanent / 100, |
|||
free: activeTab.value === 'recharge' |
|||
? m.sumRechargeFree / 100 |
|||
: m.sumConsumeFree / 100, |
|||
task: activeTab.value === 'recharge' |
|||
? m.sumRechargeTask / 100 |
|||
: m.sumConsumeTask / 100, |
|||
})) |
|||
} |
|||
|
|||
const getAdminData = async function () { |
|||
try { |
|||
const result = await API({ url: '/admin/userinfo', data: {} }) |
|||
adminData.value = result |
|||
console.log('用户信息', adminData.value) |
|||
} catch (error) { |
|||
console.log('请求失败', error) |
|||
} |
|||
} |
|||
// 获取卡片数据 |
|||
const getCardData = async () => { |
|||
onMounted(async () => { |
|||
try { |
|||
const response = await API({ url: '/workbench/getCard', data: {} }) |
|||
workDataUpdateTime.value = response.updateTime |
|||
// 周同比 |
|||
sumWow.value = response.sumWow.toFixed(2) |
|||
// 日环比 |
|||
sumDaily.value = response.sumDaily.toFixed(2) |
|||
|
|||
if (response && response.data) { |
|||
processData(response.data) |
|||
} else if (Array.isArray(response?.marketCards)) { |
|||
processData(response) |
|||
} else { |
|||
console.error('无效的API响应结构:', response) |
|||
} |
|||
} catch (error) { |
|||
console.error('获取卡片数据失败:', error) |
|||
await getUserInfo() // 先拿 account |
|||
await getMarkets() // 再拿 markets |
|||
await getCardData() // 卡片 |
|||
await getGraphData() // 图表 |
|||
} catch (err) { |
|||
console.error('初始化失败:', err) |
|||
} |
|||
} |
|||
const workDataUpdateTime = ref(null) |
|||
|
|||
// 标记当前激活的时间范围按钮 |
|||
const activeTimeRange = ref('') |
|||
// 日期选择器变化时清除按钮激活状态 |
|||
const handleDatePickerChange = () => { |
|||
activeTimeRange.value = '' |
|||
} |
|||
|
|||
onMounted(async () => { |
|||
await getAdminData() |
|||
await getCardData() |
|||
await getMarkets() |
|||
getYear() |
|||
window.addEventListener('resize', () => { |
|||
chartInstance.resize() |
|||
}) |
|||
}) |
|||
onUnmounted(() => { |
|||
destroyChart() |
|||
}) |
|||
</script> |
|||
|
|||
<style scoped> |
|||
.top { |
|||
height: 5.5vh; |
|||
width: 80vw; |
|||
display: flex; |
|||
margin-bottom: 0.5vh; |
|||
|
|||
.text { |
|||
margin-left: 2vw; |
|||
width: 20vw; |
|||
display: flex; |
|||
align-items: center; |
|||
font-size: 18px; |
|||
} |
|||
} |
|||
|
|||
.card { |
|||
height: 28vh; |
|||
margin-bottom: 0.5vh; |
|||
display: flex; |
|||
justify-content: center; |
|||
} |
|||
|
|||
.graph { |
|||
width: 100%; |
|||
display: flex; |
|||
height: 64%; |
|||
|
|||
.condition { |
|||
width: 100%; |
|||
height: 1%; |
|||
display: flex; |
|||
align-items: center; |
|||
|
|||
.stats { |
|||
display: flex; |
|||
align-items: center; |
|||
width: 35vw; |
|||
font-size: 15px; |
|||
} |
|||
} |
|||
|
|||
.graph-content { |
|||
flex: 1; |
|||
height: auto; |
|||
display: flex; |
|||
|
|||
.left { |
|||
width: 70%; |
|||
height: auto; |
|||
} |
|||
|
|||
.right { |
|||
flex: 1; |
|||
padding: 0.5vw 2vh; |
|||
} |
|||
} |
|||
} |
|||
|
|||
.center-card { |
|||
display: flex; |
|||
justify-content: center; |
|||
align-items: center; |
|||
} |
|||
|
|||
.margin-bottom { |
|||
margin-bottom: 0.5vh; |
|||
} |
|||
|
|||
.card-item { |
|||
width: 25%; |
|||
height: 28vh; |
|||
display: flex; |
|||
flex-direction: column; |
|||
justify-content: center; |
|||
margin-right: 0.25vw; |
|||
} |
|||
|
|||
.card-title { |
|||
font-weight: bold; |
|||
margin-bottom: 1vh; |
|||
display: flex; |
|||
justify-content: center; |
|||
align-items: center; |
|||
} |
|||
|
|||
.card-large { |
|||
font-weight: bold; |
|||
font-size: 16px; |
|||
text-align: center; |
|||
margin-bottom: 15px; |
|||
} |
|||
|
|||
@keyframes spin { |
|||
0% { |
|||
transform: rotate(0deg); |
|||
} |
|||
|
|||
100% { |
|||
transform: rotate(360deg); |
|||
} |
|||
} |
|||
</style> |
Write
Preview
Loading…
Cancel
Save
Reference in new issue