LineChart.vue 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. <template>
  2. <div ref="chartRef" class="line-chart" :style="{ width: width, height: height }"></div>
  3. </template>
  4. <script lang="ts" setup>
  5. import { ref, onMounted, onUnmounted, watch, nextTick } from 'vue'
  6. import * as echarts from 'echarts'
  7. interface ChartDataItem {
  8. date: string
  9. value: number
  10. }
  11. interface Props {
  12. data: ChartDataItem[]
  13. width?: string
  14. height?: string
  15. title?: string
  16. color?: string
  17. showGrid?: boolean
  18. showTooltip?: boolean
  19. showLegend?: boolean
  20. smooth?: boolean
  21. areaStyle?: boolean
  22. }
  23. const props = withDefaults(defineProps<Props>(), {
  24. width: '100%',
  25. height: '270px',
  26. title: '',
  27. color: '#167AF0',
  28. showGrid: true,
  29. showTooltip: true,
  30. showLegend: false,
  31. smooth: false,
  32. areaStyle: false
  33. })
  34. const chartRef = ref<HTMLElement>()
  35. let chartInstance: echarts.ECharts | null = null
  36. // 初始化图表
  37. const initChart = () => {
  38. if (!chartRef.value) return
  39. chartInstance = echarts.init(chartRef.value)
  40. updateChart()
  41. }
  42. // 更新图表数据
  43. const updateChart = () => {
  44. if (!chartInstance) return
  45. const dates = props.data.map(item => item.date)
  46. const values = props.data.map(item => item.value)
  47. const option: echarts.EChartsOption = {
  48. tooltip: props.showTooltip ? {
  49. trigger: 'axis',
  50. backgroundColor: 'rgba(255, 255, 255, 0.95)',
  51. borderColor: '#E5E5E5',
  52. borderWidth: 1,
  53. textStyle: {
  54. color: '#333',
  55. fontSize: 12
  56. },
  57. axisPointer: {
  58. type: 'line',
  59. lineStyle: {
  60. color: props.color,
  61. width: 1,
  62. type: 'dashed'
  63. }
  64. },
  65. formatter: function(params: any) {
  66. const data = params[0]
  67. return `<div style="padding: 8px;">
  68. <div style="font-weight: 500; margin-bottom: 4px;">${data.name}</div>
  69. <div style="color: ${props.color}; font-size: 14px; font-weight: 500;">
  70. ${data.seriesName}: ${data.value}
  71. </div>
  72. </div>`
  73. }
  74. } : undefined,
  75. legend: props.showLegend ? {
  76. data: [props.title || '数据'],
  77. top: '40px',
  78. textStyle: {
  79. fontSize: 12,
  80. color: '#666'
  81. }
  82. } : undefined,
  83. grid: {
  84. left: '0%',
  85. right: '0%',
  86. bottom: '0%',
  87. top: '10px',
  88. containLabel: true
  89. },
  90. xAxis: {
  91. type: 'category',
  92. boundaryGap: true,
  93. data: dates,
  94. axisLine: {
  95. lineStyle: {
  96. color: '#E5E5E5'
  97. }
  98. },
  99. axisTick: {
  100. show: false
  101. },
  102. axisLabel: {
  103. color: 'rgba(100, 100, 100, 1)',
  104. fontSize: 14
  105. },
  106. },
  107. yAxis: {
  108. type: 'value',
  109. axisLine: {
  110. show: false
  111. },
  112. axisTick: {
  113. show: false
  114. },
  115. axisLabel: {
  116. color: 'rgba(100, 100, 100, 1)',
  117. fontSize: 14,
  118. },
  119. splitLine: props.showGrid ? {
  120. lineStyle: {
  121. color: 'rgba(230, 230, 230, 1)',
  122. type: 'dashed'
  123. }
  124. } : {
  125. show: false
  126. }
  127. },
  128. series: [
  129. {
  130. name: props.title || '数据',
  131. type: 'line',
  132. data: values,
  133. smooth: props.smooth,
  134. symbol: 'circle',
  135. symbolSize: 8,
  136. lineStyle: {
  137. color: props.color,
  138. width: 2
  139. },
  140. itemStyle: {
  141. color: props.color,
  142. borderWidth: 2,
  143. borderColor: props.color,
  144. },
  145. emphasis: {
  146. itemStyle: {
  147. color: props.color,
  148. borderWidth: 2,
  149. borderColor: props.color,
  150. }
  151. }
  152. }
  153. ]
  154. }
  155. chartInstance.setOption(option)
  156. }
  157. // 监听数据变化
  158. watch(() => props.data, () => {
  159. nextTick(() => {
  160. updateChart()
  161. })
  162. }, { deep: true })
  163. // 监听窗口大小变化
  164. const handleResize = () => {
  165. if (chartInstance) {
  166. chartInstance.resize()
  167. }
  168. }
  169. onMounted(() => {
  170. initChart()
  171. window.addEventListener('resize', handleResize)
  172. })
  173. onUnmounted(() => {
  174. if (chartInstance) {
  175. chartInstance.dispose()
  176. chartInstance = null
  177. }
  178. window.removeEventListener('resize', handleResize)
  179. })
  180. // 暴露方法给父组件
  181. defineExpose({
  182. getChartInstance: () => chartInstance,
  183. resize: handleResize
  184. })
  185. </script>
  186. <style scoped lang="scss">
  187. .line-chart {
  188. display: block;
  189. box-sizing: border-box;
  190. }
  191. </style>