水温监测右侧弹框接口对接

This commit is contained in:
王兴凯 2026-05-20 08:45:44 +08:00
commit 363c353bdf
6 changed files with 2266 additions and 660 deletions

View File

@ -1,7 +1,65 @@
import request from '@/utils/request';
export function getKendoListCust(data: any) {
return request({
url: '/sw/alongList/qgc/GetKendoListCust',
url: '/api/wmp-env-server/sw/alongList/qgc/GetKendoListCust',
method: 'post',
data
});
}
//获取水温下拉框
export function wbsbGetKendoList(data: any) {
return request({
url: '/api/dec-lygk-base-server/base/wbsb/GetKendoList',
method: 'post',
data
});
}
/**
*
* @param data
* @returns Promise
*/
export function getChuiXiangShuiWenTreeStcd(data: any) {
return request({
url: '/api/wmp-env-server/base/sdrvwts/default/treeStcd',
method: 'post',
data,
});
}
/**
*
* @param data { filter, sort }
* @returns Promise
*/
export function getCxswList(data: any) {
return request({
url: '/api/wmp-env-server/sw/dzCxList/GetKendoListCust',
method: 'post',
data
});
}
//获取出入库水温下拉选则树
export function getVmsstbprpt(data: any) {
return request({
url: '/api/dec-lygk-base-server/base/vmsstbprpt/GetKendoList',
method: 'post',
data
});
}
//出入库水温图表数据
//
export function inOutOneGetKendoListCust(data: any) {
return request({
url: '/api/wmp-env-server/sw/inOutOne/GetKendoListCust',
method: 'post',
data
});
}
//水温监测工作开展情况
export function baseEvnmAutoMonitorGetKendoListCust(data: any) {
return request({
url: '/api/dec-lygk-base-server/base/evnmAutoMonitor/GetKendoListCust',
method: 'post',
data
});

View File

@ -33,25 +33,45 @@
<img v-else src="@/assets/components/arrow-down.png" alt="">
</div>
<div v-if="moreSelect.show">
<a-tree-select v-model:value="moreSelectValue" show-search
:dropdown-style="{ maxHeight: '400px', overflow: 'auto' }" placeholder="Please select"
allow-clear tree-default-expand-all :tree-data="moreSelect.options"
tree-node-filter-prop="label">
<a-tree-select v-model:value="moreSelectValue" show-search :size="'small'" style="width: 110px"
:dropdown-style="{ maxHeight: '400px', overflow: 'auto',minWidth: '180px' }" placeholder=" "
:tree-data="processedMoreSelectOptions"
:field-names="{ label: 'title', value: 'value', children: 'children' }"
tree-node-filter-prop="label"
:expanded-keys="expandedKeys"
popup-class-name="no-wrap-tree-select"
@select="handleTreeSelect"
@expand="handleTreeExpand"
@dropdownVisibleChange="handleDropdownVisibleChange">
</a-tree-select>
</div>
<div v-if="datetimePicker.show">
<!-- 添加 locale 属性来设置语言 -->
<a-date-picker v-model:value="datetimeValue" show-time
:style="{ width: datetimePicker.picker === 'year' ? '80px' : '120px' }"
<a-date-picker
v-model:value="datetimeValue"
show-time
:style="{ width: datetimePicker.picker === 'year' ? '80px' : '130px' }"
:format="datetimePicker.format !== null ? datetimePicker.format : undefined"
:picker="datetimePicker.picker" placeholder=" " @change="handleDateTimeChange"
:size="'small'" />
:picker="datetimePicker.picker"
:allowClear="false"
placeholder=" "
@change="handleDateTimeChange"
:size="'small'"
:disabledDate="createDisabledDateFn(datetimePicker.picker)"
:disabledTime="disabledTimeForSinglePicker" />
<!-- 修改为 locale 变量 -->
</div>
<div v-if="scopeDate.show" class="title_scopeDate">
<a-range-picker v-model:value="scopeDateValue" :picker="scopeDate.picker"
:style="{ width: scopeDate.picker === 'year' ? '80px' : (scopeDate.picker === 'month' ? '150px' : '') }"
:format="scopeDate.format" :range-separator="' 至 '" :size="'small'" />
<a-range-picker
v-model:value="scopeDateValue"
:picker="scopeDate.picker"
:allowClear="false"
:style="{ width: scopeDate.picker === 'year' ? '80px' : (scopeDate.picker === 'month' ? '180px' : '') }"
:format="scopeDate.format"
:range-separator="' 至 '"
:size="'small'"
:presets="computedScopeDatePresets"
:disabledDate="createDisabledDateFn(scopeDate.picker)" />
</div>
<div v-if="tabs.show" class="typeOne">
<div @click="handleTabClick('one')" :class="tabsValue == 'one' ? 'typezhong' : ''">图片</div>
@ -66,7 +86,7 @@
</template>
<script lang="ts" setup>
import { ref, onMounted } from 'vue';
import { ref, onMounted, watch, computed } from 'vue';
import {
QuestionCircleOutlined,
InfoCircleOutlined
@ -169,10 +189,10 @@ const props = defineProps({
})
}
});
const emit = defineEmits(['tab-change']);
const emit = defineEmits(['tab-change', 'update-values']);
const isExpand = ref(true);
const selectValue = ref(props.select.value)
const moreSelectValue = ref(props.select.value)
const moreSelectValue = ref(props.moreSelect.value)
const datetimeValue = ref<Dayjs | null>(props.datetimePicker.value ? dayjs(props.datetimePicker.value) : null);
const scopeDateValue = ref<[Dayjs, Dayjs] | undefined>(
props.scopeDate.value && Array.isArray(props.scopeDate.value)
@ -180,21 +200,338 @@ const scopeDateValue = ref<[Dayjs, Dayjs] | undefined>(
: undefined
);
const tabsValue = ref(props.tabs.value)
//
const expandedKeys = ref<string[]>([]);
/**
* 创建针对不同 picker 类型的日期禁用函数
* @param pickerType - 选择器类型: year | month | quarter | week | date
* @returns disabledDate 回调函数
*/
const createDisabledDateFn = (pickerType: string) => {
return (current: Dayjs) => {
if (!current) return false
const now = dayjs()
switch (pickerType) {
case 'year':
// >
return current.year() > now.year()
case 'month':
// >
return current.isAfter(now, 'month')
case 'quarter':
// >
return current.isAfter(now, 'quarter')
case 'week':
// >
return current.isAfter(now, 'week')
case 'date':
default:
// >
return current.isAfter(now, 'day')
}
}
}
/**
* 单日期选择器的时间禁用函数针对 show-time
* 精确限制到当前时刻的时分秒
*/
const disabledTimeForSinglePicker = () => {
// datetimePicker date
if (!props.datetimePicker.show || props.datetimePicker.picker !== 'date') {
return undefined
}
const now = dayjs()
const selectedDate = datetimeValue.value
//
if (!selectedDate || !selectedDate.isSame(now, 'day')) {
return {}
}
// start end-1
const range = (start: number, end: number) => {
const result: number[] = []
for (let i = start; i < end; i++) {
result.push(i)
}
return result
}
return {
//
disabledHours: () => {
return range(now.hour() + 1, 24)
},
//
disabledMinutes: (selectedHour: number) => {
if (selectedHour === now.hour()) {
return range(now.minute() + 1, 60)
}
return []
},
//
disabledSeconds: (selectedHour: number, selectedMinute: number) => {
if (selectedHour === now.hour() && selectedMinute === now.minute()) {
return range(now.second() + 1, 60)
}
return []
}
}
}
// // locale
// const locale = zhCN;
// console.log(locale, "zhCN");
/**
* 递归处理树数据为父节点添加 selectable: false
* @param data 原始树数据
* @returns 处理后的树数据
*/
const processTreeData = (data: any[]): any[] => {
if (!data || !Array.isArray(data)) return [];
return data.map(item => {
const newItem = { ...item };
//
if (newItem.children && newItem.children.length > 0) {
newItem.selectable = false;
//
newItem.children = processTreeData(newItem.children);
} else {
//
newItem.selectable = true;
}
return newItem;
});
};
//
const processedMoreSelectOptions = computed(() => {
return processTreeData(props.moreSelect.options || []);
});
/**
* 计算属性根据 picker 类型动态生成快捷日期选项
*/
const computedScopeDatePresets = computed(() => {
const now = dayjs();
const presets: Array<{ label: string; value: [Dayjs, Dayjs] }> = [];
// picker
switch (props.scopeDate.picker) {
case 'year':
//
presets.push({
label: '今年',
value: [now.startOf('year'), now]
});
break;
case 'month':
//
presets.push(
{
label: '今天',
value: [now.startOf('day'), now.endOf('day')]
},
{
label: '昨天',
value: [now.subtract(1, 'day').startOf('day'), now.subtract(1, 'day').endOf('day')]
},
{
label: '最近七天',
value: [now.subtract(6, 'day').startOf('day'), now.endOf('day')]
},
{
label: '最近一个月',
value: [now.subtract(1, 'month').startOf('day'), now.endOf('day')]
},
{
label: '最近三个月',
value: [now.subtract(3, 'month').startOf('day'), now.endOf('day')]
},
{
label: '今年',
value: [now.startOf('year'), now.endOf('day')]
}
);
break;
case 'quarter':
//
presets.push(
{
label: '最近一季度',
value: [now.subtract(1, 'quarter').startOf('quarter'), now.endOf('quarter')]
},
{
label: '今年',
value: [now.startOf('year'), now.endOf('quarter')]
}
);
break;
case 'week':
//
presets.push(
{
label: '本周',
value: [now.startOf('week'), now.endOf('week')]
},
{
label: '上周',
value: [now.subtract(1, 'week').startOf('week'), now.subtract(1, 'week').endOf('week')]
},
{
label: '最近四周',
value: [now.subtract(3, 'week').startOf('week'), now.endOf('week')]
}
);
break;
case 'date':
default:
//
presets.push(
{
label: '今天',
value: [now.startOf('day'), now.endOf('day')]
},
{
label: '昨天',
value: [now.subtract(1, 'day').startOf('day'), now.subtract(1, 'day').endOf('day')]
},
{
label: '最近七天',
value: [now.subtract(6, 'day').startOf('day'), now.endOf('day')]
},
{
label: '最近一个月',
value: [now.subtract(1, 'month').startOf('day'), now.endOf('day')]
},
{
label: '最近三个月',
value: [now.subtract(3, 'month').startOf('day'), now.endOf('day')]
},
{
label: '今年',
value: [now.startOf('year'), now.endOf('day')]
}
);
break;
}
return presets;
});
/**
* 处理树节点选择事件
* 由于父节点已设置 selectable: false这里只会接收到叶子节点
* @param selectedKeys 选中的键值可能是字符串或数组
* @param info 节点信息对象本身就是节点不是包含 node 的对象
*/
const handleTreeSelect = (selectedKeys: string | string[], info: any) => {
// selectedKeys
let selectedValue: string;
if (typeof selectedKeys === 'string') {
// selectedKeys
selectedValue = selectedKeys;
} else if (Array.isArray(selectedKeys)) {
//
selectedValue = selectedKeys[0];
} else {
// info
selectedValue = info?.value || '';
}
moreSelectValue.value = selectedValue;
};
/**
* 处理树节点展开/收起事件
* @param keys 展开的节点键值数组
*/
const handleTreeExpand = (keys: string[]) => {
expandedKeys.value = keys;
};
/**
* 查找节点的父节点路径
* @param treeData 树数据
* @param targetValue 目标节点的值
* @returns 父节点值的数组从根到直接父节点
*/
const findParentKeys = (treeData: any[], targetValue: string): string[] => {
const parentKeys: string[] = [];
const search = (nodes: any[], target: string, parents: string[]): boolean => {
for (const node of nodes) {
if (node.value === target) {
//
parentKeys.push(...parents);
return true;
}
if (node.children && node.children.length > 0) {
//
if (search(node.children, target, [...parents, node.value])) {
return true;
}
}
}
return false;
};
search(treeData, targetValue, []);
return parentKeys;
};
/**
* 处理下拉框显示/隐藏变化事件
* 当下拉框打开且有选中值时自动展开该节点的所有父节点
* @param visible 下拉框是否可见
*/
const handleDropdownVisibleChange = (visible: boolean) => {
if (visible && moreSelectValue.value && processedMoreSelectOptions.value.length > 0) {
//
const parentKeys = findParentKeys(processedMoreSelectOptions.value, moreSelectValue.value);
if (parentKeys.length > 0) {
//
const mergedKeys = Array.from(new Set([...expandedKeys.value, ...parentKeys]));
expandedKeys.value = mergedKeys;
}
}
};
//
const handleChange = (value: string) => {
console.log(`selected ${value}`);
// Handle change
};
const handleBlur = () => {
console.log('blur');
// Handle blur
};
const handleFocus = () => {
console.log('focus');
// Handle focus
};
const filterOption = (input: string, option?: { value: string }) => {
@ -209,7 +546,7 @@ const getPopupContainer = (trigger: HTMLElement) => {
//
const handleDateTimeChange = (date: any | null, dateString: string) => {
console.log('Selected DateTime:', date, dateString);
// Handle date time change
};
const handleTabClick = (value: string) => {
@ -220,9 +557,137 @@ const handleTabClick = (value: string) => {
tabLabel: value === 'one' ? '图片' : '视频'
});
};
//
const emitAllValues = () => {
const payload: any = {
select: selectValue.value,
moreSelect: moreSelectValue.value,
tabs: tabsValue.value,
};
//
if (datetimeValue.value) {
// 使 format picker
const format = props.datetimePicker.format || getDefaultFormat(props.datetimePicker.picker);
payload.datetime = datetimeValue.value.format(format);
} else {
payload.datetime = null;
}
//
if (scopeDateValue.value && Array.isArray(scopeDateValue.value)) {
// 使 format picker
const format = props.scopeDate.format || getDefaultFormat(props.scopeDate.picker);
payload.scopeDate = scopeDateValue.value.map(d => d ? d.format(format) : null);
} else {
payload.scopeDate = [];
}
emit('update-values', payload);
};
// picker
const getDefaultFormat = (picker: string): string => {
switch (picker) {
case 'year':
return 'YYYY';
case 'month':
return 'YYYY-MM';
case 'quarter':
return 'YYYY-[Q]Q';
case 'week':
return 'YYYY-wo';
case 'date':
default:
return 'YYYY-MM-DD';
}
};
//
watch([selectValue, moreSelectValue, datetimeValue, scopeDateValue, tabsValue], () => {
emitAllValues();
});
// props
watch(
() => props.select.value,
(newVal) => {
if (newVal !== selectValue.value) {
selectValue.value = newVal;
}
}
);
watch(
() => props.moreSelect.value,
(newVal) => {
if (newVal !== moreSelectValue.value) {
moreSelectValue.value = newVal;
//
if (newVal && processedMoreSelectOptions.value.length > 0) {
const parentKeys = findParentKeys(processedMoreSelectOptions.value, newVal);
if (parentKeys.length > 0) {
expandedKeys.value = Array.from(new Set([...expandedKeys.value, ...parentKeys]));
}
}
}
}
);
watch(
() => props.datetimePicker.value,
(newVal) => {
const newDayjs = newVal ? dayjs(newVal) : null;
if (!datetimeValue.value || !newDayjs || !datetimeValue.value.isSame(newDayjs)) {
datetimeValue.value = newDayjs;
}
}
);
watch(
() => props.scopeDate.value,
(newVal) => {
if (newVal && Array.isArray(newVal) && newVal.length === 2) {
const newRange: [Dayjs, Dayjs] = [dayjs(newVal[0]), dayjs(newVal[1])];
const currentRange = scopeDateValue.value;
//
if (!currentRange ||
!currentRange[0].isSame(newRange[0]) ||
!currentRange[1].isSame(newRange[1])) {
scopeDateValue.value = newRange;
}
} else {
if (scopeDateValue.value) {
scopeDateValue.value = undefined;
}
}
}
);
watch(
() => props.tabs.value,
(newVal) => {
if (newVal !== tabsValue.value) {
tabsValue.value = newVal;
}
}
);
//
onMounted(() => {
console.log(props.select);
//
emitAllValues();
// tree-select
if (moreSelectValue.value && processedMoreSelectOptions.value.length > 0) {
const parentKeys = findParentKeys(processedMoreSelectOptions.value, moreSelectValue.value);
if (parentKeys.length > 0) {
expandedKeys.value = parentKeys;
}
}
});
</script>

View File

@ -1,150 +1,429 @@
<!-- 垂向水温变化图 -->
<template>
<SidePanelItem title="垂向水温变化">
<SidePanelItem title="垂向水温变化" :moreSelect="select" :datetimePicker="datetimePicker"
@update-values="handlePanelChange1">
<div class="chart-wrapper">
<!-- 图例与翻页控制 -->
<div class="chart-header">
<div class="legend-container">
<div
v-for="item in currentLegendItems"
:key="item.name"
class="legend-item"
:class="{ inactive: legendInactiveSet.has(item.name) }"
@click="toggleLegend(item.name)"
>
<span class="legend-dot" :style="{ backgroundColor: legendInactiveSet.has(item.name) ? '#ccc' : item.color }"></span>
<span class="legend-text" :style="{ color: legendInactiveSet.has(item.name) ? '#ccc' : '#333' }">{{ item.name }}</span>
</div>
</div>
<div class="pagination">
<span class="page-btn" :class="{ disabled: currentPage === 1 }" @click="prevPage"></span>
<span class="page-info">{{ currentPage }}/{{ totalPages }}</span>
<span class="page-btn" :class="{ disabled: currentPage === totalPages }" @click="nextPage"></span>
</div>
</div>
<!-- 图表容器 -->
<div ref="chartRef" class="chart-container"></div>
<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>
</div>
</SidePanelItem>
</template>
<script lang="ts" setup>
import { ref, computed, onMounted, onBeforeUnmount } from 'vue';
import { ref, computed, onMounted, onBeforeUnmount, watch, nextTick } from 'vue';
import * as echarts from 'echarts';
import type { ECharts } from 'echarts';
import SidePanelItem from '@/components/SidePanelItem/index.vue';
import { useJidiSelectEventStore } from "@/store/modules/jidiSelectEvent";
import { getChuiXiangShuiWenTreeStcd, getCxswList } from "@/api/sw";
const JidiSelectEventStore = useJidiSelectEventStore();
const baseid = ref('');
//
defineOptions({
name: 'chuixiangshuiwenChangeMod'
});
// ==================== ====================
const showemit = ref(false); //
const loading = ref(false); //
// ==================== ====================
/**
* 智能刻度计算函数
*/
function chartMax(
targetTickCount: number,
max: number,
min: number,
round: boolean = true,
base: number = 0
): { min: number; max: number; scale: number } {
const range = max - min;
if (range === 0) {
const expandedMax = max * 1.1;
const expandedMin = min * 0.9;
return {
min: expandedMin,
max: expandedMax,
scale: (expandedMax - expandedMin) / targetTickCount
};
}
let idealScale = range / targetTickCount;
const magnitude = Math.pow(10, Math.floor(Math.log10(idealScale)));
const normalized = idealScale / magnitude;
let friendlyScale: number;
if (normalized < 1.5) {
friendlyScale = 1 * magnitude;
} else if (normalized < 3) {
friendlyScale = 2 * magnitude;
} else if (normalized < 7) {
friendlyScale = 5 * magnitude;
} else {
friendlyScale = 10 * magnitude;
}
let newMin = Math.floor(min / friendlyScale) * friendlyScale;
let newMax = Math.ceil(max / friendlyScale) * friendlyScale;
if (newMin > min) newMin -= friendlyScale;
if (newMax < max) newMax += friendlyScale;
if (round) {
newMin = Math.round(newMin);
newMax = Math.round(newMax);
friendlyScale = Math.round(friendlyScale);
}
return {
min: newMin,
max: newMax,
scale: friendlyScale
};
}
/**
* 数组最大值
*/
function arrMax(arr: number[]): number {
return Math.max(...arr.filter(v => v !== null && v !== undefined));
}
/**
* 数组最小值
*/
function arrMin(arr: number[]): number {
return Math.min(...arr.filter(v => v !== null && v !== undefined));
}
/**
* API 返回的 aggregates 数据转换为图表数据格式
*/
function transformAggregatesToChartData(aggregates: Record<string, any[]>): Array<{
name: string;
dataXy: Array<[number, number]>;
}> {
const result: Array<{ name: string; dataXy: Array<[number, number]> }> = [];
for (let month = 1; month <= 12; month++) {
const key = month.toString();
const monthData = aggregates[key];
// React
if (!monthData || monthData.length === 0) {
result.push({
name: `${month}`,
dataXy: [] // ECharts
});
continue;
}
// { wthg, vwt } [vwt, wthg]
const dataXy: Array<[number, number]> = monthData.map((item: any) => [
parseFloat(item.vwt), //
parseInt(item.wthg) // /
]);
result.push({
name: `${month}`,
dataXy
});
}
return result;
}
/**
* 根据当前年月生成颜色和图例显隐状态
*/
function generateColorsAndVisibility(
dataLength: number, // 12
sDate: string | number
): {
colors: string[];
selected: Record<string, boolean>;
} {
const baseColors = [
'#6ca4f7', '#78c300', '#9556a4', '#df91ab',
'#7399c6', '#dbb629', '#56c2e3', '#f56a06',
'#cdba75', '#76523b', '#df75b6', '#00a050'
];
const currentYear = new Date().getFullYear();
const currentMonth = new Date().getMonth() + 1;
const selectedYear = typeof sDate === 'string' ? parseInt(sDate) : sDate;
const colors: string[] = [];
const selected: Record<string, boolean> = {};
// 12 dataLength
for (let i = 0; i < 12; i++) {
const monthName = `${i + 1}`;
if (selectedYear < currentYear) {
//
colors.push(baseColors[i]);
selected[monthName] = true;
} else {
//
if (i + 1 < currentMonth) {
colors.push(baseColors[i]);
selected[monthName] = true;
} else {
colors.push('#D3D3D3');
selected[monthName] = false;
}
}
}
return { colors, selected };
}
// ==================== ====================
const getCurrentHourTime = () => {
const now = new Date();
const year = now.getFullYear();
return `${year}`;
};
const getCurrentHourTimeTwo = () => {
const now = new Date();
const endTime = new Date(now);
endTime.setHours(endTime.getHours() - 1);
endTime.setMinutes(0);
endTime.setSeconds(0);
endTime.setMilliseconds(0);
const startTime = new Date(endTime);
startTime.setMonth(startTime.getMonth() - 1);
const formatDateTime = (date) => {
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
const hours = String(date.getHours()).padStart(2, '0');
const minutes = String(date.getMinutes()).padStart(2, '0');
const seconds = String(date.getSeconds()).padStart(2, '0');
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
};
console.log('开始时间:', formatDateTime(startTime));
console.log('结束时间:', formatDateTime(endTime));
return [formatDateTime(startTime), formatDateTime(endTime)];
};
const select = ref({
show: true,
value: '',
options: [],
picker: undefined,
format: undefined,
});
const datetimePicker = ref({
show: true,
value: computed(() => getCurrentHourTime()),
format: "YYYY",
picker: "year" as const,
options: [],
});
/**
* 获取站点下拉选项数据
*/
const selectOptions = async () => {
loading.value = true; //
try {
let filters = [
{
"field": "mway",
"operator": "eq",
"dataType": "string",
"value": 2
},
{
"field": "tm",
"operator": "gte",
"dataType": "date",
"value": getCurrentHourTimeTwo()[0]
},
{
"field": "tm",
"operator": "lte",
"dataType": "date",
"value": getCurrentHourTimeTwo()[1]
}
];
if (baseid.value && baseid.value !== 'all') {
filters.push({
"field": "baseId",
"operator": "eq",
"dataType": "string",
"value": baseid.value
});
}
let filter = {
"logic": "and",
"filters": filters
};
let sort = baseid.value && baseid.value !== 'all' ? [
{
"field": "rstcdStepSort",
"dir": "asc"
}
] : [
{
"field": "baseId",
"dir": "asc"
},
{
"field": "rstcdStepSort",
"dir": "asc"
}
];
const data = { filter, sort };
const res = await getChuiXiangShuiWenTreeStcd(data);
let dataOne = res?.data?.data || res.data
if (dataOne.length > 0) {
const treeData = dataOne.map(item => {
return {
title: item.ennm,
value: item.rstcd,
selectable: false,
children: (item.stcdVo || []).map(child => ({
title: child.stnm,
value: child.stcd
}))
};
});
if (baseid.value == 'all') {
select.value.value = '008640203800001014'
select.value.options = treeData;
} else {
select.value.value = treeData[0].children[0].value
select.value.options = treeData;
}
} else {
select.value.options = []
select.value.value = ''
}
} catch (error) {
console.error('获取站点数据失败:', error);
} finally {
loading.value = false; // loading
}
}
// ==================== ====================
const chartRef = ref<HTMLElement | null>(null);
let chartInstance: ECharts | null = null;
//
const ITEMS_PER_PAGE = 5; // 5
const currentPage = ref(1);
//
const chartData = ref<Array<{ name: string; dataXy: Array<[number, number]> }>>([]);
// inactive
const legendInactiveSet = ref<Set<string>>(new Set());
// Y1=2=
const type = ref(1);
// 12稿
const monthColors: Record<string, string> = {
'1月': '#5B9BD5',
'2月': '#70AD47',
'3月': '#7030A0',
'4月': '#ED7D31',
'5月': '#A5A5A5',
'6月': '#FFC000',
'7月': '#4472C4',
'8月': '#548235',
'9月': '#6F35A0',
'10月': '#C55A11',
'11月': '#8497B0',
'12月': '#7F7F7F'
//
const colors = ref<string[]>([]);
const selected = ref<Record<string, boolean>>({});
//
const dataLoading = ref(false);
// ==================== ====================
const fetchChartData = async (stcd: string, year: string | number) => {
if (loading.value) return; //
loading.value = true; // loading
try {
const params = {
filter: {
logic: "and",
filters: [
{
field: "year",
operator: "eq",
dataType: "string",
value: String(year)
},
{
field: "stcd",
operator: "eq",
dataType: "string",
value: stcd
}
]
},
sort: [
{
field: "depth",
dir: "asc"
}
]
};
// 12
const allLegendItems = ref(
Object.keys(monthColors).map(month => ({
name: month,
color: monthColors[month]
}))
const res = await getCxswList(params);
const aggregates = res?.data?.aggregates;
if (aggregates) {
//
chartData.value = transformAggregatesToChartData(aggregates);
// 12 chartData.value.length
const { colors: newColors, selected: newSelected } = generateColorsAndVisibility(
12, // 12
year
);
colors.value = newColors;
selected.value = newSelected;
//
const totalPages = computed(() => {
return Math.ceil(allLegendItems.value.length / ITEMS_PER_PAGE);
});
//
const currentLegendItems = computed(() => {
const start = (currentPage.value - 1) * ITEMS_PER_PAGE;
const end = start + ITEMS_PER_PAGE;
return allLegendItems.value.slice(start, end);
});
// -
const depths = [0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80];
//
const generateMonthData = (monthIndex: number) => {
const baseSurfaceTemp = 2 + monthIndex * 2.2;
const deepWaterTemp = 3.5 + monthIndex * 0.3;
return depths.map((depth, index) => {
let temp;
if (depth <= 10) {
temp = baseSurfaceTemp - depth * 0.15 + Math.sin(index * 0.8) * 0.5;
} else if (depth <= 40) {
const middleFactor = (depth - 10) / 30;
temp = baseSurfaceTemp - 1.5 - middleFactor * (baseSurfaceTemp - deepWaterTemp) * 0.6;
// showemit
if (chartData.value.length > 0 && chartData.value.some(item => item.dataXy.length > 0)) {
showemit.value = true; //
} else {
temp = deepWaterTemp + Math.sin(index * 0.3) * 0.2;
showemit.value = false; //
}
return Math.max(0, Math.round(temp * 100) / 100);
});
};
// [, ]
const monthData = ref(
Array.from({ length: 12 }, (_, i) => ({
month: `${i + 1}`,
data: generateMonthData(i).map((temp, idx) => [temp, depths[idx]]) // [, ]
}))
);
// /
const toggleLegend = (name: string) => {
if (legendInactiveSet.value.has(name)) {
legendInactiveSet.value.delete(name);
//
await nextTick();
updateChart();
} else {
legendInactiveSet.value.add(name);
chartData.value = [];
colors.value = [];
selected.value = {};
showemit.value = false; //
}
updateChart();
};
//
const prevPage = () => {
if (currentPage.value > 1) {
currentPage.value--;
updateChart();
} catch (error) {
// console.error(':', error);
chartData.value = [];
showemit.value = false; //
} finally {
loading.value = false; // loading
}
};
//
const nextPage = () => {
if (currentPage.value < totalPages.value) {
currentPage.value++;
updateChart();
}
};
// ==================== ====================
//
const initChart = () => {
if (!chartRef.value) return;
@ -155,186 +434,207 @@ const initChart = () => {
chartInstance = echarts.init(chartRef.value);
updateChart();
// (ECharts)
// chartInstance.on('legendselectchanged', (params: any) => { ... });
};
//
const updateChart = () => {
if (!chartInstance) return;
const option = {
// ===== =====
const xData: number[] = [];
const yData: any[] = [];
const legend: string[] = [];
chartData.value.forEach((item) => {
if (item.dataXy.length > 0) {
xData.push(item.dataXy[0][0]);
}
legend.push(item.name);
yData.push({
name: item.name,
data: item.dataXy,
type: 'line',
smooth: true,
connectNulls: true,
symbolSize: 2
});
});
// ===== =====
const maxy1 = arrMax(xData);
const miny1 = arrMin(xData);
const y1 = chartMax(
5,
miny1 === maxy1 ? maxy1 * 1.1 : maxy1,
miny1 === maxy1 ? miny1 * 0.9 : miny1,
true,
0
);
// X 0
const curMin = 0;
// ===== =====
const option: any = {
tooltip: {
trigger: 'item',
backgroundColor: 'rgba(255, 255, 255, 0.95)',
borderColor: '#ccc',
borderWidth: 1,
textStyle: {
color: '#333',
fontSize: 12
},
show: true,
formatter: (params: any) => {
if (!params || !params.seriesName) return '';
const temperature = params.data[0]; //
const depth = params.data[1]; //
const temperature = params.data[0];
const depth = params.data[1];
return `<div style="font-weight:bold;margin-bottom:8px;">${params.seriesName}</div>
<div>水温(°C)${temperature}</div>
<div>深度(m)${depth}</div>`;
return `<span>${params.seriesName}</span><br />
<span>水温()${temperature}</span><br />
<span>深度(m)${depth}</span>`;
}
},
color: colors.value,
grid: {
right: '15%',
bottom: '10%'
},
legend: {
show: true,
data: allLegendItems.value.map(item => ({
name: item.name,
icon: 'circle'
})),
selected: allLegendItems.value.reduce((acc, item) => {
acc[item.name] = !legendInactiveSet.value.has(item.name);
return acc;
}, {} as Record<string, boolean>),
// ECharts使
top: -9999
type: 'scroll',
data: legend,
selected: selected.value,
textStyle: {
align: 'middle' as const
},
grid: {
left: '70px',
right: '100px',
bottom: '60px',
top: '30px'
top: '1%',
left: 'center' as const
},
xAxis: {
type: 'value',
position: 'top',
name: '水温(°C)',
nameLocation: 'end',
nameGap: 20,
nameTextStyle: {
color: '#666',
fontSize: 12,
align: 'right',
verticalAlign: 'top'
},
min: 0,
max: 8,
interval: null,
axisLine: {
show: true,
lineStyle: {
color: '#d0d0d0',
width: 1
}
},
type: 'value' as const,
boundaryGap: false,
position: 'top' as const,
nameLocation: 'end' as const,
axisTick: {
show: true,
length: 4,
inside: false,
lineStyle: {
color: '#d0d0d0'
}
},
axisLabel: {
color: '#666',
fontSize: 11,
margin: 6,
formatter: (value: number) => {
const showTicks = [0, 3, 6, 8];
if (showTicks.includes(value)) {
return value.toString();
}
return '';
}
show: true
},
interval: y1.scale,
splitLine: {
show: false
}
},
yAxis: {
type: 'value',
position: 'left',
inverse: true,
type: 'value' as const,
name: type.value === 2 ? '高程(m)' : '水深(m)',
min: 0,
max: 80,
interval: 20,
name: '水深(m)',
nameLocation: 'end',
nameGap: 25,
nameRotate: 0,
nameTextStyle: {
color: '#666',
fontSize: 12,
align: 'center',
verticalAlign: 'top'
scale: false, // Y 0
nameGap: type.value === 2 ? 22 : 10,
inverse: type.value !== 2
},
axisLine: {
show: true,
lineStyle: {
color: '#d0d0d0',
width: 1
}
},
axisTick: {
show: true,
length: 3,
inside: false,
lineStyle: {
color: '#d0d0d0'
}
},
axisLabel: {
color: '#666',
fontSize: 11,
inside: false,
margin: 8
},
splitLine: {
show: true,
lineStyle: {
color: '#e5e5e5',
type: 'dashed',
width: 1
}
}
},
series: monthData.value.map((monthItem: any) => ({
name: monthItem.month,
type: 'line',
smooth: true,
symbol: 'circle',
symbolSize: 2,
lineStyle: {
width: 1,
color: monthColors[monthItem.month]
},
itemStyle: {
color: monthColors[monthItem.month]
},
data: monthItem.data.map((item: any) => [item[0], item[1]]),
connectNulls: false
}))
dataZoom: [{ type: 'inside' as const }],
series: yData
};
option.xAxis.min = curMin;
chartInstance.setOption(option, { notMerge: true });
};
//
const handleChartClick = (params: any) => {
if (!params || !params.seriesName) return;
const matchMonth = params.seriesName.match(/\d+/);
const monthNum = matchMonth ? parseInt(matchMonth[0]) : 1;
const year = typeof datetimePicker.value.value === 'string'
? parseInt(datetimePicker.value.value)
: datetimePicker.value.value;
const startTime = `${year}-${String(monthNum).padStart(2, '0')}-01 00:00:00`;
const lastDay = new Date(year, monthNum, 0).getDate();
const endTime = `${year}-${String(monthNum).padStart(2, '0')}-${lastDay} 23:59:59`;
const currentStcd = select.value.value;
let currentStnm = '';
select.value.options.forEach((station: any) => {
station.children?.forEach((child: any) => {
if (child.value === currentStcd) {
currentStnm = child.title;
}
});
});
import('@/store/modules/model').then(({ useModelStore }) => {
import('@/components/MapModal/setting.config').then(({ handleTabs }) => {
const modelStore = useModelStore();
modelStore.params = {
sttp: 'WT',
stcd: currentStcd,
titleName: currentStnm,
enfc: '1',
timeRange: [startTime, endTime]
} as any;
modelStore.title = currentStnm ? `${currentStnm} 详情信息` : '详情信息';
modelStore.tabList = handleTabs(modelStore.params);
modelStore.modalVisible = true;
});
});
};
// ==================== ====================
onMounted(() => {
initChart();
//
window.addEventListener('resize', () => {
chartInstance?.resize();
});
});
//
onBeforeUnmount(() => {
chartInstance?.dispose();
window.removeEventListener('resize', () => {
chartInstance?.resize();
});
});
watch(
() => JidiSelectEventStore.selectedItem,
async (newVal) => {
if (!newVal || !newVal.wbsCode) {
return;
}
baseid.value = newVal.wbsCode;
await selectOptions();
},
{ deep: true, immediate: true }
);
// //
// watch(
// () => [select.value.value, datetimePicker.value.value],
// async ([newStcd, newYear]) => {
// if (newStcd && newYear) {
// await fetchChartData(newStcd, newYear);
// }
// },
// { deep: true }
// );
const handlePanelChange1 = (data: any) => {
console.log('当前所有控件状态:', data);
if (data.moreSelect && data.datetime) {
fetchChartData(data.moreSelect, data.datetime)
} else {
showemit.value = true
chartData.value = [];
colors.value = [];
selected.value = {};
showemit.value = false;
}
};
</script>
<style scoped>
@ -345,80 +645,16 @@ onBeforeUnmount(() => {
flex-direction: column;
}
.chart-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 8px 12px;
background: rgba(255, 255, 255, 0.9);
border-bottom: 1px solid #e8e8e8;
}
.legend-container {
display: flex;
flex-wrap: wrap;
gap: 12px;
flex: 1;
}
.legend-item {
display: flex;
align-items: center;
gap: 6px;
cursor: pointer;
transition: opacity 0.3s;
}
.legend-item.inactive {
opacity: 0.5;
}
.legend-dot {
width: 10px;
height: 10px;
border-radius: 50%;
display: inline-block;
}
.legend-text {
font-size: 12px;
user-select: none;
}
.pagination {
display: flex;
align-items: center;
gap: 8px;
}
.page-btn {
cursor: pointer;
padding: 2px 8px;
border: 1px solid #d9d9d9;
border-radius: 4px;
font-size: 12px;
user-select: none;
transition: all 0.3s;
}
.page-btn:hover:not(.disabled) {
border-color: #1890ff;
color: #1890ff;
}
.page-btn.disabled {
cursor: not-allowed;
opacity: 0.5;
}
.page-info {
font-size: 12px;
color: #666;
}
.chart-container {
flex: 1;
width: 100%;
min-height: 300px;
min-height: 252px !important;
display: flex;
justify-content: center;
align-items: center;
}
:deep(.ant-spin-nested-loading) {
height: 252px !important;
}
</style>

View File

@ -1,227 +1,280 @@
<!-- SidePanelItem.vue -->
<!--
出入库水温图表组件
功能展示水库入库水温和出库水温的时序变化曲线
数据来源inOutOneGetKendoListCust接口
特性支持月份选择站点筛选数据缩放空值断开显示
-->
<template>
<SidePanelItem title="出入库水温">
<div ref="chartContainer" class="chart-container"></div>
<SidePanelItem title="出入库水温" :moreSelect="select" :scopeDate="scopeDate" @update-values="handlePanelChange1">
<!-- ECharts图表容器 -->
<a-spin :spinning="loading" tip="加载中...">
<div v-show="showemit && !loading" ref="chartContainer" 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, onUnmounted } from 'vue';
import { ref, onMounted, onUnmounted, watch } from 'vue';
import * as echarts from 'echarts';
import dayjs from 'dayjs';
import SidePanelItem from '@/components/SidePanelItem/index.vue';
import { getVmsstbprpt, inOutOneGetKendoListCust } from '@/api/sw';
import { useJidiSelectEventStore } from "@/store/modules/jidiSelectEvent";
// ==================== ====================
// (便)
defineOptions({
name: 'churukushuiwenMod'
});
const chartContainer = ref<HTMLDivElement | null>(null);
let chartInstance: echarts.ECharts | null = null;
// ==================== ====================
const JidiSelectEventStore = useJidiSelectEventStore(); // Store
//
onMounted(() => {
// , DOM
setTimeout(() => {
if (chartContainer.value) {
initChart();
window.addEventListener('resize', handleResize);
}
}, 100);
// ==================== DOM ====================
const chartContainer = ref<HTMLDivElement | null>(null); // DOM
let chartInstance: echarts.ECharts | null = null; // ECharts
const currentMonth = new Date().toISOString().slice(0, 7); // (: YYYY-MM)
const chartDataLoaded = ref(false); // ,
// ==================== ====================
const showemit = ref(false); //
const loading = ref(false); //
// ==================== () ====================
const select = ref({
show: true, //
value: '', // ()
options: [], //
picker: undefined, // (使)
format: undefined, // (使)
});
onUnmounted(() => {
if (chartInstance) {
chartInstance.dispose();
chartInstance = null;
}
window.removeEventListener('resize', handleResize);
// ==================== ====================
const scopeDate = ref({
show: true, //
value: [currentMonth, currentMonth] as any, // 使 as any
format: "YYYY-MM", //
picker: "month" as const, // :
options: [], // (使)
});
const handleResize = () => {
if (chartInstance) {
chartInstance.resize();
}
};
// ==================== ====================
const baseid = ref(''); // ID
const initChart = () => {
if (!chartContainer.value) return;
// , 0
const containerHeight = chartContainer.value.offsetHeight;
if (containerHeight === 0) {
setTimeout(() => initChart(), 100);
return;
}
chartInstance = echarts.init(chartContainer.value);
const option = {
tooltip: {
trigger: 'axis',
backgroundColor: 'rgba(50, 50, 50, 0.9)',
borderColor: 'transparent',
textStyle: {
color: '#fff',
fontSize: 14
},
axisPointer: {
type: 'line',
lineStyle: {
color: 'rgba(91, 143, 249, 0.15)',
width: 30,
type: 'solid'
}
},
formatter: function(params: any) {
if (!params || params.length === 0) return '';
const date = params[0].name;
let result = `<div style="font-weight: bold; margin-bottom: 8px; font-size: 14px;">${date}</div>`;
params.forEach((item: any) => {
result += `<div style="display: flex; align-items: center; gap: 6px; margin: 4px 0; font-size: 14px;">
<span style="display: inline-block; width: 10px; height: 10px; border-radius: 50%; background-color: ${item.color};"></span>
<span>${item.seriesName}</span>
<span style="font-weight: bold; margin-left: auto;">${item.value}°C</span>
</div>`;
});
return result;
}
},
title: {
text: '水温(°C)',
left: 17,
top: 13,
textStyle: {
fontSize: 13,
color: '#000000',
fontWeight: 'normal'
}
},
legend: {
data: ['入库水温', '出库水温'],
top: 13,
left: 'center',
itemWidth: 10,
itemHeight: 10,
itemGap: 20,
textStyle: {
fontSize: 13,
color: '#000000'
}
},
grid: {
left: '10%',
right: '5%',
top: '15%',
bottom: '18%'
},
xAxis: {
type: 'category',
data: ['2026-04-01', '2026-04-02', '2026-04-03', '2026-04-04', '2026-04-05', '2026-04-06', '2026-04-07'],
boundaryGap: true,
axisLine: {
show: true,
lineStyle: {
color: '#000000'
}
},
axisTick: {
show: false
},
axisLabel: {
color: '#000000',
fontSize: 13,
margin: 10,
interval: 2
},
splitLine: {
show: true,
lineStyle: {
color: '#bfbfbf',
type: 'solid'
}
}
},
dataZoom: [
// ==================== () ====================
const jiDiList: any = ref([
{
type: 'inside',
start: 0,
end: 100,
minValueSpan: 2,
zoomOnMouseWheel: true,
moveOnMouseMove: true,
moveOnMouseWheel: false,
zoomLock: false,
throttle: 50
}
],
yAxis: {
type: 'value',
min: 5.2,
max: 5.9,
interval: 0.1,
axisLine: {
show: true,
lineStyle: {
color: '#000000',
width: 1
}
},
axisTick: {
show: true,
lineStyle: {
color: '#000000',
width: 1
},
length: 4
},
axisLabel: {
color: '#000000',
fontSize: 12,
formatter: '{value}'
},
splitLine: {
show: true,
lineStyle: {
color: '#bfbfbf',
type: 'solid'
}
}
},
series: [
{
name: '入库水温',
type: 'line',
smooth: true,
symbol: 'circle',
symbolSize: 6,
lineStyle: {
width: 2,
color: '#4b79ab'
},
itemStyle: {
color: '#4b79ab'
},
data: [5.4, 5.3, 5.6, 5.4, 5.5, 5.6, 5.6]
"baseid": "all",
"basename": "当前全部",
"id": "9BFEC848-83EA-AD22-6DE2-10E969476693"
},
{
name: '出库水温',
type: 'line',
smooth: true,
symbol: 'circle',
symbolSize: 6,
lineStyle: {
width: 2,
color: '#78c300'
"baseid": "01",
"basename": "金沙江干流",
"id": "A33040B7-8977-D9F1-5E02-D7F0241AB8AA"
},
itemStyle: {
color: '#78c300'
{
"baseid": "02",
"basename": "雅砻江干流",
"id": "C63D6020-995D-FE97-2F4A-F619A7142C79"
},
data: [5.8, 5.8, 5.8, 5.8, 5.8, 5.8, 5.8]
{
"baseid": "03",
"basename": "大渡河干流",
"id": "CCB0766B-F1D4-7DD6-650F-5C556B7231B3"
},
{
"baseid": "04",
"basename": "乌江干流",
"id": "E8A66641-B4F4-CC85-0815-38C5B2F93DBD"
},
{
"baseid": "05",
"basename": "长江上游干流",
"id": "E02C11E9-CEA5-A030-202F-3BFE84465D03"
},
{
"baseid": "10",
"basename": "湘西",
"id": "B1D1D52D-CEEF-6DC4-27DF-9990EB572F8D"
},
{
"baseid": "08",
"basename": "黄河上游干流",
"id": "BC6AF135-263D-09A9-D5CA-C99B2598FE6E"
},
{
"baseid": "09",
"basename": "黄河中游干流",
"id": "60EEEC28-128F-A2A8-44F3-6EAAC8FD8BB6"
},
{
"baseid": "06",
"basename": "南盘江-红水河",
"id": "7BB9A8F4-34B5-42B4-A7FC-CE910AD7F203"
},
{
"baseid": "12",
"basename": "东北",
"id": "47F8EF06-924E-E161-FCAF-62A66BBF252D"
},
{
"baseid": "07",
"basename": "澜沧江干流",
"id": "A966A4C9-278C-B0DA-2B97-2D10B7A6E96A"
},
{
"baseid": "13",
"basename": "怒江干流",
"id": "FA89E8CB-67A8-76DA-DC1E-23AE4C54F9E4"
},
{
"baseid": "11",
"basename": "闽浙赣",
"id": "CD98F995-EEB2-1021-D807-DA1B1AD9E49A"
},
{
"baseid": "other",
"basename": "其他",
"id": "AFBDFC67-B955-4EFD-959A-014CFB59EBFC"
}
])
// ==================== ====================
/**
* 转换图表数据格式
* 将后端返回的扁平数据结构转换为ECharts所需的双线数据格式
* @param rawData - 原始数据数组,包含tm(时间)dwtp(类型:IWT/DWT)wt(水温)字段
* @returns 包含日期数组入库水温数组出库水温数组的对象
*/
function transformChartData(rawData: any[]) {
// 1.
const dateSet = new Set<string>()
rawData.forEach(item => {
const date = item.tm?.split(' ')[0] // "2026-04-01 00:00:00" -> "2026-04-01"
if (date) dateSet.add(date)
})
const dates = Array.from(dateSet).sort()
// 2. 线()
const iwtData: (number | null)[] = [] // (Inlet Water Temperature)
const dwtData: (number | null)[] = [] // (Discharge Water Temperature)
dates.forEach(date => {
// (dwtp='IWT')
const iwtItem = rawData.find(item =>
item.tm?.startsWith(date) && item.dwtp === 'IWT'
)
// ,null(线)
iwtData.push(iwtItem ? Number(iwtItem.wt.toFixed(1)) : null)
// (dwtp='DWT')
const dwtItem = rawData.find(item =>
item.tm?.startsWith(date) && item.dwtp === 'DWT'
)
// ,null(线)
dwtData.push(dwtItem ? Number(dwtItem.wt.toFixed(1)) : null)
})
return { dates, iwtData, dwtData }
}
/**
* 计算Y轴显示范围
* 基于数据最小值和最大值,上下各扩展0.2的缓冲空间
* @param data - 水温数据数组(可能包含null值)
* @returns Y轴的最小值和最大值对象
*/
function calculateYAxisRange(data: (number | null)[]) {
//
const validValues = data.filter(v => v !== null) as number[]
if (validValues.length === 0) return { min: 0, max: 10 } //
const min = Math.min(...validValues)
const max = Math.max(...validValues)
// 0.2,线
return {
min: Number((min - 0.2).toFixed(1)), // 0.2
max: Number((max + 0.2).toFixed(1)) // 0.2
}
}
/**
* 计算X轴刻度间隔
* 确保最多显示4个刻度标签,避免标签重叠
* @param dateCount - 日期总数
* @returns X轴刻度间隔值(interval属性)
*/
function calculateXAxisInterval(dateCount: number): number {
if (dateCount <= 4) return 0 // 4,
return Math.ceil(dateCount / 4) - 1 // ,4
}
const paramsOne = ref({
value: '',
tm: [currentMonth, currentMonth]
})
/**
* 获取图表数据
* 调用后端接口获取出入库水温数据,并触发图表更新
*/
async function fetchChartData() {
if (loading.value) return; //
loading.value = true; // loading
try {
//
const startDate = paramsOne.value.tm[0] + '-01 00:00:00';
const endDate = dayjs(paramsOne.value.tm[1]).endOf('month').format('YYYY-MM-DD') + ' 23:59:59';
// (,)
const params = {
filter: {
logic: "and",
filters: [
{
// OR:
logic: "or",
filters: [
{ field: "engDwtCode", operator: "eq", value: paramsOne.value.value }, //
{ field: "engIwtCode", operator: "eq", value: paramsOne.value.value } //
]
};
},
//
{ field: "dt", operator: "gte", dataType: "date", value: startDate },
{ field: "dt", operator: "lte", dataType: "date", value: endDate }
]
}
}
chartInstance.setOption(option);
// API
const res = await inOutOneGetKendoListCust(params)
const rawData = res?.data?.data || []
console.log('原始数据:', rawData)
// showemit
if (rawData.length > 0) {
showemit.value = true; //
//
const { dates, iwtData, dwtData } = transformChartData(rawData)
console.log('转换后的数据:', { dates, iwtData, dwtData })
//
setTimeout(() => {
if (!chartInstance) {
initChartWithData(dates, iwtData, dwtData); //
} else {
updateChart(dates, iwtData, dwtData, 0); //
}
// ,
setTimeout(() => {
@ -229,12 +282,455 @@ const initChart = () => {
chartInstance.resize();
}
}, 50);
}, 50);
} else {
showemit.value = false; //
}
chartDataLoaded.value = true; //
} catch (error) {
console.error('获取图表数据失败:', error);
showemit.value = false; //
chartDataLoaded.value = true; // 使,
} finally {
loading.value = false; // loading
}
}
/**
* 带数据初始化图表
* @param dates - 日期数组
* @param iwtData - 入库水温数据
* @param dwtData - 出库水温数据
*/
const initChartWithData = (dates: string[], iwtData: (number | null)[], dwtData: (number | null)[]) => {
if (!chartContainer.value) return;
const containerHeight = chartContainer.value.offsetHeight;
if (containerHeight === 0) {
setTimeout(() => initChartWithData(dates, iwtData, dwtData), 100);
return;
}
chartInstance = echarts.init(chartContainer.value);
updateChart(dates, iwtData, dwtData, 0);
};
/**
* 更新图表配置
* 根据处理后的数据动态生成ECharts配置项
* @param dates - 日期数组(X轴数据)
* @param iwtData - 入库水温数据数组
* @param dwtData - 出库水温数据数组
* @param yAxisMax - 废弃参数,保留以兼容旧代码
*/
function updateChart(dates: string[], iwtData: (number | null)[], dwtData: (number | null)[], yAxisMax: number) {
if (!chartInstance) {
console.warn('图表实例未初始化')
return
}
try {
// ==================== ====================
const xAxisInterval = calculateXAxisInterval(dates.length) // X
const { min: yAxisMin, max: yAxisMaxValue } = calculateYAxisRange([...iwtData, ...dwtData]) // Y
console.log('X轴间隔:', xAxisInterval, 'Y轴范围:', { min: yAxisMin, max: yAxisMaxValue })
// ==================== ECharts ====================
const option = {
//
tooltip: {
trigger: 'axis', // :
backgroundColor: 'rgba(50, 50, 50, 0.9)', //
borderColor: 'transparent', //
textStyle: {
color: '#fff', //
fontSize: 14 //
},
axisPointer: {
type: 'line', // :线
lineStyle: {
color: 'rgba(91, 143, 249, 0.15)', // 线
width: 30, // 线
type: 'solid' // 线:线
}
},
//
formatter: function (params: any) {
if (!params || params.length === 0) return '';
const date = params[0].name; //
let result = `<div style="font-weight: bold; margin-bottom: 8px; font-size: 14px;">${date}</div>`;
params.forEach((item: any) => {
// '-',
const value = item.value !== null && item.value !== undefined ? `${item.value}°C` : '-'
result += `<div style="display: flex; align-items: center; gap: 6px; margin: 4px 0; font-size: 14px;">
<span style="display: inline-block; width: 10px; height: 10px; border-radius: 50%; background-color: ${item.color};"></span>
<span>${item.seriesName}</span>
<span style="font-weight: bold; margin-left: auto;">${value}</span>
</div>`;
});
return result;
}
},
//
title: {
text: '水温(°C)', //
left: 17, //
top: 13, //
textStyle: {
fontSize: 13,
color: '#000000',
fontWeight: 'normal'
}
},
//
legend: {
data: ['入库水温', '出库水温'], //
top: 13, //
left: 'center', //
itemWidth: 10, //
itemHeight: 10, //
itemGap: 20, //
textStyle: {
fontSize: 13,
color: '#000000'
}
},
// ()
grid: {
left: '10%',
right: '5%',
top: '15%',
bottom: '10%'
},
// X
xAxis: {
type: 'category', //
data: dates, //
boundaryGap: true, //
interval: xAxisInterval, // ,4
axisLine: {
show: true,
lineStyle: {
color: '#000000' // 线
}
},
axisTick: {
show: false // 线
},
axisLabel: {
color: '#000000', //
fontSize: 13, //
margin: 10 // 线
},
splitLine: {
show: true, // 线
lineStyle: {
color: '#bfbfbf', // 线
type: 'solid' // 线
}
}
},
// ()
dataZoom: [
{
type: 'inside', //
start: 0, // 0%
end: 100, // 100%
minValueSpan: 2, // 2
zoomOnMouseWheel: true, //
moveOnMouseMove: true, //
moveOnMouseWheel: false, //
zoomLock: false, //
throttle: 50 // 50ms
}
],
// Y
yAxis: {
type: 'value', //
min: yAxisMin, // (-0.2)
max: yAxisMaxValue, // (+0.2)
scale: true, // ,0
axisLine: {
show: true,
lineStyle: {
color: '#000000',
width: 1
}
},
axisTick: {
show: true, // 线
lineStyle: {
color: '#000000',
width: 1
},
length: 4 // 线
},
axisLabel: {
color: '#000000',
fontSize: 12,
formatter: '{value}' //
},
splitLine: {
show: true, // 线
lineStyle: {
color: '#bfbfbf',
type: 'solid'
}
}
},
// (线)
series: [
{
name: '入库水温', //
type: 'line', // :线
smooth: true, // 线
symbol: 'circle', // :
symbolSize: 4, //
connectNulls: true, // :,线
lineStyle: {
width: 2, // 线
color: '#4b79ab' // 线:
},
itemStyle: {
color: '#4b79ab' //
},
data: iwtData //
},
{
name: '出库水温', //
type: 'line', // :线
smooth: true, // 线
symbol: 'circle', // :
symbolSize: 4, //
connectNulls: true, // ,
lineStyle: {
width: 2, // 线
color: '#78c300' // 线:绿
},
itemStyle: {
color: '#78c300' //
},
data: dwtData //
}
]
};
//
chartInstance.setOption(option, true); // true
console.log('图表更新成功')
// ,
setTimeout(() => {
if (chartInstance) {
chartInstance.resize(); //
}
}, 50);
} catch (error) {
console.error('更新图表失败:', error)
}
}
// ==================== ====================
/**
* 组件挂载时执行
* 初始化图表并获取数据
*/
onMounted(() => {
// 100ms,DOM
setTimeout(() => {
if (chartContainer.value) {
// fetchChartData(); //
window.addEventListener('resize', handleResize); //
}
}, 100);
});
/**
* 组件卸载时执行
* 清理资源,防止内存泄漏
*/
onUnmounted(() => {
if (chartInstance) {
chartInstance.dispose(); //
chartInstance = null;
}
window.removeEventListener('resize', handleResize); //
});
/**
* 窗口大小变化处理函数
* 自适应调整图表尺寸
*/
const handleResize = () => {
if (chartInstance) {
chartInstance.resize(); //
}
};
// ==================== ====================
/**
* SidePanelItem面板变化回调
* 处理下拉框和日期选择器的值变化
* @param data - 包含moreSelect(下拉框值)和datetime(日期范围)的对象
*/
const handlePanelChange1 = (data: any) => {
// console.log(':', data);
// debugger
// TODO:
if (data.moreSelect) {
paramsOne.value.value = data.moreSelect;
}
paramsOne.value.tm = data.scopeDate
fetchChartData()
};
/**
* 获取站点下拉选项数据
* 根据选中的基地ID查询对应的站点列表
*/
const getselsectData = async () => {
loading.value = true; //
try {
let params = {
"filter": {
"logic": "and",
"filters": [
// baseid'all',
baseid.value != 'all' ? {
"field": "baseId",
"operator": "eq",
"dataType": "string",
"value": baseid.value
} : null,
// :ENG
{
"field": "sttpCode",
"operator": "contains",
"dataType": "string",
"value": "ENG"
},
// (dtin=1)
{
"field": "dtin",
"operator": "eq",
"dataType": "string",
"value": 1
}
].filter(Boolean) // null
},
"sort": [
// 'all',;
baseid.value == 'all' ? {
"field": "baseStepSort",
"dir": "asc"
} : null,
{
"field": "rvcdStepSort",
"dir": "asc"
},
{
"field": "siteStepSort",
"dir": "asc"
}
].filter(Boolean), // null
"select": [
"stcd", //
"stnm", //
"lgtd", //
"lttd", //
"siteStepSort", //
"baseId", // ID
"baseName" //
]
}
// API
const res = await getVmsstbprpt(params)
console.log('res', res)
const data = res?.data?.data || res?.data
const obj: any = {}
if (data) {
// ()
data.map((e) => {
if (obj[e.baseId]) {
// ,
obj[e.baseId] = { ...obj[e.baseId], children: [...obj[e.baseId].children, { ...e, value: e.stcd, title: e.stnm }] }
} else {
//
obj[e.baseId] = { value: e.baseId, title: e.baseName, selectable: false, key: e.baseId, children: [{ ...e, value: e.stcd, title: e.stnm }] }
}
})
// jiDiList
let treeData: any = []
const dataMapNameArr: any = Object.entries(obj).map(([, v]) => v)
treeData = dataMapNameArr?.sort((a: any, b: any) => {
const indexA = jiDiList.value.findIndex(item => item.baseid === a.value);
const indexB = jiDiList.value.findIndex(item => item.baseid === b.value);
return indexA - indexB;
})
console.log('treeData', treeData)
//
select.value.options = treeData
//
if (baseid.value == 'all') {
select.value.value = '008640203800000001' //
} else {
select.value.value = treeData?.[0]?.children[0]?.value //
}
}
} catch (error) {
console.error('获取站点数据失败:', error);
} finally {
loading.value = false; // loading
}
}
// ==================== Watch ====================
/**
* 监听基地选择变化
* 当用户切换基地时,重新加载站点下拉选项
*/
watch(
() => JidiSelectEventStore.selectedItem, // Store
(newVal) => {
baseid.value = newVal.wbsCode; // ID
getselsectData() //
},
{ deep: true, immediate: true } // ,
);
</script>
<style lang="scss" scoped>
/* 图表容器样式 */
.chart-container {
width: 100%;
height: 252px;
display: flex;
justify-content: center;
align-items: center;
/* 固定高度252px */
}
:deep(.ant-spin-nested-loading ){
height: 252px !important;
}
</style>

View File

@ -1,43 +1,40 @@
<!-- SidePanelItem.vue -->
<template>
<SidePanelItem title="水温监测工作开展情况">
<a-spin :spinning="loading" tip="加载中...">
<div class="facility-grid">
<div v-for="facility in facilities" :key="facility.name" class="facility-card">
<div
style="
<div style="
width: 60px;
height: 62px;
display: flex;
align-items: center;
justify-content: center;
"
>
">
<div class="facility-icon">
<i
style="color: #fff"
:class="facility.icon"
type="icon-shengtailiuliang2"
></i>
<i style="color: #fff" :class="facility.icon" type="icon-shengtailiuliang2"></i>
</div>
</div>
<div class="facility-info">
<div class="facility-name">{{ facility.name }}</div>
<div style="font-size: 16px">
<span class="facility-count">{{ facility.count }}</span
><span></span>
<span class="facility-count">{{ facility.count }}</span><span></span>
</div>
</div>
</div>
</div>
</a-spin>
</SidePanelItem>
</template>
<script lang="ts" setup>
import { ref, onMounted } from "vue";
import { ref, onMounted, watch } from "vue";
import SidePanelItem from "@/components/SidePanelItem/index.vue";
import { getKendoListCust } from "@/api/sw";
import { getKendoListCust, baseEvnmAutoMonitorGetKendoListCust } from "@/api/sw";
import { useJidiSelectEventStore } from "@/store/modules/jidiSelectEvent";
const JidiSelectEventStore = useJidiSelectEventStore();
// 便
defineOptions({
name: "shuiwenjiancegongzuokaizhangqingkuang",
@ -47,34 +44,72 @@ defineOptions({
const facilities = ref([
{
name: "表层水温",
count: 145,
count: 0,
icon: "icon iconfont icon-shuizhijiancezhan",
},
{
name: "垂向水温",
count: 24,
count: 0,
icon: "icon iconfont icon-diwenshuijianhuan",
},
]);
// Loading
const loading = ref(false);
const init = async () => {
//
if (loading.value) return;
try {
loading.value = true;
const params = {
filter: {
logic: "and",
filters: [
{ field: "rvcd", operator: "eq", dataType: "string", value: "SJLY1U" },
{ field: "tm", operator: "gte", dataType: "date", value: "2026-04-17 00:00:00" },
{ field: "tm", operator: "lte", dataType: "date", value: "2026-05-17 23:00:00" },
],
"filter": {
"logic": "and",
"filters": [
{
"field": "showIds",
"operator": "in",
"value": [
"26",
"27"
]
},
sort: [{ field: "sort", dir: "asc" }],
};
await getKendoListCust(params);
baseid.value != 'all' ? {
"field": "baseId",
"operator": "eq",
"value": baseid.value
}:null
].filter(Boolean)
}
}
let res = await baseEvnmAutoMonitorGetKendoListCust(params);
console.log(res);
let data = res.data.data || res.data
data.forEach((item: any, index: any) => {
facilities.value[index].count = item.cnt
});
} catch (error) {
console.error('获取水温监测数据失败:', error);
} finally {
loading.value = false;
}
};
//
onMounted(() => {
// init();
init();
});
const baseid = ref("")
watch(
() => JidiSelectEventStore.selectedItem,
(newVal) => {
baseid.value = newVal.wbsCode;
init();
},
{ deep: true, immediate: true }
);
</script>
<style lang="scss" scoped>

View File

@ -1,47 +1,139 @@
<!-- SidePanelItem.vue -->
<template>
<SidePanelItem
title="沿程水温变化"
:prompt="prompts"
:select="select"
:datetimePicker="datetimePicker"
>
<div ref="chartRef" class="chart-container"></div>
<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 } from "vue";
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 } from "@/api/sw";
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";
// 便
defineOptions({
name: "yanchengshuiwenChangeMod",
});
//
const select = ref({
//
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: undefined,
options: [],
picker: undefined,
format: undefined,
});
//
const datetimePicker = ref({
show: true,
value: "2026-05-15 15",
format: "YYYY-MM-DD HH",
picker: "date" as const,
options: [],
});
const prompts = ref({
show: true,
value: "注:最新数据时间为2026-04-08 23",
});
value: `注:最新数据时间为${dataOne.value || getCurrentHourTime()}`,
}));
const jiDiList: any = ref([
{
"baseid": "all",
"basename": "当前全部",
"id": "9BFEC848-83EA-AD22-6DE2-10E969476693"
},
{
"baseid": "01",
"basename": "金沙江干流",
"id": "A33040B7-8977-D9F1-5E02-D7F0241AB8AA"
},
{
"baseid": "02",
"basename": "雅砻江干流",
"id": "C63D6020-995D-FE97-2F4A-F619A7142C79"
},
{
"baseid": "03",
"basename": "大渡河干流",
"id": "CCB0766B-F1D4-7DD6-650F-5C556B7231B3"
},
{
"baseid": "04",
"basename": "乌江干流",
"id": "E8A66641-B4F4-CC85-0815-38C5B2F93DBD"
},
{
"baseid": "05",
"basename": "长江上游干流",
"id": "E02C11E9-CEA5-A030-202F-3BFE84465D03"
},
{
"baseid": "10",
"basename": "湘西",
"id": "B1D1D52D-CEEF-6DC4-27DF-9990EB572F8D"
},
{
"baseid": "08",
"basename": "黄河上游干流",
"id": "BC6AF135-263D-09A9-D5CA-C99B2598FE6E"
},
{
"baseid": "09",
"basename": "黄河中游干流",
"id": "60EEEC28-128F-A2A8-44F3-6EAAC8FD8BB6"
},
{
"baseid": "06",
"basename": "南盘江-红水河",
"id": "7BB9A8F4-34B5-42B4-A7FC-CE910AD7F203"
},
{
"baseid": "12",
"basename": "东北",
"id": "47F8EF06-924E-E161-FCAF-62A66BBF252D"
},
{
"baseid": "07",
"basename": "澜沧江干流",
"id": "A966A4C9-278C-B0DA-2B97-2D10B7A6E96A"
},
{
"baseid": "13",
"basename": "怒江干流",
"id": "FA89E8CB-67A8-76DA-DC1E-23AE4C54F9E4"
},
{
"baseid": "11",
"basename": "闽浙赣",
"id": "CD98F995-EEB2-1021-D807-DA1B1AD9E49A"
},
{
"baseid": "other",
"basename": "其他",
"id": "AFBDFC67-B955-4EFD-959A-014CFB59EBFC"
}
])
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;
@ -52,9 +144,25 @@ const stationNames = ref([]);
// - (°C) null
const waterTemperatures = ref([]);
//
const currentTime = "2026-04-08 23";
// 使 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;
@ -72,6 +180,16 @@ const initChart = () => {
fontWeight: "normal",
},
},
dataZoom: [
{
type: 'inside',
start: 0,
end: 100,
zoomOnMouseWheel: true,
moveOnMouseMove: true,
filterMode: 'filter'
}
],
tooltip: {
trigger: "axis",
formatter: (params: any) => {
@ -79,7 +197,7 @@ const initChart = () => {
const data = params[0];
// null
if (data.value !== null && data.value !== undefined) {
return `${currentTime}<br/>${data.name}${data.value}°C`;
return `${currentTime.value}<br/>${data.name}${data.value}°C`;
}
}
return "";
@ -199,24 +317,38 @@ const handleResize = () => {
};
const init = async () => {
if (loading.value) return; //
loading.value = true;
try {
const params = {
filter: {
logic: "and",
filters: [
{ field: "rvcd", operator: "eq", dataType: "string", value: "SJLY1U" },
{ field: "tm", operator: "gte", dataType: "date", value: "2026-04-17 00:00:00" },
{ field: "tm", operator: "lte", dataType: "date", value: "2026-05-17 23:00:00" },
{ field: "rvcd", operator: "eq", dataType: "string", value: paramsOne.rvcd },
{ field: "tm", operator: "gte", dataType: "date", value: paramsOne.tm[0] },
{ field: "tm", operator: "lte", dataType: "date", value: paramsOne.tm[1] },
],
},
sort: [{ field: "sort", dir: "asc" }],
};
let res = await getKendoListCust(params);
stationNames.value = res.data.data
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);
waterTemperatures.value = res.data.data
waterTemperatures.value = data
.filter((item: any) => item.sttp == "2")
.map((item: any) => item.temperature);
// waterTemperatures.value = res.data.data.map((item: any) => item.temperature);
@ -244,13 +376,191 @@ const init = async () => {
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 = {}
jiDiList.value.forEach((item: any) => {
jiDiListMap[item.baseid] = item.basename
})
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] }
})
// debugger
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 = jiDiList.value.findIndex(item => item.baseid === a.value);
const indexB = jiDiList.value.findIndex(item => item.baseid === 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);
});
@ -265,5 +575,11 @@ onBeforeUnmount(() => {
.chart-container {
width: 100%;
height: 252px ;
display: flex;
justify-content: center;
align-items: center;
}
:deep(.ant-spin-nested-loading ){
height: 252px !important;
}
</style>