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.

423 lines
12 KiB

2 weeks ago
2 weeks ago
2 weeks ago
2 weeks ago
2 weeks ago
2 weeks ago
2 weeks ago
2 weeks ago
2 weeks ago
2 weeks ago
2 weeks ago
2 weeks ago
2 weeks ago
2 weeks ago
2 weeks ago
2 weeks ago
2 weeks ago
2 weeks ago
2 weeks ago
2 weeks ago
2 weeks ago
2 weeks ago
2 weeks ago
2 weeks ago
2 weeks ago
2 weeks ago
2 weeks ago
2 weeks ago
2 weeks ago
2 weeks ago
2 weeks ago
2 weeks ago
2 weeks ago
2 weeks ago
2 weeks ago
2 weeks ago
2 weeks ago
2 weeks ago
2 weeks ago
2 weeks ago
  1. <template>
  2. <el-col :span="4">
  3. <el-card class="center-card margin-bottom">数据总览</el-card>
  4. </el-col>
  5. <el-row :span="24">
  6. <!-- 第一个卡片 -->
  7. <el-card class="card-item">
  8. <template #header>
  9. <div class="card-header">
  10. <div class="card-title">当前金币余量</div>
  11. <div>100&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;较前一日 -100</div>
  12. </div>
  13. </template>
  14. <div>
  15. <div class="margin-bottom">永久金币100</div>
  16. <div class="margin-bottom">免费金币100</div>
  17. <div class="margin-bottom">[六月到期|100]&nbsp;&nbsp;&nbsp;&nbsp;[12月到期|100]</div>
  18. <div>任务金币100</div>
  19. </div>
  20. </el-card>
  21. <!-- 第二个卡片 -->
  22. <el-card class="card-item">
  23. <div class="card-title">全年累计充值金币数</div>
  24. <div class="card-title">100</div>
  25. <div>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</div>
  26. <div class="center-card">折合新币累计金额:100</div>
  27. <template #footer >
  28. <el-col class="margin-bottom center-card">昨日新增100</el-col>
  29. <el-col class="margin-bottom center-card">其中充值100</el-col>
  30. </template>
  31. </el-card>
  32. <!-- 第三个卡片 -->
  33. <el-card class="card-item">
  34. <div class="card-title">全年累计消耗金币数</div>
  35. <div class="card-title">100</div>
  36. <div class="center-card">消费100</div>
  37. <div class="center-card">退款100</div>
  38. <template #footer>
  39. <div></div>
  40. <div class="margin-bottom center-card">昨日新增消耗100</div>
  41. <div class="margin-bottom center-card">昨日新增消费100</div>
  42. <div class="margin-bottom center-card">昨日新增退款100</div>
  43. </template>
  44. </el-card>
  45. <!-- 第四个卡片 -->
  46. <el-card class="card-item">
  47. <el-col class="card-title">全年累计充值人头数</el-col>
  48. <el-col class="card-title">100</el-col>
  49. <el-col class="center-card">周同比:</el-col>
  50. <el-col class="center-card">日环比</el-col>
  51. <template #footer>
  52. <el-col class="margin-bottom center-card">昨日充值人数100</el-col>
  53. <el-col class="margin-bottom center-card">其中首充100</el-col>
  54. </template>
  55. </el-card>
  56. </el-row>
  57. <el-row :gutter="10" style="margin-top: 20px">
  58. <el-col :span="24">
  59. <el-card style="width: 100%">
  60. <el-row>
  61. <el-col :span="15">
  62. <el-tabs v-model="activeTab" @tab-change="handleTabChange">
  63. <el-tab-pane label="金币充值" name="recharge"></el-tab-pane>
  64. <el-tab-pane label="金币消费" name="consume"></el-tab-pane>
  65. </el-tabs>
  66. </el-col>
  67. <el-col :span="9" style="text-align: right">
  68. <el-radio-group v-model="timeRange" @change="handleTimeRangeChange" style="margin-right: 5px; margin-top:5px">
  69. <el-radio-button label="day" style="border-color: white">今日</el-radio-button>
  70. <el-radio-button label="week" style="border-color: white">本周</el-radio-button>
  71. <el-radio-button label="month" style="border-color: white">本月</el-radio-button>
  72. <el-radio-button label="year" style="border-color: white">本年</el-radio-button>
  73. </el-radio-group>
  74. <el-date-picker
  75. v-model="dateRange"
  76. type="daterange"
  77. range-separator="→"
  78. start-placeholder="开始时间"
  79. end-placeholder="结束时间"
  80. style="width: 200px"
  81. @change="handleDateRangeChange"
  82. />
  83. <el-button style="margin-left: 5px" @click="loadChart">查询</el-button>
  84. </el-col>
  85. </el-row>
  86. <el-row :gutter="20" style="margin-top: 20px">
  87. <el-col :span="18">
  88. <div class="bar">
  89. <div v-show="activeTab === 'recharge'" id="recharge" style="width: 100%; height: 400px"></div>
  90. <div v-show="activeTab === 'consume'" id="consume" style="width: 100%; height: 400px"></div>
  91. </div>
  92. </el-col>
  93. <el-col :span="6">
  94. <el-card class="rank-card" style="width: 100%; height: 100%">
  95. <div class="card-large margin-bottom">金币{{ activeTab === 'recharge' ? '充值' : '消费' }}排名</div>
  96. <el-select v-model="selectedType" style="width: 100%; margin-bottom: 15px">
  97. <el-option label="全部类型" value="all"></el-option>
  98. <el-option label="永久金币" value="permanent"></el-option>
  99. <el-option label="免费金币" value="free"></el-option>
  100. <el-option label="任务金币" value="task"></el-option>
  101. </el-select>
  102. <el-table :data="tableData" height="320px">
  103. <el-table-column prop="rank" label="排名" width="60" align="center"></el-table-column>
  104. <el-table-column prop="region" label="地区" align="center"></el-table-column>
  105. <el-table-column prop="coinAmount" label="金币数量" align="center">
  106. <template #default="{ row }">
  107. {{ row.coinAmount.toLocaleString() }}
  108. </template>
  109. </el-table-column>
  110. </el-table>
  111. </el-card>
  112. </el-col>
  113. </el-row>
  114. </el-card>
  115. </el-col>
  116. </el-row>
  117. </template>
  118. <script setup>
  119. import * as echarts from 'echarts'
  120. import { ref, onMounted, onBeforeUnmount } from 'vue'
  121. import API from '@/util/http'
  122. const histogramData = ref([])
  123. const middleCategory = ref([])
  124. const middleRecharge = ref([])
  125. const middleFree = ref([])
  126. const middleTask = ref([])
  127. const tableData = ref([])
  128. const area = ref([])
  129. // 变量
  130. const activeTab = ref('consume')
  131. const updateType = ref(1)
  132. const timeRange = ref('day')
  133. const dateRange = ref([])
  134. const selectedType = ref('all')
  135. // ECharts实例
  136. let rechargeBar = null
  137. let consumeBar = null
  138. const formatDate = (date) => {
  139. const year = date.getFullYear()
  140. const month = String(date.getMonth() + 1).padStart(2, '0')
  141. const day = String(date.getDate()).padStart(2, '0')
  142. return `${year}-${month}-${day}`
  143. }
  144. // 本周一
  145. const getWeekStart = (date) => {
  146. const day = date.getDay()
  147. const diff = date.getDate() - day + (day === 0 ? -6 : 1) // 如果周日,则返回上周一
  148. return new Date(date.setDate(diff))
  149. }
  150. // 本周日
  151. const getWeekEnd = (date) => {
  152. const start = getWeekStart(new Date(date))
  153. const end = new Date(start)
  154. end.setDate(start.getDate() + 6)
  155. return end
  156. }
  157. // 月份第一天
  158. const getMonthStart = (date) => {
  159. return new Date(date.getFullYear(), date.getMonth(), 1)
  160. }
  161. // 月份最后一天
  162. const getMonthEnd = (date) => {
  163. return new Date(date.getFullYear(), date.getMonth() + 1, 0)
  164. }
  165. // 年份第一天
  166. const getYearStart = (date) => {
  167. return new Date(date.getFullYear(), 0, 1)
  168. }
  169. // 年份最后一天
  170. const getYearEnd = (date) => {
  171. return new Date(date.getFullYear(), 11, 31)
  172. }
  173. const handleTimeRangeChange = (value) => {
  174. const now = new Date()
  175. let start, end
  176. switch (value) {
  177. case 'day': // 今日
  178. start = new Date(now)
  179. end = new Date(now)
  180. break
  181. case 'week': // 本周
  182. start = getWeekStart(new Date(now))
  183. end = getWeekEnd(new Date(now))
  184. break
  185. case 'month': // 本月
  186. start = getMonthStart(now)
  187. end = getMonthEnd(now)
  188. break
  189. case 'year': // 本年
  190. start = getYearStart(now)
  191. end = getYearEnd(now)
  192. break
  193. default:
  194. start = new Date()
  195. end = new Date()
  196. }
  197. dateRange.value = [start, end]
  198. loadChart()
  199. }
  200. const handleDateRangeChange = () => {
  201. timeRange.value = ''
  202. }
  203. // 获取地区数据
  204. const getAreas = async () => {
  205. try {
  206. const result = await API({
  207. url: 'http://18.143.76.3:10704/general/market',
  208. data: {}
  209. })
  210. middleCategory.value = result.data.map(item => item.name)
  211. // 生成表格数据(模拟)
  212. tableData.value = result.data.slice(0, 11).map((item, index) => ({
  213. rank: index + 1,
  214. region: item.name,
  215. coinAmount: Math.floor(Math.random() * 1000000) + 500000
  216. }))// D老师加的这一段(????????????????)
  217. } catch (error) {
  218. console.error('获取地区数据失败:', error)
  219. }
  220. }
  221. const getChartData = async () => {
  222. try {
  223. const params = {
  224. updateType: updateType.value
  225. }
  226. if (dateRange.value && dateRange.value.length === 2) {
  227. params.searchStartTime = formatDate(dateRange.value[0])
  228. params.searchEndTime = formatDate(dateRange.value[1])
  229. }
  230. const result = await API({
  231. url: '/statistics/getCoinTime',
  232. data: params
  233. })
  234. histogramData.value = result.data || []
  235. middleRecharge.value = histogramData.value.map(item =>
  236. Math.abs(item.rechargeSumCoin || 0)
  237. )
  238. middleFree.value = histogramData.value.map(item =>
  239. Math.abs(item.freeSumCoin || 0)
  240. )
  241. middleTask.value = histogramData.value.map(item =>
  242. Math.abs(item.taskSumCoin || 0)
  243. )
  244. updateChart()
  245. } catch (error) {
  246. console.error('请求图表数据失败:', error)
  247. }
  248. }
  249. const updateChart = () => {
  250. const option = {
  251. tooltip: {
  252. trigger: 'axis',
  253. axisPointer: { type: 'shadow' },
  254. formatter: function (params) {
  255. let total = 0
  256. let content = `${params[0].name}<br/>`
  257. params.forEach((param) => {
  258. content += `${param.seriesName}: ${param.value.toLocaleString()}<br/>`
  259. total += param.value
  260. })
  261. content += `总和: ${total.toLocaleString()}`
  262. return content
  263. }
  264. },
  265. legend: {
  266. right: '2%',
  267. orient: 'vertical',
  268. itemGap: 10
  269. },
  270. grid: {
  271. left: '3%',
  272. right: '10%',
  273. bottom: '3%',
  274. containLabel: true
  275. },
  276. xAxis: {
  277. type: 'category',
  278. data: middleCategory.value,
  279. axisLabel: {
  280. interval: 0,
  281. rotate: 30
  282. }
  283. },
  284. yAxis: {
  285. type: 'value',
  286. axisLabel: {
  287. formatter: (value) => value.toLocaleString()
  288. }
  289. },
  290. series: [
  291. {
  292. name: '永久金币',
  293. color: '#35e383',
  294. type: 'bar',
  295. stack: 'total',
  296. data: middleRecharge.value
  297. },
  298. {
  299. name: '免费金币',
  300. color: '#5f8ff5',
  301. type: 'bar',
  302. stack: 'total',
  303. data: middleFree.value
  304. },
  305. {
  306. name: '任务金币',
  307. color: '#ffe733',
  308. type: 'bar',
  309. stack: 'total',
  310. data: middleTask.value
  311. }
  312. ]
  313. }
  314. if (activeTab.value === 'recharge' && rechargeBar) {
  315. rechargeBar.setOption(option)
  316. rechargeBar.resize()
  317. } else if (consumeBar) {
  318. consumeBar.setOption(option)
  319. consumeBar.resize()
  320. }
  321. }
  322. const initCharts = () => {
  323. const rechargeDom = document.getElementById('recharge')
  324. const consumeDom = document.getElementById('consume')
  325. if (rechargeDom) {
  326. rechargeBar = echarts.init(rechargeDom)
  327. }
  328. if (consumeDom) {
  329. consumeBar = echarts.init(consumeDom)
  330. }
  331. }
  332. const loadChart = async () => {
  333. await getAreas()
  334. await getChartData()
  335. }
  336. // 页面切换
  337. const handleTabChange = (tab) => {
  338. updateType.value = tab === 'recharge' ? 0 : 1
  339. loadChart()
  340. }
  341. const handleResize = () => {
  342. if (rechargeBar) rechargeBar.resize()
  343. if (consumeBar) consumeBar.resize()
  344. }
  345. onMounted(() => {
  346. initCharts()
  347. loadChart()
  348. window.addEventListener('resize', handleResize)
  349. })
  350. onBeforeUnmount(() => {
  351. if (rechargeBar) rechargeBar.dispose()
  352. if (consumeBar) consumeBar.dispose()
  353. window.removeEventListener('resize', handleResize)
  354. })
  355. </script>
  356. <style scoped>
  357. .center-card {
  358. display: flex;
  359. justify-content: center;
  360. align-items: center;
  361. }
  362. .margin-bottom {
  363. margin-bottom: 5px;
  364. }
  365. .card-item {
  366. width: 24%;
  367. height: 260px;
  368. margin-right: 10px;
  369. display: flex;
  370. flex-direction: column;
  371. justify-content: center;
  372. }
  373. .card-title {
  374. font-weight: bold;
  375. margin-bottom: 10px;
  376. display: flex;
  377. justify-content: center;
  378. align-items: center;
  379. }
  380. .margin-top{
  381. margin-top: 5px;
  382. }
  383. </style>