<template>
|
<el-dialog
|
title="历史值"
|
:close-on-click-modal="false"
|
width="80%"
|
v-model="visible"
|
>
|
<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>
|
|
<script lang="ts" setup>
|
import { ref, reactive, nextTick } from 'vue'
|
import * as echarts from 'echarts'
|
import * as suggestSnapshotApi from '@/api/model/sche/suggest/suggestSnapshotRecord';
|
|
const { message } = useMessage()
|
const visible = ref(false)
|
const dataList = ref([])
|
const selectedData = ref([])
|
const charts = ref()
|
const chartDoms = ref({})
|
const chartInstances = ref([])
|
const loading = ref(false)
|
|
|
const open = async (suggestId: string) => {
|
visible.value = true
|
await getDataList(suggestId)
|
}
|
|
defineExpose({ open })
|
|
/** 获取数据列表 */
|
const getDataList = async (suggestId: string) => {
|
try {
|
const res = await suggestSnapshotApi.getList(suggestId)
|
dataList.value = res
|
selectedData.value = [] // 清空已选项
|
refreshCharts()
|
} catch (error) {
|
console.error(error)
|
message.error('获取数据列表失败')
|
}
|
}
|
|
/** 刷新图表 */
|
const refreshCharts = async () => {
|
if (selectedData.value.length === 0) {
|
destroyCharts()
|
charts.value = []
|
return
|
}
|
|
loading.value = true
|
try {
|
const selectedDataList = selectedData.value.map(code =>
|
dataList.value.find(d => d.dataName === code)
|
).filter(Boolean) // 过滤无效项
|
const chartData = await suggestSnapshotApi.getChartList(
|
selectedDataList
|
)
|
destroyCharts()
|
|
// 生成图表配置数据
|
charts.value = selectedData.value.map(code => {
|
const item = dataList.value.find(d => d.dataName === code)
|
return {
|
id: `chart-${code}`,
|
name: item?.dataName || code,
|
data: chartData.find((d: any) => d.dataName === code)
|
}
|
})
|
|
await nextTick()
|
renderCharts()
|
} catch (error) {
|
console.error(error)
|
message.error('获取图表数据失败')
|
} finally {
|
loading.value = false
|
}
|
}
|
|
/** 销毁图表实例 */
|
const destroyCharts = () => {
|
chartInstances.value.forEach(instance => instance?.dispose())
|
chartInstances.value = []
|
}
|
|
/** 渲染图表 */
|
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,
|
textStyle: { fontSize: 14 }
|
},
|
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,
|
},
|
dataZoom: [{ type: 'inside' }],
|
series: [{
|
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)
|
return chart
|
})
|
}
|
</script>
|
|
<style scoped>
|
.chart-container {
|
height: 400px;
|
margin: 20px 0;
|
border: 1px solid #eee;
|
border-radius: 4px;
|
padding: 10px;
|
}
|
</style>
|