534 lines
14 KiB
Vue
534 lines
14 KiB
Vue
<!-- SidePanelItem.vue -->
|
||
<template>
|
||
<SidePanelItem title="沿程水温变化" :prompt="prompts" :moreSelect="select" :datetimePicker="datetimePicker"
|
||
@update-values="handlePanelChange1">
|
||
<a-spin :spinning="loading" tip="加载中...">
|
||
<div v-show="showemit && !loading" ref="chartRef" class="chart-container"></div>
|
||
<div v-show="!showemit && !loading" class="chart-container"> <a-empty /></div>
|
||
</a-spin>
|
||
</SidePanelItem>
|
||
|
||
</template>
|
||
|
||
<script lang="ts" setup>
|
||
import { ref, onMounted, onBeforeUnmount, watch, computed } from "vue";
|
||
import * as echarts from "echarts";
|
||
import type { ECharts } from "echarts";
|
||
import SidePanelItem from "@/components/SidePanelItem/index.vue";
|
||
import { getKendoListCust, wbsbGetKendoList } from "@/api/sw";
|
||
import { useJidiSelectEventStore } from "@/store/modules/jidiSelectEvent";
|
||
|
||
import { da } from "element-plus/es/locale/index.mjs";
|
||
import { f } from "vue-router/dist/router-CWoNjPRp.mjs";
|
||
import { useModelStore } from "@/store/modules/model";
|
||
|
||
const modelStore = useModelStore();
|
||
|
||
|
||
|
||
// 定义组件名(便于调试和递归)
|
||
defineOptions({
|
||
name: "yanchengshuiwenChangeMod",
|
||
});
|
||
// 改为动态获取当前时间
|
||
const getCurrentHourTime = () => {
|
||
const now = new Date();
|
||
// 减去一小时
|
||
now.setHours(now.getHours() - 1);
|
||
const year = now.getFullYear();
|
||
const month = String(now.getMonth() + 1).padStart(2, '0');
|
||
const day = String(now.getDate()).padStart(2, '0');
|
||
const hour = String(now.getHours()).padStart(2, '0');
|
||
return `${year}-${month}-${day} ${hour}`;
|
||
};
|
||
const JidiSelectEventStore = useJidiSelectEventStore();
|
||
const baseid: any = ref(null);
|
||
const paramsOne = {
|
||
rvcd: '',
|
||
tm: []
|
||
}
|
||
const dataOne = ref('')
|
||
// 使用 computed 使 prompts 响应 dataOne.value 的变化
|
||
const prompts = computed(() => ({
|
||
show: true,
|
||
value: `注:最新数据时间为${dataOne.value || getCurrentHourTime()}`,
|
||
}));
|
||
const showemit = ref(false)
|
||
const loading = ref(false)
|
||
// const selectOptions: any = ref([])
|
||
const optionsFmt = (options: any) =>
|
||
options.map((item: any) => ({
|
||
label: item.wbsName,
|
||
value: item.wbsCode
|
||
}))
|
||
|
||
const chartRef = ref<HTMLElement | null>(null);
|
||
let chartInstance: ECharts | null = null;
|
||
|
||
// 静态数据 - 站点名称
|
||
const stationNames = ref([]);
|
||
|
||
// 静态数据 - 水温值 (°C),班多无数据设为 null,使用对象格式携带stcd信息
|
||
const waterTemperatures = ref<{ value: number | null; stcd: string; stnm: string }[]>([]);
|
||
|
||
// 使用 computed 使 currentTime 响应 dataOne.value 的变化
|
||
const currentTime = computed(() => dataOne.value || getCurrentHourTime());
|
||
// 选择器配置
|
||
const select = ref({
|
||
show: true,
|
||
value: '',
|
||
options: [],
|
||
picker: undefined,
|
||
format: undefined,
|
||
});
|
||
|
||
// 日期选择器配置
|
||
const datetimePicker = ref({
|
||
show: true,
|
||
value: computed(() => getCurrentHourTime()),
|
||
format: "YYYY-MM-DD HH",
|
||
picker: "date" as const,
|
||
options: [],
|
||
});
|
||
// 初始化图表
|
||
const initChart = () => {
|
||
if (!chartRef.value) return;
|
||
|
||
chartInstance = echarts.init(chartRef.value);
|
||
|
||
const option = {
|
||
title: {
|
||
text: "水温(°C)",
|
||
left: 5,
|
||
top: 0,
|
||
textStyle: {
|
||
color: "#000000",
|
||
fontSize: 12,
|
||
fontWeight: "normal",
|
||
},
|
||
},
|
||
dataZoom: [
|
||
{
|
||
type: 'inside',
|
||
start: 0,
|
||
end: 100,
|
||
zoomOnMouseWheel: true,
|
||
moveOnMouseMove: true,
|
||
filterMode: 'filter'
|
||
}
|
||
],
|
||
tooltip: {
|
||
trigger: "axis",
|
||
formatter: (params: any) => {
|
||
if (params && params.length > 0) {
|
||
const data = params[0];
|
||
// 根据项目规范:空值统一显示为短横线 `-`
|
||
if (data.value != null) {
|
||
return `${currentTime.value}<br/>${data.name}:${data.value}°C`;
|
||
} else {
|
||
return `${currentTime.value}<br/>${data.name}:-`;
|
||
}
|
||
}
|
||
return "";
|
||
},
|
||
},
|
||
grid: {
|
||
left: "3%",
|
||
right: "4%",
|
||
bottom: "3%",
|
||
top: "15%",
|
||
containLabel: true,
|
||
},
|
||
xAxis: {
|
||
type: "category",
|
||
data: stationNames.value,
|
||
boundaryGap: true,
|
||
axisLine: {
|
||
show: true,
|
||
lineStyle: {
|
||
color: "#8f8f8f",
|
||
},
|
||
},
|
||
axisTick: {
|
||
show: false,
|
||
},
|
||
axisLabel: {
|
||
color: "#333",
|
||
fontSize: 12,
|
||
margin: 8,
|
||
interval: 0,
|
||
formatter: (value: string, index: number) => {
|
||
// 偶数索引(0, 2, 4, 6)的标签在上方
|
||
// 奇数索引(1, 3, 5)的标签在下方
|
||
// 通过添加空行实现上下错位
|
||
if (index % 2 === 0) {
|
||
return `${value}\n `; // 上方标签:文字 + 换行 + 空格占位
|
||
} else {
|
||
return ` \n${value}`; // 下方标签:空格占位 + 换行 + 文字
|
||
}
|
||
},
|
||
},
|
||
splitLine: {
|
||
show: true,
|
||
lineStyle: {
|
||
color: "#e0e0e0",
|
||
type: "solid",
|
||
},
|
||
},
|
||
},
|
||
yAxis: {
|
||
type: "value",
|
||
min: 0,
|
||
// max: 10,
|
||
interval: 2,
|
||
axisLine: {
|
||
show: true,
|
||
lineStyle: {
|
||
color: "#8f8f8f",
|
||
},
|
||
},
|
||
axisTick: {
|
||
show: true,
|
||
length: 3,
|
||
lineStyle: {
|
||
color: "#8f8f8f",
|
||
},
|
||
},
|
||
axisLabel: {
|
||
color: "#333",
|
||
fontSize: 12,
|
||
},
|
||
splitLine: {
|
||
show: true,
|
||
lineStyle: {
|
||
color: "#e0e0e0",
|
||
type: "solid",
|
||
},
|
||
},
|
||
},
|
||
series: [
|
||
{
|
||
name: "水温",
|
||
type: "line",
|
||
smooth: true,
|
||
symbol: "circle",
|
||
symbolSize: 6,
|
||
connectNulls: true, // 连接空值两侧的线段,避免折线断开
|
||
lineStyle: {
|
||
color: "#6ca4f7",
|
||
width: 2,
|
||
},
|
||
itemStyle: {
|
||
color: "#6ca4f7",
|
||
},
|
||
areaStyle: {
|
||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||
{
|
||
offset: 0,
|
||
color: "rgba(108, 164, 247, 0.3)",
|
||
},
|
||
{
|
||
offset: 1,
|
||
color: "rgba(108, 164, 247, 0.05)",
|
||
},
|
||
]),
|
||
},
|
||
data: waterTemperatures.value,
|
||
},
|
||
],
|
||
};
|
||
|
||
chartInstance.setOption(option);
|
||
|
||
// 添加点击事件监听器
|
||
chartInstance.on('click', (params: any) => {
|
||
if (params.componentType === 'series') {
|
||
console.log('点击数据点:', params);
|
||
console.log('stcd:', params.data.stcd);
|
||
console.log('stnm:', params.data.stnm);
|
||
console.log('temperature:', params.data.value);
|
||
modelStore.modalVisible = true;
|
||
modelStore.params.sttp = "WT";
|
||
modelStore.title = params.data.stnm;
|
||
modelStore.params.stcd = params.data.stcd;
|
||
modelStore.params.date = paramsOne.tm;
|
||
}
|
||
});
|
||
};
|
||
|
||
// 处理窗口大小变化
|
||
const handleResize = () => {
|
||
chartInstance?.resize();
|
||
};
|
||
|
||
const init = async () => {
|
||
if (loading.value) return; // 防止重复请求
|
||
|
||
loading.value = true;
|
||
|
||
try {
|
||
const params = {
|
||
filter: {
|
||
logic: "and",
|
||
filters: [
|
||
{ field: "rvcd", operator: "eq", dataType: "string", value: paramsOne.rvcd },
|
||
{ field: "tm", operator: "gte", dataType: "date", value: paramsOne.tm[1] },
|
||
{ field: "tm", operator: "lte", dataType: "date", value: paramsOne.tm[0] },
|
||
],
|
||
},
|
||
sort: [{ field: "sort", dir: "asc" }],
|
||
};
|
||
|
||
let res = await getKendoListCust(params);
|
||
let data = res.data.data || res.data
|
||
|
||
if (data.length > 0) {
|
||
showemit.value = true
|
||
} else {
|
||
showemit.value = false
|
||
}
|
||
|
||
stationNames.value = data
|
||
.filter((item: any) => item.sttp == "1")
|
||
.map((item: any) => item.stnm);
|
||
console.log(stationNames.value);
|
||
|
||
// 修改为对象格式,携带stcd和stnm信息
|
||
waterTemperatures.value = data
|
||
.filter((item: any) => item.sttp == "2")
|
||
.map((item: any) => ({
|
||
value: item.temperature,
|
||
stcd: item.stcd,
|
||
stnm: item.stnm
|
||
}));
|
||
console.log(waterTemperatures.value);
|
||
|
||
// 数据获取完成后,初始化或更新图表
|
||
setTimeout(() => {
|
||
if (!chartInstance) {
|
||
initChart();
|
||
} else {
|
||
// 如果图表已存在,只更新数据
|
||
chartInstance.setOption({
|
||
xAxis: {
|
||
data: stationNames.value,
|
||
},
|
||
series: [
|
||
{
|
||
data: waterTemperatures.value,
|
||
},
|
||
],
|
||
});
|
||
}
|
||
// 强制重绘,确保尺寸正确
|
||
setTimeout(() => {
|
||
chartInstance?.resize();
|
||
}, 100);
|
||
}, 50);
|
||
} catch (error) {
|
||
console.error('数据加载失败:', error);
|
||
showemit.value = false;
|
||
} finally {
|
||
loading.value = false;
|
||
}
|
||
};
|
||
//获取选择器配置参数
|
||
const getSelectConfig = async () => {
|
||
loading.value = true;
|
||
try {
|
||
let obj: any = {}
|
||
if (baseid.value === 'all') {
|
||
obj = { rstcdStepSort: 'asc', siteStepSort: 'asc' }
|
||
} else {
|
||
obj = { siteStepSort: 'asc' }
|
||
}
|
||
const filters = [
|
||
{
|
||
field: 'wbsType',
|
||
operator: 'eq',
|
||
value: 'PSB_RVCD'
|
||
}
|
||
]
|
||
if (baseid.value && baseid.value !== 'all') {
|
||
filters.push({
|
||
field: 'objId',
|
||
operator: 'eq', // contains
|
||
value: baseid.value
|
||
})
|
||
}
|
||
let data: any = {
|
||
filter: {
|
||
logic: 'and',
|
||
filters
|
||
}
|
||
// select: ['WBS_CODE', 'WBS_NAME']
|
||
}
|
||
|
||
if (obj) {
|
||
data.sort = [obj]
|
||
} else {
|
||
data = {
|
||
...data,
|
||
group: [
|
||
{ dir: 'asc', field: 'orderIndex' },
|
||
{ dir: 'asc', field: 'wbsCode' },
|
||
{ dir: 'asc', field: 'wbsName' },
|
||
{ dir: 'asc', field: 'objid' },
|
||
],
|
||
groupResultFlat: true
|
||
}
|
||
}
|
||
let res = await wbsbGetKendoList(data)
|
||
let dataOne = res?.data?.data || res?.data
|
||
|
||
if (dataOne.length > 0) {
|
||
let dataMapNameMap: any = {}
|
||
let jiDiListMap: any = {}
|
||
JidiSelectEventStore.jidiData.forEach((item: any) => {
|
||
jiDiListMap[item.wbsCode] = item.wbsName
|
||
})
|
||
|
||
dataOne.map((item: any, index: number) => {
|
||
const { stcd, stnm, objId } = item
|
||
if (dataMapNameMap[objId]) {
|
||
dataMapNameMap[objId].push({ ...item, baseName: jiDiListMap[objId], baseId: objId })
|
||
} else {
|
||
dataMapNameMap[objId] = [{ ...item, baseName: jiDiListMap[objId], baseId: objId }]
|
||
}
|
||
return { label: item.wbsName, value: item.wbsCode, baseId: objId, baseName: jiDiListMap[objId] }
|
||
})
|
||
let dataMapNameArr: any = []
|
||
|
||
Object.keys(dataMapNameMap).forEach((item: any) => {
|
||
dataMapNameArr.push({
|
||
title: dataMapNameMap[item]?.[0]?.baseName,
|
||
value: item,
|
||
selectable: false,
|
||
// baseId:dataMapNameMap[item]?.[0]?.baseId,
|
||
children: dataMapNameMap[item].map((item2: any) => {
|
||
return {
|
||
title: item2.wbsName,
|
||
value: item2.wbsCode
|
||
}
|
||
})
|
||
})
|
||
})
|
||
dataMapNameArr = dataMapNameArr.sort((a: any, b: any) => {
|
||
const indexA = JidiSelectEventStore.jidiData.findIndex(item => item.wbsCode === a.value);
|
||
const indexB = JidiSelectEventStore.jidiData.findIndex(item => item.wbsCode === b.value);
|
||
return indexA - indexB;
|
||
})
|
||
select.value.options = filterSelectOptions(dataMapNameArr)
|
||
if (baseid.value === 'all') {
|
||
select.value.value = 'SJLY1U'
|
||
} else if (filterSelectOptions(dataMapNameArr)[0]?.children) {
|
||
select.value.value = filterSelectOptions(dataMapNameArr)[0]?.children[0]?.value
|
||
} else if (filterSelectOptions(dataMapNameArr)[0]?.value) {
|
||
select.value.value = filterSelectOptions(dataMapNameArr)[0]?.value
|
||
} else {
|
||
select.value.value = ''
|
||
}
|
||
}
|
||
} catch (error) {
|
||
console.error('选择器配置加载失败:', error);
|
||
} finally {
|
||
loading.value = false;
|
||
}
|
||
}
|
||
|
||
const filterSelectOptions = (selectOptions: any) => {
|
||
if (selectOptions?.length === 1 && selectOptions?.[0]?.children?.length === 1) {
|
||
return [{ ...selectOptions?.[0]?.children?.[0], isLeaf: true }]
|
||
} else if (selectOptions.length) {
|
||
return selectOptions.map((e) => {
|
||
if (e?.children?.length > 1) {
|
||
return { ...e, isLeaf: false, selectable: true, children: e.children.map((item) => ({ ...item, isLeaf: true })) }
|
||
} else {
|
||
return { ...e.children[0], isLeaf: true }
|
||
}
|
||
})
|
||
}
|
||
return []
|
||
}
|
||
//监听子组件的数据变化
|
||
const handlePanelChange1 = (data) => {
|
||
console.log('当前所有控件状态:', data);
|
||
if (data.moreSelect) {
|
||
paramsOne.rvcd = data.moreSelect
|
||
}
|
||
|
||
if (data.datetime) {
|
||
dataOne.value = data.datetime
|
||
const datetime = data.datetime;
|
||
|
||
// 解析日期和时间
|
||
const [datePart, hourPart] = datetime.split(' ');
|
||
const [year, month, day] = datePart.split('-').map(Number);
|
||
|
||
// 创建当前时间的 Date 对象
|
||
const currentDate = new Date(year, month - 1, day, parseInt(hourPart), 0, 0);
|
||
|
||
// 创建一个月前的 Date 对象
|
||
const previousDate = new Date(year, month - 2, day, 0, 0, 0);
|
||
|
||
// 格式化函数:将 Date 对象转为 "YYYY-MM-DD HH:mm:ss" 格式
|
||
function formatDate(date) {
|
||
const y = date.getFullYear();
|
||
const m = String(date.getMonth() + 1).padStart(2, '0');
|
||
const d = String(date.getDate()).padStart(2, '0');
|
||
const h = String(date.getHours()).padStart(2, '0');
|
||
const min = String(date.getMinutes()).padStart(2, '0');
|
||
const s = String(date.getSeconds()).padStart(2, '0');
|
||
return `${y}-${m}-${d} ${h}:${min}:${s}`;
|
||
}
|
||
|
||
// 生成结果数组
|
||
const result = [
|
||
formatDate(currentDate), // "2026-05-15 15:00:00"
|
||
formatDate(previousDate) // "2026-04-15 00:00:00"
|
||
];
|
||
paramsOne.tm = result
|
||
console.log(paramsOne.tm, 'paramsOne.tm')
|
||
}
|
||
if (paramsOne.rvcd || paramsOne.tm.length > 0) {
|
||
init();
|
||
}
|
||
|
||
}
|
||
|
||
watch(
|
||
() => JidiSelectEventStore.selectedItem,
|
||
(newVal) => {
|
||
baseid.value = newVal.wbsCode;
|
||
getSelectConfig()
|
||
},
|
||
{ deep: true, immediate: true }
|
||
);
|
||
// 页面加载时执行的逻辑
|
||
onMounted(() => {
|
||
// 延迟初始化,确保 DOM 渲染完成
|
||
init();
|
||
window.addEventListener("resize", handleResize);
|
||
});
|
||
|
||
// 组件卸载前清理
|
||
onBeforeUnmount(() => {
|
||
window.removeEventListener("resize", handleResize);
|
||
chartInstance?.dispose();
|
||
});
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
.chart-container {
|
||
width: 100%;
|
||
height: 252px;
|
||
display: flex;
|
||
justify-content: center;
|
||
align-items: center;
|
||
}
|
||
|
||
:deep(.ant-spin-nested-loading) {
|
||
height: 252px !important;
|
||
}
|
||
</style>
|