horderTrend.vue 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  1. <script lang="ts" setup>
  2. import * as echarts from 'echarts'
  3. import {
  4. ANIMATION,
  5. ChartData,
  6. createLegend,
  7. createTooltip,
  8. FONT_FAMILY,
  9. formatDateLabel,
  10. formatSeriesName,
  11. THEME
  12. } from '@/utils/kb'
  13. import { IotStatApi } from '@/api/pms/stat'
  14. const chartData = ref<ChartData>({
  15. xAxis: [],
  16. series: []
  17. })
  18. const chartRef = ref<HTMLDivElement>()
  19. let chart: echarts.ECharts | null = null
  20. function getChartOption(data: ChartData): echarts.EChartsOption {
  21. const xAxisData = data.xAxis || []
  22. const seriesData = data.series || []
  23. return {
  24. ...ANIMATION,
  25. grid: { ...THEME.grid, top: 36 },
  26. tooltip: createTooltip({
  27. trigger: 'axis',
  28. axisPointer: {
  29. type: 'line'
  30. }
  31. }),
  32. legend: createLegend(
  33. { top: 5 },
  34. seriesData.map((item) => formatSeriesName(item.name))
  35. ),
  36. xAxis: {
  37. type: 'category',
  38. boundaryGap: false,
  39. data: xAxisData,
  40. axisLine: {
  41. show: false
  42. },
  43. axisTick: {
  44. show: false
  45. },
  46. axisLabel: {
  47. color: THEME.text.regular,
  48. fontSize: 14,
  49. fontFamily: FONT_FAMILY,
  50. formatter(value: string) {
  51. return formatDateLabel(value)
  52. }
  53. }
  54. },
  55. yAxis: [
  56. {
  57. type: 'value',
  58. splitNumber: 4,
  59. axisLabel: {
  60. color: THEME.text.regular,
  61. fontSize: 12,
  62. fontFamily: FONT_FAMILY
  63. },
  64. splitLine: {
  65. lineStyle: {
  66. color: THEME.split,
  67. type: 'dashed'
  68. }
  69. },
  70. axisLine: {
  71. lineStyle: {
  72. color: THEME.split
  73. }
  74. },
  75. position: 'left'
  76. },
  77. {
  78. type: 'value',
  79. splitNumber: 4,
  80. axisLabel: {
  81. color: THEME.text.regular,
  82. fontSize: 12,
  83. fontFamily: FONT_FAMILY
  84. },
  85. splitLine: {
  86. show: false
  87. },
  88. axisLine: {
  89. lineStyle: {
  90. color: THEME.split
  91. }
  92. },
  93. position: 'right'
  94. }
  95. ],
  96. series: seriesData.map((item, index) => {
  97. const colorList = Object.values(THEME.color)
  98. const color = colorList[index % colorList.length]
  99. const yAxisIndex = index < 2 ? 0 : 1
  100. return {
  101. name: formatSeriesName(item.name),
  102. type: 'line',
  103. smooth: true,
  104. data: item.data,
  105. yAxisIndex,
  106. symbol: 'circle',
  107. symbolSize: 8,
  108. showSymbol: true,
  109. lineStyle: {
  110. width: 2,
  111. color: color.line
  112. },
  113. itemStyle: {
  114. color: color.line
  115. },
  116. areaStyle: {
  117. color: color.bg
  118. },
  119. emphasis: {
  120. focus: 'series',
  121. scale: true
  122. }
  123. }
  124. })
  125. }
  126. }
  127. function initChart() {
  128. if (!chartRef.value) return
  129. if (chart) {
  130. chart.dispose()
  131. }
  132. chart = echarts.init(chartRef.value, undefined, {
  133. renderer: 'svg'
  134. })
  135. renderChart()
  136. }
  137. function renderChart() {
  138. if (!chart) return
  139. const data = chartData.value || []
  140. chart.setOption(getChartOption(data), true)
  141. }
  142. function resizeChart() {
  143. chart?.resize()
  144. }
  145. function destroyChart() {
  146. if (chart) {
  147. chart.dispose()
  148. chart = null
  149. }
  150. }
  151. async function loadChart() {
  152. try {
  153. const res = await IotStatApi.getOrderSeven()
  154. chartData.value = {
  155. xAxis: res.xAxis || [],
  156. series: (res.series || []).map((item) => ({
  157. name: item.name,
  158. data: item.data || []
  159. }))
  160. }
  161. renderChart()
  162. } catch (error) {
  163. console.error('获取近7日工单趋势失败:', error)
  164. chartData.value = {
  165. xAxis: [],
  166. series: []
  167. }
  168. renderChart()
  169. }
  170. }
  171. onMounted(() => {
  172. initChart()
  173. loadChart()
  174. window.addEventListener('resize', resizeChart)
  175. window.addEventListener('homekb:resize', resizeChart)
  176. })
  177. onUnmounted(() => {
  178. window.removeEventListener('resize', resizeChart)
  179. window.removeEventListener('homekb:resize', resizeChart)
  180. destroyChart()
  181. })
  182. </script>
  183. <template>
  184. <div class="panel flex flex-col">
  185. <div class="panel-title">
  186. <div class="icon-decorator">
  187. <span></span>
  188. <span></span>
  189. </div>
  190. 工单数量统计
  191. </div>
  192. <div ref="chartRef" class="flex-1 min-h-0"></div>
  193. </div>
  194. </template>
  195. <style lang="scss" scoped>
  196. @import url('@/styles/kb.scss');
  197. </style>