From d6fb6076423a6d97da240804b0b43f30781b5fc2 Mon Sep 17 00:00:00 2001
From: dengzedong <dengzedong@email>
Date: 星期三, 11 六月 2025 17:32:46 +0800
Subject: [PATCH] 建议快照 dom虚拟渲染导致chartDoms不更新的bug

---
 src/views/model/sche/suggest/suggestSnapshot.vue |  308 ++++++++++++++++++++++++++++----------------------
 1 files changed, 172 insertions(+), 136 deletions(-)

diff --git a/src/views/model/sche/suggest/suggestSnapshot.vue b/src/views/model/sche/suggest/suggestSnapshot.vue
index 31ea85d..801a654 100644
--- a/src/views/model/sche/suggest/suggestSnapshot.vue
+++ b/src/views/model/sche/suggest/suggestSnapshot.vue
@@ -5,26 +5,23 @@
     width="80%"
     v-model="visible"
   >
-    <div class="flex-container">
-        <el-tag
-          v-for="item in dataList"
-          :key="item.dataCode"
-          class="data-tag"
-          :type="selectedData.includes(item.dataCode) ? '' : 'info'"
-          @click="toggleDataSelection(item)"
-        >
-          {{ item.dataName }}
-        </el-tag>
-      </div>
-      <div class="right-panel">
-        <div
-          v-for="chart in charts"
-          :key="chart.id"
-          class="chart-container"
-          ref="chartDom"
-          v-loading="loading"
-        ></div>
-      </div>
+    <el-checkbox-group v-model="selectedData" @change="refreshCharts">
+      <el-checkbox
+        v-for="item in dataList"
+        :label="item.dataName"
+        :key="item.dataName"
+      >
+        {{ item.dataName }}
+      </el-checkbox>
+    </el-checkbox-group>
+
+    <div
+      v-for="chart in charts"
+      :key="chart.id"
+      class="chart-container"
+      :ref="el => chartDoms[chart.id] = el"
+      v-loading="loading"
+    ></div>
   </el-dialog>
 </template>
 
@@ -33,78 +30,64 @@
   import * as echarts from 'echarts'
   import * as suggestSnapshotApi from '@/api/model/sche/suggest/suggestSnapshotRecord';
 
-  const message = useMessage()
+  const { message } = useMessage()
   const visible = ref(false)
   const dataList = ref([])
   const selectedData = ref([])
-  const charts = ref([])
+  const charts = ref()
+  const chartDoms = ref({})
   const chartInstances = ref([])
   const loading = ref(false)
 
-  const dataForm = reactive({
-    id: "",
-  })
 
-  /** 打开弹窗 */
-  const open = async (id: string) => {
+  const open = async (suggestId: string) => {
     visible.value = true
-    dataForm.id = id
-    await getDataList()
+    await getDataList(suggestId)
   }
 
   defineExpose({ open })
 
   /** 获取数据列表 */
-  const getDataList = async () => {
+  const getDataList = async (suggestId: string) => {
     try {
-      const res = await suggestSnapshotApi.getList(dataForm.id)
+      const res = await suggestSnapshotApi.getList(suggestId)
       dataList.value = res
+      selectedData.value = [] // 清空已选项
+      refreshCharts()
     } catch (error) {
       console.error(error)
       message.error('获取数据列表失败')
     }
   }
 
-  /** 切换数据选择 */
-  const toggleDataSelection = (item) => {
-    const index = selectedData.value.indexOf(item.dataCode)
-    if (index === -1) {
-      selectedData.value.push(item.dataCode)
-    } else {
-      selectedData.value.splice(index, 1)
-    }
-    refreshCharts()
-  }
-
   /** 刷新图表 */
   const refreshCharts = async () => {
     if (selectedData.value.length === 0) {
+      destroyCharts()
       charts.value = []
       return
     }
 
     loading.value = true
     try {
-      const params = {
-        chooseDataList: selectedData.value,
-      }
-      const chartData = await suggestSnapshotApi.getChartList(params)
+      const selectedDataList = selectedData.value.map(code =>
+        dataList.value.find(d => d.dataName === code)
+      ).filter(Boolean) // 过滤无效项
+      const chartData = await suggestSnapshotApi.getChartList(
+        selectedDataList
+      )
+      destroyCharts()
 
-      // 销毁之前的图表实例
-      chartInstances.value.forEach(instance => instance.dispose())
-      chartInstances.value = []
-
-      // 准备新的图表数据
-      charts.value = selectedData.value.map((code, index) => {
-        const item = dataList.value.find(d => d.dataCode === code)
+      // 生成图表配置数据
+      charts.value = selectedData.value.map(code => {
+        const item = dataList.value.find(d => d.dataName === code)
         return {
-          id: `chart-${index}`,
+          id: `chart-${code}`,
           name: item?.dataName || code,
-          data: chartData[index] // 假设返回数据是按顺序对应的
+          data: chartData.find((d: any) => d.dataName === code)
         }
       })
 
-      // 渲染新图表
       await nextTick()
       renderCharts()
     } catch (error) {
@@ -115,105 +98,158 @@
     }
   }
 
-  /** 渲染所有图表 */
-  const renderCharts = () => {
-    const chartContainers = document.querySelectorAll('.chart-container')
+  /** 销毁图表实例 */
+  const destroyCharts = () => {
+    chartInstances.value.forEach(instance => instance?.dispose())
+    chartInstances.value = []
+  }
 
-    chartContainers.forEach((container, index) => {
-      const chart = echarts.init(container)
-      const chartInfo = charts.value[index]
+  /** 渲染图表 */
+  const renderCharts = () => {
+    chartInstances.value = charts.value.map((chartInfo, index) => {
+      const dom = chartDoms.value[chartInfo.id]
+      if (!dom) return null
+      const chart = echarts.init(dom)
+
+      if (!chartInfo) return chart
+
+      const markLineData = []
+
+      if (chartInfo.data?.limitH !== null) {
+        markLineData.push({
+          yAxis: chartInfo.data?.limitH, // 上限
+          label: {
+            show: true,
+            formatter: '上限',
+            position: 'insideStartTop',
+            color: '#FF9A3D'
+          },
+          lineStyle: {
+            color: '#FF9A3D',
+            type: 'dashed'
+          },
+        })
+      }
+      if (chartInfo.data?.limitL !== null) {
+        markLineData.push({
+          yAxis: chartInfo.data?.limitL, // 下限
+          label: {
+            show: true,
+            formatter: '下限',
+            position: 'insideStartBottom',
+            color: '#00C2FF'
+          },
+          lineStyle: {
+            color: '#00C2FF',
+            type: 'dashed'
+          },
+        })
+      }
+      if (chartInfo.data?.scheduleTime !== null) {
+        markLineData.push({
+          xAxis: chartInfo.data?.scheduleTime, // 真实数据分割线
+          label: {
+            show: true,
+            formatter: '建\n议\n时\n间',
+            position: 'insideEndBottom',
+            color: '#5DFF9E',
+            rotate: 0
+          },
+          lineStyle: {
+            color: '#5DFF9E',
+          },
+        })
+      }
+      // 采纳
+      if (chartInfo.data?.status === 1) {
+        markLineData.push({
+          xAxis: chartInfo.data?.handleTime,
+          label: {
+            show: true,
+            formatter: '采\n纳\n时\n间',
+            position: 'insideEndBottom',
+            color: '#2196F3',
+            rotate: 0
+          },
+          lineStyle: {
+            color: '#2196F3',
+          },
+        })
+      }
+      // 忽略
+      if (chartInfo.data?.status === 2) {
+        markLineData.push({
+          xAxis: chartInfo.data?.handleTime,
+          label: {
+            show: true,
+            formatter: '忽\n略\n时\n间',
+            position: 'insideEndBottom',
+            color: '#999999',
+            rotate: 0
+          },
+          lineStyle: {
+            color: '#999999',
+          },
+        })
+      }
+
+      // 冲顶触底时间
+      if (chartInfo.data?.overLimitTimes?.length > 0) {
+        chartInfo.data?.overLimitTimes.forEach(overLimitTime => {
+          markLineData.push({
+            xAxis: overLimitTime,
+            lineStyle: {
+              color: '#ff0000',
+            },
+          })
+        })
+      }
 
       const option = {
         title: {
           text: chartInfo.name,
-          top: 0,
-          left: "1%",
-          textStyle: {
-            fontSize: 14,
-          },
+          textStyle: { fontSize: 14 }
         },
-        tooltip: {
-          trigger: "axis",
-          axisPointer: {
-            type: "line",
-            lineStyle: {
-              color: "#cccccc",
-              width: "1",
-              type: "dashed",
-            },
-          },
+        tooltip: { trigger: 'axis' },
+        grid: { top: '10%', left: '3%', right: '5%', bottom: 20 },
+        xAxis: {type: 'category'},
+        yAxis: { type: 'value',
+          max: (value) => chartInfo.data?.limitH && value.max < chartInfo.data.limitH ? chartInfo.data?.limitH : null,
+          min: (value) => chartInfo.data?.limitL && value.min > chartInfo.data.limitL ? chartInfo.data?.limitL : null,
         },
-        legend: {
-          show: false,
-          top: 10,
-        },
-        grid: {
-          top: 30,
-          left: "3%",
-          right: "5%",
-          bottom: 10,
-          containLabel: true,
-        },
-        xAxis: {
-          type: "category",
-          boundaryGap: false,
-          data: chartInfo.data.categories,
-        },
-        yAxis: {
-          type: "value",
-        },
-        dataZoom: [
-          {
-            type: "inside",
-          },
-        ],
+        dataZoom: [{ type: 'inside' }],
         series: [{
-          name: chartInfo.name,
-          type: "line",
-          data: chartInfo.data.series[0].data,
-          showSymbol: true,
-          smooth: false,
-          lineStyle: {
-            normal: {
-              color: "#5B8FF9",
+          type: 'line',
+          data: chartInfo.data?.dataList || [],
+          lineStyle: { color: '#5B8FF9', width: 1 },
+          markLine: {
+            silent: true,
+            symbol: ['none', 'none'],
+            lineStyle: {
+              type: 'solid',
               width: 1,
+              color: '#95E6FF',
             },
+            label: {
+              show: false,
+            },
+            data: markLineData,
           },
-        }],
+        }]
       }
 
       chart.setOption(option)
-      chartInstances.value.push(chart)
+      return chart
     })
   }
 </script>
 
 <style scoped>
-  .flex-container {
-    display: flex;
-    height: 600px;
-  }
-
-  .left-panel {
-    width: 250px;
-    padding-right: 20px;
-    border-right: 1px solid #eee;
-    overflow-y: auto;
-  }
-
-  .right-panel {
-    flex: 1;
-    padding-left: 20px;
-    overflow-y: auto;
-  }
-
-  .data-tag {
-    margin: 5px;
-    cursor: pointer;
-  }
-
   .chart-container {
-    height: 300px;
-    margin-bottom: 20px;
+    height: 400px;
+    margin: 20px 0;
+    border: 1px solid #eee;
+    border-radius: 4px;
+    padding: 10px;
   }
 </style>

--
Gitblit v1.9.3