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.

846 lines
25 KiB

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