You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

855 lines
26 KiB

1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
1 month ago
2 months ago
1 month ago
2 months ago
1 month ago
2 months ago
1 month ago
2 months ago
2 months ago
1 month ago
1 month ago
1 month ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
1 month ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
  1. <template>
  2. <el-row>
  3. <!-- 数据总览卡片 -->
  4. <el-col :span="4" style="padding-right: 10px;"> <!-- 适当留白避免拥挤 -->
  5. <el-card class="center-card margin-bottom">数据总览</el-card>
  6. </el-col>
  7. <!-- 最后更新时间 -->
  8. <el-col :span="18" style="display: flex; align-items: center; font-size: 18px">
  9. 最后更新时间{{ workDataUpdateTime && workDataUpdateTime !== '1970-01-01 08:00:00' ? workDataUpdateTime : '该地区暂无数据' }}
  10. </el-col>
  11. <!-- 剩余栅格空间可选用于占满一行 -->
  12. <el-col :span="18"></el-col>
  13. </el-row>
  14. <el-row :gutter="10">
  15. <!-- 第一个卡片 -->
  16. <el-col :span="6">
  17. <el-card class="card-item">
  18. <template #header>
  19. <div class="card-header">
  20. <div class="card-title">当前金币余量</div>
  21. <div>{{ currentGold / 100 }}&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;较前一日 {{
  22. dailyChange / 100 }}
  23. <template v-if="dailyChange > 0">
  24. <el-icon style="color:red">
  25. <ArrowUpBold />
  26. </el-icon>
  27. </template>
  28. <template v-else-if="dailyChange < 0">
  29. <el-icon style="color:forestgreen">
  30. <ArrowDownBold />
  31. </el-icon>
  32. </template>
  33. <template v-else>
  34. <el-icon style="color:grey">
  35. <SemiSelect />
  36. </el-icon>
  37. </template>
  38. </div>
  39. </div>
  40. </template>
  41. <div>
  42. <div class="margin-bottom">永久金币{{ currentPermanent / 100 }}</div>
  43. <div class="margin-bottom">免费金币{{ currentFree / 100 }}</div>
  44. <div class="margin-bottom">[六月到期|{{ currentFreeJune / 100 }}]&nbsp;&nbsp;&nbsp;&nbsp;[12月到期|{{
  45. currentFreeDecember /
  46. 100 }}]</div>
  47. <div>任务金币{{ currentTask / 100 }}</div>
  48. </div>
  49. </el-card>
  50. </el-col>
  51. <!-- 第二个卡片 -->
  52. <el-col :span="6">
  53. <el-card class="card-item">
  54. <div class="card-title">全年累计充值金币数</div>
  55. <div class="card-title">{{ yearlyRecharge / 100 }}</div>
  56. <div>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</div>
  57. <div class="center-card">折合新币累计金额:{{ yearlyMoney / 100 }}</div>
  58. <template #footer>
  59. <el-col class="margin-bottom center-card">昨日新增{{ recharge / 100 }}</el-col>
  60. <el-col class="margin-bottom center-card">其中充值{{ money / 100 }}</el-col>
  61. </template>
  62. </el-card>
  63. </el-col>
  64. <!-- 第三个卡片 -->
  65. <el-col :span="6">
  66. <el-card class="card-item">
  67. <div class="card-title">全年累计消费金币数</div>
  68. <div class="card-title">{{ yearlyReduce / 100 }}</div>
  69. <div class="center-card">消费{{ yearlyConsume / 100 }}</div>
  70. <div class="center-card">退款{{ yearlyRefund / 100 }}</div>
  71. <template #footer>
  72. <div></div>
  73. <div class="margin-bottom center-card">昨日新增消费{{ dailyConsume / 100 }}</div>
  74. <div class="margin-bottom center-card">昨日新增消耗{{ dailyReduce / 100 }}</div>
  75. <div class="margin-bottom center-card">昨日新增退款{{ dailyRefund / 100 }}</div>
  76. </template>
  77. </el-card>
  78. </el-col>
  79. <!-- 第四个卡片 -->
  80. <el-col :span="6">
  81. <el-card class="card-item">
  82. <el-col class="card-title">全年累计充值人头数</el-col>
  83. <el-col class="card-title">{{ yearlyRechargeNum }}</el-col>
  84. <el-col class="center-card">周同比:{{ sumWow }}%&nbsp;&nbsp;&nbsp;&nbsp;
  85. <template v-if="sumWow > 0">
  86. <el-icon style="color:red">
  87. <ArrowUpBold />
  88. </el-icon>
  89. </template>
  90. <template v-else-if="sumWow < 0">
  91. <el-icon style="color:forestgreen">
  92. <ArrowDownBold />
  93. </el-icon>
  94. </template>
  95. <template v-else>
  96. <el-icon style="color:grey">
  97. <SemiSelect />
  98. </el-icon>
  99. </template>
  100. </el-col>
  101. <el-col class="center-card">日环比:{{ sumDaily }}%&nbsp;&nbsp;&nbsp;&nbsp;
  102. <template v-if="sumDaily > 0">
  103. <el-icon style="color:red">
  104. <ArrowUpBold />
  105. </el-icon>
  106. </template>
  107. <template v-else-if="sumDaily < 0">
  108. <el-icon style="color:forestgreen">
  109. <ArrowDownBold />
  110. </el-icon>
  111. </template>
  112. <template v-else>
  113. <el-icon style="color:grey">
  114. <SemiSelect />
  115. </el-icon>
  116. </template>
  117. </el-col>
  118. <template #footer>
  119. <el-col class="margin-bottom center-card">昨日充值人数{{ ydayRechargeNum }}</el-col>
  120. <el-col class="margin-bottom center-card">其中首充{{ firstRecharge }}</el-col>
  121. </template>
  122. </el-card>
  123. </el-col>
  124. </el-row>
  125. <el-row :gutter="10" style="margin-top: 20px">
  126. <el-col :span="24">
  127. <el-card style="width: 100%">
  128. <el-row>
  129. <el-col :span="21">
  130. <el-tabs v-model="activeTab" @tab-change="handleTabChange">
  131. <el-tab-pane label="金币充值" name="recharge"></el-tab-pane>
  132. <el-tab-pane label="金币消费" name="consume"></el-tab-pane>
  133. </el-tabs>
  134. </el-col>
  135. <el-col :span="24">
  136. <el-row>
  137. <div style="margin-top:5px">合计&nbsp;&nbsp;&nbsp;&nbsp;
  138. 永久金币 {{ activeTab === 'recharge' ? sumRechargePermanent / 100 : sumConsumePermanent / 100
  139. }}&nbsp;&nbsp;&nbsp;&nbsp;
  140. 免费金币 {{ activeTab === 'recharge' ? sumRechargeFree / 100 : sumConsumeFree / 100
  141. }}&nbsp;&nbsp;&nbsp;&nbsp;
  142. 任务金币 {{ activeTab === 'recharge' ? sumRechargeTask / 100 : sumConsumeTask / 100
  143. }}&nbsp;&nbsp;&nbsp;&nbsp;
  144. </div>
  145. <div @change="handleDatePickerChange">
  146. <el-button @click="getToday()" label="day" style="margin-left:250px" :type="activeTimeRange === 'today' ? 'primary' : ''">今日</el-button>
  147. <el-button @click="getWeek()" label="week" :type="activeTimeRange === 'week' ? 'primary' : ''">本周</el-button>
  148. <el-button @click="getMonth()" label="month" :type="activeTimeRange === 'month' ? 'primary' : ''">本月</el-button>
  149. <el-button @click="getYear()" label="year" :type="activeTimeRange === 'year' ? 'primary' : ''">本年</el-button>
  150. </div>
  151. <el-date-picker v-model="dateRange" type="datetimerange" range-separator="" start-placeholder="开始时间"
  152. end-placeholder="结束时间" style="margin-left:10px" format="YYYY-MM-DD HH:mm:ss" value-format="YYYY-MM-DD HH:mm:ss" />
  153. <el-button type="primary" style="margin-left: 5px" @click="getChartData">查询</el-button>
  154. </el-row>
  155. </el-col>
  156. </el-row>
  157. <el-row :gutter="20" style="margin-top: 20px">
  158. <el-col :span="18">
  159. <div class="bar">
  160. <!-- <div v-if="chartLoading" class="loading-overlay">-->
  161. <!-- <div class="loading-spinner"></div>-->
  162. <!-- </div>-->
  163. <div ref="chartRef" style="width: 100%; height: 400px"></div>
  164. </div>
  165. </el-col>
  166. <el-col :span="6">
  167. <el-card class="rank-card" style="width: 100%; height: 100%">
  168. <div class="card-large margin-bottom">金币{{ activeTab === 'recharge' ? '充值' : '消费' }}排名</div>
  169. <el-select v-model="selectedType" style="width: 100%; margin-bottom: 15px">
  170. <el-option label="全部类型" value="all"></el-option>
  171. <el-option label="永久金币" value="permanent"></el-option>
  172. <el-option label="免费金币" value="free"></el-option>
  173. <el-option label="任务金币" value="task"></el-option>
  174. </el-select>
  175. <el-table :data="tableData" height="320px">
  176. <el-table-column prop="rank" label="排名" width="60" align="center"></el-table-column>
  177. <el-table-column prop="market" label="地区" align="center"></el-table-column>
  178. <el-table-column prop="coinAmount" label="金币数量" align="center">
  179. <template #default="{ row }">
  180. {{ row.coinAmount.toLocaleString() }}
  181. </template>
  182. </el-table-column>
  183. </el-table>
  184. </el-card>
  185. </el-col>
  186. </el-row>
  187. </el-card>
  188. </el-col>
  189. </el-row>
  190. </template>
  191. <script setup>
  192. import * as echarts from 'echarts'
  193. import { ref, onMounted, nextTick, watch, onUnmounted } from 'vue'
  194. import API from '@/util/http'
  195. import { ElMessage } from 'element-plus'
  196. import dayjs from 'dayjs';
  197. import utc from 'dayjs-plugin-utc'
  198. dayjs.extend(utc)
  199. import { ArrowUpBold, ArrowDownBold, SemiSelect } from '@element-plus/icons-vue'
  200. // 地区数据
  201. const markets = ref([])
  202. // 图表相关
  203. const dateRange = ref([])
  204. const activeTab = ref('recharge')
  205. const selectedType = ref('all')
  206. const tableData = ref([])
  207. const chartRef = ref(null)
  208. let chartInstance = null
  209. // 图表合计数
  210. const sumRechargePermanent = ref(0)
  211. const sumRechargeFree = ref(0)
  212. const sumRechargeTask = ref(0)
  213. const sumConsumePermanent = ref(0)
  214. const sumConsumeFree = ref(0)
  215. const sumConsumeTask = ref(0)
  216. // 用户信息
  217. const adminData = ref({})
  218. // 卡片数据相关
  219. const currentGold = ref(0)
  220. const dailyChange = ref(0)
  221. const currentPermanent = ref(0)
  222. const currentFree = ref(0)
  223. const currentFreeJune = ref(0)
  224. const currentFreeDecember = ref(0)
  225. const currentTask = ref(0)
  226. const yearlyRecharge = ref(0)
  227. const yearlyMoney = ref(0)
  228. const recharge = ref(0)
  229. const money = ref(0)
  230. const yearlyReduce = ref(0)
  231. const yearlyConsume = ref(0)
  232. const yearlyRefund = ref(0)
  233. const dailyReduce = ref(0)
  234. const dailyConsume = ref(0)
  235. const dailyRefund = ref(0)
  236. const yearlyRechargeNum = ref(0)
  237. const sumWow = ref(0)
  238. const sumDaily = ref(0)
  239. const rechargeNum = ref(0)
  240. const ydayRechargeNum = ref(0)
  241. const firstRecharge = ref(0)
  242. const length = ref(0)
  243. // 加载状态
  244. const chartLoading = ref(true)
  245. const handleResize = () => {
  246. if (chartInstance.value) {
  247. try {
  248. chartInstance.value.resize()
  249. console.log('resize一下')
  250. } catch (error) {
  251. console.error('图表resize失败:', error)
  252. }
  253. }
  254. }
  255. // 初始化图表
  256. const initChart = () => {
  257. if (!chartInstance && chartRef.value) {
  258. chartInstance = echarts.init(chartRef.value)
  259. window.addEventListener('resize', handleResize)
  260. }
  261. }
  262. // 销毁图表
  263. const destroyChart = () => {
  264. if (chartInstance.value) {
  265. try {
  266. chartInstance.value.dispose()
  267. } catch (error) {
  268. console.error('图表销毁失败:', error)
  269. }
  270. chartInstance.value = null
  271. }
  272. window.removeEventListener('resize', handleResize)
  273. }
  274. const formatDate = function(date) {
  275. const year = date.getFullYear();
  276. const month = String(date.getMonth() + 1).padStart(2, '0');
  277. const day = String(date.getDate()).padStart(2, '0');
  278. const hours = String(date.getHours()).padStart(2, '0');
  279. const minutes = String(date.getMinutes()).padStart(2, '0');
  280. const seconds = String(date.getSeconds()).padStart(2, '0');
  281. return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
  282. }
  283. // 今天
  284. const getToday = function () {
  285. const today = dayjs()
  286. const startTime = today.startOf('day').format('YYYY-MM-DD HH:mm:ss')
  287. const endTime = today.add(1,'day').startOf('day').format('YYYY-MM-DD HH:mm:ss')
  288. dateRange.value = [startTime, endTime]
  289. console.log('看看dateRange', dateRange.value)
  290. activeTimeRange.value = 'today' // 标记当前激活状态
  291. getChartData()
  292. }
  293. // 本周
  294. const getWeek = function () {
  295. const today = dayjs()
  296. const startTime = (today.startOf('week').add(1,'day')).format('YYYY-MM-DD HH:mm:ss')
  297. const endTime = today.add(1,'week').startOf('week').add(1,'day').format('YYYY-MM-DD HH:mm:ss')
  298. dateRange.value = [startTime, endTime]
  299. console.log('看看dateRange', dateRange.value)
  300. activeTimeRange.value = 'week' // 标记当前激活状态
  301. getChartData()
  302. }
  303. // 本月
  304. const getMonth = function () {
  305. const today = dayjs()
  306. const startTime = today.startOf('month').format('YYYY-MM-DD HH:mm:ss')
  307. const endTime = today.add(1,'month').startOf('month').format('YYYY-MM-DD HH:mm:ss')
  308. dateRange.value = [startTime, endTime]
  309. console.log('看看dateRange', dateRange.value)
  310. activeTimeRange.value = 'month' // 标记当前激活状态
  311. getChartData()
  312. }
  313. // 本年
  314. const getYear = function () {
  315. const today = dayjs()
  316. const startTime = today.startOf('year').format('YYYY-MM-DD HH:mm:ss')
  317. const endTime = today.add(1,'year').startOf('year').format('YYYY-MM-DD HH:mm:ss')
  318. dateRange.value = [startTime, endTime]
  319. console.log('看看dateRange', dateRange.value)
  320. activeTimeRange.value = 'year' // 标记当前激活状态
  321. getChartData()
  322. }
  323. // 要加上所有市场的,还有额外计算的(总数 = 永久 + 6月 + 12月 + 免费 + 任务)
  324. const processData = (data) => {
  325. const summary = {
  326. currentGold: 0,
  327. dailyChange: 0,
  328. currentPermanent: 0,
  329. currentFreeJune: 0,
  330. currentFreeDecember: 0,
  331. currentTask: 0,
  332. currentFree: 0,
  333. recharge: 0,
  334. money: 0,
  335. yearlyRecharge: 0,
  336. yearlyMoney: 0,
  337. consumePermanent: 0,
  338. consumeFreeJune: 0,
  339. consumeFreeDecember: 0,
  340. consumeTask: 0,
  341. refundPermanent: 0,
  342. refundFreeJune: 0,
  343. refundFreeDecember: 0,
  344. refundTask: 0,
  345. dailyReduce: 0,
  346. yearlyConsume: 0,
  347. yearlyRefund: 0,
  348. yearlyReduce: 0,
  349. rechargeNum: 0,
  350. ydayRechargeNum: 0,
  351. firstRecharge: 0,
  352. sumWow: 0,
  353. sumDaily: 0,
  354. yearlyRechargeNum: 0
  355. }
  356. // 遍历市场
  357. data.marketCards.forEach(market => {
  358. for (const i in summary) {
  359. if (market[i] !== undefined && market[i] !== null) { // 其实还应该卡一个number
  360. summary[i] += market[i]
  361. }
  362. }
  363. })
  364. // wow和daily除一下
  365. length.value = data.markets.length
  366. console.log(length.value)
  367. // 计算昨日新增消费和退款
  368. const yesterdayConsume = summary.consumePermanent + summary.consumeFreeJune + summary.consumeFreeDecember + summary.consumeTask
  369. const yesterdayRefund = summary.refundPermanent + summary.refundFreeJune + summary.refundFreeDecember + summary.refundTask
  370. // 更新卡片数据
  371. currentGold.value = summary.currentGold.toFixed(2)
  372. dailyChange.value = summary.dailyChange.toFixed(2)
  373. currentPermanent.value = summary.currentPermanent.toFixed(2)
  374. currentFree.value = summary.currentFree.toFixed(2)
  375. currentFreeJune.value = summary.currentFreeJune.toFixed(2)
  376. currentFreeDecember.value = summary.currentFreeDecember.toFixed(2)
  377. currentTask.value = summary.currentTask.toFixed(2)
  378. yearlyRecharge.value = summary.yearlyRecharge.toFixed(2)
  379. yearlyMoney.value = summary.yearlyMoney.toFixed(2)
  380. recharge.value = summary.recharge.toFixed(2)
  381. money.value = summary.money.toFixed(2)
  382. yearlyReduce.value = summary.yearlyReduce.toFixed(2)
  383. yearlyConsume.value = summary.yearlyConsume.toFixed(2)
  384. yearlyRefund.value = summary.yearlyRefund.toFixed(2)
  385. dailyReduce.value = summary.dailyReduce.toFixed(2)
  386. dailyConsume.value = yesterdayConsume.toFixed(2)
  387. dailyRefund.value = yesterdayRefund.toFixed(2)
  388. yearlyRechargeNum.value = summary.yearlyRechargeNum
  389. // // 周同比
  390. // sumWow.value = (marketCards.sumWow / length.value).toFixed(2)
  391. // // 日环比
  392. // sumDaily.value = (marketCards.sumDaily / length.value).toFixed(2)
  393. // rechargeNum.value = summary.rechargeNum
  394. ydayRechargeNum.value = summary.ydayRechargeNum
  395. firstRecharge.value = summary.firstRecharge
  396. }
  397. // 获取市场列表
  398. const getMarkets = async () => {
  399. console.log("adminData",adminData.value.account)
  400. try {
  401. const response = await API({
  402. url: '/general/adminMarkets',
  403. data: {
  404. account: adminData.value.account
  405. }
  406. })
  407. if (Array.isArray(response.data)) {
  408. markets.value = response.data
  409. console.log('市场列表获取成功:', markets.value)
  410. } else {
  411. console.error('获取市场列表失败', response)
  412. ElMessage.error('获取市场列表失败')
  413. }
  414. } catch (error) {
  415. console.error('获取市场列表失败:', error)
  416. ElMessage.error('获取市场列表失败')
  417. }
  418. }
  419. // 获取图表数据
  420. const getChartData = async () => {
  421. try {
  422. // 校验市场数据到底有没有
  423. if (!markets.value || markets.value.length === 0) {
  424. await getMarkets()
  425. }
  426. // 本年
  427. if(!dateRange.value || dateRange.value.length === 0){
  428. getYear()
  429. }
  430. const params = {
  431. markets: markets.value,
  432. startDate: dateRange.value[0],
  433. endDate: dateRange.value[1]
  434. };
  435. const response = await API({
  436. url: '/workbench/getGraph',
  437. data: params
  438. })
  439. console.log('看看params', params)
  440. if (Array.isArray(response.marketGraphs)) {
  441. // 处理图表数据
  442. processChartData(response.marketGraphs)
  443. // 处理排名数据
  444. processRankingData(response.marketGraphs)
  445. } else {
  446. console.error('获取图表数据失败:', response)
  447. ElMessage.error('获取图表数据失败')
  448. }
  449. } catch (error) {
  450. console.error('获取图表数据失败:', error)
  451. ElMessage.error('获取图表数据失败')
  452. }
  453. }
  454. // 处理图表数据
  455. const processChartData = (marketCards) => {
  456. const chartData = {
  457. rechargePermanent: [],
  458. rechargeFree: [],
  459. rechargeTask: [],
  460. consumePermanent: [],
  461. consumeFree: [],
  462. consumeTask: []
  463. }
  464. // 这是图表的合计数,怎样遍历?????
  465. const sumRechargePermanent1 = ref(0)
  466. const sumRechargeFree1 = ref(0)
  467. const sumRechargeTask1 = ref(0)
  468. const sumConsumePermanent1 = ref(0)
  469. const sumConsumeFree1 = ref(0)
  470. const sumConsumeTask1 = ref(0)
  471. marketCards.forEach(market => {
  472. chartData.rechargePermanent.push(market.sumRechargePermanent / 100 || 0)
  473. chartData.rechargeFree.push(market.sumRechargeFree / 100 || 0)
  474. chartData.rechargeTask.push(market.sumRechargeTask / 100 || 0)
  475. chartData.consumePermanent.push(market.sumConsumePermanent / 100 || 0)
  476. chartData.consumeFree.push(market.sumConsumeFree / 100 || 0)
  477. chartData.consumeTask.push(market.sumConsumeTask / 100 || 0)
  478. // 合计数合计数合计数咋算
  479. sumRechargePermanent1.value += (market.sumRechargePermanent || 0)
  480. sumRechargeFree1.value += (market.sumRechargeFree || 0)
  481. //sumRechargeTask1.value += (market.sumRechargeTask || 0)
  482. sumConsumePermanent1.value += (market.sumConsumePermanent || 0)
  483. sumConsumeFree1.value += (market.sumConsumeFree || 0)
  484. sumConsumeTask1.value += (market.sumConsumeTask || 0)
  485. })
  486. sumRechargePermanent.value = sumRechargePermanent1.value
  487. sumRechargeFree.value = sumRechargeFree1.value
  488. sumRechargeTask.value = 0
  489. sumConsumePermanent.value = sumConsumePermanent1.value
  490. sumConsumeFree.value = sumConsumeFree1.value
  491. sumConsumeTask.value = sumConsumeTask1.value
  492. updateChart(chartData)
  493. }
  494. const processRankingData = (marketCards) => {
  495. // 每个市场的总金币数
  496. const rankingData = marketCards.map(market => {
  497. let coinAmount = 0;
  498. if (activeTab.value === 'recharge') {
  499. // 充值排名
  500. switch (selectedType.value) {
  501. case 'all':
  502. coinAmount = (market.sumRechargePermanent / 100 || 0) + (market.sumRechargeFree / 100 || 0) + (market.sumRechargeTask / 100 || 0);
  503. break;
  504. case 'permanent':
  505. coinAmount = market.sumRechargePermanent / 100 || 0;
  506. break;
  507. case 'free':
  508. coinAmount = market.sumRechargeFree / 100 || 0;
  509. break;
  510. case 'task':
  511. coinAmount = market.sumRechargeTask / 100 || 0;
  512. break;
  513. }
  514. } else {
  515. // 消费排名
  516. switch (selectedType.value) {
  517. case 'all':
  518. coinAmount = (market.sumConsumePermanent / 100 || 0) + (market.sumConsumeFree / 100 || 0) + (market.sumConsumeTask / 100 || 0);
  519. break;
  520. case 'permanent':
  521. coinAmount = market.sumConsumePermanent / 100 || 0;
  522. break;
  523. case 'free':
  524. coinAmount = market.sumConsumeFree / 100 || 0;
  525. break;
  526. case 'task':
  527. coinAmount = market.sumConsumeTask / 100 || 0;
  528. break;
  529. }
  530. }
  531. return {
  532. market: market.market,
  533. coinAmount: coinAmount
  534. };
  535. });
  536. // 按金币数量排序
  537. rankingData.sort((a, b) => b.coinAmount - a.coinAmount);
  538. // 排名序号
  539. tableData.value = rankingData.map((item, index) => ({
  540. rank: index + 1,
  541. ...item
  542. }));
  543. }
  544. watch(selectedType, () => {
  545. getChartData();
  546. });
  547. // 更新图表
  548. const updateChart = (chartData) => {
  549. if (!chartInstance) {
  550. initChart()
  551. }
  552. chartLoading.value = true
  553. try{
  554. let series = []
  555. let legend = []
  556. if (activeTab.value === 'recharge') {
  557. series = [
  558. {
  559. name: '永久金币',
  560. type: 'bar',
  561. stack: 'recharge',
  562. data: chartData.rechargePermanent,
  563. itemStyle: { color: '#5470c6' },
  564. barWidth: 30
  565. },
  566. {
  567. name: '免费金币',
  568. type: 'bar',
  569. stack: 'recharge',
  570. data: chartData.rechargeFree,
  571. itemStyle: { color: '#91cc75' },
  572. barWidth: 30
  573. },
  574. {
  575. name: '任务金币',
  576. type: 'bar',
  577. stack: 'recharge',
  578. data: chartData.rechargeTask,
  579. itemStyle: { color: '#fac858' },
  580. barWidth: 30
  581. }
  582. ]
  583. legend = ['永久金币', '免费金币', '任务金币']
  584. } else {
  585. series = [
  586. {
  587. name: '永久金币',
  588. type: 'bar',
  589. stack: 'consume',
  590. data: chartData.consumePermanent,
  591. itemStyle: { color: '#5470c6' },
  592. barWidth: 30
  593. },
  594. {
  595. name: '免费金币',
  596. type: 'bar',
  597. stack: 'consume',
  598. data: chartData.consumeFree,
  599. itemStyle: { color: '#91cc75' },
  600. barWidth: 30
  601. },
  602. {
  603. name: '任务金币',
  604. type: 'bar',
  605. stack: 'consume',
  606. data: chartData.consumeTask,
  607. itemStyle: { color: '#fac858' },
  608. barWidth: 30
  609. }
  610. ]
  611. legend = ['永久金币', '免费金币', '任务金币']
  612. }
  613. const option = {
  614. tooltip: {
  615. trigger: 'axis',
  616. axisPointer: {
  617. type: 'shadow'
  618. },
  619. formatter: function (params) {
  620. let result = params[0].name + '<br/>'
  621. params.forEach(param => {
  622. result += `${param.seriesName}: ${param.value.toLocaleString()}<br/>`
  623. })
  624. return result
  625. }
  626. },
  627. legend: {
  628. data: legend,
  629. bottom: 10
  630. },
  631. grid: {
  632. left: '3%',
  633. right: '4%',
  634. bottom: '15%',
  635. containLabel: true
  636. },
  637. xAxis: {
  638. type: 'category',
  639. data: markets.value,
  640. axisLabel: {
  641. interval: 0,
  642. rotate: 30
  643. }
  644. },
  645. yAxis: {
  646. type: 'value',
  647. axisLabel: {
  648. formatter: function (value) {
  649. return value.toLocaleString()
  650. }
  651. }
  652. },
  653. series: series,
  654. // dataZoom: [
  655. // {
  656. // type: 'slider',
  657. // show: true,
  658. // start: 0,
  659. // end: 100,
  660. // maxSpan: 100,
  661. // minSpan: 100,
  662. //
  663. // height: 2,
  664. // },
  665. // ]
  666. }
  667. chartInstance.setOption(option)
  668. } catch (error) {
  669. console.error('图表更新失败:', error)
  670. ElMessage.error('图表渲染失败')
  671. } finally {
  672. setTimeout(() => {
  673. chartLoading.value = false
  674. }, 300)
  675. }
  676. }
  677. // 处理标签切换
  678. const handleTabChange = () => {
  679. getChartData()
  680. console.log('标签切换调用图表')
  681. }
  682. const getAdminData = async function () {
  683. try {
  684. const result = await API({ url: '/admin/userinfo', data: {} })
  685. adminData.value = result
  686. console.log('用户信息', adminData.value)
  687. } catch (error) {
  688. console.log('请求失败', error)
  689. }
  690. }
  691. // 获取卡片数据
  692. const getCardData = async () => {
  693. try {
  694. const response = await API({ url: '/workbench/getCard', data: {} })
  695. workDataUpdateTime.value = response.updateTime
  696. // 周同比
  697. sumWow.value = response.sumWow .toFixed(2)
  698. // 日环比
  699. sumDaily.value = response.sumDaily.toFixed(2)
  700. if (response && response.data) {
  701. processData(response.data)
  702. } else if (Array.isArray(response?.marketCards)) {
  703. processData(response)
  704. } else {
  705. console.error('无效的API响应结构:', response)
  706. }
  707. } catch (error) {
  708. console.error('获取卡片数据失败:', error)
  709. }
  710. }
  711. const workDataUpdateTime = ref(null)
  712. // 标记当前激活的时间范围按钮
  713. const activeTimeRange = ref('')
  714. // 日期选择器变化时清除按钮激活状态
  715. const handleDatePickerChange = () => {
  716. activeTimeRange.value = ''
  717. }
  718. onMounted(async () => {
  719. await getAdminData()
  720. await getCardData()
  721. await getMarkets()
  722. getYear()
  723. window.addEventListener('resize', () => {
  724. chartInstance.resize()
  725. })
  726. })
  727. onUnmounted(() => {
  728. destroyChart()
  729. })
  730. </script>
  731. <style scoped>
  732. .center-card {
  733. display: flex;
  734. justify-content: center;
  735. align-items: center;
  736. }
  737. .margin-bottom {
  738. margin-bottom: 5px;
  739. }
  740. .card-item {
  741. height: 260px;
  742. display: flex;
  743. flex-direction: column;
  744. justify-content: center;
  745. }
  746. .card-title {
  747. font-weight: bold;
  748. margin-bottom: 10px;
  749. display: flex;
  750. justify-content: center;
  751. align-items: center;
  752. }
  753. .rank-card {
  754. height: 500px;
  755. }
  756. .card-large {
  757. font-weight: bold;
  758. font-size: 16px;
  759. text-align: center;
  760. margin-bottom: 15px;
  761. }
  762. .bar {
  763. background: white;
  764. border-radius: 8px;
  765. padding: 15px;
  766. box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
  767. }
  768. /* 添加加载动画 */
  769. .loading-overlay {
  770. position: absolute;
  771. top: 0;
  772. left: 0;
  773. right: 0;
  774. bottom: 0;
  775. background: rgba(255, 255, 255, 0.8);
  776. display: flex;
  777. justify-content: center;
  778. align-items: center;
  779. z-index: 10;
  780. }
  781. .loading-spinner {
  782. width: 40px;
  783. height: 40px;
  784. border: 4px solid #f3f3f3;
  785. border-top: 4px solid #3498db;
  786. border-radius: 50%;
  787. animation: spin 1s linear infinite;
  788. }
  789. @keyframes spin {
  790. 0% {
  791. transform: rotate(0deg);
  792. }
  793. 100% {
  794. transform: rotate(360deg);
  795. }
  796. }
  797. </style>