TradeTrendCard.vue 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. <template>
  2. <el-card shadow="never">
  3. <template #header>
  4. <div class="flex flex-row items-center justify-between">
  5. <CardTitle title="交易量趋势" />
  6. <!-- 查询条件 -->
  7. <div class="flex flex-row items-center gap-2">
  8. <el-radio-group v-model="timeRangeType" @change="handleTimeRangeTypeChange">
  9. <el-radio-button v-for="[key, value] in timeRange.entries()" :key="key" :label="key">
  10. {{ value.name }}
  11. </el-radio-button>
  12. </el-radio-group>
  13. </div>
  14. </div>
  15. </template>
  16. <!-- 折线图 -->
  17. <Echart :height="300" :options="eChartOptions" />
  18. </el-card>
  19. </template>
  20. <script lang="ts" setup>
  21. import dayjs, { Dayjs } from 'dayjs'
  22. import { EChartsOption } from 'echarts'
  23. import * as TradeStatisticsApi from '@/api/mall/statistics/trade'
  24. import { fenToYuan } from '@/utils'
  25. import { formatDate } from '@/utils/formatTime'
  26. import { CardTitle } from '@/components/Card'
  27. /** 交易量趋势 */
  28. defineOptions({ name: 'TradeTrendCard' })
  29. enum TimeRangeTypeEnum {
  30. DAY30 = 1,
  31. WEEK = 7,
  32. MONTH = 30,
  33. YEAR = 365
  34. } // 日期类型
  35. const timeRangeType = ref(TimeRangeTypeEnum.DAY30) // 日期快捷选择按钮, 默认30天
  36. const loading = ref(true) // 加载中
  37. // 时间范围 Map
  38. const timeRange = new Map()
  39. .set(TimeRangeTypeEnum.DAY30, {
  40. name: '30天',
  41. series: [
  42. { name: '订单金额', type: 'bar', smooth: true, data: [] },
  43. { name: '订单数量', type: 'line', smooth: true, data: [] }
  44. ]
  45. })
  46. .set(TimeRangeTypeEnum.WEEK, {
  47. name: '周',
  48. series: [
  49. { name: '上周金额', type: 'bar', smooth: true, data: [] },
  50. { name: '本周金额', type: 'bar', smooth: true, data: [] },
  51. { name: '上周数量', type: 'line', smooth: true, data: [] },
  52. { name: '本周数量', type: 'line', smooth: true, data: [] }
  53. ]
  54. })
  55. .set(TimeRangeTypeEnum.MONTH, {
  56. name: '月',
  57. series: [
  58. { name: '上月金额', type: 'bar', smooth: true, data: [] },
  59. { name: '本月金额', type: 'bar', smooth: true, data: [] },
  60. { name: '上月数量', type: 'line', smooth: true, data: [] },
  61. { name: '本月数量', type: 'line', smooth: true, data: [] }
  62. ]
  63. })
  64. .set(TimeRangeTypeEnum.YEAR, {
  65. name: '年',
  66. series: [
  67. { name: '去年金额', type: 'bar', smooth: true, data: [] },
  68. { name: '今年金额', type: 'bar', smooth: true, data: [] },
  69. { name: '去年数量', type: 'line', smooth: true, data: [] },
  70. { name: '今年数量', type: 'line', smooth: true, data: [] }
  71. ]
  72. })
  73. /** 图表配置 */
  74. const eChartOptions = reactive<EChartsOption>({
  75. grid: {
  76. left: 20,
  77. right: 20,
  78. bottom: 20,
  79. top: 80,
  80. containLabel: true
  81. },
  82. legend: {
  83. top: 50,
  84. data: []
  85. },
  86. series: [],
  87. toolbox: {
  88. feature: {
  89. // 数据区域缩放
  90. dataZoom: {
  91. yAxisIndex: false // Y轴不缩放
  92. },
  93. brush: {
  94. type: ['lineX', 'clear'] // 区域缩放按钮、还原按钮
  95. },
  96. saveAsImage: { show: true, name: '订单量趋势' } // 保存为图片
  97. }
  98. },
  99. tooltip: {
  100. trigger: 'axis',
  101. axisPointer: {
  102. type: 'cross'
  103. },
  104. padding: [5, 10]
  105. },
  106. xAxis: {
  107. type: 'category',
  108. inverse: true,
  109. boundaryGap: false,
  110. axisTick: {
  111. show: false
  112. },
  113. data: [],
  114. axisLabel: {
  115. formatter: (date: string) => {
  116. switch (timeRangeType.value) {
  117. case TimeRangeTypeEnum.DAY30:
  118. return formatDate(date, 'MM-DD')
  119. case TimeRangeTypeEnum.WEEK:
  120. let weekDay = formatDate(date, 'ddd')
  121. if (weekDay == '0') weekDay = '日'
  122. return '周' + weekDay
  123. case TimeRangeTypeEnum.MONTH:
  124. return formatDate(date, 'D')
  125. case TimeRangeTypeEnum.YEAR:
  126. return formatDate(date, 'M') + '月'
  127. default:
  128. return date
  129. }
  130. }
  131. }
  132. },
  133. yAxis: {
  134. axisTick: {
  135. show: false
  136. }
  137. }
  138. }) as EChartsOption
  139. /** 时间范围类型单选按钮选中 */
  140. const handleTimeRangeTypeChange = async () => {
  141. // 设置时间范围
  142. let beginTime: Dayjs
  143. let endTime: Dayjs
  144. switch (timeRangeType.value) {
  145. case TimeRangeTypeEnum.WEEK:
  146. beginTime = dayjs().startOf('week')
  147. endTime = dayjs().endOf('week')
  148. break
  149. case TimeRangeTypeEnum.MONTH:
  150. beginTime = dayjs().startOf('month')
  151. endTime = dayjs().endOf('month')
  152. break
  153. case TimeRangeTypeEnum.YEAR:
  154. beginTime = dayjs().startOf('year')
  155. endTime = dayjs().endOf('year')
  156. break
  157. case TimeRangeTypeEnum.DAY30:
  158. default:
  159. beginTime = dayjs().subtract(30, 'day').startOf('d')
  160. endTime = dayjs().endOf('d')
  161. break
  162. }
  163. // 发送时间范围选中事件
  164. await getOrderCountTrendComparison(beginTime, endTime)
  165. }
  166. /** 查询订单数量趋势对照数据 */
  167. const getOrderCountTrendComparison = async (
  168. beginTime: dayjs.ConfigType,
  169. endTime: dayjs.ConfigType
  170. ) => {
  171. loading.value = true
  172. // 查询数据
  173. const list = await TradeStatisticsApi.getOrderCountTrendComparison(
  174. timeRangeType.value,
  175. beginTime,
  176. endTime
  177. )
  178. // 处理数据
  179. const dates: string[] = []
  180. const series = [...timeRange.get(timeRangeType.value).series]
  181. for (let item of list) {
  182. dates.push(item.value.date)
  183. if (series.length === 2) {
  184. series[0].data.push(fenToYuan(item?.value?.orderPayPrice || 0)) // 当前金额
  185. series[1].data.push(fenToYuan(item?.value?.orderPayCount || 0)) // 当前数量
  186. } else {
  187. series[0].data.push(fenToYuan(item?.reference?.orderPayPrice || 0)) // 对照金额
  188. series[1].data.push(fenToYuan(item?.value?.orderPayPrice || 0)) // 当前金额
  189. series[2].data.push(item?.reference?.orderPayCount || 0) // 对照数量
  190. series[3].data.push(item?.value?.orderPayCount || 0) // 当前数量
  191. }
  192. }
  193. eChartOptions.xAxis!['data'] = dates
  194. eChartOptions.series = series
  195. // legend在4个切换到2个的时候,还是显示成4个,需要手动配置一下
  196. eChartOptions.legend['data'] = series.map((item) => item.name)
  197. loading.value = false
  198. }
  199. /** 初始化 **/
  200. onMounted(() => {
  201. handleTimeRangeTypeChange()
  202. })
  203. </script>