修改水温垂向右侧点击,添加录像视频和建设状态,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
});
}
// 录像视频 - 时间查询 没接
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) {
return request({

View File

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

View File

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

View File

@ -158,12 +158,12 @@ const columnsMap: Record<string, any[]> = {
},
{
title: '性腺发育期',
dataIndex: 'fwgh',
dataIndex: 'st',
width: 140
},
{
title: '早期资源量和种类',
dataIndex: 'fwgh',
dataIndex: 'ps',
width: 220
},
{
@ -203,7 +203,7 @@ const columnsMap: Record<string, any[]> = {
},
{
title: '电导率(μS/cm)',
dataIndex: 'tm',
dataIndex: 'cond',
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 = () => {
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());
fetchData();
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() !== ''
? record.tecnt
: '-';
const transplant =
record?.transplant !== undefined &&
record?.transplant !== null &&
String(record.transplant).trim() !== ''
? record.transplant
const plantask =
record?.plantask !== undefined &&
record?.plantask !== null &&
String(record.plplantask).trim() !== ''
? record.plplantask
: '-';
return `${tecnt}/${transplant}`;
return `${tecnt}/${plantask}`;
}
},
{
@ -123,13 +123,13 @@ const tableColumns = ref([
String(record.surnum).trim() !== ''
? record.surnum
: '-';
const plantask =
record?.plantask !== undefined &&
record?.plantask !== null &&
String(record.plantask).trim() !== ''
? record.plantask
const transplant =
record?.transplant !== undefined &&
record?.transplant !== null &&
String(record.transplant).trim() !== ''
? record.transplant
: '-';
return `${surnum}/${plantask}`;
return `${surnum}/${transplant}`;
}
},
{

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -32,6 +32,34 @@ export class PointLayerManager {
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() {
return this.layerRegistry;
@ -128,6 +156,7 @@ export class PointLayerManager {
const vectorLayer = this.layerRegistry.get(layerKey);
if (vectorLayer && vectorLayer.getVisible() !== visible) {
vectorLayer.setVisible(visible);
vectorLayer.changed();
}
}

View File

@ -204,6 +204,213 @@ export class PopupManager {
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 状态,供地图销毁和切换时复用。
reset() {
if (this.animationFrameId) {
@ -264,6 +471,7 @@ export class PopupManager {
// 备注:销毁 Popup 管理器内部状态和 Overlay 引用。
destroy() {
this.clearBatchPopups();
this.reset();
if (this.popupElement && this.popupMouseEnterHandler) {
this.popupElement.removeEventListener(

View File

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

View File

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

View File

@ -107,6 +107,8 @@ export const useMapOrchestrator = () => {
mapDataStore.setLoading(true);
try {
const previousPageKey = mapConfigStore.lastLoadOptions?.pageKey || '';
const previousCheckedKeys = mapViewStore.getCheckedLayerKeys();
const { layerConfig, legendOriginal, pageLegend } =
await mapConfigStore.loadPageMapConfig({
systemId: SYSTEM_ID,
@ -122,11 +124,26 @@ export const useMapOrchestrator = () => {
if (layerConfig.length > 0) {
mapStore.setLayerData(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);
}
} 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;
const targetPoint = mapDataStore.pointData.find((item: any) => {
return item.stcd === pointId || item._id === pointId;

View File

@ -1,36 +1,73 @@
<template>
<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-select v-model:value="formValue.dataDimensionVal" placeholder="请选择" style="width: 200px"
@change="handleBaseChange">
<a-select-option v-for="item in jdList" :key="item.wbsCode" :value="item.wbsCode">
<a-select
v-model:value="formValue.dataDimensionVal"
placeholder="请选择"
style="width: 200px"
@change="handleBaseChange"
>
<a-select-option
v-for="item in jdList"
:key="item.wbsCode"
:value="item.wbsCode"
>
{{ item.wbsName }}
</a-select-option>
</a-select>
</a-form-item>
<a-form-item label="所在流域" name="rvcd">
<a-select v-model:value="formValue.rvcd" placeholder="请选择" style="width: 200px"
:disabled="!formValue.dataDimensionVal" @change="handleRvcdChange">
<a-select-option v-for="item in lyList" :key="item.value" :value="item.value">
<a-select
v-model:value="formValue.rvcd"
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 }}
</a-select-option>
</a-select>
</a-form-item>
<a-form-item label="断面名称" name="stcd">
<a-select 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">
<a-select
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 }}
</a-select-option>
</a-select>
</a-form-item>
<a-form-item label="月份" name="tm">
<a-date-picker 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-date-picker
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>
@ -45,8 +82,14 @@
<div style="width: 100%; display: flex">
<!-- 图表容器始终存在不再使用 v-if -->
<div class="chart-wrapper" ref="chartRef"></div>
<BasicTable ref="tableRef" :scrollY="460" :columns="columns" :list-url="DetGetKendoListCust"
:search-params="{ sort: sort }" :transform-data="customTransform">
<BasicTable
ref="tableRef"
:scrollY="460"
:columns="columns"
:list-url="DetGetKendoListCust"
:search-params="{ sort: sort }"
:transform-data="customTransform"
>
<template #action="{ record }">
<a @click="handleViewDetail(record)" class="text_hocer">查看详情</a>
</template>
@ -816,8 +859,12 @@ const handleViewDetail = (record: any) => {
});
modelStore.modalVisible = true;
modelStore.params.sttp = 'WT';
modelStore.title = stnm ;
modelStore.title = stnm;
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({

View File

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

View File

@ -1,6 +1,11 @@
<!-- SidePanelItem.vue -->
<template>
<SidePanelItem title="水温监测" :select="select" :scopeDate="datetimePicker" @update-values="handlePanelChange1">
<SidePanelItem
title="水温监测"
:select="select"
:scopeDate="datetimePicker"
@update-values="handlePanelChange1"
>
<div class="chart-wrapper">
<a-spin :spinning="loading">
<!-- 始终渲染图表容器确保有固定宽高 -->
@ -19,9 +24,10 @@ import { ref, onMounted, onUnmounted, nextTick, watch } from 'vue';
import * as echarts from 'echarts';
import type { EChartsOption } from 'echarts';
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 { useModelStore } from "@/store/modules/model";
import { useModelStore } from '@/store/modules/model';
import dayjs from 'dayjs';
//
defineOptions({
name: 'qixidishuiwenbianhua'
@ -32,7 +38,7 @@ const JidiSelectEventStore = useJidiSelectEventStore();
//
const chartRef = ref<HTMLDivElement>();
let chartInstance: echarts.ECharts | null = null;
const baseid = ref('')
const baseid = ref('');
// Loading
const loading = ref(false);
@ -52,9 +58,13 @@ const datetimePicker: any = ref({
show: true,
value: (() => {
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 lastMonthStr = `${lastMonth.getFullYear()}-${String(lastMonth.getMonth() + 1).padStart(2, '0')}`;
const lastMonthStr = `${lastMonth.getFullYear()}-${String(
lastMonth.getMonth() + 1
).padStart(2, '0')}`;
return [lastMonthStr, currentMonth];
})(),
format: 'YY-MM',
@ -69,7 +79,16 @@ const modelStore = useModelStore();
const unita = ref('℃');
// - 使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');
//
// 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({
dt: `2024-${month}`,
@ -151,12 +173,17 @@ const updateChart = () => {
let 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)
: '-';
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;
@ -242,14 +269,15 @@ const updateChart = () => {
nameGap: 8, // 线 20px
nameTextStyle: {
color: '#333333',
fontSize: 12,
fontSize: 12
},
// Y
min: hasData && echartsData.value.length > 0
min:
hasData && echartsData.value.length > 0
? Math.floor(Math.min(...echartsData.value.map(item => item.wt)))
: undefined,
max: hasData && echartsData.value.length > 0
max:
hasData && echartsData.value.length > 0
? Math.ceil(Math.max(...echartsData.value.map(item => item.wt)))
: undefined,
//
@ -281,7 +309,9 @@ const updateChart = () => {
series: [
{
name: _legendData[0],
data: hasData ? (echartsData.value?.map((item: any) => item.wt) || []) : [],
data: hasData
? echartsData.value?.map((item: any) => item.wt) || []
: [],
type: 'line',
smooth: true,
symbol: 'circle',
@ -328,11 +358,15 @@ const handleDataPointClick = (params: any) => {
索引: dataIndex
});
modelStore.modalVisible = true;
modelStore.params.sttp = "WT";
modelStore.params.sttp = 'WT';
modelStore.title = '水温监测';
modelStore.params.stcd = dataItem.stcd;
modelStore.showStcdSelector = true
modelStore.stcdOptions = select.value.options
modelStore.showStcdSelector = true;
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 stcd 测站代码
*/
const getChartData = async (startDate: string, endDate: string, stcd: string) => {
const getChartData = async (
startDate: string,
endDate: string,
stcd: string
) => {
try {
console.log('=== 开始加载图表数据 ===');
console.log('请求参数:', { startDate, endDate, stcd });
@ -370,31 +408,31 @@ const getChartData = async (startDate: string, endDate: string, stcd: string) =>
// TODO: API
//
const params = {
"filter": {
"logic": "and",
"filters": [
filter: {
logic: 'and',
filters: [
{
"field": "DT",
"operator": "gte",
"value": startDate // : "2026-05-01 00:00:00"
field: 'DT',
operator: 'gte',
value: startDate // : "2026-05-01 00:00:00"
},
{
"field": "DT",
"operator": "lte",
"value": endDate // : "2026-06-30 23:59:59"
field: 'DT',
operator: 'lte',
value: endDate // : "2026-06-30 23:59:59"
},
{
"field": "stcd",
"operator": "eq",
"dataType": "string",
"value": stcd
field: 'stcd',
operator: 'eq',
dataType: 'string',
value: stcd
}
]
},
"sort": [
sort: [
{
"field": "dt",
"dir": "asc"
field: 'dt',
dir: 'asc'
}
]
};
@ -456,7 +494,6 @@ const getChartData = async (startDate: string, endDate: string, stcd: string) =>
await nextTick();
updateChart();
console.log('✅ 图表数据加载完成');
} catch (error) {
console.error('获取图表数据失败:', error);
hasData.value = false;
@ -501,13 +538,18 @@ const convertDateRange = (dateRange: string[]) => {
// 31228
const startMonthLastDay = new Date(fullStartYear, startMonth, 0).getDate();
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
//
const endMonthLastDay = new Date(fullEndYear, endMonth, 0).getDate();
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 {
startDate,
@ -515,7 +557,7 @@ const convertDateRange = (dateRange: string[]) => {
};
};
const handlePanelChange1 = async (data) => {
const handlePanelChange1 = async data => {
console.log('=== 用户交互触发 ===');
console.log('当前所有控件状态:', data);
// scopeDate: ['26-05', '26-06']
@ -543,67 +585,63 @@ const handlePanelChange1 = async (data) => {
select: data.select
});
}
}
};
//
const getselsectData = async () => {
let params = {
"filter": {
"logic": "and",
"filters": [
baseid.value != 'all' ? {
"field": "baseId",
"operator": "eq",
"dataType": "string",
"value": baseid.value
} : null,
filter: {
logic: 'and',
filters: [
baseid.value != 'all'
? {
field: 'baseId',
operator: 'eq',
dataType: 'string',
value: baseid.value
}
: null,
{
"field": "fhstcd",
"operator": "isnotnull"
field: 'fhstcd',
operator: 'isnotnull'
},
{
"logic": "or",
"filters": [
logic: 'or',
filters: [
{
"field": "sttpCode",
"operator": "eq",
"dataType": "string",
"value": "WTRV"
field: 'sttpCode',
operator: 'eq',
dataType: 'string',
value: 'WTRV'
}
]
}
].filter(Boolean)
},
"select": [
"stcd",
"stnm",
"fhstcd",
"fhstnm",
"sttpCode"
]
}
let res = await msstbprptGetKendoList(params)
console.log(res)
select: ['stcd', 'stnm', 'fhstcd', 'fhstnm', 'sttpCode']
};
let res = await msstbprptGetKendoList(params);
console.log(res);
if (!res?.data?.data || res.data.data.length == 0) {
select.value.value = ''
select.value.options = []
return
select.value.value = '';
select.value.options = [];
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) => {
return {
label: item.stnm,
value: item.stcd
}
})
};
});
// debugger
};
////
watch(
() => JidiSelectEventStore.selectedItem,
(newVal) => {
newVal => {
baseid.value = newVal.wbsCode;
getselsectData()
getselsectData();
},
{ deep: true, immediate: true }
);

View File

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

View File

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

View File

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