From 27c7661cae945f65f8d2752cae41801e3c2b1485 Mon Sep 17 00:00:00 2001
From: zxl <763096477@qq.com>
Date: 星期三, 25 三月 2026 14:21:37 +0800
Subject: [PATCH] 修改折线图

---
 src/views/dataAnalysis/components/DataReLineChart.vue |  403 ++++++++++++++++++++++++++++++++++++++++++++-------------
 1 files changed, 309 insertions(+), 94 deletions(-)

diff --git a/src/views/dataAnalysis/components/DataReLineChart.vue b/src/views/dataAnalysis/components/DataReLineChart.vue
index 764b5d8..228831b 100644
--- a/src/views/dataAnalysis/components/DataReLineChart.vue
+++ b/src/views/dataAnalysis/components/DataReLineChart.vue
@@ -22,13 +22,204 @@
       type: Boolean,
       default: false,
     },
+    enablePointClick: {
+      type: Boolean,
+      default: false,
+    },
   },
   data() {
     return {
       chartEntity: null,
+      zrClickHandler: null,
+      zrMoveHandler: null,
+      zrMouseOutHandler: null,
+      lastHoverIndex: -1,
+      lastEmitName: '',
+      lastEmitAt: 0,
+      lastLegendToggleAt: 0,
     }
   },
   methods: {
+    getOffsetPoint(event) {
+      if (!event) {
+        return null
+      }
+      if (event.offsetX !== undefined && event.offsetY !== undefined) {
+        return [event.offsetX, event.offsetY]
+      }
+      if (event.event && event.event.offsetX !== undefined && event.event.offsetY !== undefined) {
+        return [event.event.offsetX, event.event.offsetY]
+      }
+      return null
+    },
+    isPointInGrid(point) {
+      if (!this.chartEntity || !this.chartEntity.containPixel || !point) {
+        return false
+      }
+      return this.chartEntity.containPixel({ gridIndex: 0 }, point)
+    },
+    getXData() {
+      return this.chartData && this.chartData.xData ? this.chartData.xData : []
+    },
+    resolveNearestIndexByOffsetX(offsetX) {
+      let xData = this.getXData()
+      if (!this.chartEntity || !this.chartEntity.convertToPixel || !xData.length) {
+        return -1
+      }
+      let nearestIndex = -1
+      let minDistance = Number.MAX_VALUE
+      for (let i = 0; i < xData.length; i++) {
+        let xPixel = this.chartEntity.convertToPixel({ xAxisIndex: 0 }, xData[i])
+        if (Array.isArray(xPixel)) {
+          xPixel = xPixel[0]
+        }
+        if (xPixel === undefined || xPixel === null || Number.isNaN(Number(xPixel))) {
+          continue
+        }
+        let distance = Math.abs(Number(offsetX) - Number(xPixel))
+        if (distance < minDistance) {
+          minDistance = distance
+          nearestIndex = i
+        }
+      }
+      if (nearestIndex === -1) {
+        return -1
+      }
+      return nearestIndex
+    },
+    resolveClickIndex(event, params) {
+      let xData = this.getXData()
+      if (!xData.length) {
+        return -1
+      }
+      let point = this.getOffsetPoint(event)
+      if (!this.isPointInGrid(point)) {
+        return -1
+      }
+      if (params && typeof params.dataIndex === 'number' && xData[params.dataIndex] !== undefined) {
+        return params.dataIndex
+      }
+      if (params && params.name) {
+        let nameIndex = xData.findIndex((item) => item === params.name)
+        if (nameIndex > -1) {
+          return nameIndex
+        }
+      }
+      if (this.chartEntity && this.chartEntity.convertFromPixel && event) {
+        let xAxisValue = this.chartEntity.convertFromPixel({ xAxisIndex: 0 }, [event.offsetX, event.offsetY])
+        if (xAxisValue !== undefined && xAxisValue !== null && !Number.isNaN(Number(xAxisValue))) {
+          let xIndex = Math.round(Number(xAxisValue))
+          if (xIndex >= 0 && xIndex < xData.length) {
+            return xIndex
+          }
+        }
+      }
+      if (event && event.offsetX !== undefined && event.offsetX !== null) {
+        return this.resolveNearestIndexByOffsetX(event.offsetX)
+      }
+      return -1
+    },
+    emitChartClickByIndex(index, seriesName) {
+      let xData = this.getXData()
+      if (index < 0 || index >= xData.length) {
+        return
+      }
+      let clickName = xData[index]
+      let now = Date.now()
+      if (this.lastEmitName === clickName && now - this.lastEmitAt < 120) {
+        return
+      }
+      this.lastEmitName = clickName
+      this.lastEmitAt = now
+      this.$emit('chart-click', {
+        name: clickName,
+        seriesName: seriesName || null,
+      })
+    },
+    hideTooltip() {
+      if (!this.chartEntity) {
+        return
+      }
+      this.chartEntity.dispatchAction({
+        type: 'hideTip',
+      })
+    },
+    bindChartClick() {
+      if (!this.chartEntity) {
+        return
+      }
+      this.chartEntity.off('click')
+      if (this.chartEntity.getZr) {
+        let zr = this.chartEntity.getZr()
+        if (this.zrClickHandler) {
+          zr.off('click', this.zrClickHandler)
+        }
+        if (this.zrMoveHandler) {
+          zr.off('mousemove', this.zrMoveHandler)
+        }
+        if (this.zrMouseOutHandler) {
+          zr.off('globalout', this.zrMouseOutHandler)
+        }
+      }
+      if (!this.enablePointClick) {
+        let chartDom = document.getElementById(this.domId)
+        if (chartDom) {
+          chartDom.style.cursor = 'default'
+        }
+        return
+      }
+      let chartDom = document.getElementById(this.domId)
+      if (chartDom) {
+        chartDom.style.cursor = 'pointer'
+      }
+      this.chartEntity.on('legendselectchanged', () => {
+        this.lastLegendToggleAt = Date.now()
+      })
+      this.chartEntity.on('click', (params) => {
+        if (params && params.componentType === 'legend') {
+          return
+        }
+        if (Date.now() - this.lastLegendToggleAt < 180) {
+          return
+        }
+        let index = this.resolveClickIndex(params && params.event ? params.event : null, params)
+        this.emitChartClickByIndex(index, params ? params.seriesName : null)
+      })
+      this.zrMoveHandler = (event) => {
+        let xData = this.getXData()
+        if (!xData.length) {
+          return
+        }
+        let xIndex = this.resolveClickIndex(event, null)
+        if (xIndex < 0 || xIndex >= xData.length) {
+          return
+        }
+        if (this.lastHoverIndex === xIndex) {
+          return
+        }
+        this.lastHoverIndex = xIndex
+        this.chartEntity.dispatchAction({
+          type: 'showTip',
+          seriesIndex: 0,
+          dataIndex: xIndex,
+        })
+      }
+      this.zrMouseOutHandler = () => {
+        this.lastHoverIndex = -1
+        this.hideTooltip()
+      }
+      this.zrClickHandler = (event) => {
+        if (Date.now() - this.lastLegendToggleAt < 180) {
+          return
+        }
+        let index = this.resolveClickIndex(event, null)
+        this.emitChartClickByIndex(index, null)
+      }
+      let zr = this.chartEntity.getZr()
+      zr.on('mousemove', this.zrMoveHandler)
+      zr.on('globalout', this.zrMouseOutHandler)
+      zr.on('click', this.zrClickHandler)
+    },
     setChart() {
       if (!this.chartEntity) {
         let chartDom = document.getElementById(this.domId)
@@ -38,6 +229,8 @@
       var option = {
         tooltip: {
           trigger: 'axis',
+          confine: true,
+          extraCssText: 'pointer-events:none;',
           axisPointer: {
             // 鍧愭爣杞存寚绀哄櫒锛屽潗鏍囪酱瑙﹀彂鏈夋晥
             type: 'line', // 榛樿涓虹洿绾匡紝鍙�変负锛�'line' | 'shadow'
@@ -54,15 +247,29 @@
           shadowBlur: 10,
           formatter: (params) => {
             let strName1 = params[0].name
-            let value1 = params[0].value
-            let value2 = params[1] ? params[1].value : '鏈紑鍚�'
-            return `<div style="color:#fff;font-size:16px;">${strName1}</div>
-              <div><span style="color:#fff;display: inline-block;width: 86px;">${
-                this.chartData.barName
-              }</span><span style="color:#5DB6FB">${value1}
-              </span></div><div><span style="color:#fff;display: inline-block;width: 86px;">${
-                this.chartData.lineName
-              }</span><span style="color:#5DB6FB">${value2}${this.showpercent ? '%' : ''}`
+            let result = `<div style="color:#fff;font-size:16px;">${strName1}</div>`
+            
+            if (params[0] && this.chartData.barName) {
+              let value1 = params[0].value !== undefined ? params[0].value : 0
+              result += `<div><span style="color:#fff;display: inline-block;width: 86px;">${this.chartData.barName}</span><span style="color:#5DB6FB">${value1}</span></div>`
+            }
+            
+            if (params[1] && this.chartData.lineName) {
+              let value2 = params[1].value !== undefined ? params[1].value : 0
+              result += `<div><span style="color:#fff;display: inline-block;width: 86px;">${this.chartData.lineName}</span><span style="color:#5DB6FB">${value2}</span></div>`
+            }
+            
+            if (params[2] && this.chartData.barName2) {
+              let value3 = params[2].value !== undefined ? params[2].value : 0
+              result += `<div><span style="color:#fff;display: inline-block;width: 86px;">${this.chartData.barName2}</span><span style="color:#FF6B6B">${value3}</span></div>`
+            }
+            
+            if (params[3] && this.chartData.lineName2) {
+              let value4 = params[3].value !== undefined ? params[3].value : 0
+              result += `<div><span style="color:#fff;display: inline-block;width: 86px;">${this.chartData.lineName2}</span><span style="color:#FFD93D">${value4}</span></div>`
+            }
+            
+            return result
           },
           textStyle: {
             rich: {
@@ -119,95 +326,95 @@
               show: false,
             },
           },
-          {
-            type: 'value',
-            nameTextStyle: {
-              color: 'rgba(185, 185, 185, 1)',
-            },
-            position: 'right',
-            axisLine: {
-              show: false,
-            },
-            splitLine: {
-              show: false,
-            },
-            axisTick: {
-              show: false,
-            },
-            axisLabel: {
-              show: true,
-              formatter: '{value} %', //鍙充晶Y杞存枃瀛楁樉绀�
-              formatter: (value, index) => {
-                return value + (this.showpercent ? '%' : '')
-              },
-              textStyle: {
-                color: 'rgba(185, 185, 185, 1)',
-              },
-            },
-          },
         ],
 
-        series: [
-          {
-            name: this.chartData.barName,
-            type: 'bar',
-            barWidth: '12px',
-            itemStyle: {
-              normal: {
-                color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
-                  {
-                    offset: 0,
-                    color: '#5B81F9',
-                  },
-                  {
-                    offset: 1,
-                    color: '#151A22',
-                  },
-                ]),
-              },
-            },
-            data: this.chartData.barData,
-          },
-
-          {
-            name: this.chartData.lineName,
-            type: 'line',
-            // smooth: true,
-            yAxisIndex: 1, //浣跨敤鐨� y 杞寸殑 index锛屽湪鍗曚釜鍥捐〃瀹炰緥涓瓨鍦ㄥ涓� y杞寸殑鏃跺�欐湁鐢�
-
-            symbol: 'emptycircle', //鏍囪鐨勫浘褰负瀹炲績鍦�
-            symbolSize: 0, //鏍囪鐨勫ぇ灏�
-            areaStyle: {
-              normal: {
-                color: {
-                  type: 'linear',
-                  x: 0,
-                  y: 0,
-                  x2: 0,
-                  y2: 1,
-                  colorStops: [
-                    {
-                      offset: 0,
-                      color: 'rgba(87, 147, 67, .5)', // 0% 澶勭殑棰滆壊
-                    },
-                    {
-                      offset: 1,
-                      color: 'rgba(87, 147, 67, 0.1)', // 100% 澶勭殑棰滆壊
-                    },
-                  ],
-                  global: false, // 缂虹渷涓� false
-                },
-              },
-            },
-            itemStyle: {
-              color: 'rgba(65, 197, 95, 1)',
-            },
-
-            data: this.chartData.lineData,
-          },
-        ],
+        series: [],
       }
+      
+      if (this.chartData.barData) {
+        option.series.push({
+          name: this.chartData.barName,
+          type: 'bar',
+          barWidth: '12px',
+          itemStyle: {
+            normal: {
+              color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
+                {
+                  offset: 0,
+                  color: '#5B81F9',
+                },
+                {
+                  offset: 1,
+                  color: '#151A22',
+                },
+              ]),
+            },
+          },
+          data: this.chartData.barData,
+        })
+      }
+
+      if (this.chartData.lineData) {
+        option.series.push({
+          name: this.chartData.lineName,
+          type: 'line',
+          smooth: true,
+          symbol: 'circle',
+          symbolSize: 6,
+          lineStyle: {
+            width: 2,
+            color: '#16B777',
+          },
+          itemStyle: {
+            color: '#16B777',
+          },
+          data: this.chartData.lineData,
+        })
+      }
+
+      if (this.chartData.barData2) {
+        option.series.push({
+          name: this.chartData.barName2,
+          type: 'bar',
+          barWidth: '12px',
+          itemStyle: {
+            normal: {
+              color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
+                {
+                  offset: 0,
+                  color: '#FF6B6B',
+                },
+                {
+                  offset: 1,
+                  color: '#8B0000',
+                },
+              ]),
+            },
+          },
+          data: this.chartData.barData2,
+        })
+      }
+
+      if (this.chartData.lineData2) {
+        option.series.push({
+          name: this.chartData.lineName2,
+          type: 'line',
+          smooth: true,
+          symbol: 'circle',
+          symbolSize: 6,
+          lineStyle: {
+            width: 2,
+            color: '#FFD93D',
+          },
+          itemStyle: {
+            color: '#FFD93D',
+          },
+          data: this.chartData.lineData2,
+        })
+      }
+      
       option && this.chartEntity.setOption(option, true)
+      this.bindChartClick()
     },
     setPieUpChart() {
       if (!this.chartEntity) {
@@ -233,6 +440,8 @@
       var option = {
         tooltip: {
           trigger: 'axis',
+          confine: true,
+          extraCssText: 'pointer-events:none;',
           axisPointer: {
             // 鍧愭爣杞存寚绀哄櫒锛屽潗鏍囪酱瑙﹀彂鏈夋晥
             type: 'line', // 榛樿涓虹洿绾匡紝鍙�変负锛�'line' | 'shadow'
@@ -291,6 +500,7 @@
         series: [...data],
       }
       option && this.chartEntity.setOption(option, true)
+      this.bindChartClick()
     },
     setOnlyLineChart() {
       if (!this.chartEntity) {
@@ -301,6 +511,8 @@
       var option = {
         tooltip: {
           trigger: 'axis',
+          confine: true,
+          extraCssText: 'pointer-events:none;',
           axisPointer: {
             // 鍧愭爣杞存寚绀哄櫒锛屽潗鏍囪酱瑙﹀彂鏈夋晥
             type: 'line', // 榛樿涓虹洿绾匡紝鍙�変负锛�'line' | 'shadow'
@@ -434,6 +646,7 @@
         ],
       }
       option && this.chartEntity.setOption(option, true)
+      this.bindChartClick()
     },
   },
 }
@@ -441,5 +654,7 @@
 <style lang="less" scoped>
 .line-chart {
   height: 20vh;
+  position: relative;
+  overflow: hidden;
 }
 </style>

--
Gitblit v1.8.0