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