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.

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