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.

744 lines
23 KiB

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