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.

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