修改水温垂向右侧点击,添加录像视频和建设状态,bug修改

This commit is contained in:
扈兆增 2026-06-16 11:36:17 +08:00
parent f1b8ffebf1
commit ce8855d3c2
27 changed files with 5926 additions and 4181 deletions

View File

@ -91,6 +91,24 @@ export function getVideoList(data: any) {
data data
}); });
} }
// 录像视频 - 时间查询 没接
export function getNoLiveVideoYear(data: any) {
return request({
url: '/wmp-env-server/env/vd/GetKendoListCust',
method: 'post',
data
});
}
// 录像视频
export function getNoLiveVideoList(data: any) {
return request({
url: '/wmp-env-server/env/vd/runData/GetKendoListCust',
method: 'post',
data
});
}
// 实时视频单个查询 // 实时视频单个查询
export function getVideoDetail(data: any) { export function getVideoDetail(data: any) {
return request({ return request({

View File

@ -74,11 +74,6 @@ const baseColumns = [
dataIndex: 'rowId', dataIndex: 'rowId',
width: 60 width: 60
}, },
{
title: '告警设施',
dataIndex: 'stnm',
width: 220
},
{ {
title: '告警时间', title: '告警时间',
dataIndex: 'tms', dataIndex: 'tms',
@ -91,12 +86,7 @@ const baseColumns = [
width: 120 width: 120
}, },
{ {
title: '监测要素', title: '限值(m³/s)',
dataIndex: 'ysName',
width: 120
},
{
title: '判断标准',
dataIndex: 'bzz', dataIndex: 'bzz',
width: 120 width: 120
}, },

View File

@ -21,6 +21,11 @@
</div> </div>
</div> </div>
<div class="filter-row"> <div class="filter-row">
<a-select
v-model:value="timeType"
style="margin-right: 12px"
:options="timeTypeOptions"
/>
<a-range-picker <a-range-picker
v-model:value="dateRange" v-model:value="dateRange"
:format="dateFormat" :format="dateFormat"

View File

@ -158,12 +158,12 @@ const columnsMap: Record<string, any[]> = {
}, },
{ {
title: '性腺发育期', title: '性腺发育期',
dataIndex: 'fwgh', dataIndex: 'st',
width: 140 width: 140
}, },
{ {
title: '早期资源量和种类', title: '早期资源量和种类',
dataIndex: 'fwgh', dataIndex: 'ps',
width: 220 width: 220
}, },
{ {
@ -203,7 +203,7 @@ const columnsMap: Record<string, any[]> = {
}, },
{ {
title: '电导率(μS/cm)', title: '电导率(μS/cm)',
dataIndex: 'tm', dataIndex: 'cond',
width: 180 width: 180
} }
], ],

View File

@ -0,0 +1,355 @@
<template>
<div class="fish-facility-build-state">
<a-spin :spinning="loading">
<!-- 步骤条 -->
<div class="steps-container">
<div class="steps-wrapper">
<div
v-for="(item, index) in bltList"
:key="item.code"
class="step-item"
:class="{
'step-active': currentStep > item.code,
'step-current': currentStep === item.code,
'step-last': index === bltList.length - 1
}"
>
<span class="step-label">{{ item.name }}</span>
<i
v-if="currentStep > item.code"
class="step-icon icon iconfont icon-check"
></i>
<div v-if="currentStep == 0" class="step-line"></div>
</div>
</div>
</div>
<!-- 建设进度 -->
<div class="progress-container">
<span class="progress-label">建设进度</span>
<div class="progress-bar-wrapper">
<div class="progress-bar">
<div
class="progress-fill"
:style="{ width: progressPercent + '%' }"
></div>
</div>
<span class="progress-text">{{ progressText }}</span>
</div>
</div>
<!-- 日期信息表格 -->
<div class="info-table">
<table>
<tbody>
<tr>
<td class="label-cell">计划开工日期</td>
<td class="value-cell">{{ planStartDate || '-' }}</td>
<td class="label-cell">计划完工日期</td>
<td class="value-cell">{{ planEndDate || '-' }}</td>
</tr>
<tr>
<td class="label-cell">实际开工日期</td>
<td class="value-cell">{{ startDate || '-' }}</td>
<td class="label-cell">建成日期</td>
<td class="value-cell">{{ endDate || '-' }}</td>
</tr>
</tbody>
</table>
</div>
</a-spin>
</div>
</template>
<script setup lang="ts">
import { ref, watch } from 'vue';
import { useModelStore } from '@/store/modules/model';
import dayjs from 'dayjs';
import { queryPostUrlList } from '@/api/mapModal';
const modelStore = useModelStore();
const loading = ref(false);
const hasLoaded = ref(false);
const props = defineProps({
url: {
type: String,
default: ''
},
isActive: {
type: Boolean,
default: false
}
});
const bltList = ref<any[]>([]);
const currentStep = ref(0);
const progressPercent = ref(0);
const progressText = ref('0%');
const planStartDate = ref('');
const planEndDate = ref('');
const startDate = ref('');
const endDate = ref('');
const formatDate = (date: any) => {
if (!date) return '';
return dayjs(date).format('YYYY-MM-DD');
};
const getData = async () => {
if (hasLoaded.value || !props.url || !modelStore.params.stcd) return;
loading.value = true;
try {
const params = {
filter: {
logic: 'and',
filters: [
{
field: 'stcd',
operator: 'eq',
dataType: 'string',
value: modelStore.params.stcd
}
]
}
};
const res: any = await queryPostUrlList(props.url, params);
if (res && res.data) {
const data = Array.isArray(res.data.data)
? res.data.data[0]
: res.data.data;
if (data) {
bltList.value = data.bltList || [];
currentStep.value = data.blprd || 0;
//
const progress = data.progress || '0%';
progressText.value = progress;
progressPercent.value = parseInt(progress) || 0;
planStartDate.value = formatDate(data.planStartDate);
planEndDate.value = formatDate(data.planEndDate);
startDate.value = formatDate(data.startDate);
endDate.value = formatDate(data.endDate);
}
}
} catch (error) {
console.error('获取建设状态数据失败:', error);
} finally {
loading.value = false;
hasLoaded.value = true;
}
};
watch(
() => props.isActive,
active => {
if (active && !hasLoaded.value) {
getData();
}
},
{ immediate: true }
);
watch(
() => modelStore.params.stcd,
(newStcd, oldStcd) => {
if (newStcd && newStcd !== oldStcd && props.url) {
hasLoaded.value = false;
getData();
}
}
);
</script>
<style scoped lang="scss">
.fish-facility-build-state {
width: 880px;
margin: 0 auto;
margin-top: 24px;
border: 1px solid #dfe6ec;
//
.steps-container {
background: #e4ecf2;
padding: 0;
margin-bottom: 24px;
.step-line {
&::after {
content: '';
position: absolute;
right: 0;
right: -6px; /* 负值让第二条线更靠右 */
top: 50%;
transform: translateY(-50%) rotate(45deg);
width: 26px;
height: 26px;
border-top: 2px solid #fff;
border-right: 2px solid #fff;
}
}
.steps-wrapper {
display: flex;
align-items: stretch;
.step-item {
display: flex;
align-items: center;
justify-content: center;
position: relative;
flex: 1;
min-height: 40px;
gap: 6px;
.step-label {
font-size: 14px;
color: #999;
white-space: nowrap;
}
.step-icon {
font-size: 16px;
color: #2f6b98;
}
// -
&::after {
content: '';
position: absolute;
right: 0;
top: 0;
height: 0;
width: 0;
}
//
&.step-active {
background: #f0f4f8;
.step-label {
color: #00000080;
}
&::after {
border-top: 20px solid transparent;
border-left: 15px solid #f0f4f8;
border-bottom: 20px solid transparent;
}
}
// div -
&.step-active:has(+ .step-current)::after {
border-top-color: #2f6b98;
border-bottom-color: #2f6b98;
}
// - +
&.step-current {
background: #2f6b98;
.step-label {
color: #fff;
}
//
&::after {
content: '';
position: absolute;
right: 0;
top: 0;
height: 0;
width: 0;
border-top: 20px solid #e4ecf2;
border-left: 15px solid #2f6b98;
border-bottom: 20px solid #e4ecf2;
}
}
//
&.step-last::after {
display: none;
}
}
}
}
//
.progress-container {
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 24px;
.progress-label {
font-size: 14px;
color: #333;
margin-right: 16px;
white-space: nowrap;
}
.progress-bar-wrapper {
display: flex;
align-items: center;
flex: 1;
max-width: 500px;
.progress-bar {
flex: 1;
height: 12px;
background: #e8e8e8;
border-radius: 6px;
overflow: hidden;
margin-right: 12px;
.progress-fill {
height: 100%;
background: linear-gradient(90deg, #2a7aaf 0%, #5cb85c 100%);
border-radius: 6px;
transition: width 0.3s ease;
}
}
.progress-text {
font-size: 14px;
color: #2a7aaf;
font-weight: 500;
white-space: nowrap;
}
}
}
//
.info-table {
padding: 16px;
table {
width: 100%;
border-collapse: collapse;
tr {
td {
padding: 12px 16px;
border: 1px solid #a8d8f0;
font-size: 14px;
&.label-cell {
background: #f4f7f9;
color: #333;
width: 18%;
text-align: right;
white-space: nowrap;
}
&.value-cell {
color: #333;
width: 32%;
}
}
}
}
}
}
</style>

View File

@ -472,6 +472,13 @@ const handleSearch = () => fetchData();
const initData = () => { const initData = () => {
if (hasLoaded.value) return; if (hasLoaded.value) return;
console.log(modelStore.filter.rangeTm);
if (modelStore.filter.rangeTm.length > 0) {
dateRange.value = [
dayjs(modelStore.filter.rangeTm[0]),
dayjs(modelStore.filter.rangeTm[1])
];
}
nextTick(() => initChart()); nextTick(() => initChart());
fetchData(); fetchData();
hasLoaded.value = true; hasLoaded.value = true;

View File

@ -0,0 +1,396 @@
<template>
<div class="no-live-video">
<!-- 视频模式 -->
<div class="video-mode">
<div class="video-main" v-loading="videoLoading">
<!-- 视频显示 -->
<div v-if="activeMedia.src" class="media-display">
<LiveVideoBox
ref="liveVideoRef"
:videoData="{ url: activeMedia.src }"
height="100%"
:showTitle="false"
/>
</div>
<div v-else class="media-empty">
<a-empty description="请选择右侧数据" />
</div>
</div>
<div class="video-sidebar" v-loading="videoLoading">
<!-- 时间范围查询 -->
<div class="sidebar-search">
<a-range-picker
v-model:value="dateRange"
format="YYYY-MM-DD"
value-format="YYYY-MM-DD"
style="width: 100%"
@change="handleDateChange"
/>
<a-button type="primary" class="ml-2" @click="handleSearch">
查询
</a-button>
</div>
<!-- 列表 -->
<div class="sidebar-list" v-if="videoList.length > 0">
<div
v-for="item in videoList"
:key="item.flpth"
class="sidebar-item"
:class="{ active: activeFlpth === item.flpth }"
@click="handleSelectMedia(item)"
>
<div class="item-thumb">
<img
v-if="item.imgPath"
:src="item.imgPath"
@error="handleImageError"
/>
<div v-else class="thumb-placeholder">
<img :src="videoCover" />
</div>
</div>
<div class="item-time">
<div>{{ item.titleName || item.stnm || '-' }}</div>
{{ item.tm }}
</div>
</div>
</div>
<a-empty
v-else
description="暂无数据"
class="empty-wrapper h-full flex items-center justify-center flex-col"
/>
<!-- 分页 -->
<div class="sidebar-pagination">
<a-pagination
v-model:current="videoPage"
v-model:pageSize="videoTake"
:page-size-options="videoPageSizeOptions"
:total="videoTotal"
show-size-changer
size="small"
@change="handleVideoPageChange"
/>
</div>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, watch } from 'vue';
import dayjs from 'dayjs';
import LiveVideoBox from '@/views/shiPinJianKong/components/LiveVideoBox.vue';
import { getNoLiveVideoYear, getNoLiveVideoList } from '@/api/mapModal';
import { useModelStore } from '@/store/modules/model';
import videoCover from '@/assets/images/videFm.jpg';
const modelStore = useModelStore();
const props = defineProps({
isActive: { type: Boolean, default: false }
});
const hasLoaded = ref(false);
//
const dateRange = ref<[string, string] | null>(null);
//
const videoPage = ref(1);
const videoTake = ref(10);
const videoPageSizeOptions = ['10', '20', '50'];
const videoTotal = ref(0);
const videoLoading = ref(false);
const videoList = ref<any[]>([]);
const activeFlpth = ref('');
const activeMedia = ref<{ src: string }>({ src: '' });
const liveVideoRef = ref();
// getNoLiveVideoYear
const initDefaultDateRange = async () => {
try {
const res: any = await getNoLiveVideoYear({
filter: {
logic: 'and',
filters: [
{
field: 'stcd',
operator: 'in',
value: [modelStore.params.stcd]
}
]
},
sort: [{ field: 'tm', dir: 'desc' }]
});
const data = res?.data?.data || res?.data || [];
if (data.length > 0) {
//
const latestTm = data[0].tm;
const endDate = dayjs(latestTm);
const startDate = endDate.subtract(7, 'day');
dateRange.value = [
startDate.format('YYYY-MM-DD'),
endDate.format('YYYY-MM-DD')
];
} else {
// 7
const endDate = dayjs();
const startDate = endDate.subtract(7, 'day');
dateRange.value = [
startDate.format('YYYY-MM-DD'),
endDate.format('YYYY-MM-DD')
];
}
} catch (error) {
console.error('获取时间范围失败:', error);
// 7
const endDate = dayjs();
const startDate = endDate.subtract(7, 'day');
dateRange.value = [
startDate.format('YYYY-MM-DD'),
endDate.format('YYYY-MM-DD')
];
}
};
//
const initData = async () => {
await initDefaultDateRange();
fetchVideoList();
};
//
const handleDateChange = () => {
videoPage.value = 1;
fetchVideoList();
};
//
const handleSearch = () => {
videoPage.value = 1;
fetchVideoList();
};
//
const fetchVideoList = async () => {
videoLoading.value = true;
try {
const skip = (videoPage.value - 1) * videoTake.value;
const timeFilters: any[] = [];
if (dateRange.value && dateRange.value[0] && dateRange.value[1]) {
timeFilters.push({
field: 'tm',
operator: 'gte',
dataType: 'date',
value: `${dateRange.value[0]} 00:00:00`
});
timeFilters.push({
field: 'tm',
operator: 'lte',
dataType: 'date',
value: `${dateRange.value[1]} 23:59:59`
});
}
const res: any = await getNoLiveVideoList({
take: videoTake.value,
skip,
filter: {
logic: 'and',
filters: [
{
field: 'stcd',
operator: 'in',
value: [modelStore.params.stcd]
},
...timeFilters
]
},
sort: [{ field: 'tm', dir: 'desc' }]
});
res?.data?.data.forEach(item => {
item.tm = dayjs(item.tm).format('YYYY-MM-DD HH') || '-';
});
videoList.value = res?.data?.data || [];
videoTotal.value = res?.data?.total || 0;
if (videoList.value.length > 0 && !activeFlpth.value) {
handleSelectMedia(videoList.value[0]);
}
} catch (error) {
console.error('获取视频列表失败:', error);
videoList.value = [];
} finally {
videoLoading.value = false;
}
};
//
const handleSelectMedia = (item: any) => {
activeFlpth.value = item.flpth;
activeMedia.value = { src: item.flpth || '' };
};
//
const handleVideoPageChange = (page: number) => {
videoPage.value = page;
activeFlpth.value = '';
activeMedia.value = { src: '' };
fetchVideoList();
};
//
const handleImageError = (e: Event) => {
const target = e.target as HTMLImageElement;
target.style.display = 'none';
};
// isActive
watch(
() => props.isActive,
active => {
if (active && !hasLoaded.value) {
initData();
}
},
{ immediate: true }
);
// stcd
watch(
() => modelStore.params.stcd,
async (newStcd, oldStcd) => {
if (newStcd && newStcd !== oldStcd) {
videoList.value = [];
activeFlpth.value = '';
activeMedia.value = { src: '' };
await initDefaultDateRange();
fetchVideoList();
}
}
);
</script>
<style lang="scss" scoped>
.no-live-video {
width: 100%;
.video-mode {
display: flex;
height: 600px;
gap: 12px;
.video-main {
flex: 0 0 60%;
background: #000;
border-radius: 4px;
overflow: hidden;
.media-display {
width: 100%;
height: 100%;
}
.media-empty {
display: flex;
align-items: center;
justify-content: center;
height: 100%;
}
}
.video-sidebar {
flex: 0 0 40%;
display: flex;
flex-direction: column;
border: 1px solid #e8e8e8;
border-radius: 4px;
.sidebar-search {
padding: 8px;
display: flex;
align-items: center;
border-bottom: 1px solid #e8e8e8;
}
.sidebar-list {
height: 600px;
overflow-y: auto;
display: grid;
grid-template-columns: repeat(2, 1fr);
grid-auto-rows: min-content;
gap: 8px;
padding: 8px;
.sidebar-item {
cursor: pointer;
border: 1px solid #f0f0f0;
border-radius: 4px;
overflow: hidden;
transition: all 0.2s;
&:hover {
border-color: #1890ff;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
&.active {
border-color: #1890ff;
background: #e6f7ff;
}
.item-thumb {
width: 100%;
height: 105px;
background: #1a1a1a;
overflow: hidden;
display: flex;
align-items: center;
justify-content: center;
img {
width: 100%;
height: 100%;
object-fit: cover;
}
.thumb-placeholder {
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
img {
width: 100%;
height: 100%;
object-fit: cover;
}
}
}
.item-time {
padding: 4px;
font-size: 11px;
color: #666;
text-align: center;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
}
}
.sidebar-pagination {
padding: 8px;
text-align: center;
border-top: 1px solid #e8e8e8;
}
}
}
}
</style>

View File

@ -103,13 +103,13 @@ const tableColumns = ref([
String(record.tecnt).trim() !== '' String(record.tecnt).trim() !== ''
? record.tecnt ? record.tecnt
: '-'; : '-';
const transplant = const plantask =
record?.transplant !== undefined && record?.plantask !== undefined &&
record?.transplant !== null && record?.plantask !== null &&
String(record.transplant).trim() !== '' String(record.plplantask).trim() !== ''
? record.transplant ? record.plplantask
: '-'; : '-';
return `${tecnt}/${transplant}`; return `${tecnt}/${plantask}`;
} }
}, },
{ {
@ -123,13 +123,13 @@ const tableColumns = ref([
String(record.surnum).trim() !== '' String(record.surnum).trim() !== ''
? record.surnum ? record.surnum
: '-'; : '-';
const plantask = const transplant =
record?.plantask !== undefined && record?.transplant !== undefined &&
record?.plantask !== null && record?.transplant !== null &&
String(record.plantask).trim() !== '' String(record.transplant).trim() !== ''
? record.plantask ? record.transplant
: '-'; : '-';
return `${surnum}/${plantask}`; return `${surnum}/${transplant}`;
} }
}, },
{ {

View File

@ -479,6 +479,12 @@ const handleSearch = () => fetchData();
const initData = () => { const initData = () => {
if (hasLoaded.value) return; if (hasLoaded.value) return;
nextTick(() => { nextTick(() => {
if (modelStore.filter.rangeTm.length > 0) {
dateRange.value = [
dayjs(modelStore.filter.rangeTm[0]),
dayjs(modelStore.filter.rangeTm[1])
];
}
if (chartRef.value && !chartInstance) if (chartRef.value && !chartInstance)
chartInstance = echarts.init(chartRef.value); chartInstance = echarts.init(chartRef.value);
}); });

View File

@ -903,7 +903,15 @@ const handleExport = () => {
const initData = () => { const initData = () => {
if (hasLoaded.value) return; if (hasLoaded.value) return;
console.log('modelStore.filter.rangeTm:', modelStore.filter.rangeTm);
nextTick(() => { nextTick(() => {
if (modelStore.filter.rangeTm.length > 0) {
dateRange.value = [
dayjs(modelStore.filter.rangeTm[0]),
dayjs(modelStore.filter.rangeTm[1])
];
}
if (chartRef.value && !chartInstance) if (chartRef.value && !chartInstance)
chartInstance = echarts.init(chartRef.value); chartInstance = echarts.init(chartRef.value);
}); });

View File

@ -37,7 +37,12 @@
<!-- 同期对比列的自定义渲染 --> <!-- 同期对比列的自定义渲染 -->
<template #contrast="{ record }"> <template #contrast="{ record }">
<span <span
v-if="record.wt && record.beforeWt && !isNaN(Number(record.wt)) && !isNaN(Number(record.beforeWt))" v-if="
record.wt &&
record.beforeWt &&
!isNaN(Number(record.wt)) &&
!isNaN(Number(record.beforeWt))
"
:style="{ :style="{
color: color:
Number(record.wt) - Number(record.beforeWt) > 0 Number(record.wt) - Number(record.beforeWt) > 0
@ -664,6 +669,12 @@ const initData = () => {
setTimeout(() => { setTimeout(() => {
initChart(); initChart();
}, 200); }, 200);
if (modelStore.filter.rangeTm.length > 0) {
rangePicker.value = [
dayjs(modelStore.filter.rangeTm[0]),
dayjs(modelStore.filter.rangeTm[1])
];
}
hasLoaded.value = true; // hasLoaded.value = true; //
}; };

View File

@ -10,8 +10,8 @@
class="map-modal" class="map-modal"
> >
<!-- 问题 <!-- 问题
1.基础信息图片展示是什么逻辑 1.基础信息图片展示是什么逻辑
2. 电站专题展示逻辑 2. 电站专题展示逻辑
3. 实时视频回放 少接口 3. 实时视频回放 少接口
4. 预警提示 少接口 4. 预警提示 少接口
5. 生态流量 达标率查询不对() 5. 生态流量 达标率查询不对()
@ -20,13 +20,13 @@
8. 栖息地-流量监测 没有水位视频和流量视频 字段没数据 8. 栖息地-流量监测 没有水位视频和流量视频 字段没数据
9. 栖息地 水温水质流量没有数据没法测 9. 栖息地 水温水质流量没有数据没法测
10.鱼类增殖站 - 运行数据 要添加字典 10.鱼类增殖站 - 运行数据 要添加字典
11.珍惜植物园 - 种植要求字段不知道 11.珍惜植物园 - 种植要求字段不知道
12.水生调查断面 - 监测数据 不知道电导率 性腺发育期早期资源量和种类 12.水生调查断面 - 监测数据 不知道电导率 性腺发育期早期资源量和种类
13.水电告警情况 生态流量 不知道字段 接口传参有问题 13.水电告警情况 生态流量 不知道字段 接口传参有问题
14. 地图抽吸 碰撞检测 放大到具体层级锚点抽吸了但是popup弹框还在显示;
15.运行情况 计划开始运行时间 接口没接
没弄 环保设施
--> -->
<div v-if="modelStore.showStcdSelector" class="stcd-selector-wrapper"> <div v-if="modelStore.showStcdSelector" class="stcd-selector-wrapper">
<a-select <a-select
@ -88,18 +88,23 @@
v-show="currentActiveKey === 'videoInfo'" v-show="currentActiveKey === 'videoInfo'"
:is-active="currentActiveKey === 'videoInfo'" :is-active="currentActiveKey === 'videoInfo'"
/> />
<!-- 录像视频 -->
<NoLiveVideo
v-show="currentActiveKey === 'noLiveVideo'"
:is-active="currentActiveKey === 'noLiveVideo'"
/>
<!-- 全景影像 --> <!-- 全景影像 -->
<PanoramaInfo <PanoramaInfo
v-show="currentActiveKey === 'panoramaInfo'" v-show="currentActiveKey === 'panoramaInfo'"
:url="getTabUrl('panoramaInfo')" :url="getTabUrl('panoramaInfo')"
:is-active="currentActiveKey === 'panoramaInfo'" :is-active="currentActiveKey === 'panoramaInfo'"
/> />
<!-- 监测数据 - 电站运行过程线 --> <!-- 监测数据 - 电站运行过程线 -->
<MonitorInfo <MonitorInfo
v-show="currentActiveKey === 'monitorInfo'" v-show="currentActiveKey === 'monitorInfo'"
:is-active="currentActiveKey === 'monitorInfo'" :is-active="currentActiveKey === 'monitorInfo'"
/> />
<!-- 监测数据 - 水温 --> <!-- 监测数据 - 水温 -->
<WaterTemperature <WaterTemperature
v-show="currentActiveKey === 'WaterTemperature'" v-show="currentActiveKey === 'WaterTemperature'"
:is-active="currentActiveKey === 'WaterTemperature'" :is-active="currentActiveKey === 'WaterTemperature'"
@ -111,7 +116,7 @@
" "
:wt-config="modelStore.wtConfig" :wt-config="modelStore.wtConfig"
/> />
<!-- 监测数据 - 垂向水温 --> <!-- 监测数据 - 垂向水温 -->
<VerticalWaterTemperature <VerticalWaterTemperature
v-show="currentActiveKey === 'VerticalWaterTemperature'" v-show="currentActiveKey === 'VerticalWaterTemperature'"
:is-active="currentActiveKey === 'VerticalWaterTemperature'" :is-active="currentActiveKey === 'VerticalWaterTemperature'"
@ -167,6 +172,12 @@
v-show="currentActiveKey === 'FishZHFX'" v-show="currentActiveKey === 'FishZHFX'"
:is-active="currentActiveKey === 'FishZHFX'" :is-active="currentActiveKey === 'FishZHFX'"
/> />
<!-- 建设情况 -->
<FishFacilityBuildState
v-show="currentActiveKey === 'FishFacilityBuildState'"
:is-active="currentActiveKey === 'FishFacilityBuildState'"
:url="getTabUrl('FishFacilityBuildState')"
/>
<!-- AI运行识别 --> <!-- AI运行识别 -->
<AIYXSB <AIYXSB
v-show="currentActiveKey === 'AIYXSB'" v-show="currentActiveKey === 'AIYXSB'"
@ -178,12 +189,12 @@
:tabs-items="getTabChildren('tableTabs')" :tabs-items="getTabChildren('tableTabs')"
:is-active="currentActiveKey === 'tableTabs'" :is-active="currentActiveKey === 'tableTabs'"
/> />
<!-- 生态流量 --> <!-- 生态流量 -->
<EcologicalFlow <EcologicalFlow
v-show="currentActiveKey === 'EcologicalFlow'" v-show="currentActiveKey === 'EcologicalFlow'"
:is-active="currentActiveKey === 'EcologicalFlow'" :is-active="currentActiveKey === 'EcologicalFlow'"
/> />
<!-- 鱼类繁殖适宜性分析 --> <!-- 鱼类繁殖适宜性分析 -->
<WaterTemperatureRep <WaterTemperatureRep
v-show="currentActiveKey === 'WaterTemperatureRep'" v-show="currentActiveKey === 'WaterTemperatureRep'"
:is-active="currentActiveKey === 'WaterTemperatureRep'" :is-active="currentActiveKey === 'WaterTemperatureRep'"
@ -259,6 +270,7 @@ import { ref, watch, reactive, computed } from 'vue';
// Tab // Tab
import BasicInfo from './components/BasicInfo.vue'; // import BasicInfo from './components/BasicInfo.vue'; //
import VideoInfo from './components/videoInfo.vue'; // import VideoInfo from './components/videoInfo.vue'; //
import NoLiveVideo from './components/NoLiveVideo.vue'; //
import PanoramaInfo from './components/PanoramaInfo.vue'; // import PanoramaInfo from './components/PanoramaInfo.vue'; //
import MonitorInfo from './components/MonitorInfo.vue'; // 线 import MonitorInfo from './components/MonitorInfo.vue'; // 线
import WaterTemperature from './components/WaterTemperature.vue'; // import WaterTemperature from './components/WaterTemperature.vue'; //
@ -270,6 +282,7 @@ import FlowMeasure from './components/FlowMeasure.vue'; // 流量监测
import FishFacilityMonitorData from './components/FishFacilityMonitorData.vue'; // 线 import FishFacilityMonitorData from './components/FishFacilityMonitorData.vue'; // 线
import FishFacilityRunState from './components/FishFacilityRunState.vue'; // import FishFacilityRunState from './components/FishFacilityRunState.vue'; //
import FlowDischarge from './components/FlowDischarge.vue'; // - import FlowDischarge from './components/FlowDischarge.vue'; // -
import FishFacilityBuildState from './components/FishFacilityBuildState.vue'; //
// //
import NormalOperationData from './components/NormalOperationData/index.vue'; // import NormalOperationData from './components/NormalOperationData/index.vue'; //
import ProcessDiagram from './components/ProcessDiagram.vue'; // import ProcessDiagram from './components/ProcessDiagram.vue'; //
@ -999,6 +1012,12 @@ const handleClose = () => {
modelStore.verticalWaterStcd = ''; modelStore.verticalWaterStcd = '';
modelStore.aiSites = []; modelStore.aiSites = [];
modelStore.wtConfig = {}; modelStore.wtConfig = {};
modelStore.filter = {
stllgzlx: '', // -
stllTmType: '', // -
tm: '',
rangeTm: []
};
modelStore.wqShow = false; modelStore.wqShow = false;
modelStore.fishTabInfo = null; modelStore.fishTabInfo = null;
}; };

View File

@ -5,24 +5,40 @@
<div class="title_left"> <div class="title_left">
<span class="texttitle">{{ title }}</span> <span class="texttitle">{{ title }}</span>
<span v-if="prompt.show" class="title_icon"> <span v-if="prompt.show" class="title_icon">
<a-tooltip placement="top" :title="prompt.value" :get-popup-container="getPopupContainer"> <a-tooltip
placement="top"
:title="prompt.value"
:get-popup-container="getPopupContainer"
>
<QuestionCircleOutlined /> <QuestionCircleOutlined />
</a-tooltip> </a-tooltip>
</span> </span>
<span v-if="clickprompt.show" class="title_icon"> <span v-if="clickprompt.show" class="title_icon">
<a-tooltip placement="top" trigger="click" :title="clickprompt.value" <a-tooltip
:get-popup-container="getPopupContainer"> placement="top"
trigger="click"
:title="clickprompt.value"
:get-popup-container="getPopupContainer"
>
<InfoCircleOutlined /> <InfoCircleOutlined />
</a-tooltip> </a-tooltip>
</span> </span>
<span v-if="iconmap.show" class="title_icon"> <span v-if="iconmap.show" class="title_icon">
<a-tooltip placement="top" :title="iconmap.value" :get-popup-container="getPopupContainer"> <a-tooltip
placement="top"
:title="iconmap.value"
:get-popup-container="getPopupContainer"
>
<span :class="iconmap.icon"></span> <span :class="iconmap.icon"></span>
</a-tooltip> </a-tooltip>
</span> </span>
<!-- 新增的 clickAction 插槽区域 --> <!-- 新增的 clickAction 插槽区域 -->
<span v-if="clickAction.show" class="title_icon"> <span v-if="clickAction.show" class="title_icon">
<a-tooltip placement="top" trigger="click" :get-popup-container="getPopupContainer"> <a-tooltip
placement="top"
trigger="click"
:get-popup-container="getPopupContainer"
>
<template #title> <template #title>
<slot name="click-action-content"></slot> <slot name="click-action-content"></slot>
</template> </template>
@ -32,45 +48,108 @@
</div> </div>
<div class="title_right"> <div class="title_right">
<div v-if="select.show"> <div v-if="select.show">
<a-select v-model:value="selectValue" show-search placeholder="请选择" :size="'small'" <a-select
style="width: 120px" :options="select.options" :filter-option="filterOption" v-model:value="selectValue"
@focus="handleFocus" @blur="handleBlur" @change="handleChange"></a-select> show-search
placeholder="请选择"
:size="'small'"
style="width: 120px"
:options="select.options"
:filter-option="filterOption"
@focus="handleFocus"
@blur="handleBlur"
@change="handleChange"
></a-select>
</div> </div>
<div v-if="shrink" class="title_shrink" @click="isExpand = !isExpand"> <div v-if="shrink" class="title_shrink" @click="isExpand = !isExpand">
<img v-if="isExpand" src="@/assets/components/arrow-up.png" alt=""> <img v-if="isExpand" src="@/assets/components/arrow-up.png" alt="" />
<img v-else src="@/assets/components/arrow-down.png" alt=""> <img v-else src="@/assets/components/arrow-down.png" alt="" />
</div> </div>
<div v-if="moreSelect.show"> <div v-if="moreSelect.show">
<a-tree-select v-model:value="moreSelectValue" v-model:tree-expanded-keys="treeExpandedKeys" <a-tree-select
show-search :size="'small'" style="width: 110px" v-model:value="moreSelectValue"
:dropdown-style="{ maxHeight: '400px', overflow: 'auto', minWidth: '180px' }" placeholder=" " v-model:tree-expanded-keys="treeExpandedKeys"
show-search
:size="'small'"
style="width: 110px"
:dropdown-style="{
maxHeight: '400px',
overflow: 'auto',
minWidth: '200px'
}"
placeholder=" "
:tree-data="processedMoreSelectOptions" :tree-data="processedMoreSelectOptions"
:field-names="{ label: 'title', value: 'value', children: 'children' }" :field-names="{
tree-node-filter-prop="label" popup-class-name="no-wrap-tree-select" @select="handleTreeSelect" label: 'title',
@expand="handleTreeExpand"> value: 'value',
children: 'children'
}"
tree-node-filter-prop="label"
popup-class-name="no-wrap-tree-select"
@select="handleTreeSelect"
@expand="handleTreeExpand"
>
</a-tree-select> </a-tree-select>
</div> </div>
<div v-if="datetimePicker.show"> <div v-if="datetimePicker.show">
<!-- 添加 locale 属性来设置语言 --> <!-- 添加 locale 属性来设置语言 -->
<a-date-picker v-model:value="datetimeValue" <a-date-picker
v-model:value="datetimeValue"
:show-time="timePickerConfig" :show-time="timePickerConfig"
:style="{ width: datetimePicker.picker === 'year' ? '80px' : datetimePicker.picker === 'month' ? '90px' : '130px' }" :style="{
:format="datetimePicker.format !== null ? datetimePicker.format : undefined" width:
:picker="datetimePicker.picker" :allowClear="false" placeholder=" " datetimePicker.picker === 'year'
@change="handleDateTimeChange" :size="'small'" ? '80px'
: datetimePicker.picker === 'month'
? '90px'
: '130px'
}"
:format="
datetimePicker.format !== null ? datetimePicker.format : undefined
"
:picker="datetimePicker.picker"
:allowClear="false"
placeholder=" "
@change="handleDateTimeChange"
:size="'small'"
:disabledDate="createDisabledDateFn(datetimePicker.picker)" :disabledDate="createDisabledDateFn(datetimePicker.picker)"
:disabledTime="disabledTimeForSinglePicker" /> :disabledTime="disabledTimeForSinglePicker"
/>
<!-- 修改为 locale 变量 --> <!-- 修改为 locale 变量 -->
</div> </div>
<div v-if="scopeDate.show" class="title_scopeDate"> <div v-if="scopeDate.show" class="title_scopeDate">
<a-range-picker v-model:value="scopeDateValue" :picker="scopeDate.picker" :allowClear="false" <a-range-picker
:style="{ width: scopeDate.picker === 'year' ? '80px' : (scopeDate.picker === 'month' ? '180px' : '') }" v-model:value="scopeDateValue"
:format="scopeDate.format" :range-separator="' 至 '" :size="'small'" :picker="scopeDate.picker"
:presets="computedScopeDatePresets" :disabledDate="createDisabledDateFn(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>
<div v-if="tabs.show" class="typeOne"> <div v-if="tabs.show" class="typeOne">
<div @click="handleTabClick('one')" :class="tabsValue == 'one' ? 'typezhong' : ''">图片</div> <div
<div @click="handleTabClick('two')" :class="tabsValue == 'two' ? 'typezhong' : ''">视频</div> @click="handleTabClick('one')"
:class="tabsValue == 'one' ? 'typezhong' : ''"
>
图片
</div>
<div
@click="handleTabClick('two')"
:class="tabsValue == 'two' ? 'typezhong' : ''"
>
视频
</div>
</div> </div>
</div> </div>
</div> </div>
@ -112,43 +191,50 @@ defineOptions({
// props // props
const props = defineProps({ const props = defineProps({
title: { // title: {
//
type: String, type: String,
default: '' default: ''
}, },
shrink: { // shrink: {
//
type: Boolean, type: Boolean,
default: false default: false
}, },
prompt: { // prompt: {
//
type: Object as () => PromptConfig,
default: () => ({
show: false,
value: ''
})
},
clickprompt: {
//
type: Object as () => PromptConfig,
default: () => ({
show: false,
value: ''
})
},
iconmap: {
//
type: Object as () => PromptConfig, type: Object as () => PromptConfig,
default: () => ({ default: () => ({
show: false, show: false,
value: '', value: '',
icon: 'iconfont icon-time'
}) })
}, },
clickprompt: { // clickAction: {
type: Object as () => PromptConfig, //
default: () => ({
show: false,
value: '',
})
},
iconmap: {//
type: Object as () => PromptConfig,
default: () => ({
show: false,
value: '',
icon: 'iconfont icon-time',
})
},
clickAction: { //
type: Object as () => { show: boolean }, type: Object as () => { show: boolean },
default: () => ({ default: () => ({
show: false show: false
}) })
}, },
select: { // select: {
//
type: Object as () => SelectConfig, type: Object as () => SelectConfig,
default: () => ({ default: () => ({
show: false, show: false,
@ -156,7 +242,8 @@ const props = defineProps({
options: [] options: []
}) })
}, },
moreSelect: {// moreSelect: {
//
type: Object as () => SelectConfig, type: Object as () => SelectConfig,
default: () => ({ default: () => ({
show: false, show: false,
@ -164,7 +251,8 @@ const props = defineProps({
options: [] options: []
}) })
}, },
datetimePicker: { // datetimePicker: {
//
type: Object as () => SelectConfig & { type: Object as () => SelectConfig & {
timeFormat?: string; // timeFormat?: string; //
}, },
@ -176,7 +264,8 @@ const props = defineProps({
timeFormat: 'HH:mm:ss' // timeFormat: 'HH:mm:ss' //
}) })
}, },
scopeDate: { // scopeDate: {
//
type: Object as () => SelectConfig, type: Object as () => SelectConfig,
default: () => ({ default: () => ({
show: false, show: false,
@ -189,28 +278,33 @@ const props = defineProps({
type: Object, type: Object,
default: () => ({ default: () => ({
show: false, show: false,
value: 'one', value: 'one'
}) })
} }
}); });
const emit = defineEmits(['tab-change', 'update-values']); const emit = defineEmits(['tab-change', 'update-values']);
const isExpand = ref(true); const isExpand = ref(true);
const selectValue = ref(props.select.value) const selectValue = ref(props.select.value);
const moreSelectValue = ref(props.moreSelect.value) const moreSelectValue = ref(props.moreSelect.value);
const datetimeValue = ref<Dayjs | null>(props.datetimePicker.value ? dayjs(props.datetimePicker.value) : null); const datetimeValue = ref<Dayjs | null>(
props.datetimePicker.value ? dayjs(props.datetimePicker.value) : null
);
const scopeDateValue = ref<[Dayjs, Dayjs] | undefined>( const scopeDateValue = ref<[Dayjs, Dayjs] | undefined>(
props.scopeDate.value && Array.isArray(props.scopeDate.value) props.scopeDate.value && Array.isArray(props.scopeDate.value)
? [dayjs(props.scopeDate.value[0]), dayjs(props.scopeDate.value[1])] ? [dayjs(props.scopeDate.value[0]), dayjs(props.scopeDate.value[1])]
: undefined : undefined
); );
const tabsValue = ref(props.tabs.value) const tabsValue = ref(props.tabs.value);
// lgtdlttd
const selectedNodeExtra = ref<{ lgtd?: any; lttd?: any }>({});
// yearmonth // yearmonth
const lastMonthValue = ref<string | null>(null); const lastMonthValue = ref<string | null>(null);
// //
const treeExpandedKeys = ref<string[]>([]) const treeExpandedKeys = ref<string[]>([]);
const nodeMap = new Map<string, { node: any; parentKey: string | null }>() const nodeMap = new Map<string, { node: any; parentKey: string | null }>();
/** /**
* 创建针对不同 picker 类型的日期禁用函数 * 创建针对不同 picker 类型的日期禁用函数
@ -219,34 +313,34 @@ const nodeMap = new Map<string, { node: any; parentKey: string | null }>()
*/ */
const createDisabledDateFn = (pickerType: string) => { const createDisabledDateFn = (pickerType: string) => {
return (current: Dayjs) => { return (current: Dayjs) => {
if (!current) return false if (!current) return false;
const now = dayjs() const now = dayjs();
switch (pickerType) { switch (pickerType) {
case 'year': case 'year':
// > // >
return current.year() > now.year() return current.year() > now.year();
case 'month': case 'month':
// > // >
return current.isAfter(now, 'month') return current.isAfter(now, 'month');
case 'quarter': case 'quarter':
// > // >
return current.isAfter(now, 'quarter') return current.isAfter(now, 'quarter');
case 'week': case 'week':
// > // >
return current.isAfter(now, 'week') return current.isAfter(now, 'week');
case 'date': case 'date':
default: default:
// > // >
return current.isAfter(now, 'day') return current.isAfter(now, 'day');
} }
} };
} };
/** /**
* 单日期选择器的时间禁用函数针对 show-time * 单日期选择器的时间禁用函数针对 show-time
@ -256,52 +350,52 @@ const createDisabledDateFn = (pickerType: string) => {
const disabledTimeForSinglePicker = (current?: Dayjs) => { const disabledTimeForSinglePicker = (current?: Dayjs) => {
// datetimePicker date // datetimePicker date
if (!props.datetimePicker.show || props.datetimePicker.picker !== 'date') { if (!props.datetimePicker.show || props.datetimePicker.picker !== 'date') {
return undefined return undefined;
} }
const now = dayjs() const now = dayjs();
// 使 current datetimeValue.value // 使 current datetimeValue.value
// current // current
const selectedDate = current || datetimeValue.value const selectedDate = current || datetimeValue.value;
// //
if (!selectedDate || !selectedDate.isSame(now, 'day')) { if (!selectedDate || !selectedDate.isSame(now, 'day')) {
return {} return {};
} }
// start end-1 // start end-1
const range = (start: number, end: number) => { const range = (start: number, end: number) => {
const result: number[] = [] const result: number[] = [];
for (let i = start; i < end; i++) { for (let i = start; i < end; i++) {
result.push(i) result.push(i);
}
return result
} }
return result;
};
return { return {
// //
disabledHours: () => { disabledHours: () => {
return range(now.hour() + 1, 24) return range(now.hour() + 1, 24);
}, },
// //
disabledMinutes: (selectedHour: number) => { disabledMinutes: (selectedHour: number) => {
if (selectedHour === now.hour()) { if (selectedHour === now.hour()) {
return range(now.minute() + 1, 60) return range(now.minute() + 1, 60);
} }
return [] return [];
}, },
// //
disabledSeconds: (selectedHour: number, selectedMinute: number) => { disabledSeconds: (selectedHour: number, selectedMinute: number) => {
if (selectedHour === now.hour() && selectedMinute === now.minute()) { if (selectedHour === now.hour() && selectedMinute === now.minute()) {
return range(now.second() + 1, 60) return range(now.second() + 1, 60);
} }
return [] return [];
} }
} };
} };
// // locale // // locale
// const locale = zhCN; // const locale = zhCN;
@ -344,21 +438,21 @@ const processedMoreSelectOptions = computed(() => {
* @param parentKey 父节点key * @param parentKey 父节点key
*/ */
const buildNodeMap = (treeData: any[], parentKey: string | null = null) => { const buildNodeMap = (treeData: any[], parentKey: string | null = null) => {
if (!treeData || !Array.isArray(treeData)) return if (!treeData || !Array.isArray(treeData)) return;
treeData.forEach(node => { treeData.forEach(node => {
// //
nodeMap.set(node.value, { nodeMap.set(node.value, {
node, node,
parentKey parentKey
}) });
// //
if (node.children && node.children.length > 0) { if (node.children && node.children.length > 0) {
buildNodeMap(node.children, node.value) buildNodeMap(node.children, node.value);
} }
}) });
} };
/** /**
* 获取目标节点的所有父节点keys * 获取目标节点的所有父节点keys
@ -366,25 +460,29 @@ const buildNodeMap = (treeData: any[], parentKey: string | null = null) => {
* @returns 父节点keys数组从根到直接父节点 * @returns 父节点keys数组从根到直接父节点
*/ */
const getParentKeys = (targetValue: string): string[] => { const getParentKeys = (targetValue: string): string[] => {
const parentKeys: string[] = [] const parentKeys: string[] = [];
let currentKey: string | null = targetValue let currentKey: string | null = targetValue;
// //
while (currentKey !== null && currentKey !== undefined) { while (currentKey !== null && currentKey !== undefined) {
const nodeInfo = nodeMap.get(currentKey) const nodeInfo = nodeMap.get(currentKey);
// //
if (!nodeInfo || nodeInfo.parentKey === null || nodeInfo.parentKey === undefined) { if (
break !nodeInfo ||
nodeInfo.parentKey === null ||
nodeInfo.parentKey === undefined
) {
break;
} }
// //
parentKeys.unshift(nodeInfo.parentKey) parentKeys.unshift(nodeInfo.parentKey);
currentKey = nodeInfo.parentKey currentKey = nodeInfo.parentKey;
} }
return parentKeys return parentKeys;
} };
/** /**
* 计算属性根据 picker 类型动态生成快捷日期选项 * 计算属性根据 picker 类型动态生成快捷日期选项
@ -412,7 +510,10 @@ const computedScopeDatePresets = computed(() => {
}, },
{ {
label: '昨天', label: '昨天',
value: [now.subtract(1, 'day').startOf('day'), now.subtract(1, 'day').endOf('day')] value: [
now.subtract(1, 'day').startOf('day'),
now.subtract(1, 'day').endOf('day')
]
}, },
{ {
label: '最近七天', label: '最近七天',
@ -438,7 +539,10 @@ const computedScopeDatePresets = computed(() => {
presets.push( presets.push(
{ {
label: '最近一季度', label: '最近一季度',
value: [now.subtract(1, 'quarter').startOf('quarter'), now.endOf('quarter')] value: [
now.subtract(1, 'quarter').startOf('quarter'),
now.endOf('quarter')
]
}, },
{ {
label: '今年', label: '今年',
@ -456,7 +560,10 @@ const computedScopeDatePresets = computed(() => {
}, },
{ {
label: '上周', label: '上周',
value: [now.subtract(1, 'week').startOf('week'), now.subtract(1, 'week').endOf('week')] value: [
now.subtract(1, 'week').startOf('week'),
now.subtract(1, 'week').endOf('week')
]
}, },
{ {
label: '最近四周', label: '最近四周',
@ -475,7 +582,10 @@ const computedScopeDatePresets = computed(() => {
}, },
{ {
label: '昨天', label: '昨天',
value: [now.subtract(1, 'day').startOf('day'), now.subtract(1, 'day').endOf('day')] value: [
now.subtract(1, 'day').startOf('day'),
now.subtract(1, 'day').endOf('day')
]
}, },
{ {
label: '最近七天', label: '最近七天',
@ -542,6 +652,14 @@ const handleTreeSelect = (selectedKeys: string | string[], info: any) => {
} }
moreSelectValue.value = selectedValue; moreSelectValue.value = selectedValue;
// lgtdlttd
if (info && typeof info === 'object') {
selectedNodeExtra.value = {
lgtd: info.lgtd,
lttd: info.lttd
};
}
}; };
/** /**
@ -604,12 +722,15 @@ const emitAllValues = () => {
select: selectValue.value, select: selectValue.value,
moreSelect: moreSelectValue.value, moreSelect: moreSelectValue.value,
tabs: tabsValue.value, tabs: tabsValue.value,
selectedNodeExtra: selectedNodeExtra.value
}; };
// //
if (datetimeValue.value) { if (datetimeValue.value) {
// 使 format picker // 使 format picker
const format = props.datetimePicker.format || getDefaultFormat(props.datetimePicker.picker); const format =
props.datetimePicker.format ||
getDefaultFormat(props.datetimePicker.picker);
payload.datetime = datetimeValue.value.format(format); payload.datetime = datetimeValue.value.format(format);
} else { } else {
payload.datetime = null; payload.datetime = null;
@ -618,8 +739,11 @@ const emitAllValues = () => {
// //
if (scopeDateValue.value && Array.isArray(scopeDateValue.value)) { if (scopeDateValue.value && Array.isArray(scopeDateValue.value)) {
// 使 format picker // 使 format picker
const format = props.scopeDate.format || getDefaultFormat(props.scopeDate.picker); const format =
payload.scopeDate = scopeDateValue.value.map(d => d ? d.format(format) : null); props.scopeDate.format || getDefaultFormat(props.scopeDate.picker);
payload.scopeDate = scopeDateValue.value.map(d =>
d ? d.format(format) : null
);
} else { } else {
payload.scopeDate = []; payload.scopeDate = [];
} }
@ -645,14 +769,17 @@ const getDefaultFormat = (picker: string): string => {
}; };
// //
watch([selectValue, moreSelectValue, datetimeValue, scopeDateValue, tabsValue], () => { watch(
[selectValue, moreSelectValue, datetimeValue, scopeDateValue, tabsValue],
() => {
emitAllValues(); emitAllValues();
}); }
);
// props // props
watch( watch(
() => props.select.value, () => props.select.value,
(newVal) => { newVal => {
if (newVal !== selectValue.value) { if (newVal !== selectValue.value) {
selectValue.value = newVal; selectValue.value = newVal;
} }
@ -662,7 +789,7 @@ watch(
// moreSelectValue // moreSelectValue
watch( watch(
() => props.moreSelect.value, () => props.moreSelect.value,
(newVal) => { newVal => {
if (newVal !== moreSelectValue.value) { if (newVal !== moreSelectValue.value) {
moreSelectValue.value = newVal; moreSelectValue.value = newVal;
} }
@ -671,9 +798,13 @@ watch(
watch( watch(
() => props.datetimePicker.value, () => props.datetimePicker.value,
(newVal) => { newVal => {
const newDayjs = newVal ? dayjs(newVal) : null; const newDayjs = newVal ? dayjs(newVal) : null;
if (!datetimeValue.value || !newDayjs || !datetimeValue.value.isSame(newDayjs)) { if (
!datetimeValue.value ||
!newDayjs ||
!datetimeValue.value.isSame(newDayjs)
) {
datetimeValue.value = newDayjs; datetimeValue.value = newDayjs;
} }
} }
@ -681,15 +812,17 @@ watch(
watch( watch(
() => props.scopeDate.value, () => props.scopeDate.value,
(newVal) => { newVal => {
if (newVal && Array.isArray(newVal) && newVal.length === 2) { if (newVal && Array.isArray(newVal) && newVal.length === 2) {
const newRange: [Dayjs, Dayjs] = [dayjs(newVal[0]), dayjs(newVal[1])]; const newRange: [Dayjs, Dayjs] = [dayjs(newVal[0]), dayjs(newVal[1])];
const currentRange = scopeDateValue.value; const currentRange = scopeDateValue.value;
// //
if (!currentRange || if (
!currentRange ||
!currentRange[0].isSame(newRange[0]) || !currentRange[0].isSame(newRange[0]) ||
!currentRange[1].isSame(newRange[1])) { !currentRange[1].isSame(newRange[1])
) {
scopeDateValue.value = newRange; scopeDateValue.value = newRange;
} }
} else { } else {
@ -702,7 +835,7 @@ watch(
watch( watch(
() => props.tabs.value, () => props.tabs.value,
(newVal) => { newVal => {
if (newVal !== tabsValue.value) { if (newVal !== tabsValue.value) {
tabsValue.value = newVal; tabsValue.value = newVal;
} }
@ -714,7 +847,12 @@ watch(
() => props.datetimePicker.picker, () => props.datetimePicker.picker,
(newPicker, oldPicker) => { (newPicker, oldPicker) => {
// year month // year month
if (oldPicker === 'year' && newPicker === 'month' && lastMonthValue.value && datetimeValue.value) { if (
oldPicker === 'year' &&
newPicker === 'month' &&
lastMonthValue.value &&
datetimeValue.value
) {
const currentYear = datetimeValue.value.year(); // const currentYear = datetimeValue.value.year(); //
const lastMonth = dayjs(lastMonthValue.value); // const lastMonth = dayjs(lastMonthValue.value); //
const lastMonthNum = lastMonth.month(); // 0-11 const lastMonthNum = lastMonth.month(); // 0-11
@ -735,39 +873,47 @@ watch(
); );
// moreSelectValue // moreSelectValue
watch(() => moreSelectValue.value, (newValue) => { watch(
console.log('moreSelectValue 变化:', newValue) () => moreSelectValue.value,
newValue => {
console.log('moreSelectValue 变化:', newValue);
if (newValue && nodeMap.size > 0) { if (newValue && nodeMap.size > 0) {
// nodeMap // nodeMap
const parentKeys = getParentKeys(newValue as string) const parentKeys = getParentKeys(newValue as string);
treeExpandedKeys.value = parentKeys treeExpandedKeys.value = parentKeys;
console.log('自动展开父节点:', parentKeys) console.log('自动展开父节点:', parentKeys);
} else { } else {
treeExpandedKeys.value = [] treeExpandedKeys.value = [];
} }
}, { immediate: true }) // immediate: true },
{ immediate: true }
); // immediate: true
// //
watch(() => processedMoreSelectOptions.value, (newData) => { watch(
console.log('树数据变化,重新构建映射') () => processedMoreSelectOptions.value,
newData => {
console.log('树数据变化,重新构建映射');
if (newData && newData.length > 0) { if (newData && newData.length > 0) {
// //
nodeMap.clear() nodeMap.clear();
// //
buildNodeMap(newData) buildNodeMap(newData);
// //
if (moreSelectValue.value) { if (moreSelectValue.value) {
nextTick(() => { nextTick(() => {
const parentKeys = getParentKeys(moreSelectValue.value as string) const parentKeys = getParentKeys(moreSelectValue.value as string);
treeExpandedKeys.value = parentKeys treeExpandedKeys.value = parentKeys;
console.log('数据加载后自动展开父节点:', parentKeys) console.log('数据加载后自动展开父节点:', parentKeys);
}) });
} }
} }
}, { deep: true }) },
{ deep: true }
);
// //
onMounted(() => { onMounted(() => {
@ -826,7 +972,6 @@ onUnmounted(() => {
margin-left: 5px; margin-left: 5px;
cursor: pointer; cursor: pointer;
} }
} }
.title_right { .title_right {
@ -834,7 +979,6 @@ onUnmounted(() => {
align-items: center; align-items: center;
div { div {
margin-right: 2px; margin-right: 2px;
} }
} }
@ -854,7 +998,7 @@ onUnmounted(() => {
text-align: center; text-align: center;
background: #fff; background: #fff;
margin: 0px; margin: 0px;
color: rgba(0, 0, 0, .85); color: rgba(0, 0, 0, 0.85);
} }
.typezhong { .typezhong {
@ -866,7 +1010,7 @@ onUnmounted(() => {
.qgc_title:before { .qgc_title:before {
position: absolute; position: absolute;
content: ""; content: '';
display: inline-block; display: inline-block;
left: 0; left: 0;
width: 2px; width: 2px;
@ -887,7 +1031,6 @@ onUnmounted(() => {
height: calc(100% - 36px); height: calc(100% - 36px);
box-sizing: border-box; box-sizing: border-box;
p { p {
text-indent: 2em; text-indent: 2em;
} }

View File

@ -33,6 +33,7 @@ export class MapClass implements MapClassInterface {
this.service = new MapOl(); this.service = new MapOl();
} }
static getInstance(): MapClass { static getInstance(): MapClass {
console.log('getInstance');
if (!this.instance) { if (!this.instance) {
this.instance = new MapClass(); this.instance = new MapClass();
} }

View File

@ -72,6 +72,7 @@ export class MapOl implements MapInterface {
private hoveredFeatureId: string | number | null = null; private hoveredFeatureId: string | number | null = null;
private popupManager: PopupManager; private popupManager: PopupManager;
private regionMaskManager: RegionMaskManager; private regionMaskManager: RegionMaskManager;
private isBatchPopupMode = false;
constructor() { constructor() {
this.pointLayerManager = new PointLayerManager({ this.pointLayerManager = new PointLayerManager({
map: null, map: null,
@ -142,7 +143,11 @@ export class MapOl implements MapInterface {
this.popupManager.setMap(this.map); this.popupManager.setMap(this.map);
this.regionMaskManager.setContext(this.map, this.view); this.regionMaskManager.setContext(this.map, this.view);
this.view.on('change:resolution', () => {}); this.view.on('change:resolution', () => {
this.handleZoomChange();
const zoom = this.view.getZoom();
console.log('mouse zoom:', zoom);
});
this.map.on('click', evt => { this.map.on('click', evt => {
this.popupManager.showPopup(undefined, undefined); this.popupManager.showPopup(undefined, undefined);
this.popupManager.handleMapClick(evt.pixel, detectedFeature => { this.popupManager.handleMapClick(evt.pixel, detectedFeature => {
@ -169,9 +174,24 @@ export class MapOl implements MapInterface {
targetElement.style.cursor = payload.hoveredId ? 'pointer' : ''; targetElement.style.cursor = payload.hoveredId ? 'pointer' : '';
} }
// 批量 popup 模式下(>= 14 级)不触发 hover popup
if (!this.isBatchPopupMode) {
this.showPopup(payload.detectedFeature, payload.coordinate); this.showPopup(payload.detectedFeature, payload.coordinate);
}
}); });
}); });
this.map.on('pointerdrag', () => {
if (this.isBatchPopupMode) {
this.updateBatchPopups();
}
});
this.map.on('moveend', () => {
if (this.isBatchPopupMode) {
this.updateBatchPopups();
}
});
return Promise.resolve(this.map); return Promise.resolve(this.map);
} catch (e) { } catch (e) {
console.error('OL Init Error', e); console.error('OL Init Error', e);
@ -220,6 +240,52 @@ export class MapOl implements MapInterface {
showPopup(feature: Feature | undefined, coordinate: number[] | undefined) { showPopup(feature: Feature | undefined, coordinate: number[] | undefined) {
this.popupManager.showPopup(feature, coordinate); this.popupManager.showPopup(feature, coordinate);
} }
/**
* >= 15 popup
*/
private handleZoomChange() {
if (!this.view) return;
const zoom = this.view.getZoom();
if (zoom === undefined) return;
if (zoom >= 15) {
this.enableBatchPopupMode();
} else {
this.disableBatchPopupMode();
}
}
/**
* popup popup
*/
private enableBatchPopupMode() {
this.isBatchPopupMode = true;
this.popupManager.clearBatchPopups();
this.updateBatchPopups();
}
/**
* popup popup
*/
private disableBatchPopupMode() {
this.isBatchPopupMode = false;
this.popupManager.clearBatchPopups();
this.popupManager.showPopup(undefined, undefined);
}
/**
* popups
*/
private updateBatchPopups() {
const features = this.pointLayerManager.getFeaturesInViewport();
// 传入样式检查器,过滤掉 declutter/距离过滤等隐藏的锚点
this.popupManager.showPopupsForFeatures(features, feature => {
const style = this.createPointStyle(feature);
return style !== null;
});
}
/** /**
* ( Leaflet DivIcon ) * ( Leaflet DivIcon )
*/ */
@ -660,6 +726,10 @@ export class MapOl implements MapInterface {
const layerInstance = this.layerRegistry.get(registryKey as string); const layerInstance = this.layerRegistry.get(registryKey as string);
if (layerInstance) { if (layerInstance) {
layerInstance.setVisible(checked); layerInstance.setVisible(checked);
// 图层显隐变化时刷新 batch popup
if (this.isBatchPopupMode) {
this.updateBatchPopups();
}
} else { } else {
console.warn( console.warn(
`未找到标识为 [${registryKey}] 的图层实例,当前注册表 keys:`, `未找到标识为 [${registryKey}] 的图层实例,当前注册表 keys:`,
@ -669,6 +739,10 @@ export class MapOl implements MapInterface {
} }
mdLayerTreeShowOrHidden(layerType: string, checked?: boolean): void { mdLayerTreeShowOrHidden(layerType: string, checked?: boolean): void {
this.pointLayerManager.setLayerVisible(layerType, checked); this.pointLayerManager.setLayerVisible(layerType, checked);
// 图层显隐变化时刷新 batch popup
if (this.isBatchPopupMode) {
this.updateBatchPopups();
}
} }
hasLayer(layerType: string): void { hasLayer(layerType: string): void {
@ -1212,7 +1286,6 @@ export class MapOl implements MapInterface {
// 使用 Object.assign 批量设置样式,代码更整洁 // 使用 Object.assign 批量设置样式,代码更整洁
Object.assign(container.style, { Object.assign(container.style, {
position: 'absolute', position: 'absolute',
backgroundColor: 'rgba(255, 255, 255, 0.9)',
border: '1px solid #000', border: '1px solid #000',
padding: '4px 8px', padding: '4px 8px',
borderRadius: '4px', borderRadius: '4px',
@ -1223,7 +1296,6 @@ export class MapOl implements MapInterface {
display: 'flex', display: 'flex',
alignItems: 'center', alignItems: 'center',
zIndex: '1001', zIndex: '1001',
boxShadow: '0 2px 4px rgba(0,0,0,0.2)',
pointerEvents: 'auto' // 确保可以点击 pointerEvents: 'auto' // 确保可以点击
}); });
@ -1309,6 +1381,10 @@ export class MapOl implements MapInterface {
key || '', key || '',
!!checked !!checked
); );
// 图例变化时刷新 batch popup
if (this.isBatchPopupMode) {
this.updateBatchPopups();
}
} }
/** /**
@ -1327,6 +1403,10 @@ export class MapOl implements MapInterface {
anchoPointState, anchoPointState,
checked checked
); );
// 图例变化时刷新 batch popup
if (this.isBatchPopupMode) {
this.updateBatchPopups();
}
} }
switchView(_type): void {} switchView(_type): void {}

View File

@ -32,6 +32,34 @@ export class PointLayerManager {
this.map = map; this.map = map;
} }
// 备注:获取当前屏幕可视区域内的所有点要素(仅包含图层可见且图例可见的要素)。
getFeaturesInViewport(): Feature[] {
if (!this.map) return [];
const extent = this.map.getView().calculateExtent(this.map.getSize());
if (!extent) return [];
const [minX, minY, maxX, maxY] = extent;
const result: Feature[] = [];
this.forEachFeature((feature, layer) => {
// 跳过图层本身不可见的要素
if (!layer.getVisible()) return;
const geom = feature.getGeometry();
if (!geom || geom.getType() !== 'Point') return;
const coords = (geom as Point).getCoordinates();
const [x, y] = coords;
if (x >= minX && x <= maxX && y >= minY && y <= maxY) {
result.push(feature);
}
});
return result;
}
// 备注:统一返回当前点图层注册表,供外层做遍历或清理。 // 备注:统一返回当前点图层注册表,供外层做遍历或清理。
getRegistry() { getRegistry() {
return this.layerRegistry; return this.layerRegistry;
@ -128,6 +156,7 @@ export class PointLayerManager {
const vectorLayer = this.layerRegistry.get(layerKey); const vectorLayer = this.layerRegistry.get(layerKey);
if (vectorLayer && vectorLayer.getVisible() !== visible) { if (vectorLayer && vectorLayer.getVisible() !== visible) {
vectorLayer.setVisible(visible); vectorLayer.setVisible(visible);
vectorLayer.changed();
} }
} }

View File

@ -204,6 +204,213 @@ export class PopupManager {
this.popupElement.style.display = 'none'; this.popupElement.style.display = 'none';
} }
// 备注:批量显示多个要素的 Popup使用绝对定位的 div 而非 Overlay。
showPopupsForFeatures(
features: Feature[],
styleChecker?: (feature: Feature) => boolean
) {
if (!this.map || !this.popupElement) return;
// 清除之前批量显示的 popups
this.clearBatchPopups();
// 过滤:只显示图例可见的要素
const visibleFeatures = features.filter(
f => f.get('_legendVisible') !== false
);
if (visibleFeatures.length === 0) return;
const mapElement = this.map.getTargetElement();
const batchContainer = document.createElement('div');
batchContainer.className = 'batch-popup-container';
batchContainer.style.cssText = `
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
pointer-events: none;
z-index: 1000;
`;
batchContainer.id = 'batch-popup-container';
// 先添加容器到 DOM以便后续元素能正确测量尺寸
mapElement.appendChild(batchContainer);
// 用于碰撞检测的已放置 popup 列表
const placedPopups: Array<{
left: number;
right: number;
top: number;
bottom: number;
}> = [];
let styleHiddenCount = 0;
let blockedCount = 0;
let renderedCount = 0;
const renderedIds: Array<string | number | null> = [];
const blockedIds: Array<string | number | null> = [];
// 用于 declutter 模拟检测的已放置要素边界框列表
const placedAnchors: Array<{
bbLeft: number;
bbRight: number;
bbTop: number;
bbBottom: number;
}> = [];
visibleFeatures.forEach(feature => {
const geom = feature.getGeometry();
if (!geom || geom.getType() !== 'Point') return;
const coords = (geom as Point).getCoordinates();
const pixel = this.map?.getPixelFromCoordinate(coords);
if (!pixel) return;
// 检查样式是否隐藏declutter、距离过滤等
if (styleChecker && !styleChecker(feature)) {
styleHiddenCount += 1;
return;
}
// 模拟 OpenLayers declutter基于图标+文字的边界框碰撞检测
const zoom = this.map.getView().getZoom() ?? 14;
let dynamicScale = 0.7 + (zoom - 4.5) * 0.08;
dynamicScale = Math.max(0.5, Math.min(3.0, dynamicScale));
// 计算要素的碰撞边界框
const labelText = (feature.get('_labelText') as string) || '';
const labelLineCount = labelText.split('\n').length;
const labelWidth = Math.min(
labelText
.split('\n')
.reduce((max, line) => Math.max(max, line.length), 0) *
7 *
dynamicScale,
200
);
const labelHeight = 12 * dynamicScale * labelLineCount;
const labelOffsetY =
labelLineCount > 1 ? -30 * dynamicScale : -22 * dynamicScale;
const iconSize = 32 * dynamicScale; // 假设图标原始大小 32x32
// 碰撞框 = 图标框 文字框
const bbLeft = pixel[0] - Math.max(iconSize / 2, labelWidth / 2);
const bbRight = pixel[0] + Math.max(iconSize / 2, labelWidth / 2);
const bbTop = pixel[1] + labelOffsetY - labelHeight;
const bbBottom = pixel[1] + iconSize / 2;
// 检查是否和已放置的要素碰撞
let blocked = false;
for (const placed of placedAnchors) {
if (
bbRight > placed.bbLeft &&
bbLeft < placed.bbRight &&
bbBottom > placed.bbTop &&
bbTop < placed.bbBottom
) {
blocked = true;
break;
}
}
if (blocked) {
blockedCount += 1;
blockedIds.push((feature.getId?.() as string | number | null) ?? null);
return;
}
const props = feature.getProperties();
const popupHtml = props.popupHtml || generatePopupHtml(props);
if (!popupHtml) return;
// 记录边界框用于 declutter 检测
placedAnchors.push({ bbLeft, bbRight, bbTop, bbBottom });
// 创建与原始 popupElement 完全相同的样式
const popupEl = document.createElement('div');
popupEl.className = this.popupElement.className;
popupEl.innerHTML = popupHtml;
// 不设置自定义 cssText只设置定位对应原始 popup 的 positioning: 'bottom-center', offset: [0, -10]
popupEl.style.position = 'absolute';
popupEl.style.display = 'block';
popupEl.style.transform = 'translate(-50%, -100%)'; // bottom-center 对齐
popupEl.style.pointerEvents = 'none';
// 临时设置 visibility: hidden 来测量尺寸
popupEl.style.visibility = 'hidden';
popupEl.style.left = `${pixel[0]}px`;
popupEl.style.top = `${pixel[1] - 10}px`;
batchContainer.appendChild(popupEl);
// 获取 popup 尺寸
const rect = popupEl.getBoundingClientRect();
const popupWidth = rect.width;
const popupHeight = rect.height;
// 计算 popup 的位置(对应原始 positioning: 'bottom-center', offset: [0, -10]
const popupX = pixel[0];
const popupY = pixel[1] - 10; // bottom-center 对齐 + 10px 偏移
const popupRect = {
left: popupX - popupWidth / 2,
right: popupX + popupWidth / 2,
top: popupY - popupHeight,
bottom: popupY
};
// 碰撞检测
let hasCollision = false;
for (const placed of placedPopups) {
if (this.checkCollision(popupRect, placed)) {
hasCollision = true;
break;
}
}
if (!hasCollision) {
// 无碰撞,显示 popup
popupEl.style.visibility = 'visible';
popupEl.style.left = `${popupX}px`;
popupEl.style.top = `${popupY}px`;
renderedCount += 1;
renderedIds.push((feature.getId?.() as string | number | null) ?? null);
placedPopups.push({
left: popupRect.left,
right: popupRect.right,
top: popupRect.top,
bottom: popupRect.bottom
});
} else {
// 有碰撞,移除该 popup
blockedCount += 1;
blockedIds.push((feature.getId?.() as string | number | null) ?? null);
batchContainer.removeChild(popupEl);
}
});
}
// 备注:清除批量显示的 popups。
clearBatchPopups() {
const existing = document.getElementById('batch-popup-container');
if (existing) {
existing.remove();
}
}
// 备注:检测两个 popup 矩形是否碰撞。
private checkCollision(
rect1: { left: number; right: number; top: number; bottom: number },
rect2: { left: number; right: number; top: number; bottom: number }
): boolean {
const padding = 4;
return !(
rect1.right + padding < rect2.left ||
rect1.left - padding > rect2.right ||
rect1.bottom + padding < rect2.top ||
rect1.top - padding > rect2.bottom
);
}
// 备注:统一重置 hover 和 Popup 状态,供地图销毁和切换时复用。 // 备注:统一重置 hover 和 Popup 状态,供地图销毁和切换时复用。
reset() { reset() {
if (this.animationFrameId) { if (this.animationFrameId) {
@ -264,6 +471,7 @@ export class PopupManager {
// 备注:销毁 Popup 管理器内部状态和 Overlay 引用。 // 备注:销毁 Popup 管理器内部状态和 Overlay 引用。
destroy() { destroy() {
this.clearBatchPopups();
this.reset(); this.reset();
if (this.popupElement && this.popupMouseEnterHandler) { if (this.popupElement && this.popupMouseEnterHandler) {
this.popupElement.removeEventListener( this.popupElement.removeEventListener(

View File

@ -1,11 +1,19 @@
<!-- 垂向水温变化图 --> <!-- 垂向水温变化图 -->
<template> <template>
<SidePanelItem title="垂向水温变化" :moreSelect="select" :datetimePicker="datetimePicker" <SidePanelItem
@update-values="handlePanelChange1"> title="垂向水温变化"
:moreSelect="select"
:datetimePicker="datetimePicker"
@update-values="handlePanelChange1"
>
<div class="chart-wrapper"> <div class="chart-wrapper">
<!-- 图表容器 --> <!-- 图表容器 -->
<a-spin :spinning="loading" tip="加载中..."> <a-spin :spinning="loading" tip="加载中...">
<div v-show="showemit && !loading" ref="chartRef" class="chart-container"></div> <div
v-show="showemit && !loading"
ref="chartRef"
class="chart-container"
></div>
<div v-show="!showemit && !loading" class="chart-container"> <div v-show="!showemit && !loading" class="chart-container">
<a-empty /> <a-empty />
</div> </div>
@ -15,18 +23,29 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref, computed, onMounted, onBeforeUnmount, watch, nextTick } from 'vue'; import {
ref,
computed,
onMounted,
onBeforeUnmount,
watch,
nextTick
} from 'vue';
import * as echarts from 'echarts'; import * as echarts from 'echarts';
import type { ECharts } from 'echarts'; import type { ECharts } from 'echarts';
import SidePanelItem from '@/components/SidePanelItem/index.vue'; import SidePanelItem from '@/components/SidePanelItem/index.vue';
import { useJidiSelectEventStore } from "@/store/modules/jidiSelectEvent"; import { useJidiSelectEventStore } from '@/store/modules/jidiSelectEvent';
import { getChuiXiangShuiWenTreeStcd, getCxswList } from "@/api/sw"; import { getChuiXiangShuiWenTreeStcd, getCxswList } from '@/api/sw';
import { useModelStore } from "@/store/modules/model"; import { useModelStore } from '@/store/modules/model';
import { useMapModalStore } from '@/store/modules/mapModal';
import { MapClass } from '@/components/gis/map.class';
const modelStore = useModelStore(); const modelStore = useModelStore();
const JidiSelectEventStore = useJidiSelectEventStore(); const JidiSelectEventStore = useJidiSelectEventStore();
const baseid = ref(''); const baseid = ref('');
//
const year = ref('');
// //
defineOptions({ defineOptions({
name: 'chuixiangshuiwenChangeMod' name: 'chuixiangshuiwenChangeMod'
@ -179,9 +198,18 @@ function generateColorsAndVisibility(
selected: Record<string, boolean>; selected: Record<string, boolean>;
} { } {
const baseColors = [ const baseColors = [
'#6ca4f7', '#78c300', '#9556a4', '#df91ab', '#6ca4f7',
'#7399c6', '#dbb629', '#56c2e3', '#f56a06', '#78c300',
'#cdba75', '#76523b', '#df75b6', '#00a050' '#9556a4',
'#df91ab',
'#7399c6',
'#dbb629',
'#56c2e3',
'#f56a06',
'#cdba75',
'#76523b',
'#df75b6',
'#00a050'
]; ];
const currentYear = new Date().getFullYear(); const currentYear = new Date().getFullYear();
@ -233,7 +261,7 @@ const getCurrentHourTimeTwo = () => {
const startTime = new Date(endTime); const startTime = new Date(endTime);
startTime.setMonth(startTime.getMonth() - 1); startTime.setMonth(startTime.getMonth() - 1);
const formatDateTime = (date) => { const formatDateTime = date => {
const year = date.getFullYear(); const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0'); const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0'); const day = String(date.getDate()).padStart(2, '0');
@ -253,15 +281,15 @@ const select = ref({
value: '', value: '',
options: [], options: [],
picker: undefined, picker: undefined,
format: undefined, format: undefined
}); });
const datetimePicker = ref({ const datetimePicker = ref({
show: true, show: true,
value: computed(() => getCurrentHourTime()), value: computed(() => getCurrentHourTime()),
format: "YYYY", format: 'YYYY',
picker: "year" as const, picker: 'year' as const,
options: [], options: []
}); });
/** /**
@ -273,58 +301,61 @@ const selectOptions = async () => {
try { try {
let filters = [ let filters = [
{ {
"field": "mway", field: 'mway',
"operator": "eq", operator: 'eq',
"dataType": "string", dataType: 'string',
"value": 2 value: 2
}, },
{ {
"field": "tm", field: 'tm',
"operator": "gte", operator: 'gte',
"dataType": "date", dataType: 'date',
"value": getCurrentHourTimeTwo()[0] value: getCurrentHourTimeTwo()[0]
}, },
{ {
"field": "tm", field: 'tm',
"operator": "lte", operator: 'lte',
"dataType": "date", dataType: 'date',
"value": getCurrentHourTimeTwo()[1] value: getCurrentHourTimeTwo()[1]
} }
]; ];
if (baseid.value && baseid.value !== 'all') { if (baseid.value && baseid.value !== 'all') {
filters.push({ filters.push({
"field": "baseId", field: 'baseId',
"operator": "eq", operator: 'eq',
"dataType": "string", dataType: 'string',
"value": baseid.value value: baseid.value
}); });
} }
let filter = { let filter = {
"logic": "and", logic: 'and',
"filters": filters filters: filters
}; };
let sort = baseid.value && baseid.value !== 'all' ? [ let sort =
baseid.value && baseid.value !== 'all'
? [
{ {
"field": "rstcdStepSort", field: 'rstcdStepSort',
"dir": "asc" dir: 'asc'
} }
] : [ ]
: [
{ {
"field": "baseId", field: 'baseId',
"dir": "asc" dir: 'asc'
}, },
{ {
"field": "rstcdStepSort", field: 'rstcdStepSort',
"dir": "asc" dir: 'asc'
} }
]; ];
const data = { filter, sort }; const data = { filter, sort };
const res = await getChuiXiangShuiWenTreeStcd(data); const res = await getChuiXiangShuiWenTreeStcd(data);
let dataOne = res?.data?.data || res.data let dataOne = res?.data?.data || res.data;
if (dataOne.length > 0) { if (dataOne.length > 0) {
const treeData = dataOne.map(item => { const treeData = dataOne.map(item => {
@ -334,27 +365,29 @@ const selectOptions = async () => {
selectable: false, selectable: false,
children: (item.stcdVo || []).map(child => ({ children: (item.stcdVo || []).map(child => ({
title: child.stnm, title: child.stnm,
value: child.stcd value: child.stcd,
lgtd: child.lgtd,
lttd: child.lttd
})) }))
}; };
}); });
if (baseid.value == 'all') { if (baseid.value == 'all') {
select.value.value = '008640203800001014' select.value.value = '008640203800001014';
select.value.options = treeData; select.value.options = treeData;
} else { } else {
select.value.value = treeData[0].children[0].value select.value.value = treeData[0].children[0].value;
select.value.options = treeData; select.value.options = treeData;
} }
} else { } else {
select.value.options = [] select.value.options = [];
select.value.value = '' select.value.value = '';
} }
} catch (error) { } catch (error) {
console.error('获取站点数据失败:', error); console.error('获取站点数据失败:', error);
} finally { } finally {
loading.value = false; // loading loading.value = false; // loading
} }
} };
// ==================== ==================== // ==================== ====================
@ -362,14 +395,16 @@ const chartRef = ref<HTMLElement | null>(null);
let chartInstance: ECharts | null = null; let chartInstance: ECharts | null = null;
// //
const chartData = ref<Array<{ const chartData = ref<
Array<{
name: string; name: string;
dataXy: Array<{ dataXy: Array<{
value: [number, number]; value: [number, number];
stcd: string; stcd: string;
stnm: string; stnm: string;
}>; }>;
}>>([]); }>
>([]);
// Y1=2= // Y1=2=
const type = ref(1); const type = ref(1);
@ -390,26 +425,26 @@ const fetchChartData = async (stcd: string, year: string | number) => {
try { try {
const params = { const params = {
filter: { filter: {
logic: "and", logic: 'and',
filters: [ filters: [
{ {
field: "year", field: 'year',
operator: "eq", operator: 'eq',
dataType: "string", dataType: 'string',
value: String(year) value: String(year)
}, },
{ {
field: "stcd", field: 'stcd',
operator: "eq", operator: 'eq',
dataType: "string", dataType: 'string',
value: stcd value: stcd
} }
] ]
}, },
sort: [ sort: [
{ {
field: "depth", field: 'depth',
dir: "asc" dir: 'asc'
} }
] ]
}; };
@ -429,10 +464,15 @@ const fetchChartData = async (stcd: string, year: string | number) => {
}); });
// stcd stnm // stcd stnm
chartData.value = transformAggregatesToChartData(aggregates, stcd, currentStnm); chartData.value = transformAggregatesToChartData(
aggregates,
stcd,
currentStnm
);
// 12 chartData.value.length // 12 chartData.value.length
const { colors: newColors, selected: newSelected } = generateColorsAndVisibility( const { colors: newColors, selected: newSelected } =
generateColorsAndVisibility(
12, // 12 12, // 12
year year
); );
@ -440,7 +480,10 @@ const fetchChartData = async (stcd: string, year: string | number) => {
selected.value = newSelected; selected.value = newSelected;
// showemit // showemit
if (chartData.value.length > 0 && chartData.value.some(item => item.dataXy.length > 0)) { if (
chartData.value.length > 0 &&
chartData.value.some(item => item.dataXy.length > 0)
) {
showemit.value = true; // showemit.value = true; //
} else { } else {
showemit.value = false; // showemit.value = false; //
@ -490,12 +533,26 @@ const initChart = () => {
console.log('站点名称:', stnm); console.log('站点名称:', stnm);
console.log('水温:', temperature); console.log('水温:', temperature);
console.log('深度:', depth); console.log('深度:', depth);
const matchMonth = params.seriesName.match(/\d+/);
const monthNum = matchMonth ? parseInt(matchMonth[0]) : 1;
const startTime = `${year.value}-${String(monthNum).padStart(
2,
'0'
)}-01 00:00:00`;
const lastDay = new Date(year.value, monthNum, 0).getDate();
const endTime = `${year.value}-${String(monthNum).padStart(
2,
'0'
)}-${lastDay} 23:59:59`;
console.log(endTime);
modelStore.modalVisible = true; modelStore.modalVisible = true;
modelStore.params.sttp = "WT"; modelStore.params.sttp = 'WT';
modelStore.params.enfc = '1'; modelStore.params.enfc = '1';
modelStore.title = stnm ; modelStore.title = stnm;
// modelStore.isBasicEdit = true; // modelStore.isBasicEdit = true;
modelStore.params.stcd = stcd; modelStore.params.stcd = stcd;
modelStore.filter.rangeTm = [startTime, endTime];
// TODO: // TODO:
}); });
@ -511,7 +568,7 @@ const updateChart = () => {
const yData: any[] = []; const yData: any[] = [];
const legend: string[] = []; const legend: string[] = [];
chartData.value.forEach((item) => { chartData.value.forEach(item => {
if (item.dataXy.length > 0) { if (item.dataXy.length > 0) {
// value[0] // value[0]
xData.push(item.dataXy[0].value[0]); xData.push(item.dataXy[0].value[0]);
@ -612,13 +669,17 @@ const handleChartClick = (params: any) => {
const matchMonth = params.seriesName.match(/\d+/); const matchMonth = params.seriesName.match(/\d+/);
const monthNum = matchMonth ? parseInt(matchMonth[0]) : 1; const monthNum = matchMonth ? parseInt(matchMonth[0]) : 1;
const year = typeof datetimePicker.value.value === 'string' const year =
typeof datetimePicker.value.value === 'string'
? parseInt(datetimePicker.value.value) ? parseInt(datetimePicker.value.value)
: datetimePicker.value.value; : datetimePicker.value.value;
const startTime = `${year}-${String(monthNum).padStart(2, '0')}-01 00:00:00`; const startTime = `${year}-${String(monthNum).padStart(2, '0')}-01 00:00:00`;
const lastDay = new Date(year, monthNum, 0).getDate(); const lastDay = new Date(year, monthNum, 0).getDate();
const endTime = `${year}-${String(monthNum).padStart(2, '0')}-${lastDay} 23:59:59`; const endTime = `${year}-${String(monthNum).padStart(
2,
'0'
)}-${lastDay} 23:59:59`;
const currentStcd = select.value.value; const currentStcd = select.value.value;
let currentStnm = ''; let currentStnm = '';
@ -669,7 +730,7 @@ onBeforeUnmount(() => {
watch( watch(
() => JidiSelectEventStore.selectedItem, () => JidiSelectEventStore.selectedItem,
async (newVal) => { async newVal => {
if (!newVal || !newVal.wbsCode) { if (!newVal || !newVal.wbsCode) {
return; return;
} }
@ -689,20 +750,28 @@ watch(
// }, // },
// { deep: true } // { deep: true }
// ); // );
const handlePanelChange1 = (data: any, type: string) => {
const handlePanelChange1 = (data: any) => {
console.log('当前所有控件状态:', data); console.log('当前所有控件状态:', data);
if (data.moreSelect && data.datetime) { if (data.moreSelect && data.datetime) {
fetchChartData(data.moreSelect, data.datetime) if (type == 'click') {
const mapClass = MapClass.getInstance();
mapClass.flyTopanto(
[
Number(data.selectedNodeExtra.lgtd),
Number(data.selectedNodeExtra.lttd)
],
15
);
}
year.value = data.datetime;
fetchChartData(data.moreSelect, data.datetime);
} else { } else {
showemit.value = true showemit.value = true;
chartData.value = []; chartData.value = [];
colors.value = []; colors.value = [];
selected.value = {}; selected.value = {};
showemit.value = false; showemit.value = false;
} }
}; };
</script> </script>

View File

@ -6,21 +6,39 @@
特性支持月份选择站点筛选数据缩放空值断开显示 特性支持月份选择站点筛选数据缩放空值断开显示
--> -->
<template> <template>
<SidePanelItem title="出入库水温" :moreSelect="select" :scopeDate="scopeDate" @update-values="handlePanelChange1"> <SidePanelItem
title="出入库水温"
:moreSelect="select"
:scopeDate="scopeDate"
@update-values="handlePanelChange1"
>
<!-- ECharts图表容器 --> <!-- ECharts图表容器 -->
<a-spin :spinning="loading" tip="加载中..."> <a-spin :spinning="loading" tip="加载中...">
<div v-show="showemit && !loading" ref="chartContainer" class="chart-container"></div> <div
v-show="showemit && !loading"
ref="chartContainer"
class="chart-container"
></div>
<div v-show="!showemit && !loading" class="chart-container"> <div v-show="!showemit && !loading" class="chart-container">
<a-empty /> <a-empty />
</div> </div>
</a-spin> </a-spin>
<!-- 数据点详情弹框 --> <!-- 数据点详情弹框 -->
<a-modal v-model:open="modalVisible" :title="`${stationName}出入库水温`" :width="1536" :footer="null" <a-modal
@cancel="handleModalClose"> v-model:open="modalVisible"
:title="`${stationName}出入库水温`"
:width="1536"
:footer="null"
@cancel="handleModalClose"
>
<!-- 弹框内容区域 - 待完善 --> <!-- 弹框内容区域 - 待完善 -->
<div class="modal-content"> <div class="modal-content">
<churukushuiwen v-if="clickDataInfo" :tm="[clickDataInfo.date, clickDataInfo.date]" :stcd="paramsOne.value" /> <churukushuiwen
v-if="clickDataInfo"
:tm="[clickDataInfo.date, clickDataInfo.date]"
:stcd="paramsOne.value"
/>
</div> </div>
</a-modal> </a-modal>
</SidePanelItem> </SidePanelItem>
@ -32,8 +50,8 @@ import * as echarts from 'echarts';
import dayjs from 'dayjs'; import dayjs from 'dayjs';
import SidePanelItem from '@/components/SidePanelItem/index.vue'; import SidePanelItem from '@/components/SidePanelItem/index.vue';
import { getVmsstbprpt, inOutOneGetKendoListCust } from '@/api/sw'; import { getVmsstbprpt, inOutOneGetKendoListCust } from '@/api/sw';
import { useJidiSelectEventStore } from "@/store/modules/jidiSelectEvent"; import { useJidiSelectEventStore } from '@/store/modules/jidiSelectEvent';
import churukushuiwen from './churukushuiwen.vue' import churukushuiwen from './churukushuiwen.vue';
// ==================== ==================== // ==================== ====================
// (便) // (便)
defineOptions({ defineOptions({
@ -59,16 +77,16 @@ const select = ref({
value: '', // () value: '', // ()
options: [], // options: [], //
picker: undefined, // (使) picker: undefined, // (使)
format: undefined, // (使) format: undefined // (使)
}); });
// ==================== ==================== // ==================== ====================
const scopeDate = ref({ const scopeDate = ref({
show: true, // show: true, //
value: [currentMonth, currentMonth] as any, // 使 as any value: [currentMonth, currentMonth] as any, // 使 as any
format: "YYYY-MM", // format: 'YYYY-MM', //
picker: "month" as const, // : picker: 'month' as const, // :
options: [], // (使) options: [] // (使)
}); });
// ==================== ==================== // ==================== ====================
@ -86,34 +104,34 @@ const stationName = ref(''); // 站点名称
*/ */
function transformChartData(rawData: any[]) { function transformChartData(rawData: any[]) {
// 1. // 1.
const dateSet = new Set<string>() const dateSet = new Set<string>();
rawData.forEach(item => { rawData.forEach(item => {
const date = item.tm?.split(' ')[0] // "2026-04-01 00:00:00" -> "2026-04-01" const date = item.tm?.split(' ')[0]; // "2026-04-01 00:00:00" -> "2026-04-01"
if (date) dateSet.add(date) if (date) dateSet.add(date);
}) });
const dates = Array.from(dateSet).sort() const dates = Array.from(dateSet).sort();
// 2. 线() // 2. 线()
const iwtData: (number | null)[] = [] // (Inlet Water Temperature) const iwtData: (number | null)[] = []; // (Inlet Water Temperature)
const dwtData: (number | null)[] = [] // (Discharge Water Temperature) const dwtData: (number | null)[] = []; // (Discharge Water Temperature)
dates.forEach(date => { dates.forEach(date => {
// (dwtp='IWT') // (dwtp='IWT')
const iwtItem = rawData.find(item => const iwtItem = rawData.find(
item.tm?.startsWith(date) && item.dwtp === 'IWT' item => item.tm?.startsWith(date) && item.dwtp === 'IWT'
) );
// ,null(线) // ,null(线)
iwtData.push(iwtItem ? Number(iwtItem.wt.toFixed(1)) : null) iwtData.push(iwtItem ? Number(iwtItem.wt.toFixed(1)) : null);
// (dwtp='DWT') // (dwtp='DWT')
const dwtItem = rawData.find(item => const dwtItem = rawData.find(
item.tm?.startsWith(date) && item.dwtp === 'DWT' item => item.tm?.startsWith(date) && item.dwtp === 'DWT'
) );
// ,null(线) // ,null(线)
dwtData.push(dwtItem ? Number(dwtItem.wt.toFixed(1)) : null) dwtData.push(dwtItem ? Number(dwtItem.wt.toFixed(1)) : null);
}) });
return { dates, iwtData, dwtData } return { dates, iwtData, dwtData };
} }
/** /**
@ -124,17 +142,17 @@ function transformChartData(rawData: any[]) {
*/ */
function calculateYAxisRange(data: (number | null)[]) { function calculateYAxisRange(data: (number | null)[]) {
// //
const validValues = data.filter(v => v !== null) as number[] const validValues = data.filter(v => v !== null) as number[];
if (validValues.length === 0) return { min: 0, max: 10 } // if (validValues.length === 0) return { min: 0, max: 10 }; //
const min = Math.min(...validValues) const min = Math.min(...validValues);
const max = Math.max(...validValues) const max = Math.max(...validValues);
// 0.2,线 // 0.2,线
return { return {
min: Number((min - 0.2).toFixed(1)), // 0.2 min: Number((min - 0.2).toFixed(1)), // 0.2
max: Number((max + 0.1).toFixed(1)) // 0.2 max: Number((max + 0.1).toFixed(1)) // 0.2
} };
} }
/** /**
@ -144,13 +162,13 @@ function calculateYAxisRange(data: (number | null)[]) {
* @returns X轴刻度间隔值(interval属性) * @returns X轴刻度间隔值(interval属性)
*/ */
function calculateXAxisInterval(dateCount: number): number { function calculateXAxisInterval(dateCount: number): number {
if (dateCount <= 4) return 0 // 4, if (dateCount <= 4) return 0; // 4,
return Math.ceil(dateCount / 4) - 1 // ,4 return Math.ceil(dateCount / 4) - 1; // ,4
} }
const paramsOne:any = ref({ const paramsOne: any = ref({
value: '', value: '',
tm: [currentMonth, currentMonth] tm: [currentMonth, currentMonth]
}) });
/** /**
* 获取图表数据 * 获取图表数据
* 调用后端接口获取出入库水温数据,并触发图表更新 * 调用后端接口获取出入库水温数据,并触发图表更新
@ -163,42 +181,52 @@ async function fetchChartData() {
try { try {
// //
const startDate = paramsOne.value.tm[0] + '-01 00:00:00'; 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 endDate =
dayjs(paramsOne.value.tm[1]).endOf('month').format('YYYY-MM-DD') +
' 23:59:59';
// (,) // (,)
const params = { const params = {
filter: { filter: {
logic: "and", logic: 'and',
filters: [ filters: [
{ {
// OR: // OR:
logic: "or", logic: 'or',
filters: [ filters: [
{ field: "engDwtCode", operator: "eq", value: paramsOne.value.value }, // {
{ field: "engIwtCode", operator: "eq", value: paramsOne.value.value } // 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: 'gte', dataType: 'date', value: startDate },
{ field: "dt", operator: "lte", dataType: "date", value: endDate } { field: 'dt', operator: 'lte', dataType: 'date', value: endDate }
] ]
} }
} };
// API // API
const res = await inOutOneGetKendoListCust(params) const res = await inOutOneGetKendoListCust(params);
const rawData = res?.data?.data || [] const rawData = res?.data?.data || [];
console.log('原始数据:', rawData) console.log('原始数据:', rawData);
// showemit // showemit
if (rawData.length > 0) { if (rawData.length > 0) {
showemit.value = true; // showemit.value = true; //
// //
const { dates, iwtData, dwtData } = transformChartData(rawData) const { dates, iwtData, dwtData } = transformChartData(rawData);
console.log('转换后的数据:', { dates, iwtData, dwtData }) console.log('转换后的数据:', { dates, iwtData, dwtData });
// //
setTimeout(() => { setTimeout(() => {
@ -215,13 +243,11 @@ async function fetchChartData() {
} }
}, 50); }, 50);
}, 50); }, 50);
} else { } else {
showemit.value = false; // showemit.value = false; //
} }
chartDataLoaded.value = true; // chartDataLoaded.value = true; //
} catch (error) { } catch (error) {
console.error('获取图表数据失败:', error); console.error('获取图表数据失败:', error);
showemit.value = false; // showemit.value = false; //
@ -237,7 +263,11 @@ async function fetchChartData() {
* @param iwtData - 入库水温数据 * @param iwtData - 入库水温数据
* @param dwtData - 出库水温数据 * @param dwtData - 出库水温数据
*/ */
const initChartWithData = (dates: string[], iwtData: (number | null)[], dwtData: (number | null)[]) => { const initChartWithData = (
dates: string[],
iwtData: (number | null)[],
dwtData: (number | null)[]
) => {
if (!chartContainer.value) return; if (!chartContainer.value) return;
const containerHeight = chartContainer.value.offsetHeight; const containerHeight = chartContainer.value.offsetHeight;
@ -258,18 +288,29 @@ const initChartWithData = (dates: string[], iwtData: (number | null)[], dwtData:
* @param dwtData - 出库水温数据数组 * @param dwtData - 出库水温数据数组
* @param yAxisMax - 废弃参数,保留以兼容旧代码 * @param yAxisMax - 废弃参数,保留以兼容旧代码
*/ */
function updateChart(dates: string[], iwtData: (number | null)[], dwtData: (number | null)[], yAxisMax: number) { function updateChart(
dates: string[],
iwtData: (number | null)[],
dwtData: (number | null)[],
yAxisMax: number
) {
if (!chartInstance) { if (!chartInstance) {
console.warn('图表实例未初始化') console.warn('图表实例未初始化');
return return;
} }
try { try {
// ==================== ==================== // ==================== ====================
const xAxisInterval = calculateXAxisInterval(dates.length) // X const xAxisInterval = calculateXAxisInterval(dates.length); // X
const { min: yAxisMin, max: yAxisMaxValue } = calculateYAxisRange([...iwtData, ...dwtData]) // Y const { min: yAxisMin, max: yAxisMaxValue } = calculateYAxisRange([
...iwtData,
...dwtData
]); // Y
console.log('X轴间隔:', xAxisInterval, 'Y轴范围:', { min: yAxisMin, max: yAxisMaxValue }) console.log('X轴间隔:', xAxisInterval, 'Y轴范围:', {
min: yAxisMin,
max: yAxisMaxValue
});
// ==================== ECharts ==================== // ==================== ECharts ====================
const option = { const option = {
@ -297,7 +338,10 @@ function updateChart(dates: string[], iwtData: (number | null)[], dwtData: (numb
let result = `<div style="font-weight: bold; margin-bottom: 8px; font-size: 14px;">${date}</div>`; let result = `<div style="font-weight: bold; margin-bottom: 8px; font-size: 14px;">${date}</div>`;
params.forEach((item: any) => { params.forEach((item: any) => {
// '-', // '-',
const value = item.value !== null && item.value !== undefined ? `${item.value}°C` : '-' 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;"> 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 style="display: inline-block; width: 10px; height: 10px; border-radius: 50%; background-color: ${item.color};"></span>
<span>${item.seriesName}</span> <span>${item.seriesName}</span>
@ -465,7 +509,7 @@ function updateChart(dates: string[], iwtData: (number | null)[], dwtData: (numb
chartInstance.off('click'); // chartInstance.off('click'); //
chartInstance.on('click', handleChartClick); chartInstance.on('click', handleChartClick);
console.log('图表更新成功') console.log('图表更新成功');
// , // ,
setTimeout(() => { setTimeout(() => {
@ -473,9 +517,8 @@ function updateChart(dates: string[], iwtData: (number | null)[], dwtData: (numb
chartInstance.resize(); // chartInstance.resize(); //
} }
}, 50); }, 50);
} catch (error) { } catch (error) {
console.error('更新图表失败:', error) console.error('更新图表失败:', error);
} }
} }
@ -563,13 +606,23 @@ const handleModalClose = () => {
* 处理下拉框和日期选择器的值变化 * 处理下拉框和日期选择器的值变化
* @param data - 包含moreSelect(下拉框值)和datetime(日期范围)的对象 * @param data - 包含moreSelect(下拉框值)和datetime(日期范围)的对象
*/ */
const handlePanelChange1 = (data: any) => { const handlePanelChange1 = (data: any, type: string) => {
// TODO: // TODO:
if (data.moreSelect) { if (data.moreSelect) {
paramsOne.value.value = data.moreSelect; paramsOne.value.value = data.moreSelect;
} }
paramsOne.value.tm = data.scopeDate if (type == 'click') {
fetchChartData() const mapClass = MapClass.getInstance();
mapClass.flyTopanto(
[
Number(data.selectedNodeExtra.lgtd),
Number(data.selectedNodeExtra.lttd)
],
13
);
}
paramsOne.value.tm = data.scopeDate;
fetchChartData();
}; };
/** /**
@ -581,94 +634,114 @@ const getselsectData = async () => {
try { try {
let params = { let params = {
"filter": { filter: {
"logic": "and", logic: 'and',
"filters": [ filters: [
// baseid'all', // baseid'all',
baseid.value != 'all' ? { baseid.value != 'all'
"field": "baseId", ? {
"operator": "eq", field: 'baseId',
"dataType": "string", operator: 'eq',
"value": baseid.value dataType: 'string',
} : null, value: baseid.value
}
: null,
// :ENG // :ENG
{ {
"field": "sttpCode", field: 'sttpCode',
"operator": "contains", operator: 'contains',
"dataType": "string", dataType: 'string',
"value": "ENG" value: 'ENG'
}, },
// (dtin=1) // (dtin=1)
{ {
"field": "dtin", field: 'dtin',
"operator": "eq", operator: 'eq',
"dataType": "string", dataType: 'string',
"value": 1 value: 1
} }
].filter(Boolean) // null ].filter(Boolean) // null
}, },
"sort": [ sort: [
// 'all',; // 'all',;
baseid.value == 'all' ? { baseid.value == 'all'
"field": "baseStepSort", ? {
"dir": "asc" field: 'baseStepSort',
} : null, dir: 'asc'
}
: null,
{ {
"field": "rvcdStepSort", field: 'rvcdStepSort',
"dir": "asc" dir: 'asc'
}, },
{ {
"field": "siteStepSort", field: 'siteStepSort',
"dir": "asc" dir: 'asc'
} }
].filter(Boolean), // null ].filter(Boolean), // null
"select": [ select: [
"stcd", // 'stcd', //
"stnm", // 'stnm', //
"lgtd", // 'lgtd', //
"lttd", // 'lttd', //
"siteStepSort", // 'siteStepSort', //
"baseId", // ID 'baseId', // ID
"baseName" // 'baseName' //
] ]
} };
// API // API
const res = await getVmsstbprpt(params) const res = await getVmsstbprpt(params);
console.log('res', res) console.log('res', res);
const data = res?.data?.data || res?.data const data = res?.data?.data || res?.data;
const obj: any = {} const obj: any = {};
if (data) { if (data) {
// () // ()
data.map((e) => { data.map(e => {
if (obj[e.baseId]) { if (obj[e.baseId]) {
// , // ,
obj[e.baseId] = { ...obj[e.baseId], children: [...obj[e.baseId].children, { ...e, value: e.stcd, title: e.stnm }] } obj[e.baseId] = {
...obj[e.baseId],
children: [
...obj[e.baseId].children,
{ ...e, value: e.stcd, title: e.stnm }
]
};
} else { } else {
// //
obj[e.baseId] = { value: e.baseId, title: e.baseName, selectable: false, key: e.baseId, children: [{ ...e, value: e.stcd, title: e.stnm }] } obj[e.baseId] = {
value: e.baseId,
title: e.baseName,
selectable: false,
key: e.baseId,
children: [{ ...e, value: e.stcd, title: e.stnm }]
};
} }
}) });
// jiDiList // jiDiList
let treeData: any = [] let treeData: any = [];
const dataMapNameArr: any = Object.entries(obj).map(([, v]) => v) const dataMapNameArr: any = Object.entries(obj).map(([, v]) => v);
treeData = dataMapNameArr?.sort((a: any, b: any) => { treeData = dataMapNameArr?.sort((a: any, b: any) => {
const indexA = JidiSelectEventStore.jidiData.findIndex(item => item.wbsCode === a.value); const indexA = JidiSelectEventStore.jidiData.findIndex(
const indexB = JidiSelectEventStore.jidiData.findIndex(item => item.wbsCode === b.value); item => item.wbsCode === a.value
);
const indexB = JidiSelectEventStore.jidiData.findIndex(
item => item.wbsCode === b.value
);
return indexA - indexB; return indexA - indexB;
}) });
console.log('treeData', treeData) console.log('treeData', treeData);
// //
select.value.options = treeData select.value.options = treeData;
// //
if (baseid.value == 'all') { if (baseid.value == 'all') {
select.value.value = '008640203800000001' // select.value.value = '008640203800000001'; //
} else { } else {
select.value.value = treeData?.[0]?.children[0]?.value // select.value.value = treeData?.[0]?.children[0]?.value; //
} }
} }
} catch (error) { } catch (error) {
@ -676,7 +749,7 @@ const getselsectData = async () => {
} finally { } finally {
loading.value = false; // loading loading.value = false; // loading
} }
} };
// ==================== Watch ==================== // ==================== Watch ====================
@ -686,9 +759,9 @@ const getselsectData = async () => {
*/ */
watch( watch(
() => JidiSelectEventStore.selectedItem, // Store () => JidiSelectEventStore.selectedItem, // Store
(newVal) => { newVal => {
baseid.value = newVal.wbsCode; // ID baseid.value = newVal.wbsCode; // ID
getselsectData() // getselsectData(); //
}, },
{ deep: true, immediate: true } // , { deep: true, immediate: true } // ,
); );

View File

@ -107,6 +107,8 @@ export const useMapOrchestrator = () => {
mapDataStore.setLoading(true); mapDataStore.setLoading(true);
try { try {
const previousPageKey = mapConfigStore.lastLoadOptions?.pageKey || '';
const previousCheckedKeys = mapViewStore.getCheckedLayerKeys();
const { layerConfig, legendOriginal, pageLegend } = const { layerConfig, legendOriginal, pageLegend } =
await mapConfigStore.loadPageMapConfig({ await mapConfigStore.loadPageMapConfig({
systemId: SYSTEM_ID, systemId: SYSTEM_ID,
@ -122,11 +124,26 @@ export const useMapOrchestrator = () => {
if (layerConfig.length > 0) { if (layerConfig.length > 0) {
mapStore.setLayerData(layerConfig); mapStore.setLayerData(layerConfig);
ensureBaseLayersInitialized(layerConfig); ensureBaseLayersInitialized(layerConfig);
const checkedKeys = mapViewStore.getCheckedLayerKeys(); let checkedKeys = mapViewStore.getCheckedLayerKeys();
if (previousPageKey === pageKey && previousCheckedKeys.length > 0) {
const currentLayerKeys = new Set(
layerConfig
.flatMap((item: any) => layerConfig2Flat([item]))
.map((item: any) => item?.key)
.filter(Boolean)
);
const runtimeCheckedKeys = previousCheckedKeys.filter(key =>
currentLayerKeys.has(key)
);
if (runtimeCheckedKeys.length > 0) {
mapViewStore.setCheckedLayerKeys(runtimeCheckedKeys);
checkedKeys = runtimeCheckedKeys;
}
}
await mapStore.loadAllLayerData(layerConfig, checkedKeys); await mapStore.loadAllLayerData(layerConfig, checkedKeys);
} }
} finally { } finally {
mapDataStore.setLoading(false); // mapDataStore.setLoading(false);
} }
}; };
@ -380,7 +397,7 @@ export const useMapOrchestrator = () => {
}; };
// 备注:统一处理搜索定位,按点位编码从缓存数据中查找并飞行到目标位置。 // 备注:统一处理搜索定位,按点位编码从缓存数据中查找并飞行到目标位置。
const focusPoint = (pointId: string, zoom: number = 14) => { const focusPoint = (pointId: string, zoom: number = 15) => {
if (!pointId) return; if (!pointId) return;
const targetPoint = mapDataStore.pointData.find((item: any) => { const targetPoint = mapDataStore.pointData.find((item: any) => {
return item.stcd === pointId || item._id === pointId; return item.stcd === pointId || item._id === pointId;

View File

@ -1,36 +1,73 @@
<template> <template>
<div class="monthly-average-container"> <div class="monthly-average-container">
<!-- 搜索表单 --> <!-- 搜索表单 -->
<a-form ref="formRef" :model="formValue" layout="inline" class="search-form"> <a-form
ref="formRef"
:model="formValue"
layout="inline"
class="search-form"
>
<a-form-item label="基地名称" name="dataDimensionVal"> <a-form-item label="基地名称" name="dataDimensionVal">
<a-select v-model:value="formValue.dataDimensionVal" placeholder="请选择" style="width: 200px" <a-select
@change="handleBaseChange"> v-model:value="formValue.dataDimensionVal"
<a-select-option v-for="item in jdList" :key="item.wbsCode" :value="item.wbsCode"> placeholder="请选择"
style="width: 200px"
@change="handleBaseChange"
>
<a-select-option
v-for="item in jdList"
:key="item.wbsCode"
:value="item.wbsCode"
>
{{ item.wbsName }} {{ item.wbsName }}
</a-select-option> </a-select-option>
</a-select> </a-select>
</a-form-item> </a-form-item>
<a-form-item label="所在流域" name="rvcd"> <a-form-item label="所在流域" name="rvcd">
<a-select v-model:value="formValue.rvcd" placeholder="请选择" style="width: 200px" <a-select
:disabled="!formValue.dataDimensionVal" @change="handleRvcdChange"> v-model:value="formValue.rvcd"
<a-select-option v-for="item in lyList" :key="item.value" :value="item.value"> placeholder="请选择"
style="width: 200px"
:disabled="!formValue.dataDimensionVal"
@change="handleRvcdChange"
>
<a-select-option
v-for="item in lyList"
:key="item.value"
:value="item.value"
>
{{ item.label }} {{ item.label }}
</a-select-option> </a-select-option>
</a-select> </a-select>
</a-form-item> </a-form-item>
<a-form-item label="断面名称" name="stcd"> <a-form-item label="断面名称" name="stcd">
<a-select v-model:value="formValue.stcd" placeholder="请选择" style="width: 200px" :disabled="!formValue.rvcd"> <a-select
<a-select-option v-for="item in dmList" :key="item.value" :value="item.value"> v-model:value="formValue.stcd"
placeholder="请选择"
style="width: 200px"
:disabled="!formValue.rvcd"
>
<a-select-option
v-for="item in dmList"
:key="item.value"
:value="item.value"
>
{{ item.label }} {{ item.label }}
</a-select-option> </a-select-option>
</a-select> </a-select>
</a-form-item> </a-form-item>
<a-form-item label="月份" name="tm"> <a-form-item label="月份" name="tm">
<a-date-picker v-model:value="formValue.tm" picker="month" :disabled-date="disabledDate" format="YYYY-MM" <a-date-picker
value-format="YYYY-MM-DD HH:mm:ss" :allow-clear="false" /> v-model:value="formValue.tm"
picker="month"
:disabled-date="disabledDate"
format="YYYY-MM"
value-format="YYYY-MM-DD HH:mm:ss"
:allow-clear="false"
/>
</a-form-item> </a-form-item>
<a-form-item> <a-form-item>
@ -45,8 +82,14 @@
<div style="width: 100%; display: flex"> <div style="width: 100%; display: flex">
<!-- 图表容器始终存在不再使用 v-if --> <!-- 图表容器始终存在不再使用 v-if -->
<div class="chart-wrapper" ref="chartRef"></div> <div class="chart-wrapper" ref="chartRef"></div>
<BasicTable ref="tableRef" :scrollY="460" :columns="columns" :list-url="DetGetKendoListCust" <BasicTable
:search-params="{ sort: sort }" :transform-data="customTransform"> ref="tableRef"
:scrollY="460"
:columns="columns"
:list-url="DetGetKendoListCust"
:search-params="{ sort: sort }"
:transform-data="customTransform"
>
<template #action="{ record }"> <template #action="{ record }">
<a @click="handleViewDetail(record)" class="text_hocer">查看详情</a> <a @click="handleViewDetail(record)" class="text_hocer">查看详情</a>
</template> </template>
@ -816,8 +859,12 @@ const handleViewDetail = (record: any) => {
}); });
modelStore.modalVisible = true; modelStore.modalVisible = true;
modelStore.params.sttp = 'WT'; modelStore.params.sttp = 'WT';
modelStore.title = stnm ; modelStore.title = stnm;
modelStore.params.stcd = formValue.stcd; modelStore.params.stcd = formValue.stcd;
modelStore.filter.rangeTm = [
dayjs(formValue.value.tm).startOf('month').format('YYYY-MM-DD HH:mm:ss'),
dayjs(formValue.value.tm).endOf('month').format('YYYY-MM-DD HH:mm:ss')
];
}; };
defineExpose({ defineExpose({

View File

@ -1,6 +1,11 @@
<!-- SidePanelItem.vue --> <!-- SidePanelItem.vue -->
<template> <template>
<SidePanelItem title="水文监测" :select="select" :scopeDate="datetimePicker" @update-values="handlePanelChange1"> <SidePanelItem
title="水文监测"
:select="select"
:scopeDate="datetimePicker"
@update-values="handlePanelChange1"
>
<div class="chart-wrapper"> <div class="chart-wrapper">
<a-spin :spinning="loading"> <a-spin :spinning="loading">
<!-- 始终渲染图表容器确保有固定宽高 --> <!-- 始终渲染图表容器确保有固定宽高 -->
@ -19,9 +24,10 @@ import { ref, onMounted, onUnmounted, nextTick, watch } from 'vue';
import * as echarts from 'echarts'; import * as echarts from 'echarts';
import type { EChartsOption } from 'echarts'; import type { EChartsOption } from 'echarts';
import SidePanelItem from '@/components/SidePanelItem/index.vue'; import SidePanelItem from '@/components/SidePanelItem/index.vue';
import { useJidiSelectEventStore } from "@/store/modules/jidiSelectEvent"; import { useJidiSelectEventStore } from '@/store/modules/jidiSelectEvent';
import { msstbprptGetKendoList, sdriverdaysGetKendoList } from '@/api/qxd'; import { msstbprptGetKendoList, sdriverdaysGetKendoList } from '@/api/qxd';
import { useModelStore } from "@/store/modules/model"; import { useModelStore } from '@/store/modules/model';
import dayjs from 'dayjs';
// //
defineOptions({ defineOptions({
name: 'qixidiliuliangbianhua' name: 'qixidiliuliangbianhua'
@ -29,13 +35,12 @@ defineOptions({
// ==================== ==================== // ==================== ====================
const JidiSelectEventStore = useJidiSelectEventStore(); const JidiSelectEventStore = useJidiSelectEventStore();
const baseid = ref('') const baseid = ref('');
const modelStore = useModelStore(); const modelStore = useModelStore();
// Loading // Loading
const loading = ref(false); const loading = ref(false);
const hasData = ref(true); // true initChart const hasData = ref(true); // true initChart
const select = ref({ const select = ref({
show: true, show: true,
value: undefined, value: undefined,
@ -48,9 +53,13 @@ const datetimePicker: any = ref({
show: true, show: true,
value: (() => { value: (() => {
const now = new Date(); const now = new Date();
const currentMonth = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}`; const currentMonth = `${now.getFullYear()}-${String(
now.getMonth() + 1
).padStart(2, '0')}`;
const lastMonth = new Date(now.getFullYear(), now.getMonth() - 1, 1); const lastMonth = new Date(now.getFullYear(), now.getMonth() - 1, 1);
const lastMonthStr = `${lastMonth.getFullYear()}-${String(lastMonth.getMonth() + 1).padStart(2, '0')}`; const lastMonthStr = `${lastMonth.getFullYear()}-${String(
lastMonth.getMonth() + 1
).padStart(2, '0')}`;
return [lastMonthStr, currentMonth]; return [lastMonthStr, currentMonth];
})(), })(),
format: 'YY-MM', format: 'YY-MM',
@ -207,11 +216,15 @@ const initChart = () => {
// series // series
if (params.componentType === 'series') { if (params.componentType === 'series') {
modelStore.modalVisible = true; modelStore.modalVisible = true;
modelStore.params.sttp = "fh_zq_point"; modelStore.params.sttp = 'fh_zq_point';
modelStore.title = "水文监测"; modelStore.title = '水文监测';
modelStore.params.stcd = select.value.value; modelStore.params.stcd = select.value.value;
modelStore.showStcdSelector = true modelStore.showStcdSelector = true;
modelStore.stcdOptions = select.value.options modelStore.stcdOptions = select.value.options;
modelStore.filter.rangeTm = [
dayjs(params.name).startOf('day').format('YYYY-MM-DD HH:mm:ss'),
dayjs(params.name).endOf('day').format('YYYY-MM-DD HH:mm:ss')
];
} }
}); });
}; };
@ -221,9 +234,15 @@ const updateChart = () => {
if (!chartInstance) return; if (!chartInstance) return;
// Y // Y
const qValues = echartsData.value.map(item => item.q).filter(v => v != null && !isNaN(v)); const qValues = echartsData.value
const zValues = echartsData.value.map(item => item.z).filter(v => v != null && !isNaN(v)); .map(item => item.q)
const vValues = echartsData.value.map(item => item.v).filter(v => v != null && !isNaN(v)); .filter(v => v != null && !isNaN(v));
const zValues = echartsData.value
.map(item => item.z)
.filter(v => v != null && !isNaN(v));
const vValues = echartsData.value
.map(item => item.v)
.filter(v => v != null && !isNaN(v));
const minQ = qValues.length > 0 ? Math.min(...qValues) : 0; const minQ = qValues.length > 0 ? Math.min(...qValues) : 0;
const maxQ = qValues.length > 0 ? Math.max(...qValues) : 0; const maxQ = qValues.length > 0 ? Math.max(...qValues) : 0;
@ -257,9 +276,17 @@ const updateChart = () => {
if (item.value !== undefined && item.value !== null) { if (item.value !== undefined && item.value !== null) {
// //
const formattedValue = Number(item.value).toFixed(2); const formattedValue = Number(item.value).toFixed(2);
res += `<span style="background: ${item.color}; height:10px; width: 10px; border-radius: 50%; display: inline-block; margin-right:10px;"></span>${item.seriesName} ${formattedValue}${unit?.[1] || ''}<br/>`; res += `<span style="background: ${
item.color
}; height:10px; width: 10px; border-radius: 50%; display: inline-block; margin-right:10px;"></span>${
item.seriesName
} ${formattedValue}${unit?.[1] || ''}<br/>`;
} else { } else {
res += `<span style="background: ${item.color}; height:10px; width: 10px; border-radius: 50%; display: inline-block; margin-right:10px;"></span>${item.seriesName} -${unit?.[1] || ''}<br/>`; res += `<span style="background: ${
item.color
}; height:10px; width: 10px; border-radius: 50%; display: inline-block; margin-right:10px;"></span>${
item.seriesName
} -${unit?.[1] || ''}<br/>`;
} }
}); });
return res; return res;
@ -447,7 +474,6 @@ const updateChart = () => {
// //
series: [ series: [
{ {
name: '流量(m³/s)', name: '流量(m³/s)',
data: echartsData.value.map(item => item.q), data: echartsData.value.map(item => item.q),
@ -495,7 +521,7 @@ const updateChart = () => {
width: 2 width: 2
}, },
yAxisIndex: 2 yAxisIndex: 2
}, }
] ]
}; };
@ -506,11 +532,14 @@ const updateChart = () => {
chartInstance.setOption(setting, true); chartInstance.setOption(setting, true);
// Y // Y
yAxisShowDynamic({ yAxisShowDynamic(
{
'流量(m³/s)': true, '流量(m³/s)': true,
'水位(m)': false, '水位(m)': false,
'流速(m/s)': false '流速(m/s)': false
}, setting); },
setting
);
// //
chartInstance.setOption(setting, true); chartInstance.setOption(setting, true);
@ -536,54 +565,50 @@ onUnmounted(() => {
// //
const getselsectData = async () => { const getselsectData = async () => {
let params = { let params = {
"filter": { filter: {
"logic": "and", logic: 'and',
"filters": [ filters: [
baseid.value != 'all' ? { baseid.value != 'all'
"field": "baseId", ? {
"operator": "eq", field: 'baseId',
"dataType": "string", operator: 'eq',
"value": baseid.value dataType: 'string',
} : null, value: baseid.value
}
: null,
{ {
"field": "fhstcd", field: 'fhstcd',
"operator": "isnotnull" operator: 'isnotnull'
}, },
{ {
"logic": "or", logic: 'or',
"filters": [ filters: [
{ {
"field": "sttpCode", field: 'sttpCode',
"operator": "eq", operator: 'eq',
"dataType": "string", dataType: 'string',
"value": "ZQ" value: 'ZQ'
} }
] ]
} }
].filter(Boolean) ].filter(Boolean)
}, },
"select": [ select: ['stcd', 'stnm', 'fhstcd', 'fhstnm', 'sttpCode']
"stcd", };
"stnm", let res = await msstbprptGetKendoList(params);
"fhstcd", console.log(res);
"fhstnm",
"sttpCode"
]
}
let res = await msstbprptGetKendoList(params)
console.log(res)
if (!res?.data?.data || res.data.data.length == 0) { if (!res?.data?.data || res.data.data.length == 0) {
select.value.value = '' select.value.value = '';
select.value.options = [] select.value.options = [];
return return;
} }
select.value.value = res.data.data[0].stcd select.value.value = res.data.data[0].stcd;
select.value.options = res.data.data.map((item: any) => { select.value.options = res.data.data.map((item: any) => {
return { return {
label: item.stnm, label: item.stnm,
value: item.stcd value: item.stcd
} };
}) });
// debugger // debugger
}; };
const convertDateRange = (dateRange: string[]) => { const convertDateRange = (dateRange: string[]) => {
@ -614,13 +639,18 @@ const convertDateRange = (dateRange: string[]) => {
// 31228 // 31228
const startMonthLastDay = new Date(fullStartYear, startMonth, 0).getDate(); const startMonthLastDay = new Date(fullStartYear, startMonth, 0).getDate();
const actualStartDay = Math.min(currentDay, startMonthLastDay); const actualStartDay = Math.min(currentDay, startMonthLastDay);
const startDate = `${fullStartYear}-${String(startMonth).padStart(2, '0')}-${String(actualStartDay).padStart(2, '0')} 00:00:00`; const startDate = `${fullStartYear}-${String(startMonth).padStart(
2,
'0'
)}-${String(actualStartDay).padStart(2, '0')} 00:00:00`;
// + + 23:59:59 // + + 23:59:59
// //
const endMonthLastDay = new Date(fullEndYear, endMonth, 0).getDate(); const endMonthLastDay = new Date(fullEndYear, endMonth, 0).getDate();
const actualEndDay = Math.min(currentDay, endMonthLastDay); const actualEndDay = Math.min(currentDay, endMonthLastDay);
const endDate = `${fullEndYear}-${String(endMonth).padStart(2, '0')}-${String(actualEndDay).padStart(2, '0')} 23:59:59`; const endDate = `${fullEndYear}-${String(endMonth).padStart(2, '0')}-${String(
actualEndDay
).padStart(2, '0')} 23:59:59`;
return { return {
startDate, startDate,
@ -634,7 +664,11 @@ const convertDateRange = (dateRange: string[]) => {
* @param endDate 结束日期 (格式: YYYY-MM-DD HH:mm:ss) * @param endDate 结束日期 (格式: YYYY-MM-DD HH:mm:ss)
* @param stcd 测站代码 * @param stcd 测站代码
*/ */
const getChartData = async (startDate: string, endDate: string, stcd: string) => { const getChartData = async (
startDate: string,
endDate: string,
stcd: string
) => {
try { try {
console.log('=== 开始加载图表数据 ==='); console.log('=== 开始加载图表数据 ===');
console.log('请求参数:', { startDate, endDate, stcd }); console.log('请求参数:', { startDate, endDate, stcd });
@ -644,31 +678,31 @@ const getChartData = async (startDate: string, endDate: string, stcd: string) =>
hasData.value = false; hasData.value = false;
const params = { const params = {
"filter": { filter: {
"logic": "and", logic: 'and',
"filters": [ filters: [
{ {
"field": "DT", field: 'DT',
"operator": "gte", operator: 'gte',
"value": startDate value: startDate
}, },
{ {
"field": "DT", field: 'DT',
"operator": "lte", operator: 'lte',
"value": endDate value: endDate
}, },
{ {
"field": "stcd", field: 'stcd',
"operator": "eq", operator: 'eq',
"dataType": "string", dataType: 'string',
"value": stcd value: stcd
} }
] ]
}, },
"sort": [ sort: [
{ {
"field": "dt", field: 'dt',
"dir": "asc" dir: 'asc'
} }
] ]
}; };
@ -732,7 +766,6 @@ const getChartData = async (startDate: string, endDate: string, stcd: string) =>
await nextTick(); await nextTick();
updateChart(); updateChart();
console.log('图表已更新'); console.log('图表已更新');
} catch (error) { } catch (error) {
console.error('获取图表数据失败:', error); console.error('获取图表数据失败:', error);
hasData.value = false; hasData.value = false;
@ -744,7 +777,7 @@ const getChartData = async (startDate: string, endDate: string, stcd: string) =>
} }
}; };
const handlePanelChange1 = async (data) => { const handlePanelChange1 = async data => {
console.log('=== 用户交互触发 ==='); console.log('=== 用户交互触发 ===');
console.log('当前所有控件状态:', data); console.log('当前所有控件状态:', data);
// scopeDate: ['26-05', '26-06'] // scopeDate: ['26-05', '26-06']
@ -772,12 +805,12 @@ const handlePanelChange1 = async (data) => {
select: data.select select: data.select
}); });
} }
} };
watch( watch(
() => JidiSelectEventStore.selectedItem, () => JidiSelectEventStore.selectedItem,
(newVal) => { newVal => {
baseid.value = newVal.wbsCode; baseid.value = newVal.wbsCode;
getselsectData() getselsectData();
}, },
{ deep: true, immediate: true } { deep: true, immediate: true }
); );

View File

@ -1,6 +1,11 @@
<!-- SidePanelItem.vue --> <!-- SidePanelItem.vue -->
<template> <template>
<SidePanelItem title="水温监测" :select="select" :scopeDate="datetimePicker" @update-values="handlePanelChange1"> <SidePanelItem
title="水温监测"
:select="select"
:scopeDate="datetimePicker"
@update-values="handlePanelChange1"
>
<div class="chart-wrapper"> <div class="chart-wrapper">
<a-spin :spinning="loading"> <a-spin :spinning="loading">
<!-- 始终渲染图表容器确保有固定宽高 --> <!-- 始终渲染图表容器确保有固定宽高 -->
@ -19,9 +24,10 @@ import { ref, onMounted, onUnmounted, nextTick, watch } from 'vue';
import * as echarts from 'echarts'; import * as echarts from 'echarts';
import type { EChartsOption } from 'echarts'; import type { EChartsOption } from 'echarts';
import SidePanelItem from '@/components/SidePanelItem/index.vue'; import SidePanelItem from '@/components/SidePanelItem/index.vue';
import { useJidiSelectEventStore } from "@/store/modules/jidiSelectEvent"; import { useJidiSelectEventStore } from '@/store/modules/jidiSelectEvent';
import { msstbprptGetKendoList, sdrvwtsGetKendoList } from '@/api/qxd'; import { msstbprptGetKendoList, sdrvwtsGetKendoList } from '@/api/qxd';
import { useModelStore } from "@/store/modules/model"; import { useModelStore } from '@/store/modules/model';
import dayjs from 'dayjs';
// //
defineOptions({ defineOptions({
name: 'qixidishuiwenbianhua' name: 'qixidishuiwenbianhua'
@ -32,7 +38,7 @@ const JidiSelectEventStore = useJidiSelectEventStore();
// //
const chartRef = ref<HTMLDivElement>(); const chartRef = ref<HTMLDivElement>();
let chartInstance: echarts.ECharts | null = null; let chartInstance: echarts.ECharts | null = null;
const baseid = ref('') const baseid = ref('');
// Loading // Loading
const loading = ref(false); const loading = ref(false);
@ -52,9 +58,13 @@ const datetimePicker: any = ref({
show: true, show: true,
value: (() => { value: (() => {
const now = new Date(); const now = new Date();
const currentMonth = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}`; const currentMonth = `${now.getFullYear()}-${String(
now.getMonth() + 1
).padStart(2, '0')}`;
const lastMonth = new Date(now.getFullYear(), now.getMonth() - 1, 1); const lastMonth = new Date(now.getFullYear(), now.getMonth() - 1, 1);
const lastMonthStr = `${lastMonth.getFullYear()}-${String(lastMonth.getMonth() + 1).padStart(2, '0')}`; const lastMonthStr = `${lastMonth.getFullYear()}-${String(
lastMonth.getMonth() + 1
).padStart(2, '0')}`;
return [lastMonthStr, currentMonth]; return [lastMonthStr, currentMonth];
})(), })(),
format: 'YY-MM', format: 'YY-MM',
@ -69,7 +79,16 @@ const modelStore = useModelStore();
const unita = ref('℃'); const unita = ref('℃');
// - 使HSL // - 使HSL
const Color = ['#5470c6', '#91cc75', '#fac858', '#ee6666', '#73c0de', '#3ba272', '#fc8452', '#9a60b4']; const Color = [
'#5470c6',
'#91cc75',
'#fac858',
'#ee6666',
'#73c0de',
'#3ba272',
'#fc8452',
'#9a60b4'
];
// ==================== ==================== // ==================== ====================
@ -84,7 +103,10 @@ const generateMockData = () => {
const month = String(i + 1).padStart(2, '0'); const month = String(i + 1).padStart(2, '0');
// //
// 7-81-2 // 7-81-2
const temp = baseTemp + Math.sin((i / 12) * Math.PI * 2 - Math.PI / 2) * 10 + Math.random() * 2; const temp =
baseTemp +
Math.sin((i / 12) * Math.PI * 2 - Math.PI / 2) * 10 +
Math.random() * 2;
data.push({ data.push({
dt: `2024-${month}`, dt: `2024-${month}`,
@ -151,12 +173,17 @@ const updateChart = () => {
let regx = /\(([^()]+?)\)/; let regx = /\(([^()]+?)\)/;
let unit = seriesName.match(regx); let unit = seriesName.match(regx);
// //
const finalValue = item.value !== undefined && item.value !== null const finalValue =
item.value !== undefined && item.value !== null
? Number(item.value).toFixed(1) ? Number(item.value).toFixed(1)
: '-'; : '-';
if (item.value !== undefined && item.value !== null) { if (item.value !== undefined && item.value !== null) {
res += `<span style="background: ${item.color}; height:10px; width: 10px; border-radius: 50%;display: inline-block;margin-right:10px;"></span> ${item.seriesName} ${finalValue}${unit?.[1] || ''} <br/>`; res += `<span style="background: ${
item.color
}; height:10px; width: 10px; border-radius: 50%;display: inline-block;margin-right:10px;"></span> ${
item.seriesName
} ${finalValue}${unit?.[1] || ''} <br/>`;
} }
} }
return res; return res;
@ -242,14 +269,15 @@ const updateChart = () => {
nameGap: 8, // 线 20px nameGap: 8, // 线 20px
nameTextStyle: { nameTextStyle: {
color: '#333333', color: '#333333',
fontSize: 12, fontSize: 12
}, },
// Y // Y
min: hasData && echartsData.value.length > 0 min:
hasData && echartsData.value.length > 0
? Math.floor(Math.min(...echartsData.value.map(item => item.wt))) ? Math.floor(Math.min(...echartsData.value.map(item => item.wt)))
: undefined, : undefined,
max: hasData && echartsData.value.length > 0 max:
hasData && echartsData.value.length > 0
? Math.ceil(Math.max(...echartsData.value.map(item => item.wt))) ? Math.ceil(Math.max(...echartsData.value.map(item => item.wt)))
: undefined, : undefined,
// //
@ -281,7 +309,9 @@ const updateChart = () => {
series: [ series: [
{ {
name: _legendData[0], name: _legendData[0],
data: hasData ? (echartsData.value?.map((item: any) => item.wt) || []) : [], data: hasData
? echartsData.value?.map((item: any) => item.wt) || []
: [],
type: 'line', type: 'line',
smooth: true, smooth: true,
symbol: 'circle', symbol: 'circle',
@ -328,11 +358,15 @@ const handleDataPointClick = (params: any) => {
索引: dataIndex 索引: dataIndex
}); });
modelStore.modalVisible = true; modelStore.modalVisible = true;
modelStore.params.sttp = "WT"; modelStore.params.sttp = 'WT';
modelStore.title = '水温监测'; modelStore.title = '水温监测';
modelStore.params.stcd = dataItem.stcd; modelStore.params.stcd = dataItem.stcd;
modelStore.showStcdSelector = true modelStore.showStcdSelector = true;
modelStore.stcdOptions = select.value.options modelStore.stcdOptions = select.value.options;
modelStore.filter.rangeTm = [
dayjs(dataItem.dt).startOf('day').format('YYYY-MM-DD HH:mm:ss'),
dayjs(dataItem.dt).endOf('day').format('YYYY-MM-DD HH:mm:ss')
];
} }
}; };
@ -358,7 +392,11 @@ const loadData = async () => {
* @param endDate 结束日期 (格式: YYYY-MM-DD HH:mm:ss) * @param endDate 结束日期 (格式: YYYY-MM-DD HH:mm:ss)
* @param stcd 测站代码 * @param stcd 测站代码
*/ */
const getChartData = async (startDate: string, endDate: string, stcd: string) => { const getChartData = async (
startDate: string,
endDate: string,
stcd: string
) => {
try { try {
console.log('=== 开始加载图表数据 ==='); console.log('=== 开始加载图表数据 ===');
console.log('请求参数:', { startDate, endDate, stcd }); console.log('请求参数:', { startDate, endDate, stcd });
@ -370,31 +408,31 @@ const getChartData = async (startDate: string, endDate: string, stcd: string) =>
// TODO: API // TODO: API
// //
const params = { const params = {
"filter": { filter: {
"logic": "and", logic: 'and',
"filters": [ filters: [
{ {
"field": "DT", field: 'DT',
"operator": "gte", operator: 'gte',
"value": startDate // : "2026-05-01 00:00:00" value: startDate // : "2026-05-01 00:00:00"
}, },
{ {
"field": "DT", field: 'DT',
"operator": "lte", operator: 'lte',
"value": endDate // : "2026-06-30 23:59:59" value: endDate // : "2026-06-30 23:59:59"
}, },
{ {
"field": "stcd", field: 'stcd',
"operator": "eq", operator: 'eq',
"dataType": "string", dataType: 'string',
"value": stcd value: stcd
} }
] ]
}, },
"sort": [ sort: [
{ {
"field": "dt", field: 'dt',
"dir": "asc" dir: 'asc'
} }
] ]
}; };
@ -456,7 +494,6 @@ const getChartData = async (startDate: string, endDate: string, stcd: string) =>
await nextTick(); await nextTick();
updateChart(); updateChart();
console.log('✅ 图表数据加载完成'); console.log('✅ 图表数据加载完成');
} catch (error) { } catch (error) {
console.error('获取图表数据失败:', error); console.error('获取图表数据失败:', error);
hasData.value = false; hasData.value = false;
@ -501,13 +538,18 @@ const convertDateRange = (dateRange: string[]) => {
// 31228 // 31228
const startMonthLastDay = new Date(fullStartYear, startMonth, 0).getDate(); const startMonthLastDay = new Date(fullStartYear, startMonth, 0).getDate();
const actualStartDay = Math.min(currentDay, startMonthLastDay); const actualStartDay = Math.min(currentDay, startMonthLastDay);
const startDate = `${fullStartYear}-${String(startMonth).padStart(2, '0')}-${String(actualStartDay).padStart(2, '0')} 00:00:00`; const startDate = `${fullStartYear}-${String(startMonth).padStart(
2,
'0'
)}-${String(actualStartDay).padStart(2, '0')} 00:00:00`;
// + + 23:59:59 // + + 23:59:59
// //
const endMonthLastDay = new Date(fullEndYear, endMonth, 0).getDate(); const endMonthLastDay = new Date(fullEndYear, endMonth, 0).getDate();
const actualEndDay = Math.min(currentDay, endMonthLastDay); const actualEndDay = Math.min(currentDay, endMonthLastDay);
const endDate = `${fullEndYear}-${String(endMonth).padStart(2, '0')}-${String(actualEndDay).padStart(2, '0')} 23:59:59`; const endDate = `${fullEndYear}-${String(endMonth).padStart(2, '0')}-${String(
actualEndDay
).padStart(2, '0')} 23:59:59`;
return { return {
startDate, startDate,
@ -515,7 +557,7 @@ const convertDateRange = (dateRange: string[]) => {
}; };
}; };
const handlePanelChange1 = async (data) => { const handlePanelChange1 = async data => {
console.log('=== 用户交互触发 ==='); console.log('=== 用户交互触发 ===');
console.log('当前所有控件状态:', data); console.log('当前所有控件状态:', data);
// scopeDate: ['26-05', '26-06'] // scopeDate: ['26-05', '26-06']
@ -543,67 +585,63 @@ const handlePanelChange1 = async (data) => {
select: data.select select: data.select
}); });
} }
} };
// //
const getselsectData = async () => { const getselsectData = async () => {
let params = { let params = {
"filter": { filter: {
"logic": "and", logic: 'and',
"filters": [ filters: [
baseid.value != 'all' ? { baseid.value != 'all'
"field": "baseId", ? {
"operator": "eq", field: 'baseId',
"dataType": "string", operator: 'eq',
"value": baseid.value dataType: 'string',
} : null, value: baseid.value
}
: null,
{ {
"field": "fhstcd", field: 'fhstcd',
"operator": "isnotnull" operator: 'isnotnull'
}, },
{ {
"logic": "or", logic: 'or',
"filters": [ filters: [
{ {
"field": "sttpCode", field: 'sttpCode',
"operator": "eq", operator: 'eq',
"dataType": "string", dataType: 'string',
"value": "WTRV" value: 'WTRV'
} }
] ]
} }
].filter(Boolean) ].filter(Boolean)
}, },
"select": [ select: ['stcd', 'stnm', 'fhstcd', 'fhstnm', 'sttpCode']
"stcd", };
"stnm", let res = await msstbprptGetKendoList(params);
"fhstcd", console.log(res);
"fhstnm",
"sttpCode"
]
}
let res = await msstbprptGetKendoList(params)
console.log(res)
if (!res?.data?.data || res.data.data.length == 0) { if (!res?.data?.data || res.data.data.length == 0) {
select.value.value = '' select.value.value = '';
select.value.options = [] select.value.options = [];
return return;
} }
select.value.value = res.data.data[0].stcd select.value.value = res.data.data[0].stcd;
select.value.options = res.data.data.map((item: any) => { select.value.options = res.data.data.map((item: any) => {
return { return {
label: item.stnm, label: item.stnm,
value: item.stcd value: item.stcd
} };
}) });
// debugger // debugger
}; };
//// ////
watch( watch(
() => JidiSelectEventStore.selectedItem, () => JidiSelectEventStore.selectedItem,
(newVal) => { newVal => {
baseid.value = newVal.wbsCode; baseid.value = newVal.wbsCode;
getselsectData() getselsectData();
}, },
{ deep: true, immediate: true } { deep: true, immediate: true }
); );

View File

@ -298,8 +298,12 @@ const handleExport = async () => {};
const handleViewDetail = (record: any) => { const handleViewDetail = (record: any) => {
modelStore.modalVisible = true; modelStore.modalVisible = true;
modelStore.params.sttp = 'WT'; modelStore.params.sttp = 'WT';
modelStore.title = record.stnm ; modelStore.title = record.stnm;
modelStore.params.stcd = record.stcd; modelStore.params.stcd = record.stcd;
modelStore.filter.rangeTm = [
dayjs(record.dt).startOf('month').format('YYYY-MM-DD HH:mm:ss'),
dayjs(record.dt).endOf('month').format('YYYY-MM-DD HH:mm:ss')
];
}; };
// //

View File

@ -1,6 +1,10 @@
<template> <template>
<SidePanelItem title="水温年内分布" :moreSelect="select" :datetimePicker="datetimePicker" <SidePanelItem
@update-values="handlePanelChange1"> title="水温年内分布"
:moreSelect="select"
:datetimePicker="datetimePicker"
@update-values="handlePanelChange1"
>
<a-spin :spinning="loading" tip="加载中..."> <a-spin :spinning="loading" tip="加载中...">
<div v-show="!loading && showemit" class="water-temp-chart-container"> <div v-show="!loading && showemit" class="water-temp-chart-container">
<div ref="chartRef" class="chart-wrapper"></div> <div ref="chartRef" class="chart-wrapper"></div>
@ -12,39 +16,52 @@
</SidePanelItem> </SidePanelItem>
<!-- 数据点详情弹框 --> <!-- 数据点详情弹框 -->
<a-modal v-model:open="modalVisible" title="水温数据详情" :footer="null" width="1536px" @cancel="handleModalClose"> <a-modal
<YaerAverage :tm="`${datetimePicker.value}-${String(currentData.monthInt).padStart(2, '0')}-01 00:00:00`" v-model:open="modalVisible"
:dataDimensionVal="baseid" :stcd="select.value" /> title="水温数据详情"
:footer="null"
width="1536px"
@cancel="handleModalClose"
>
<YaerAverage
:tm="`${datetimePicker.value}-${String(currentData.monthInt).padStart(
2,
'0'
)}-01 00:00:00`"
:dataDimensionVal="baseid"
:stcd="select.value"
/>
</a-modal> </a-modal>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, onMounted, onBeforeUnmount, watch } from 'vue' import { ref, onMounted, onBeforeUnmount, watch } from 'vue';
import * as echarts from 'echarts' import * as echarts from 'echarts';
import SidePanelItem from '@/components/SidePanelItem/index.vue'; import SidePanelItem from '@/components/SidePanelItem/index.vue';
import { getVmsstbprpt, yearListGetKendoListCust } from "@/api/sw"; import { getVmsstbprpt, yearListGetKendoListCust } from '@/api/sw';
import { useJidiSelectEventStore } from "@/store/modules/jidiSelectEvent"; import { useJidiSelectEventStore } from '@/store/modules/jidiSelectEvent';
import YaerAverage from "./TwoLayers/yaerAverage.vue" import YaerAverage from './TwoLayers/yaerAverage.vue';
import { MapClass } from '@/components/gis/map.class';
const JidiSelectEventStore = useJidiSelectEventStore(); const JidiSelectEventStore = useJidiSelectEventStore();
// //
const waterTempData = ref<any[]>([]) const waterTempData = ref<any[]>([]);
const loading = ref(false) const loading = ref(false);
const showemit = ref(true) const showemit = ref(true);
// //
const modalVisible = ref(false) const modalVisible = ref(false);
const currentData = ref<any>({}) const currentData = ref<any>({});
const chartRef = ref<HTMLElement | null>(null);
const chartRef = ref<HTMLElement | null>(null) let chartInstance: echarts.ECharts | null = null;
let chartInstance: echarts.ECharts | null = null
// //
const select = ref({ const select = ref({
show: true, show: true,
value: '', value: '',
options: [], options: [],
picker: undefined, picker: undefined,
format: undefined, format: undefined
}); });
// //
@ -55,110 +72,123 @@ const datetimePicker = ref({
const year = now.getFullYear(); const year = now.getFullYear();
return `${year}`; return `${year}`;
})(), })(),
format: "YYYY", format: 'YYYY',
picker: "year" as const, picker: 'year' as const,
options: [], options: []
}); });
const transUnit = (value: number | null) => { const transUnit = (value: number | null) => {
if (value === null) return null if (value === null) return null;
return value return value;
} };
const getUnitConfigByCode = (_code: string, _type: string) => { const getUnitConfigByCode = (_code: string, _type: string) => {
return { return {
unit: '℃' unit: '℃'
} };
} };
const getColorByCodeAndType = (_code: string[], _typeKey: string[]) => { const getColorByCodeAndType = (_code: string[], _typeKey: string[]) => {
return ['#4b79ab', '#78c300'] return ['#4b79ab', '#78c300'];
} };
// //
const handleChartClick = (params: any) => { const handleChartClick = (params: any) => {
if (!params || !String(params.dataIndex)) return if (!params || !String(params.dataIndex)) return;
// //
const dataIndex = params.dataIndex const dataIndex = params.dataIndex;
const dataItem = waterTempData.value[dataIndex] const dataItem = waterTempData.value[dataIndex];
if (dataItem) { if (dataItem) {
currentData.value = { currentData.value = {
...dataItem, ...dataItem,
stnm: select.value.options.find((opt: any) => stnm:
opt.children?.some((child: any) => child.value === select.value.value) select.value.options
)?.children?.find((child: any) => child.value === select.value.value)?.title || '未知站点' .find((opt: any) =>
opt.children?.some(
(child: any) => child.value === select.value.value
)
)
?.children?.find((child: any) => child.value === select.value.value)
?.title || '未知站点'
};
modalVisible.value = true;
} }
modalVisible.value = true };
}
}
// //
const handleModalClose = () => { const handleModalClose = () => {
modalVisible.value = false modalVisible.value = false;
currentData.value = {} currentData.value = {};
} };
// //
const getselectData = async () => { const getselectData = async () => {
try { try {
const params = { const params = {
filter: { filter: {
logic: "and", logic: 'and',
filters: [ filters: [
{ {
field: "sttpFullPath", field: 'sttpFullPath',
operator: "contains", operator: 'contains',
value: "ENV,ENVM,WT," value: 'ENV,ENVM,WT,'
}, },
{ {
field: "sttpCode", field: 'sttpCode',
operator: "eq", operator: 'eq',
value: "WTRV" value: 'WTRV'
}, },
baseid.value !== 'all' ? { baseid.value !== 'all'
field: "baseId", ? {
operator: "contains", field: 'baseId',
operator: 'contains',
value: baseid.value value: baseid.value
} : null }
: null
].filter(Boolean) ].filter(Boolean)
}, },
select: [ select: [
"stcd", "stnm", "lgtd", "lttd", 'stcd',
"baseId", "baseName", "siteStepSort" 'stnm',
'lgtd',
'lttd',
'baseId',
'baseName',
'siteStepSort'
] ]
} };
const res = await getVmsstbprpt(params) const res = await getVmsstbprpt(params);
// if (!res?.data?.length) { // if (!res?.data?.length) {
// select.value.options = [] // select.value.options = []
// return // return
// } // }
let data = res?.data?.date || res?.data || [] let data = res?.data?.date || res?.data || [];
// stcd // stcd
if (res.data) { if (res.data) {
select.value.value = data?.data[0]?.stcd select.value.value = data?.data[0]?.stcd;
} else { } else {
select.value.value = '' select.value.value = '';
} }
// baseId // baseId
const dataMapNameMap: Record<string, any[]> = {} const dataMapNameMap: Record<string, any[]> = {};
data.data.forEach((item: any) => { data.data.forEach((item: any) => {
const { baseId } = item const { baseId } = item;
if (dataMapNameMap[baseId]) { if (dataMapNameMap[baseId]) {
dataMapNameMap[baseId].push(item) dataMapNameMap[baseId].push(item);
} else { } else {
dataMapNameMap[baseId] = [item] dataMapNameMap[baseId] = [item];
} }
}) });
// //
const dataMapNameArr: any[] = [] const dataMapNameArr: any[] = [];
const otherArr: any[] = [] const otherArr: any[] = [];
Object.keys(dataMapNameMap).forEach((baseId: string) => { Object.keys(dataMapNameMap).forEach((baseId: string) => {
const stations = dataMapNameMap[baseId] const stations = dataMapNameMap[baseId];
const treeItem = { const treeItem = {
title: stations[0]?.baseName, title: stations[0]?.baseName,
@ -166,31 +196,34 @@ const getselectData = async () => {
selectable: false, selectable: false,
children: stations children: stations
.sort((a, b) => a.siteStepSort - b.siteStepSort) .sort((a, b) => a.siteStepSort - b.siteStepSort)
.map((station) => ({ .map(station => ({
title: station.stnm, title: station.stnm,
value: station.stcd, value: station.stcd,
lgtd: station.lgtd, lgtd: station.lgtd,
lttd: station.lttd lttd: station.lttd
})) }))
} };
// other // other
if (+baseId) { if (+baseId) {
dataMapNameArr.push(treeItem) dataMapNameArr.push(treeItem);
} else if (baseId === 'other') { } else if (baseId === 'other') {
otherArr.push(treeItem) otherArr.push(treeItem);
} }
}) });
dataMapNameArr.sort((a, b) => { dataMapNameArr.sort((a, b) => {
const indexA = JidiSelectEventStore.jidiData.findIndex((item: any) => item.wbsCode === a.value) const indexA = JidiSelectEventStore.jidiData.findIndex(
const indexB = JidiSelectEventStore.jidiData.findIndex((item: any) => item.wbsCode === b.value) (item: any) => item.wbsCode === a.value
return indexA - indexB );
}) const indexB = JidiSelectEventStore.jidiData.findIndex(
(item: any) => item.wbsCode === b.value
);
return indexA - indexB;
});
// //
select.value.options = [...dataMapNameArr, ...otherArr] select.value.options = [...dataMapNameArr, ...otherArr];
// if (baseid.value == 'all') { // if (baseid.value == 'all') {
// select.value.value = '008640202300001021' // select.value.value = '008640202300001021'
@ -198,104 +231,107 @@ const getselectData = async () => {
// else if (data.data.length > 0) { // else if (data.data.length > 0) {
// select.value.value = data.data[0].stcd // select.value.value = data.data[0].stcd
// } // }
getshuiwenList() getshuiwenList();
} catch (error) { } catch (error) {
console.error('获取站点数据失败:', error) console.error('获取站点数据失败:', error);
select.value.options = [] select.value.options = [];
} }
} };
// //
const getshuiwenList = async () => { const getshuiwenList = async () => {
if (loading.value) return; // if (loading.value) return; //
if (!select.value.value) { if (!select.value.value) {
showemit.value = false showemit.value = false;
waterTempData.value = [] waterTempData.value = [];
return; return;
} }
loading.value = true; loading.value = true;
const params = { const params = {
"filter": { filter: {
"logic": "and", logic: 'and',
"filters": [ filters: [
{ {
"field": "stcd", field: 'stcd',
"operator": "eq", operator: 'eq',
"dataType": "string", dataType: 'string',
"value": select.value.value value: select.value.value
}, },
{ {
"field": "year", field: 'year',
"operator": "eq", operator: 'eq',
"dataType": "string", dataType: 'string',
"value": datetimePicker.value.value value: datetimePicker.value.value
} }
] ]
}, },
"sort": [ sort: [
{ {
"field": "month", field: 'month',
"dir": "asc" dir: 'asc'
} }
] ]
} };
try { try {
let res = await yearListGetKendoListCust(params) let res = await yearListGetKendoListCust(params);
let data = res?.data?.data || res?.data || [] let data = res?.data?.data || res?.data || [];
// //
if (data.length === 0) { if (data.length === 0) {
showemit.value = false showemit.value = false;
waterTempData.value = [] waterTempData.value = [];
return return;
} }
showemit.value = true showemit.value = true;
// //
waterTempData.value = data waterTempData.value = data;
// //
setTimeout(() => { setTimeout(() => {
if (!chartInstance) { if (!chartInstance) {
initChart() initChart();
} else { } else {
const option = getChartOption() const option = getChartOption();
chartInstance.setOption(option, true) chartInstance.setOption(option, true);
} }
// //
setTimeout(() => { setTimeout(() => {
chartInstance?.resize() chartInstance?.resize();
}, 100) }, 100);
}, 50) }, 50);
} catch (error) { } catch (error) {
console.error('获取水温数据失败:', error) console.error('获取水温数据失败:', error);
showemit.value = false showemit.value = false;
waterTempData.value = [] waterTempData.value = [];
} finally { } finally {
loading.value = false; loading.value = false;
} }
} };
const getChartOption = () => { const getChartOption = () => {
const { unit } = getUnitConfigByCode('Other', 'ACTUALTEMP') const { unit } = getUnitConfigByCode('Other', 'ACTUALTEMP');
const legendData = ['实测值', '天然'] const legendData = ['实测值', '天然'];
const xData: string[] = [] const xData: string[] = [];
const actualData: (number | null)[] = [] const actualData: (number | null)[] = [];
const naturalData: (number | null)[] = [] const naturalData: (number | null)[] = [];
// 使waterTempDatadata // 使waterTempDatadata
waterTempData.value.forEach((item: any) => { waterTempData.value.forEach((item: any) => {
xData.push(`${item.monthInt}`) xData.push(`${item.monthInt}`);
actualData.push(item.actualTemp === null ? null : transUnit(item.actualTemp)) actualData.push(
naturalData.push(item.naturalTemp === null ? null : transUnit(item.naturalTemp)) item.actualTemp === null ? null : transUnit(item.actualTemp)
}) );
naturalData.push(
item.naturalTemp === null ? null : transUnit(item.naturalTemp)
);
});
const code = ['Other'] const code = ['Other'];
const typeKey = ['ACTUALTEMP', 'NATURALTEMP'] const typeKey = ['ACTUALTEMP', 'NATURALTEMP'];
const colors = getColorByCodeAndType(code, typeKey) const colors = getColorByCodeAndType(code, typeKey);
const options: any = { const options: any = {
color: colors, color: colors,
@ -313,7 +349,8 @@ const getChartOption = () => {
params.forEach((item: any) => { params.forEach((item: any) => {
result += `<div style="display: flex; align-items: center; margin: 4px 0;">`; result += `<div style="display: flex; align-items: center; margin: 4px 0;">`;
result += `<span style="display: inline-block; width: 10px; height: 10px; border-radius: 50%; background-color: ${item.color}; margin-right: 8px;"></span>`; result += `<span style="display: inline-block; width: 10px; height: 10px; border-radius: 50%; background-color: ${item.color}; margin-right: 8px;"></span>`;
const displayValue = item.value !== null && item.value !== undefined ? item.value: '-'; const displayValue =
item.value !== null && item.value !== undefined ? item.value : '-';
result += `<span>${item.seriesName} ${displayValue}${unit}</span>`; result += `<span>${item.seriesName} ${displayValue}${unit}</span>`;
result += `</div>`; result += `</div>`;
}); });
@ -437,87 +474,95 @@ const getChartOption = () => {
} }
} }
] ]
} };
return options return options;
} };
const initChart = async () => { const initChart = async () => {
// //
await new Promise(resolve => setTimeout(resolve, 100)) await new Promise(resolve => setTimeout(resolve, 100));
if (!chartRef.value) { if (!chartRef.value) {
console.error('图表容器未找到') console.error('图表容器未找到');
return return;
} }
try { try {
if (!chartInstance) { if (!chartInstance) {
chartInstance = echarts.init(chartRef.value) chartInstance = echarts.init(chartRef.value);
} }
const option = getChartOption() const option = getChartOption();
chartInstance.setOption(option, false) chartInstance.setOption(option, false);
// //
chartInstance.off('click') chartInstance.off('click');
chartInstance.on('click', handleChartClick) chartInstance.on('click', handleChartClick);
// //
setTimeout(() => { setTimeout(() => {
chartInstance?.resize() chartInstance?.resize();
}, 100) }, 100);
window.addEventListener('resize', handleResize) window.addEventListener('resize', handleResize);
} catch (error) { } catch (error) {
console.error('图表初始化失败:', error) console.error('图表初始化失败:', error);
} }
} };
const handleResize = () => { const handleResize = () => {
if (chartInstance) { if (chartInstance) {
chartInstance.resize() chartInstance.resize();
} }
} };
onMounted(() => { onMounted(() => {
// //
initChart() initChart();
// //
// if (select.value.value) { // if (select.value.value) {
// getshuiwenList() // getshuiwenList()
// } // }
}) });
onBeforeUnmount(() => { onBeforeUnmount(() => {
window.removeEventListener('resize', handleResize) window.removeEventListener('resize', handleResize);
if (chartInstance) { if (chartInstance) {
chartInstance.dispose() chartInstance.dispose();
chartInstance = null chartInstance = null;
} }
}) });
// //
const handlePanelChange1 = (data) => { const handlePanelChange1 = (data: any, type: string) => {
console.log('当前所有控件状态:', data); console.log('当前所有控件状态:', data);
if (type == 'click') {
const mapClass = MapClass.getInstance();
mapClass.flyTopanto(
[
Number(data.selectedNodeExtra.lgtd),
Number(data.selectedNodeExtra.lttd)
],
15
);
}
// //
if (data.moreSelect || data.datetime) { if (data.moreSelect || data.datetime) {
select.value.value = data.moreSelect select.value.value = data.moreSelect;
datetimePicker.value.value = data.datetime datetimePicker.value.value = data.datetime;
// debugger // debugger
getshuiwenList() getshuiwenList();
} }
} };
const baseid = ref('') const baseid = ref('');
watch( watch(
() => JidiSelectEventStore.selectedItem, () => JidiSelectEventStore.selectedItem,
(newVal) => { newVal => {
baseid.value = newVal.wbsCode; baseid.value = newVal.wbsCode;
getselectData() getselectData();
}, },
{ deep: true, immediate: true } { deep: true, immediate: true }
); );
</script> </script>
<style scoped> <style scoped>

View File

@ -1,8 +1,13 @@
<!-- SidePanelItem.vue --> <!-- SidePanelItem.vue -->
<template> <template>
<SidePanelItem title="沿程水质变化" :iconmap="iconmap" :select="select" :datetimePicker="datetimePicker" <SidePanelItem
@update-values="handlePanelChange1"> title="沿程水质变化"
:iconmap="iconmap"
:select="select"
:datetimePicker="datetimePicker"
@update-values="handlePanelChange1"
>
<div class="chart-wrapper"> <div class="chart-wrapper">
<a-spin :spinning="loading"> <a-spin :spinning="loading">
<!-- 始终渲染图表容器确保有固定宽高 --> <!-- 始终渲染图表容器确保有固定宽高 -->
@ -21,10 +26,13 @@ import { ref, onMounted, onBeforeUnmount, nextTick, watch } from 'vue';
import * as echarts from 'echarts'; import * as echarts from 'echarts';
import type { EChartsOption } from 'echarts'; import type { EChartsOption } from 'echarts';
import SidePanelItem from '@/components/SidePanelItem/index.vue'; import SidePanelItem from '@/components/SidePanelItem/index.vue';
import { qgcGetKendoListCust, wbsbGetKendoList } from '@/api/sz' import { qgcGetKendoListCust, wbsbGetKendoList } from '@/api/sz';
import { useJidiSelectEventStore } from "@/store/modules/jidiSelectEvent"; import { useJidiSelectEventStore } from '@/store/modules/jidiSelectEvent';
import { useModelStore } from "@/store/modules/model"; import { useModelStore } from '@/store/modules/model';
import { WATER_QUALITY_INDICATORS, type WaterQualityIndicator } from '@/modules/waterQuality/padig'; import {
WATER_QUALITY_INDICATORS,
type WaterQualityIndicator
} from '@/modules/waterQuality/padig';
const indicators = WATER_QUALITY_INDICATORS; const indicators = WATER_QUALITY_INDICATORS;
// 便 // 便
defineOptions({ defineOptions({
@ -35,7 +43,7 @@ const JidiSelectEventStore = useJidiSelectEventStore();
const iconmap = ref({ const iconmap = ref({
show: true, show: true,
value: '', value: '',
icon: 'iconfont icon-time', icon: 'iconfont icon-time'
}); });
// ==================== ==================== // ==================== ====================
const chartRef = ref<HTMLElement | null>(null); const chartRef = ref<HTMLElement | null>(null);
@ -47,8 +55,20 @@ const hasData = ref(true); // 初始化为 true因为 initChart 会使用 moc
// 8:00 // 8:00
const now = new Date(); const now = new Date();
const todayAtEightAM = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 8, 0, 0); const todayAtEightAM = new Date(
const defaultValue = `${todayAtEightAM.getFullYear()}-${String(todayAtEightAM.getMonth() + 1).padStart(2, '0')}-${String(todayAtEightAM.getDate()).padStart(2, '0')} ${String(todayAtEightAM.getHours()).padStart(2, '0')}:00`; now.getFullYear(),
now.getMonth(),
now.getDate(),
8,
0,
0
);
const defaultValue = `${todayAtEightAM.getFullYear()}-${String(
todayAtEightAM.getMonth() + 1
).padStart(2, '0')}-${String(todayAtEightAM.getDate()).padStart(
2,
'0'
)} ${String(todayAtEightAM.getHours()).padStart(2, '0')}:00`;
// 👆 :00 timeFormat: 'HH' // 👆 :00 timeFormat: 'HH'
const datetimePicker = ref({ const datetimePicker = ref({
@ -64,14 +84,22 @@ const select = ref({
value: '', value: '',
options: [], options: [],
picker: undefined, picker: undefined,
format: undefined, format: undefined
}); });
// ==================== 2==================== // ==================== 2====================
const visibleSeriesQueue = ref<string[]>([]); const visibleSeriesQueue = ref<string[]>([]);
// ==================== ==================== // ==================== ====================
const indicatorConfig = ref<Array<{ key: string; name: string; unit: string; color: string; sort: number }>>([]); const indicatorConfig = ref<
Array<{
key: string;
name: string;
unit: string;
color: string;
sort: number;
}>
>([]);
// ==================== HSL ==================== // ==================== HSL ====================
const generateRandomColor = (index: number): string => { const generateRandomColor = (index: number): string => {
@ -86,11 +114,13 @@ const generateRandomColor = (index: number): string => {
const lightness = 45 + Math.random() * 15; const lightness = 45 + Math.random() * 15;
// HSL RGB // HSL RGB
const c = (1 - Math.abs(2 * lightness / 100 - 1)) * saturation / 100; const c = ((1 - Math.abs((2 * lightness) / 100 - 1)) * saturation) / 100;
const x = c * (1 - Math.abs((hue / 60) % 2 - 1)); const x = c * (1 - Math.abs(((hue / 60) % 2) - 1));
const m = lightness / 100 - c / 2; const m = lightness / 100 - c / 2;
let r = 0, g = 0, b = 0; let r = 0,
g = 0,
b = 0;
if (hue < 60) { if (hue < 60) {
[r, g, b] = [c, x, 0]; [r, g, b] = [c, x, 0];
@ -123,10 +153,28 @@ const generateIndicatorConfig = (data: StationData[]) => {
// //
const excludeKeys = new Set([ const excludeKeys = new Set([
'stcd', 'stcd2', 'stnm', 'rstcd', 'sttp', 'tm', 'SORT', 'stcd',
'limits', 'minTm', 'wwqtg', 'wqgrd', 'wqgrdName', 'min', 'max', 'stcd2',
'_tls', 'id', 'recordUser', 'recordTime', 'modifyTime', 'stnm',
'displayRecordUser', 'departmentId', 'displayDepartment' 'rstcd',
'sttp',
'tm',
'SORT',
'limits',
'minTm',
'wwqtg',
'wqgrd',
'wqgrdName',
'min',
'max',
'_tls',
'id',
'recordUser',
'recordTime',
'modifyTime',
'displayRecordUser',
'departmentId',
'displayDepartment'
]); ]);
// key // key
@ -140,9 +188,16 @@ const generateIndicatorConfig = (data: StationData[]) => {
const value = station[key as keyof typeof station]; const value = station[key as keyof typeof station];
// null NaN // null NaN
if (value !== null && value !== undefined && typeof value === 'number' && !isNaN(value)) { if (
value !== null &&
value !== undefined &&
typeof value === 'number' &&
!isNaN(value)
) {
// indicators // indicators
const matchedIndicator = indicators.find(ind => ind.key.toLowerCase() === key.toLowerCase()); const matchedIndicator = indicators.find(
ind => ind.key.toLowerCase() === key.toLowerCase()
);
if (matchedIndicator) { if (matchedIndicator) {
validKeysSet.add(matchedIndicator.key); validKeysSet.add(matchedIndicator.key);
} }
@ -321,7 +376,9 @@ const getChartOption = (): EChartsOption => {
const value = item[config.key as keyof typeof item]; const value = item[config.key as keyof typeof item];
return typeof value === 'number' && !isNaN(value) ? value : null; return typeof value === 'number' && !isNaN(value) ? value : null;
}), }),
markLine: index === 0 ? { markLine:
index === 0
? {
symbol: ['none', 'none'], symbol: ['none', 'none'],
label: { show: false }, label: { show: false },
lineStyle: { lineStyle: {
@ -329,7 +386,8 @@ const getChartOption = (): EChartsOption => {
type: 'dashed' as const type: 'dashed' as const
}, },
data: markLineData data: markLineData
} : undefined }
: undefined
}; };
}); });
@ -340,7 +398,10 @@ const getChartOption = (): EChartsOption => {
} }
const values = data const values = data
.map(item => item[indicatorConfig.value[configIndex].key as keyof typeof item]) .map(
item =>
item[indicatorConfig.value[configIndex].key as keyof typeof item]
)
.filter((val): val is number => typeof val === 'number' && !isNaN(val)); .filter((val): val is number => typeof val === 'number' && !isNaN(val));
if (values.length === 0) { if (values.length === 0) {
@ -444,7 +505,9 @@ const getChartOption = (): EChartsOption => {
const showOnlyOneSeries = visibleSeriesQueue.value.length === 1; const showOnlyOneSeries = visibleSeriesQueue.value.length === 1;
params.forEach((param: any) => { params.forEach((param: any) => {
const config = indicatorConfig.value.find(c => c.name === param.seriesName); const config = indicatorConfig.value.find(
c => c.name === param.seriesName
);
const unit = config?.unit || ''; const unit = config?.unit || '';
if (param.value !== null && param.value !== undefined) { if (param.value !== null && param.value !== undefined) {
@ -473,16 +536,22 @@ const getChartOption = (): EChartsOption => {
if (limits.min !== undefined && limits.max !== undefined) { if (limits.min !== undefined && limits.max !== undefined) {
// //
limitText = `&nbsp;&nbsp;&nbsp;&nbsp;限值: ${limits.min}-${limits.max}${unit ? unit : ''}`; limitText = `&nbsp;&nbsp;&nbsp;&nbsp;限值: ${limits.min}-${
limits.max
}${unit ? unit : ''}`;
} else if (limits.min !== undefined) { } else if (limits.min !== undefined) {
// //
limitText = `&nbsp;&nbsp;&nbsp;&nbsp;限值: ${limits.min}${unit ? unit : ''}`; limitText = `&nbsp;&nbsp;&nbsp;&nbsp;限值: ${limits.min}${
unit ? unit : ''
}`;
} else if (limits.max !== undefined) { } else if (limits.max !== undefined) {
// //
limitText = `&nbsp;&nbsp;&nbsp;&nbsp;限值: ${limits.max}${unit ? unit : ''}`; limitText = `&nbsp;&nbsp;&nbsp;&nbsp;限值: ${limits.max}${
unit ? unit : ''
}`;
} }
result += ('<br/>'+limitText); result += '<br/>' + limitText;
} }
} }
} }
@ -568,7 +637,9 @@ const handleLegendSelectChanged = (params: any) => {
const clickedName = name; const clickedName = name;
const isNowSelected = selected[clickedName]; const isNowSelected = selected[clickedName];
console.log(`图例点击: ${clickedName}, 当前状态: ${isNowSelected ? '显示' : '隐藏'}`); console.log(
`图例点击: ${clickedName}, 当前状态: ${isNowSelected ? '显示' : '隐藏'}`
);
console.log('当前可见队列:', visibleSeriesQueue.value); console.log('当前可见队列:', visibleSeriesQueue.value);
if (isNowSelected) { if (isNowSelected) {
@ -723,10 +794,18 @@ const handleDataPointClick = (params: any) => {
if (stationData && stationData.stcd) { if (stationData && stationData.stcd) {
modelStore.modalVisible = true; modelStore.modalVisible = true;
modelStore.params.sttp = "WQFB"; modelStore.params.sttp = 'WQFB';
modelStore.title = stationData.stnm ; modelStore.title = stationData.stnm;
// modelStore.isBasicEdit = true; // modelStore.isBasicEdit = true;
modelStore.params.stcd = stationData.stcd; modelStore.params.stcd = stationData.stcd;
modelStore.filter.rangeTm = [
dayjs(datetimePicker.value.value)
.startOf('month')
.format('YYYY-MM-DD HH:mm:ss'),
dayjs(datetimePicker.value.value)
.endOf('month')
.format('YYYY-MM-DD HH:mm:ss')
];
} }
}; };
@ -749,42 +828,42 @@ const getecharts = async () => {
hasData.value = false; hasData.value = false;
let params = { let params = {
"filter": { filter: {
"logic": "and", logic: 'and',
"filters": [ filters: [
{ {
"field": "baseId", field: 'baseId',
"operator": "eq", operator: 'eq',
"dataType": "string", dataType: 'string',
"value": wbsCode.value value: wbsCode.value
}, },
{ {
"field": "rvcd", field: 'rvcd',
"operator": "eq", operator: 'eq',
"dataType": "string", dataType: 'string',
"value": select.value.value value: select.value.value
}, },
{ {
"field": "tm", field: 'tm',
"operator": "gte", operator: 'gte',
"dataType": "date", dataType: 'date',
"value": getStartTime() value: getStartTime()
}, },
{ {
"field": "tm", field: 'tm',
"operator": "lte", operator: 'lte',
"dataType": "date", dataType: 'date',
"value": getEndTime() value: getEndTime()
} }
] ]
}, },
"sort": [ sort: [
{ {
"field": "sort", field: 'sort',
"dir": "asc" dir: 'asc'
} }
] ]
} };
try { try {
console.log('请求参数:', params); console.log('请求参数:', params);
@ -940,7 +1019,13 @@ const getecharts = async () => {
const option = getChartOption(); const option = getChartOption();
chartInstance?.setOption(option, true); chartInstance?.setOption(option, true);
chartInstance?.resize(); chartInstance?.resize();
console.log('图表数据已更新,共', chartData.length, '个站点,', indicatorConfig.value.length, '个指标'); console.log(
'图表数据已更新,共',
chartData.length,
'个站点,',
indicatorConfig.value.length,
'个指标'
);
}); });
} else { } else {
// //
@ -956,7 +1041,7 @@ const getecharts = async () => {
console.log('关闭 loading 状态'); console.log('关闭 loading 状态');
loading.value = false; loading.value = false;
} }
} };
// //
const getStartTime = () => { const getStartTime = () => {
@ -998,7 +1083,7 @@ const initChart = async () => {
if (rect.width === 0 || rect.height === 0) { if (rect.width === 0 || rect.height === 0) {
console.warn('图表容器尺寸为0等待渲染...'); console.warn('图表容器尺寸为0等待渲染...');
// 使 requestAnimationFrame // 使 requestAnimationFrame
return new Promise((resolve) => { return new Promise(resolve => {
requestAnimationFrame(() => { requestAnimationFrame(() => {
setTimeout(() => { setTimeout(() => {
initChart().then(resolve); initChart().then(resolve);
@ -1077,39 +1162,39 @@ const getSelect = async () => {
} }
let params = { let params = {
"filter": { filter: {
"logic": "and", logic: 'and',
"filters": [ filters: [
{ {
"field": "objId", field: 'objId',
"operator": "eq", operator: 'eq',
"dataType": "string", dataType: 'string',
"value": wbsCode.value value: wbsCode.value
}, },
{ {
"field": "wbsType", field: 'wbsType',
"operator": "eq", operator: 'eq',
"dataType": "string", dataType: 'string',
"value": "PSB_RVCD" value: 'PSB_RVCD'
} }
] ]
}, },
"group": [ group: [
{ {
"dir": "asc", dir: 'asc',
"field": "orderIndex" field: 'orderIndex'
}, },
{ {
"dir": "des", dir: 'des',
"field": "wbsCode" field: 'wbsCode'
}, },
{ {
"dir": "des", dir: 'des',
"field": "wbsName" field: 'wbsName'
} }
], ],
"groupResultFlat": "true" groupResultFlat: 'true'
} };
try { try {
let res = await wbsbGetKendoList(params); let res = await wbsbGetKendoList(params);
@ -1154,23 +1239,23 @@ const getSelect = async () => {
} catch (error) { } catch (error) {
console.error('获取河段数据失败:', error); console.error('获取河段数据失败:', error);
} }
} };
// //
const handlePanelChange1 = async (data) => { const handlePanelChange1 = async data => {
console.log('当前所有控件状态:', data); console.log('当前所有控件状态:', data);
// //
if (data.datetime || data.select) { if (data.datetime || data.select) {
select.value.value = data.select select.value.value = data.select;
datetimePicker.value.value = data.datetime datetimePicker.value.value = data.datetime;
getecharts() getecharts();
} }
} };
// datetimePicker iconmap // datetimePicker iconmap
watch( watch(
() => datetimePicker.value.value, () => datetimePicker.value.value,
(newVal) => { newVal => {
if (newVal) { if (newVal) {
iconmap.value.value = `注:最新数据时间为${newVal}`; iconmap.value.value = `注:最新数据时间为${newVal}`;
} }
@ -1181,10 +1266,10 @@ watch(
const wbsCode = ref(''); const wbsCode = ref('');
watch( watch(
() => JidiSelectEventStore.selectedItem, () => JidiSelectEventStore.selectedItem,
(newVal) => { newVal => {
console.log(newVal); console.log(newVal);
wbsCode.value = newVal.wbsCode; wbsCode.value = newVal.wbsCode;
getSelect() getSelect();
}, },
{ deep: true, immediate: true } { deep: true, immediate: true }
); );
@ -1194,15 +1279,17 @@ watch(
() => visibleSeriesQueue.value.length, () => visibleSeriesQueue.value.length,
(newLength, oldLength) => { (newLength, oldLength) => {
// //
if (newLength !== oldLength && chartInstance && indicatorConfig.value.length > 0) { if (
newLength !== oldLength &&
chartInstance &&
indicatorConfig.value.length > 0
) {
// option formatter visibleSeriesQueue // option formatter visibleSeriesQueue
const newOption = getChartOption(); const newOption = getChartOption();
chartInstance.setOption(newOption, true); chartInstance.setOption(newOption, true);
} }
} }
); );
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>

View File

@ -1,34 +1,42 @@
<!-- SidePanelItem.vue --> <!-- SidePanelItem.vue -->
<template> <template>
<SidePanelItem title="沿程水温变化" :prompt="prompts" :moreSelect="select" :datetimePicker="datetimePicker" <SidePanelItem
@update-values="handlePanelChange1"> title="沿程水温变化"
:prompt="prompts"
:moreSelect="select"
:datetimePicker="datetimePicker"
@update-values="handlePanelChange1"
>
<a-spin :spinning="loading" tip="加载中..."> <a-spin :spinning="loading" tip="加载中...">
<div v-show="showemit && !loading" ref="chartRef" class="chart-container"></div> <div
<div v-show="!showemit && !loading" class="chart-container"> <a-empty /></div> v-show="showemit && !loading"
ref="chartRef"
class="chart-container"
></div>
<div v-show="!showemit && !loading" class="chart-container">
<a-empty />
</div>
</a-spin> </a-spin>
</SidePanelItem> </SidePanelItem>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref, onMounted, onBeforeUnmount, watch, computed } from "vue"; import { ref, onMounted, onBeforeUnmount, watch, computed } from 'vue';
import * as echarts from "echarts"; import * as echarts from 'echarts';
import type { ECharts } from "echarts"; import type { ECharts } from 'echarts';
import SidePanelItem from "@/components/SidePanelItem/index.vue"; import SidePanelItem from '@/components/SidePanelItem/index.vue';
import { getKendoListCust, wbsbGetKendoList } from "@/api/sw"; import { getKendoListCust, wbsbGetKendoList } from '@/api/sw';
import { useJidiSelectEventStore } from "@/store/modules/jidiSelectEvent"; import { useJidiSelectEventStore } from '@/store/modules/jidiSelectEvent';
import { da } from "element-plus/es/locale/index.mjs"; import { da } from 'element-plus/es/locale/index.mjs';
import { f } from "vue-router/dist/router-CWoNjPRp.mjs"; import { f } from 'vue-router/dist/router-CWoNjPRp.mjs';
import { useModelStore } from "@/store/modules/model"; import { useModelStore } from '@/store/modules/model';
const modelStore = useModelStore(); const modelStore = useModelStore();
// 便 // 便
defineOptions({ defineOptions({
name: "yanchengshuiwenChangeMod", name: 'yanchengshuiwenChangeMod'
}); });
// //
const getCurrentHourTime = () => { const getCurrentHourTime = () => {
@ -46,21 +54,21 @@ const baseid: any = ref(null);
const paramsOne = { const paramsOne = {
rvcd: '', rvcd: '',
tm: [] tm: []
} };
const dataOne = ref('') const dataOne = ref('');
// 使 computed 使 prompts dataOne.value // 使 computed 使 prompts dataOne.value
const prompts = computed(() => ({ const prompts = computed(() => ({
show: true, show: true,
value: `注:最新数据时间为${dataOne.value || getCurrentHourTime()}`, value: `注:最新数据时间为${dataOne.value || getCurrentHourTime()}`
})); }));
const showemit = ref(false) const showemit = ref(false);
const loading = ref(false) const loading = ref(false);
// const selectOptions: any = ref([]) // const selectOptions: any = ref([])
const optionsFmt = (options: any) => const optionsFmt = (options: any) =>
options.map((item: any) => ({ options.map((item: any) => ({
label: item.wbsName, label: item.wbsName,
value: item.wbsCode value: item.wbsCode
})) }));
const chartRef = ref<HTMLElement | null>(null); const chartRef = ref<HTMLElement | null>(null);
let chartInstance: ECharts | null = null; let chartInstance: ECharts | null = null;
@ -69,7 +77,9 @@ let chartInstance: ECharts | null = null;
const stationNames = ref([]); const stationNames = ref([]);
// - (°C) null使stcd // - (°C) null使stcd
const waterTemperatures = ref<{ value: number | null; stcd: string; stnm: string }[]>([]); const waterTemperatures = ref<
{ value: number | null; stcd: string; stnm: string }[]
>([]);
// 使 computed 使 currentTime dataOne.value // 使 computed 使 currentTime dataOne.value
const currentTime = computed(() => dataOne.value || getCurrentHourTime()); const currentTime = computed(() => dataOne.value || getCurrentHourTime());
@ -79,16 +89,16 @@ const select = ref({
value: '', value: '',
options: [], options: [],
picker: undefined, picker: undefined,
format: undefined, format: undefined
}); });
// //
const datetimePicker = ref({ const datetimePicker = ref({
show: true, show: true,
value: computed(() => getCurrentHourTime()), value: computed(() => getCurrentHourTime()),
format: "YYYY-MM-DD HH", format: 'YYYY-MM-DD HH',
picker: "date" as const, picker: 'date' as const,
options: [], options: []
}); });
// //
const initChart = () => { const initChart = () => {
@ -98,14 +108,14 @@ const initChart = () => {
const option = { const option = {
title: { title: {
text: "水温(°C)", text: '水温(°C)',
left: 5, left: 5,
top: 0, top: 0,
textStyle: { textStyle: {
color: "#000000", color: '#000000',
fontSize: 12, fontSize: 12,
fontWeight: "normal", fontWeight: 'normal'
}, }
}, },
dataZoom: [ dataZoom: [
{ {
@ -118,7 +128,7 @@ const initChart = () => {
} }
], ],
tooltip: { tooltip: {
trigger: "axis", trigger: 'axis',
formatter: (params: any) => { formatter: (params: any) => {
if (params && params.length > 0) { if (params && params.length > 0) {
const data = params[0]; const data = params[0];
@ -129,31 +139,31 @@ const initChart = () => {
return `${currentTime.value}<br/>${data.name}-`; return `${currentTime.value}<br/>${data.name}-`;
} }
} }
return ""; return '';
}, }
}, },
grid: { grid: {
left: "3%", left: '3%',
right: "4%", right: '4%',
bottom: "3%", bottom: '3%',
top: "15%", top: '15%',
containLabel: true, containLabel: true
}, },
xAxis: { xAxis: {
type: "category", type: 'category',
data: stationNames.value, data: stationNames.value,
boundaryGap: true, boundaryGap: true,
axisLine: { axisLine: {
show: true, show: true,
lineStyle: { lineStyle: {
color: "#8f8f8f", color: '#8f8f8f'
}, }
}, },
axisTick: { axisTick: {
show: false, show: false
}, },
axisLabel: { axisLabel: {
color: "#333", color: '#333',
fontSize: 12, fontSize: 12,
margin: 8, margin: 8,
interval: 0, interval: 0,
@ -166,76 +176,76 @@ const initChart = () => {
} else { } else {
return ` \n${value}`; // + + return ` \n${value}`; // + +
} }
}, }
}, },
splitLine: { splitLine: {
show: true, show: true,
lineStyle: { lineStyle: {
color: "#e0e0e0", color: '#e0e0e0',
type: "solid", type: 'solid'
}, }
}, }
}, },
yAxis: { yAxis: {
type: "value", type: 'value',
min: 0, min: 0,
// max: 10, // max: 10,
interval: 2, interval: 2,
axisLine: { axisLine: {
show: true, show: true,
lineStyle: { lineStyle: {
color: "#8f8f8f", color: '#8f8f8f'
}, }
}, },
axisTick: { axisTick: {
show: true, show: true,
length: 3, length: 3,
lineStyle: { lineStyle: {
color: "#8f8f8f", color: '#8f8f8f'
}, }
}, },
axisLabel: { axisLabel: {
color: "#333", color: '#333',
fontSize: 12, fontSize: 12
}, },
splitLine: { splitLine: {
show: true, show: true,
lineStyle: { lineStyle: {
color: "#e0e0e0", color: '#e0e0e0',
type: "solid", type: 'solid'
}, }
}, }
}, },
series: [ series: [
{ {
name: "水温", name: '水温',
type: "line", type: 'line',
smooth: true, smooth: true,
symbol: "circle", symbol: 'circle',
symbolSize: 6, symbolSize: 6,
connectNulls: true, // 线线 connectNulls: true, // 线线
lineStyle: { lineStyle: {
color: "#6ca4f7", color: '#6ca4f7',
width: 2, width: 2
}, },
itemStyle: { itemStyle: {
color: "#6ca4f7", color: '#6ca4f7'
}, },
areaStyle: { areaStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ {
offset: 0, offset: 0,
color: "rgba(108, 164, 247, 0.3)", color: 'rgba(108, 164, 247, 0.3)'
}, },
{ {
offset: 1, offset: 1,
color: "rgba(108, 164, 247, 0.05)", color: 'rgba(108, 164, 247, 0.05)'
}
])
}, },
]), data: waterTemperatures.value
}, }
data: waterTemperatures.value, ]
},
],
}; };
chartInstance.setOption(option); chartInstance.setOption(option);
@ -247,11 +257,12 @@ const initChart = () => {
console.log('stcd:', params.data.stcd); console.log('stcd:', params.data.stcd);
console.log('stnm:', params.data.stnm); console.log('stnm:', params.data.stnm);
console.log('temperature:', params.data.value); console.log('temperature:', params.data.value);
console.log('temperature:', paramsOne.tm);
modelStore.modalVisible = true; modelStore.modalVisible = true;
modelStore.params.sttp = "WT"; modelStore.params.sttp = 'WT';
modelStore.title = params.data.stnm; modelStore.title = params.data.stnm;
modelStore.params.stcd = params.data.stcd; modelStore.params.stcd = params.data.stcd;
modelStore.params.date = paramsOne.tm; modelStore.filter.rangeTm = [paramsOne.tm[1], paramsOne.tm[0]];
} }
}); });
}; };
@ -269,33 +280,48 @@ const init = async () => {
try { try {
const params = { const params = {
filter: { filter: {
logic: "and", logic: 'and',
filters: [ filters: [
{ field: "rvcd", operator: "eq", dataType: "string", value: paramsOne.rvcd }, {
{ field: "tm", operator: "gte", dataType: "date", value: paramsOne.tm[1] }, field: 'rvcd',
{ field: "tm", operator: "lte", dataType: "date", value: paramsOne.tm[0] }, operator: 'eq',
], dataType: 'string',
value: paramsOne.rvcd
}, },
sort: [{ field: "sort", dir: "asc" }], {
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 res = await getKendoListCust(params);
let data = res.data.data || res.data let data = res.data.data || res.data;
if (data.length > 0) { if (data.length > 0) {
showemit.value = true showemit.value = true;
} else { } else {
showemit.value = false showemit.value = false;
} }
stationNames.value = data stationNames.value = data
.filter((item: any) => item.sttp == "1") .filter((item: any) => item.sttp == '1')
.map((item: any) => item.stnm); .map((item: any) => item.stnm);
console.log(stationNames.value); console.log(stationNames.value);
// stcdstnm // stcdstnm
waterTemperatures.value = data waterTemperatures.value = data
.filter((item: any) => item.sttp == "2") .filter((item: any) => item.sttp == '2')
.map((item: any) => ({ .map((item: any) => ({
value: item.temperature, value: item.temperature,
stcd: item.stcd, stcd: item.stcd,
@ -311,13 +337,13 @@ const init = async () => {
// //
chartInstance.setOption({ chartInstance.setOption({
xAxis: { xAxis: {
data: stationNames.value, data: stationNames.value
}, },
series: [ series: [
{ {
data: waterTemperatures.value, data: waterTemperatures.value
}, }
], ]
}); });
} }
// //
@ -336,11 +362,11 @@ const init = async () => {
const getSelectConfig = async () => { const getSelectConfig = async () => {
loading.value = true; loading.value = true;
try { try {
let obj: any = {} let obj: any = {};
if (baseid.value === 'all') { if (baseid.value === 'all') {
obj = { rstcdStepSort: 'asc', siteStepSort: 'asc' } obj = { rstcdStepSort: 'asc', siteStepSort: 'asc' };
} else { } else {
obj = { siteStepSort: 'asc' } obj = { siteStepSort: 'asc' };
} }
const filters = [ const filters = [
{ {
@ -348,13 +374,13 @@ const getSelectConfig = async () => {
operator: 'eq', operator: 'eq',
value: 'PSB_RVCD' value: 'PSB_RVCD'
} }
] ];
if (baseid.value && baseid.value !== 'all') { if (baseid.value && baseid.value !== 'all') {
filters.push({ filters.push({
field: 'objId', field: 'objId',
operator: 'eq', // contains operator: 'eq', // contains
value: baseid.value value: baseid.value
}) });
} }
let data: any = { let data: any = {
filter: { filter: {
@ -362,10 +388,10 @@ const getSelectConfig = async () => {
filters filters
} }
// select: ['WBS_CODE', 'WBS_NAME'] // select: ['WBS_CODE', 'WBS_NAME']
} };
if (obj) { if (obj) {
data.sort = [obj] data.sort = [obj];
} else { } else {
data = { data = {
...data, ...data,
@ -373,31 +399,42 @@ const getSelectConfig = async () => {
{ dir: 'asc', field: 'orderIndex' }, { dir: 'asc', field: 'orderIndex' },
{ dir: 'asc', field: 'wbsCode' }, { dir: 'asc', field: 'wbsCode' },
{ dir: 'asc', field: 'wbsName' }, { dir: 'asc', field: 'wbsName' },
{ dir: 'asc', field: 'objid' }, { dir: 'asc', field: 'objid' }
], ],
groupResultFlat: true groupResultFlat: true
};
} }
} let res = await wbsbGetKendoList(data);
let res = await wbsbGetKendoList(data) let dataOne = res?.data?.data || res?.data;
let dataOne = res?.data?.data || res?.data
if (dataOne.length > 0) { if (dataOne.length > 0) {
let dataMapNameMap: any = {} let dataMapNameMap: any = {};
let jiDiListMap: any = {} let jiDiListMap: any = {};
JidiSelectEventStore.jidiData.forEach((item: any) => { JidiSelectEventStore.jidiData.forEach((item: any) => {
jiDiListMap[item.wbsCode] = item.wbsName jiDiListMap[item.wbsCode] = item.wbsName;
}) });
dataOne.map((item: any, index: number) => { dataOne.map((item: any, index: number) => {
const { stcd, stnm, objId } = item const { stcd, stnm, objId } = item;
if (dataMapNameMap[objId]) { if (dataMapNameMap[objId]) {
dataMapNameMap[objId].push({ ...item, baseName: jiDiListMap[objId], baseId: objId }) dataMapNameMap[objId].push({
...item,
baseName: jiDiListMap[objId],
baseId: objId
});
} else { } else {
dataMapNameMap[objId] = [{ ...item, baseName: jiDiListMap[objId], baseId: objId }] dataMapNameMap[objId] = [
{ ...item, baseName: jiDiListMap[objId], baseId: objId }
];
} }
return { label: item.wbsName, value: item.wbsCode, baseId: objId, baseName: jiDiListMap[objId] } return {
}) label: item.wbsName,
let dataMapNameArr: any = [] value: item.wbsCode,
baseId: objId,
baseName: jiDiListMap[objId]
};
});
let dataMapNameArr: any = [];
Object.keys(dataMapNameMap).forEach((item: any) => { Object.keys(dataMapNameMap).forEach((item: any) => {
dataMapNameArr.push({ dataMapNameArr.push({
@ -409,24 +446,29 @@ const getSelectConfig = async () => {
return { return {
title: item2.wbsName, title: item2.wbsName,
value: item2.wbsCode value: item2.wbsCode
} };
})
})
}) })
});
});
dataMapNameArr = dataMapNameArr.sort((a: any, b: any) => { dataMapNameArr = dataMapNameArr.sort((a: any, b: any) => {
const indexA = JidiSelectEventStore.jidiData.findIndex(item => item.wbsCode === a.value); const indexA = JidiSelectEventStore.jidiData.findIndex(
const indexB = JidiSelectEventStore.jidiData.findIndex(item => item.wbsCode === b.value); item => item.wbsCode === a.value
);
const indexB = JidiSelectEventStore.jidiData.findIndex(
item => item.wbsCode === b.value
);
return indexA - indexB; return indexA - indexB;
}) });
select.value.options = filterSelectOptions(dataMapNameArr) select.value.options = filterSelectOptions(dataMapNameArr);
if (baseid.value === 'all') { if (baseid.value === 'all') {
select.value.value = 'SJLY1U' select.value.value = 'SJLY1U';
} else if (filterSelectOptions(dataMapNameArr)[0]?.children) { } else if (filterSelectOptions(dataMapNameArr)[0]?.children) {
select.value.value = filterSelectOptions(dataMapNameArr)[0]?.children[0]?.value select.value.value =
filterSelectOptions(dataMapNameArr)[0]?.children[0]?.value;
} else if (filterSelectOptions(dataMapNameArr)[0]?.value) { } else if (filterSelectOptions(dataMapNameArr)[0]?.value) {
select.value.value = filterSelectOptions(dataMapNameArr)[0]?.value select.value.value = filterSelectOptions(dataMapNameArr)[0]?.value;
} else { } else {
select.value.value = '' select.value.value = '';
} }
} }
} catch (error) { } catch (error) {
@ -434,31 +476,39 @@ const getSelectConfig = async () => {
} finally { } finally {
loading.value = false; loading.value = false;
} }
} };
const filterSelectOptions = (selectOptions: any) => { const filterSelectOptions = (selectOptions: any) => {
if (selectOptions?.length === 1 && selectOptions?.[0]?.children?.length === 1) { if (
return [{ ...selectOptions?.[0]?.children?.[0], isLeaf: true }] selectOptions?.length === 1 &&
selectOptions?.[0]?.children?.length === 1
) {
return [{ ...selectOptions?.[0]?.children?.[0], isLeaf: true }];
} else if (selectOptions.length) { } else if (selectOptions.length) {
return selectOptions.map((e) => { return selectOptions.map(e => {
if (e?.children?.length > 1) { if (e?.children?.length > 1) {
return { ...e, isLeaf: false, selectable: true, children: e.children.map((item) => ({ ...item, isLeaf: true })) } return {
...e,
isLeaf: false,
selectable: true,
children: e.children.map(item => ({ ...item, isLeaf: true }))
};
} else { } else {
return { ...e.children[0], isLeaf: true } return { ...e.children[0], isLeaf: true };
} }
}) });
} }
return [] return [];
} };
// //
const handlePanelChange1 = (data) => { const handlePanelChange1 = data => {
console.log('当前所有控件状态:', data); console.log('当前所有控件状态:', data);
if (data.moreSelect) { if (data.moreSelect) {
paramsOne.rvcd = data.moreSelect paramsOne.rvcd = data.moreSelect;
} }
if (data.datetime) { if (data.datetime) {
dataOne.value = data.datetime dataOne.value = data.datetime;
const datetime = data.datetime; const datetime = data.datetime;
// //
@ -466,7 +516,14 @@ const handlePanelChange1 = (data) => {
const [year, month, day] = datePart.split('-').map(Number); const [year, month, day] = datePart.split('-').map(Number);
// Date // Date
const currentDate = new Date(year, month - 1, day, parseInt(hourPart), 0, 0); const currentDate = new Date(
year,
month - 1,
day,
parseInt(hourPart),
0,
0
);
// Date // Date
const previousDate = new Date(year, month - 2, day, 0, 0, 0); const previousDate = new Date(year, month - 2, day, 0, 0, 0);
@ -487,20 +544,19 @@ const handlePanelChange1 = (data) => {
formatDate(currentDate), // "2026-05-15 15:00:00" formatDate(currentDate), // "2026-05-15 15:00:00"
formatDate(previousDate) // "2026-04-15 00:00:00" formatDate(previousDate) // "2026-04-15 00:00:00"
]; ];
paramsOne.tm = result paramsOne.tm = result;
console.log(paramsOne.tm, 'paramsOne.tm') console.log(paramsOne.tm, 'paramsOne.tm');
} }
if (paramsOne.rvcd || paramsOne.tm.length > 0) { if (paramsOne.rvcd || paramsOne.tm.length > 0) {
init(); init();
} }
};
}
watch( watch(
() => JidiSelectEventStore.selectedItem, () => JidiSelectEventStore.selectedItem,
(newVal) => { newVal => {
baseid.value = newVal.wbsCode; baseid.value = newVal.wbsCode;
getSelectConfig() getSelectConfig();
}, },
{ deep: true, immediate: true } { deep: true, immediate: true }
); );
@ -508,12 +564,12 @@ watch(
onMounted(() => { onMounted(() => {
// DOM // DOM
init(); init();
window.addEventListener("resize", handleResize); window.addEventListener('resize', handleResize);
}); });
// //
onBeforeUnmount(() => { onBeforeUnmount(() => {
window.removeEventListener("resize", handleResize); window.removeEventListener('resize', handleResize);
chartInstance?.dispose(); chartInstance?.dispose();
}); });
</script> </script>