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.

900 lines
27 KiB

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