index.vue 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348
  1. <template>
  2. <div class="app-container home">
  3. <!-- 时间筛选区域 -->
  4. <!-- 时间筛选区域 -->
  5. <div class="time-filter">
  6. <el-button size="small" :type="timeFilter === 'day' ? 'primary' : 'default'" @click="handleTimeFilter('day')"> 今日 </el-button>
  7. <el-button size="small" :type="timeFilter === 'three' ? 'primary' : 'default'" @click="handleTimeFilter('three')"> 近三天 </el-button>
  8. <el-button size="small" :type="timeFilter === 'week' ? 'primary' : 'default'" @click="handleTimeFilter('week')"> 近七天 </el-button>
  9. <el-button size="small" :type="timeFilter === 'month' ? 'primary' : 'default'" @click="handleTimeFilter('month')"> 本月 </el-button>
  10. <span class="filter-label">选择时间:</span>
  11. <el-date-picker v-model="startTime" type="datetime" placeholder="请选择开始时间" value-format="YYYY-MM-DD HH:mm:ss" style="width: 180px" />
  12. <span>至</span>
  13. <el-date-picker v-model="endTime" type="datetime" placeholder="请选择结束时间" value-format="YYYY-MM-DD HH:mm:ss" style="width: 180px" />
  14. <el-button size="small" type="primary" @click="queryData">查询</el-button>
  15. <el-button size="small" @click="resetFilter">重置</el-button>
  16. </div>
  17. <!-- 统计卡片区域 -->
  18. <div class="statistic-cards">
  19. <div class="stat-card">
  20. <div class="card-header">比赛场次</div>
  21. <div class="card-value">{{ matchCount }}</div>
  22. </div>
  23. <div class="stat-card">
  24. <div class="card-header">充值数</div>
  25. <div class="card-value">{{ rechargeCount }}</div>
  26. </div>
  27. <div class="stat-card">
  28. <div class="card-header">充值金额</div>
  29. <div class="card-value">{{ rechargeAmount }}</div>
  30. </div>
  31. <div class="stat-card">
  32. <div class="card-header">三湘杯参赛资格发放数</div>
  33. <div class="card-value">{{ qualificationIssued }}</div>
  34. </div>
  35. <div class="stat-card">
  36. <div class="card-header">三湘杯参赛资格核销数</div>
  37. <div class="card-value">{{ qualificationUsed }}</div>
  38. </div>
  39. </div>
  40. <!-- 折线图区域 -->
  41. <div class="chart-container">
  42. <div class="chart-header">
  43. <div class="chart-title">折线图</div>
  44. </div>
  45. <div class="chart-content">
  46. <div ref="chartRef" class="chart"></div>
  47. </div>
  48. </div>
  49. </div>
  50. </template>
  51. <script setup name="Index" lang="ts">
  52. import { ref, onMounted } from 'vue';
  53. import * as echarts from 'echarts';
  54. import { getStatisticsInfo, getStatisticsGraphInfo } from '@/api/system/business/statistics'; // 导入API
  55. // 时间筛选相关
  56. const startTime = ref('');
  57. const endTime = ref('');
  58. // 修改 timeFilter 的定义
  59. const timeFilter = ref<'day' | 'three' | 'week' | 'month' | 'otherTime'>('day');
  60. // 统计数据
  61. const matchCount = ref(0);
  62. const rechargeCount = ref(0);
  63. const rechargeAmount = ref('0');
  64. const qualificationIssued = ref(0);
  65. const qualificationUsed = ref(0);
  66. // 图表相关
  67. const chartRef = ref<HTMLDivElement | null>(null);
  68. let chartInstance: any;
  69. // 时间筛选处理
  70. const handleTimeFilter = (type: string) => {
  71. timeFilter.value = type as 'day' | 'three' | 'week' | 'month' | 'otherTime';
  72. startTime.value = '';
  73. endTime.value = '';
  74. // 这里可以设置对应的时间范围
  75. fetchStatisticsData();
  76. fetchGraphData(); // 获取图表数据
  77. };
  78. // 重置筛选条件
  79. const resetFilter = () => {
  80. startTime.value = '';
  81. endTime.value = '';
  82. timeFilter.value = 'day';
  83. };
  84. // 获取图表数据
  85. const fetchGraphData = async () => {
  86. try {
  87. const params = {
  88. flagType: timeFilter.value,
  89. startTime: startTime.value || undefined,
  90. endTime: endTime.value || undefined
  91. };
  92. const res = await getStatisticsGraphInfo(params);
  93. if (res.code === 200) {
  94. const data = res.data;
  95. // 更新图表数据
  96. updateChart(data);
  97. }
  98. } catch (error) {
  99. console.error('获取图表数据失败:', error);
  100. }
  101. };
  102. // 在 updateChart 函数中添加数据验证
  103. const updateChart = (data: any) => {
  104. // 使用从后端获取的日期数据
  105. const timeData = data.dates || [];
  106. // 设置图表选项
  107. const option = {
  108. title: {
  109. /* text: 'Stacked Line',*/
  110. left: 'center'
  111. },
  112. tooltip: {
  113. trigger: 'axis',
  114. axisPointer: {
  115. type: 'shadow'
  116. }
  117. },
  118. legend: {
  119. data: ['充值数', '充值金额', '三湘杯参赛资格发放数', '三湘杯参赛资格核销数']
  120. },
  121. grid: {
  122. left: '3%',
  123. right: '4%',
  124. bottom: '15%',
  125. containLabel: true
  126. },
  127. xAxis: {
  128. type: 'category',
  129. data: timeData
  130. },
  131. yAxis: {
  132. type: 'value',
  133. minInterval: 1 // 设置最小间隔为1
  134. },
  135. series: [
  136. {
  137. name: '充值数',
  138. type: 'line',
  139. stack: 'Total',
  140. areaStyle: {},
  141. emphasis: {
  142. focus: 'series'
  143. },
  144. data: data.payOrderCountIds || []
  145. },
  146. {
  147. name: '充值金额',
  148. type: 'line',
  149. stack: 'Total',
  150. areaStyle: {},
  151. emphasis: {
  152. focus: 'series'
  153. },
  154. data:
  155. data.payOrderAmountIds?.map((item: any) => {
  156. if (item && typeof item === 'object') {
  157. // 使用 BigDecimal 的 toPlainString() 方法转换为字符串
  158. return item.toPlainString ? item.toPlainString() : Number(item);
  159. }
  160. return Number(item);
  161. }) || []
  162. },
  163. {
  164. name: '三湘杯参赛资格发放数',
  165. type: 'line',
  166. stack: 'Total',
  167. areaStyle: {},
  168. emphasis: {
  169. focus: 'series'
  170. },
  171. data: data.sanXiangCardCountIds || []
  172. },
  173. {
  174. name: '三湘杯参赛资格核销数',
  175. type: 'line',
  176. stack: 'Total',
  177. areaStyle: {},
  178. emphasis: {
  179. focus: 'series'
  180. },
  181. data: data.selectCheckRecordSanXiangCountIds || []
  182. }
  183. ]
  184. };
  185. // 设置图表选项
  186. chartInstance.setOption(option);
  187. };
  188. // 初始化图表
  189. const initChart = () => {
  190. if (!chartInstance && chartRef.value) {
  191. chartInstance = echarts.init(chartRef.value);
  192. // 初始化时设置基本配置
  193. const option = {
  194. title: {
  195. /* text: '',*/
  196. left: 'center'
  197. },
  198. tooltip: {
  199. trigger: 'axis',
  200. axisPointer: {
  201. type: 'shadow'
  202. }
  203. },
  204. legend: {
  205. data: ['充值数', '充值金额', '三湘杯参赛资格发放数', '三湘杯参赛资格核销数']
  206. },
  207. grid: {
  208. left: '3%',
  209. right: '4%',
  210. bottom: '15%',
  211. containLabel: true
  212. },
  213. xAxis: {
  214. type: 'category',
  215. data: []
  216. },
  217. yAxis: {
  218. type: 'value'
  219. },
  220. series: []
  221. };
  222. chartInstance.setOption(option);
  223. }
  224. };
  225. // 获取统计数据
  226. const fetchStatisticsData = async () => {
  227. try {
  228. const params = {
  229. flagType: timeFilter.value, // 根据实际需求调整
  230. startTime: startTime.value || undefined,
  231. endTime: endTime.value || undefined
  232. };
  233. const res = await getStatisticsInfo(params);
  234. if (res.code === 200) {
  235. const data = res.data;
  236. matchCount.value = data.tournamentCount || 0;
  237. rechargeCount.value = data.payOrderCount || 0;
  238. rechargeAmount.value = data.payOrderAmount ? data.payOrderAmount.toFixed(0) : '0';
  239. qualificationIssued.value = data.sanXiangCardCount || 0;
  240. qualificationUsed.value = data.selectCheckRecordSanXiangCount || 0;
  241. }
  242. } catch (error) {
  243. console.error('获取统计数据失败:', error);
  244. }
  245. };
  246. // 查询数据
  247. const queryData = () => {
  248. timeFilter.value = 'otherTime';
  249. fetchStatisticsData();
  250. fetchGraphData(); // 获取图表数据
  251. };
  252. // 页面加载完成后初始化图表
  253. onMounted(() => {
  254. initChart();
  255. timeFilter.value = 'month';
  256. fetchStatisticsData();
  257. fetchGraphData(); // 获取图表数据
  258. });
  259. </script>
  260. <style lang="scss" scoped>
  261. .home {
  262. padding: 20px;
  263. }
  264. .time-filter {
  265. display: flex;
  266. align-items: center;
  267. gap: 10px;
  268. margin-bottom: 20px;
  269. .filter-label {
  270. margin: 0 10px;
  271. }
  272. }
  273. .statistic-cards {
  274. display: grid;
  275. grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  276. gap: 15px;
  277. margin-bottom: 20px;
  278. }
  279. .stat-card {
  280. background: #f5f5f5;
  281. border-radius: 8px;
  282. padding: 20px;
  283. text-align: center;
  284. .card-header {
  285. font-size: 14px;
  286. color: #666;
  287. margin-bottom: 10px;
  288. }
  289. .card-value {
  290. font-size: 36px;
  291. font-weight: bold;
  292. color: #333;
  293. }
  294. }
  295. .chart-container {
  296. background: #f5f5f5;
  297. border-radius: 8px;
  298. padding: 20px;
  299. margin-top: 20px;
  300. }
  301. .chart-header {
  302. margin-bottom: 15px;
  303. }
  304. .chart-title {
  305. font-size: 24px;
  306. color: #333;
  307. }
  308. .chart-content {
  309. padding: 20px;
  310. background: #fff;
  311. border-radius: 8px;
  312. }
  313. .chart {
  314. width: 100%;
  315. height: 400px;
  316. }
  317. </style>