18 Commits
c881b86bfb
...
d40812ee74
29 changed files with 2583 additions and 5442 deletions
-
2.env.development
-
7package-lock.json
-
1package.json
-
5src/components/workspace/CashManagement.vue
-
210src/components/workspace/CashManagementMarkets.vue
-
78src/components/workspace/GoldGraph.vue
-
818src/components/workspace/GoldGraphMarkets.vue
-
4src/components/workspace/GoldManagement.vue
-
102src/views/audit/bean/beanAudit.vue
-
20src/views/audit/gold/rechargeAudit.vue
-
47src/views/audit/gold/refundAudit.vue
-
51src/views/consume/bean/articleVideo.vue
-
51src/views/consume/bean/dieHardFan.vue
-
51src/views/consume/bean/liveStream.vue
-
52src/views/consume/gold/coinConsumeDetail.vue
-
692src/views/moneyManage/receiveDetail/receiveDetail.vue
-
39src/views/permissions/rolePermission.vue
-
37src/views/permissions/userPermission.vue
-
2src/views/recharge/bean/addBeanRecharge.vue
-
52src/views/recharge/bean/beanOnlineRecharge.vue
-
54src/views/recharge/bean/beanSystemRecharge.vue
-
5src/views/recharge/gold/addCoinRecharge.vue
-
50src/views/recharge/gold/coinRechargeDetail.vue
-
51src/views/refund/gold/coinRefundDetail.vue
-
51src/views/usergold/bean/userbean.vue
-
62src/views/usergold/gold/clientCountBalance.vue
-
51src/views/usergold/gold/clientCountDetail.vue
-
73src/views/workspace/index.vue
-
4949stats.html
@ -0,0 +1,210 @@ |
|||||
|
<!--各地区的现金管理情况--> |
||||
|
<template> |
||||
|
|
||||
|
<div class="cash-management"> |
||||
|
<div class="cash-title"> |
||||
|
<div class="text1"> 现金管理 |
||||
|
<span class="text1-update-time">最后更新时间:{{ |
||||
|
workDataUpdateTime && workDataUpdateTime !== '1970-01-01 08:00:00' ? workDataUpdateTime : '该地区暂无数据' |
||||
|
}}</span> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<div class="text2"><span class="text2-income">总营收:{{ cashData.totalIncome }}</span></div> |
||||
|
|
||||
|
|
||||
|
<div class="chart-container"> |
||||
|
<!-- 左侧数据列表 --> |
||||
|
<div class="market-data"> |
||||
|
<div v-for="market in cashData.markets" :key="market.name" class="market-item"> |
||||
|
<span class="market-name">{{ market.name }}:</span> |
||||
|
<span class="market-value">{{ market.value.toLocaleString() }}</span> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<!-- 图表区域 --> |
||||
|
<div ref="chartRef" class="chart"></div> |
||||
|
</div> |
||||
|
|
||||
|
</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: 5, // 增加底部距离的 |
||||
|
left: 'center' |
||||
|
}, |
||||
|
series: [ |
||||
|
{ |
||||
|
label: {show: false}, |
||||
|
|
||||
|
type: 'pie', |
||||
|
radius: ['40%', '70%'], |
||||
|
data: cashData.value.markets, |
||||
|
center: ['60%', '45%'] //图表靠右一点 |
||||
|
} |
||||
|
] |
||||
|
} |
||||
|
chartInstance.setOption(option) |
||||
|
} |
||||
|
|
||||
|
onMounted(() => { |
||||
|
renderChart() |
||||
|
}) |
||||
|
</script> |
||||
|
|
||||
|
<style scoped> |
||||
|
/* 背景卡片大小 */ |
||||
|
.cash-management { |
||||
|
margin: 10px 5px; |
||||
|
width: 100%; |
||||
|
height: 50vh; |
||||
|
flex-shrink: 0; |
||||
|
border-radius: 8px; |
||||
|
background: #E7F4FD; |
||||
|
box-shadow: 0 2px 2px 0 #00000040; |
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
|
align-items: center; |
||||
|
} |
||||
|
|
||||
|
/* |
||||
|
.cash-card { |
||||
|
width: 100%; |
||||
|
} |
||||
|
|
||||
|
.chart { |
||||
|
width: 100%; |
||||
|
height: 200px; |
||||
|
} */ |
||||
|
|
||||
|
|
||||
|
.cash-title { |
||||
|
width: 100%; |
||||
|
height: 5vh; |
||||
|
flex-shrink: 0; |
||||
|
border-radius: 8px; |
||||
|
background: linear-gradient(90deg, #E4F0FC 0%, #C6ADFF 50%, #E4F0FC 100%); |
||||
|
box-shadow: 0 2px 2px 0 #00152940; |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
justify-content: center; |
||||
|
} |
||||
|
|
||||
|
.text1 { |
||||
|
color: #040a2d; |
||||
|
font-family: "PingFang SC"; |
||||
|
font-size: 28px; |
||||
|
font-style: normal; |
||||
|
font-weight: 900; |
||||
|
line-height: 31.79px; |
||||
|
} |
||||
|
|
||||
|
.text1-update-time { |
||||
|
width: 100%; |
||||
|
height: 26px; |
||||
|
flex-shrink: 0; |
||||
|
color: #040a2d; |
||||
|
font-family: "PingFang SC"; |
||||
|
font-size: 20px; |
||||
|
font-style: normal; |
||||
|
font-weight: 700; |
||||
|
line-height: 31.79px; |
||||
|
} |
||||
|
|
||||
|
/* 总收入的渐变框 */ |
||||
|
.text2 { |
||||
|
margin: 13px; |
||||
|
width: 95%; |
||||
|
height: 48px; |
||||
|
flex-shrink: 0; |
||||
|
border-radius: 8px; |
||||
|
background: linear-gradient(90deg, #E4F0FC 0%, #C1DCF8 50%, #E4F0FC 100%); |
||||
|
box-shadow: 0 2px 2px 0 #00152940; |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
justify-content: center; |
||||
|
} |
||||
|
|
||||
|
/* 总收入字体 */ |
||||
|
.text2-income { |
||||
|
width: 215px; |
||||
|
height: 26px; |
||||
|
flex-shrink: 0; |
||||
|
color: #040a2d; |
||||
|
font-family: "PingFang SC"; |
||||
|
font-size: 20px; |
||||
|
font-style: normal; |
||||
|
font-weight: 900; |
||||
|
line-height: 31.79px; |
||||
|
} |
||||
|
|
||||
|
/* 图表容器 */ |
||||
|
.chart-container { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
width: 100%; |
||||
|
height: 100%; |
||||
|
padding: 10px; |
||||
|
} |
||||
|
|
||||
|
/* 左侧数据列表,使用您指定的样式 */ |
||||
|
.market-data { |
||||
|
display: flex; |
||||
|
width: 179px; |
||||
|
flex-direction: column; |
||||
|
align-items: flex-start; |
||||
|
gap: 12px; |
||||
|
padding: 10px; |
||||
|
margin-left: 80px; |
||||
|
} |
||||
|
|
||||
|
.market-item { |
||||
|
display: flex; |
||||
|
justify-content: space-between; |
||||
|
width: 100%; |
||||
|
font-family: "PingFang SC"; |
||||
|
font-size: 16px; |
||||
|
color: #040a2d; |
||||
|
} |
||||
|
|
||||
|
.market-name { |
||||
|
font-weight: 700; |
||||
|
} |
||||
|
|
||||
|
.market-value { |
||||
|
font-weight: 500; |
||||
|
} |
||||
|
|
||||
|
/* 图表样式 */ |
||||
|
.chart { |
||||
|
flex: 1; |
||||
|
height: 300px; |
||||
|
margin-top: 10px; |
||||
|
} |
||||
|
</style> |
@ -0,0 +1,818 @@ |
|||||
|
<!--各地区的金币充值和消费情况柱状图--> |
||||
|
|
||||
|
<template> |
||||
|
<div class="graph"> |
||||
|
<el-card style="width:100%;" class="graph-card"> |
||||
|
<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 class="graph-card-list"> |
||||
|
<div class="card-large">金币{{ activeTab === 'recharge' ? '充值' : '消费' }}排名</div> |
||||
|
<el-select class="card-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 class="card-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: 0 |
||||
|
} |
||||
|
}, |
||||
|
yAxis: { |
||||
|
type: 'value', |
||||
|
splitLine: { |
||||
|
lineStyle: { |
||||
|
type: 'dashed', |
||||
|
width: 1, |
||||
|
color: '#000000' |
||||
|
} |
||||
|
}, |
||||
|
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 activeTimeRange = ref('') |
||||
|
// 日期选择器变化时清除按钮激活状态 |
||||
|
const handleDatePickerChange = () => { |
||||
|
activeTimeRange.value = '' |
||||
|
} |
||||
|
|
||||
|
onMounted(async () => { |
||||
|
await getAdminData() |
||||
|
await getMarkets() |
||||
|
getYear() |
||||
|
window.addEventListener('resize', () => { |
||||
|
chartInstance.resize() |
||||
|
}) |
||||
|
}) |
||||
|
onUnmounted(() => { |
||||
|
destroyChart() |
||||
|
}) |
||||
|
</script> |
||||
|
<style scoped lang="scss"> |
||||
|
|
||||
|
|
||||
|
.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); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.graph-card { |
||||
|
background: #F3FAFF; |
||||
|
box-shadow: 0 0 8px 0 #00000040; |
||||
|
} |
||||
|
|
||||
|
.graph-card-list { |
||||
|
background: #F3FAFF; |
||||
|
box-shadow: 0 0 8px 0 #00000040; |
||||
|
padding: 12px; |
||||
|
|
||||
|
.card-select { |
||||
|
:deep(.el-input__wrapper) { |
||||
|
background-color: #E7F4FD !important; |
||||
|
box-shadow: 0 0 4px 0 rgba(0, 0, 0, 0.25) !important; |
||||
|
border: none !important; |
||||
|
} |
||||
|
|
||||
|
:deep(.el-input__inner) { |
||||
|
background-color: transparent !important; |
||||
|
} |
||||
|
|
||||
|
:deep(.el-input__suffix) { |
||||
|
background-color: transparent !important; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/* 表格整体背景:把表格容器设为卡片背景 */ |
||||
|
:deep(.el-table) { |
||||
|
background-color: #F3FAFF !important; |
||||
|
box-shadow: none !important; |
||||
|
} |
||||
|
|
||||
|
/* 表头/表体 wrapper 与 table body 单元格 */ |
||||
|
:deep(.el-table__header-wrapper), |
||||
|
:deep(.el-table__body-wrapper), |
||||
|
:deep(.el-table__body), |
||||
|
:deep(.el-table__header), |
||||
|
:deep(.el-table__body tbody), |
||||
|
:deep(.el-table__body tr), |
||||
|
:deep(.el-table__row), |
||||
|
:deep(.el-table__cell), |
||||
|
:deep(.el-table__body td) { |
||||
|
background-color: transparent !important; |
||||
|
} |
||||
|
|
||||
|
/* 表头 */ |
||||
|
:deep(.el-table__header th) { |
||||
|
background-color: #F3FAFF !important; |
||||
|
} |
||||
|
|
||||
|
/* 行之间的分隔线(更像卡片内表格) */ |
||||
|
:deep(.el-table .el-table__row):not(:last-child) { |
||||
|
border-bottom: 1px solid rgba(0, 0, 0, 0.06); |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
|
||||
|
</style> |
4949
stats.html
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
Write
Preview
Loading…
Cancel
Save
Reference in new issue