修改水温垂向右侧点击,添加录像视频和建设状态,bug修改
This commit is contained in:
parent
f1b8ffebf1
commit
ce8855d3c2
@ -91,6 +91,24 @@ export function getVideoList(data: any) {
|
|||||||
data
|
data
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
// 录像视频 - 时间查询 没接
|
||||||
|
export function getNoLiveVideoYear(data: any) {
|
||||||
|
return request({
|
||||||
|
url: '/wmp-env-server/env/vd/GetKendoListCust',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 录像视频
|
||||||
|
export function getNoLiveVideoList(data: any) {
|
||||||
|
return request({
|
||||||
|
url: '/wmp-env-server/env/vd/runData/GetKendoListCust',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// 实时视频单个查询
|
// 实时视频单个查询
|
||||||
export function getVideoDetail(data: any) {
|
export function getVideoDetail(data: any) {
|
||||||
return request({
|
return request({
|
||||||
|
|||||||
@ -74,11 +74,6 @@ const baseColumns = [
|
|||||||
dataIndex: 'rowId',
|
dataIndex: 'rowId',
|
||||||
width: 60
|
width: 60
|
||||||
},
|
},
|
||||||
{
|
|
||||||
title: '告警设施',
|
|
||||||
dataIndex: 'stnm',
|
|
||||||
width: 220
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
title: '告警时间',
|
title: '告警时间',
|
||||||
dataIndex: 'tms',
|
dataIndex: 'tms',
|
||||||
@ -91,12 +86,7 @@ const baseColumns = [
|
|||||||
width: 120
|
width: 120
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '监测要素',
|
title: '限值(m³/s)',
|
||||||
dataIndex: 'ysName',
|
|
||||||
width: 120
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '判断标准',
|
|
||||||
dataIndex: 'bzz',
|
dataIndex: 'bzz',
|
||||||
width: 120
|
width: 120
|
||||||
},
|
},
|
||||||
|
|||||||
@ -21,6 +21,11 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="filter-row">
|
<div class="filter-row">
|
||||||
|
<a-select
|
||||||
|
v-model:value="timeType"
|
||||||
|
style="margin-right: 12px"
|
||||||
|
:options="timeTypeOptions"
|
||||||
|
/>
|
||||||
<a-range-picker
|
<a-range-picker
|
||||||
v-model:value="dateRange"
|
v-model:value="dateRange"
|
||||||
:format="dateFormat"
|
:format="dateFormat"
|
||||||
|
|||||||
@ -158,12 +158,12 @@ const columnsMap: Record<string, any[]> = {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '性腺发育期',
|
title: '性腺发育期',
|
||||||
dataIndex: 'fwgh',
|
dataIndex: 'st',
|
||||||
width: 140
|
width: 140
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '早期资源量和种类',
|
title: '早期资源量和种类',
|
||||||
dataIndex: 'fwgh',
|
dataIndex: 'ps',
|
||||||
width: 220
|
width: 220
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -203,7 +203,7 @@ const columnsMap: Record<string, any[]> = {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '电导率(μS/cm)',
|
title: '电导率(μS/cm)',
|
||||||
dataIndex: 'tm',
|
dataIndex: 'cond',
|
||||||
width: 180
|
width: 180
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|||||||
@ -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>
|
||||||
@ -472,6 +472,13 @@ const handleSearch = () => fetchData();
|
|||||||
|
|
||||||
const initData = () => {
|
const initData = () => {
|
||||||
if (hasLoaded.value) return;
|
if (hasLoaded.value) return;
|
||||||
|
console.log(modelStore.filter.rangeTm);
|
||||||
|
if (modelStore.filter.rangeTm.length > 0) {
|
||||||
|
dateRange.value = [
|
||||||
|
dayjs(modelStore.filter.rangeTm[0]),
|
||||||
|
dayjs(modelStore.filter.rangeTm[1])
|
||||||
|
];
|
||||||
|
}
|
||||||
nextTick(() => initChart());
|
nextTick(() => initChart());
|
||||||
fetchData();
|
fetchData();
|
||||||
hasLoaded.value = true;
|
hasLoaded.value = true;
|
||||||
|
|||||||
396
frontend/src/components/MapModal/components/NoLiveVideo.vue
Normal file
396
frontend/src/components/MapModal/components/NoLiveVideo.vue
Normal 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>
|
||||||
@ -103,13 +103,13 @@ const tableColumns = ref([
|
|||||||
String(record.tecnt).trim() !== ''
|
String(record.tecnt).trim() !== ''
|
||||||
? record.tecnt
|
? record.tecnt
|
||||||
: '-';
|
: '-';
|
||||||
const transplant =
|
const plantask =
|
||||||
record?.transplant !== undefined &&
|
record?.plantask !== undefined &&
|
||||||
record?.transplant !== null &&
|
record?.plantask !== null &&
|
||||||
String(record.transplant).trim() !== ''
|
String(record.plplantask).trim() !== ''
|
||||||
? record.transplant
|
? record.plplantask
|
||||||
: '-';
|
: '-';
|
||||||
return `${tecnt}/${transplant}`;
|
return `${tecnt}/${plantask}`;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -123,13 +123,13 @@ const tableColumns = ref([
|
|||||||
String(record.surnum).trim() !== ''
|
String(record.surnum).trim() !== ''
|
||||||
? record.surnum
|
? record.surnum
|
||||||
: '-';
|
: '-';
|
||||||
const plantask =
|
const transplant =
|
||||||
record?.plantask !== undefined &&
|
record?.transplant !== undefined &&
|
||||||
record?.plantask !== null &&
|
record?.transplant !== null &&
|
||||||
String(record.plantask).trim() !== ''
|
String(record.transplant).trim() !== ''
|
||||||
? record.plantask
|
? record.transplant
|
||||||
: '-';
|
: '-';
|
||||||
return `${surnum}/${plantask}`;
|
return `${surnum}/${transplant}`;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@ -479,6 +479,12 @@ const handleSearch = () => fetchData();
|
|||||||
const initData = () => {
|
const initData = () => {
|
||||||
if (hasLoaded.value) return;
|
if (hasLoaded.value) return;
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
|
if (modelStore.filter.rangeTm.length > 0) {
|
||||||
|
dateRange.value = [
|
||||||
|
dayjs(modelStore.filter.rangeTm[0]),
|
||||||
|
dayjs(modelStore.filter.rangeTm[1])
|
||||||
|
];
|
||||||
|
}
|
||||||
if (chartRef.value && !chartInstance)
|
if (chartRef.value && !chartInstance)
|
||||||
chartInstance = echarts.init(chartRef.value);
|
chartInstance = echarts.init(chartRef.value);
|
||||||
});
|
});
|
||||||
|
|||||||
@ -903,7 +903,15 @@ const handleExport = () => {
|
|||||||
|
|
||||||
const initData = () => {
|
const initData = () => {
|
||||||
if (hasLoaded.value) return;
|
if (hasLoaded.value) return;
|
||||||
|
console.log('modelStore.filter.rangeTm:', modelStore.filter.rangeTm);
|
||||||
|
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
|
if (modelStore.filter.rangeTm.length > 0) {
|
||||||
|
dateRange.value = [
|
||||||
|
dayjs(modelStore.filter.rangeTm[0]),
|
||||||
|
dayjs(modelStore.filter.rangeTm[1])
|
||||||
|
];
|
||||||
|
}
|
||||||
if (chartRef.value && !chartInstance)
|
if (chartRef.value && !chartInstance)
|
||||||
chartInstance = echarts.init(chartRef.value);
|
chartInstance = echarts.init(chartRef.value);
|
||||||
});
|
});
|
||||||
|
|||||||
@ -37,7 +37,12 @@
|
|||||||
<!-- 同期对比列的自定义渲染 -->
|
<!-- 同期对比列的自定义渲染 -->
|
||||||
<template #contrast="{ record }">
|
<template #contrast="{ record }">
|
||||||
<span
|
<span
|
||||||
v-if="record.wt && record.beforeWt && !isNaN(Number(record.wt)) && !isNaN(Number(record.beforeWt))"
|
v-if="
|
||||||
|
record.wt &&
|
||||||
|
record.beforeWt &&
|
||||||
|
!isNaN(Number(record.wt)) &&
|
||||||
|
!isNaN(Number(record.beforeWt))
|
||||||
|
"
|
||||||
:style="{
|
:style="{
|
||||||
color:
|
color:
|
||||||
Number(record.wt) - Number(record.beforeWt) > 0
|
Number(record.wt) - Number(record.beforeWt) > 0
|
||||||
@ -664,6 +669,12 @@ const initData = () => {
|
|||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
initChart();
|
initChart();
|
||||||
}, 200);
|
}, 200);
|
||||||
|
if (modelStore.filter.rangeTm.length > 0) {
|
||||||
|
rangePicker.value = [
|
||||||
|
dayjs(modelStore.filter.rangeTm[0]),
|
||||||
|
dayjs(modelStore.filter.rangeTm[1])
|
||||||
|
];
|
||||||
|
}
|
||||||
hasLoaded.value = true; // 标记为已请求过
|
hasLoaded.value = true; // 标记为已请求过
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -10,8 +10,8 @@
|
|||||||
class="map-modal"
|
class="map-modal"
|
||||||
>
|
>
|
||||||
<!-- 问题:
|
<!-- 问题:
|
||||||
1.基础信息图片展示是什么逻辑
|
1.基础信息图片展示是什么逻辑✅️
|
||||||
2. 电站专题展示逻辑
|
2. 电站专题展示逻辑✅️
|
||||||
3. 实时视频回放 少接口✅️
|
3. 实时视频回放 少接口✅️
|
||||||
4. 预警提示 少接口
|
4. 预警提示 少接口
|
||||||
5. 生态流量 达标率查询不对(✅️)
|
5. 生态流量 达标率查询不对(✅️)
|
||||||
@ -20,13 +20,13 @@
|
|||||||
8. 栖息地-流量监测 没有水位视频和流量视频 字段没数据
|
8. 栖息地-流量监测 没有水位视频和流量视频 字段没数据
|
||||||
9. 栖息地 水温、水质、流量没有数据没法测
|
9. 栖息地 水温、水质、流量没有数据没法测
|
||||||
10.鱼类增殖站 - 运行数据 要添加字典
|
10.鱼类增殖站 - 运行数据 要添加字典
|
||||||
11.珍惜植物园 - 种植要求字段不知道
|
11.珍惜植物园 - 种植要求字段不知道✅️
|
||||||
12.水生调查断面 - 监测数据 不知道电导率 ,性腺发育期,早期资源量和种类
|
12.水生调查断面 - 监测数据 不知道电导率 ,性腺发育期,早期资源量和种类 ✅️
|
||||||
13.水电告警情况 生态流量 不知道字段 接口传参有问题
|
13.水电告警情况 生态流量 不知道字段 ✅️ 接口传参有问题
|
||||||
|
14. 地图抽吸 碰撞检测 放大到具体层级锚点抽吸了但是popup弹框还在显示;
|
||||||
|
15.运行情况 计划开始运行时间 接口没接
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
没弄: 环保设施
|
|
||||||
-->
|
-->
|
||||||
<div v-if="modelStore.showStcdSelector" class="stcd-selector-wrapper">
|
<div v-if="modelStore.showStcdSelector" class="stcd-selector-wrapper">
|
||||||
<a-select
|
<a-select
|
||||||
@ -88,18 +88,23 @@
|
|||||||
v-show="currentActiveKey === 'videoInfo'"
|
v-show="currentActiveKey === 'videoInfo'"
|
||||||
:is-active="currentActiveKey === 'videoInfo'"
|
:is-active="currentActiveKey === 'videoInfo'"
|
||||||
/>
|
/>
|
||||||
|
<!-- 录像视频 -->
|
||||||
|
<NoLiveVideo
|
||||||
|
v-show="currentActiveKey === 'noLiveVideo'"
|
||||||
|
:is-active="currentActiveKey === 'noLiveVideo'"
|
||||||
|
/>
|
||||||
<!-- 全景影像 -->
|
<!-- 全景影像 -->
|
||||||
<PanoramaInfo
|
<PanoramaInfo
|
||||||
v-show="currentActiveKey === 'panoramaInfo'"
|
v-show="currentActiveKey === 'panoramaInfo'"
|
||||||
:url="getTabUrl('panoramaInfo')"
|
:url="getTabUrl('panoramaInfo')"
|
||||||
:is-active="currentActiveKey === 'panoramaInfo'"
|
:is-active="currentActiveKey === 'panoramaInfo'"
|
||||||
/>
|
/>
|
||||||
<!-- 监测数据 - 电站运行过程线 -->
|
<!-- 监测数据 - 电站运行过程线 ✅️-->
|
||||||
<MonitorInfo
|
<MonitorInfo
|
||||||
v-show="currentActiveKey === 'monitorInfo'"
|
v-show="currentActiveKey === 'monitorInfo'"
|
||||||
:is-active="currentActiveKey === 'monitorInfo'"
|
:is-active="currentActiveKey === 'monitorInfo'"
|
||||||
/>
|
/>
|
||||||
<!-- 监测数据 - 水温 -->
|
<!-- 监测数据 - 水温 ✅️-->
|
||||||
<WaterTemperature
|
<WaterTemperature
|
||||||
v-show="currentActiveKey === 'WaterTemperature'"
|
v-show="currentActiveKey === 'WaterTemperature'"
|
||||||
:is-active="currentActiveKey === 'WaterTemperature'"
|
:is-active="currentActiveKey === 'WaterTemperature'"
|
||||||
@ -111,7 +116,7 @@
|
|||||||
"
|
"
|
||||||
:wt-config="modelStore.wtConfig"
|
:wt-config="modelStore.wtConfig"
|
||||||
/>
|
/>
|
||||||
<!-- 监测数据 - 垂向水温 -->
|
<!-- 监测数据 - 垂向水温 ✅️ -->
|
||||||
<VerticalWaterTemperature
|
<VerticalWaterTemperature
|
||||||
v-show="currentActiveKey === 'VerticalWaterTemperature'"
|
v-show="currentActiveKey === 'VerticalWaterTemperature'"
|
||||||
:is-active="currentActiveKey === 'VerticalWaterTemperature'"
|
:is-active="currentActiveKey === 'VerticalWaterTemperature'"
|
||||||
@ -167,6 +172,12 @@
|
|||||||
v-show="currentActiveKey === 'FishZHFX'"
|
v-show="currentActiveKey === 'FishZHFX'"
|
||||||
:is-active="currentActiveKey === 'FishZHFX'"
|
:is-active="currentActiveKey === 'FishZHFX'"
|
||||||
/>
|
/>
|
||||||
|
<!-- 建设情况 -->
|
||||||
|
<FishFacilityBuildState
|
||||||
|
v-show="currentActiveKey === 'FishFacilityBuildState'"
|
||||||
|
:is-active="currentActiveKey === 'FishFacilityBuildState'"
|
||||||
|
:url="getTabUrl('FishFacilityBuildState')"
|
||||||
|
/>
|
||||||
<!-- AI运行识别 -->
|
<!-- AI运行识别 -->
|
||||||
<AIYXSB
|
<AIYXSB
|
||||||
v-show="currentActiveKey === 'AIYXSB'"
|
v-show="currentActiveKey === 'AIYXSB'"
|
||||||
@ -178,12 +189,12 @@
|
|||||||
:tabs-items="getTabChildren('tableTabs')"
|
:tabs-items="getTabChildren('tableTabs')"
|
||||||
:is-active="currentActiveKey === 'tableTabs'"
|
:is-active="currentActiveKey === 'tableTabs'"
|
||||||
/>
|
/>
|
||||||
<!-- 生态流量 -->
|
<!-- 生态流量 ✅️-->
|
||||||
<EcologicalFlow
|
<EcologicalFlow
|
||||||
v-show="currentActiveKey === 'EcologicalFlow'"
|
v-show="currentActiveKey === 'EcologicalFlow'"
|
||||||
:is-active="currentActiveKey === 'EcologicalFlow'"
|
:is-active="currentActiveKey === 'EcologicalFlow'"
|
||||||
/>
|
/>
|
||||||
<!-- 鱼类繁殖适宜性分析 -->
|
<!-- 鱼类繁殖适宜性分析 ✅️-->
|
||||||
<WaterTemperatureRep
|
<WaterTemperatureRep
|
||||||
v-show="currentActiveKey === 'WaterTemperatureRep'"
|
v-show="currentActiveKey === 'WaterTemperatureRep'"
|
||||||
:is-active="currentActiveKey === 'WaterTemperatureRep'"
|
:is-active="currentActiveKey === 'WaterTemperatureRep'"
|
||||||
@ -259,6 +270,7 @@ import { ref, watch, reactive, computed } from 'vue';
|
|||||||
// 导入预定义的 Tab 内容组件
|
// 导入预定义的 Tab 内容组件
|
||||||
import BasicInfo from './components/BasicInfo.vue'; // 基本信息
|
import BasicInfo from './components/BasicInfo.vue'; // 基本信息
|
||||||
import VideoInfo from './components/videoInfo.vue'; // 实时视频
|
import VideoInfo from './components/videoInfo.vue'; // 实时视频
|
||||||
|
import NoLiveVideo from './components/NoLiveVideo.vue'; // 录像视频
|
||||||
import PanoramaInfo from './components/PanoramaInfo.vue'; // 全景影像
|
import PanoramaInfo from './components/PanoramaInfo.vue'; // 全景影像
|
||||||
import MonitorInfo from './components/MonitorInfo.vue'; // 电站运行过程线
|
import MonitorInfo from './components/MonitorInfo.vue'; // 电站运行过程线
|
||||||
import WaterTemperature from './components/WaterTemperature.vue'; // 水温
|
import WaterTemperature from './components/WaterTemperature.vue'; // 水温
|
||||||
@ -270,6 +282,7 @@ import FlowMeasure from './components/FlowMeasure.vue'; // 流量监测
|
|||||||
import FishFacilityMonitorData from './components/FishFacilityMonitorData.vue'; // 在线监测数据
|
import FishFacilityMonitorData from './components/FishFacilityMonitorData.vue'; // 在线监测数据
|
||||||
import FishFacilityRunState from './components/FishFacilityRunState.vue'; // 运行情况
|
import FishFacilityRunState from './components/FishFacilityRunState.vue'; // 运行情况
|
||||||
import FlowDischarge from './components/FlowDischarge.vue'; // 生态流量泄放设施 - 监测数据
|
import FlowDischarge from './components/FlowDischarge.vue'; // 生态流量泄放设施 - 监测数据
|
||||||
|
import FishFacilityBuildState from './components/FishFacilityBuildState.vue'; // 建设情况
|
||||||
// 鱼类增殖站
|
// 鱼类增殖站
|
||||||
import NormalOperationData from './components/NormalOperationData/index.vue'; // 运行数据
|
import NormalOperationData from './components/NormalOperationData/index.vue'; // 运行数据
|
||||||
import ProcessDiagram from './components/ProcessDiagram.vue'; // 过程图
|
import ProcessDiagram from './components/ProcessDiagram.vue'; // 过程图
|
||||||
@ -999,6 +1012,12 @@ const handleClose = () => {
|
|||||||
modelStore.verticalWaterStcd = '';
|
modelStore.verticalWaterStcd = '';
|
||||||
modelStore.aiSites = [];
|
modelStore.aiSites = [];
|
||||||
modelStore.wtConfig = {};
|
modelStore.wtConfig = {};
|
||||||
|
modelStore.filter = {
|
||||||
|
stllgzlx: '', // 生态流量 - 规则类型
|
||||||
|
stllTmType: '', // 生态流量 - 时间类型
|
||||||
|
tm: '',
|
||||||
|
rangeTm: []
|
||||||
|
};
|
||||||
modelStore.wqShow = false;
|
modelStore.wqShow = false;
|
||||||
modelStore.fishTabInfo = null;
|
modelStore.fishTabInfo = null;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -5,24 +5,40 @@
|
|||||||
<div class="title_left">
|
<div class="title_left">
|
||||||
<span class="texttitle">{{ title }}</span>
|
<span class="texttitle">{{ title }}</span>
|
||||||
<span v-if="prompt.show" class="title_icon">
|
<span v-if="prompt.show" class="title_icon">
|
||||||
<a-tooltip placement="top" :title="prompt.value" :get-popup-container="getPopupContainer">
|
<a-tooltip
|
||||||
|
placement="top"
|
||||||
|
:title="prompt.value"
|
||||||
|
:get-popup-container="getPopupContainer"
|
||||||
|
>
|
||||||
<QuestionCircleOutlined />
|
<QuestionCircleOutlined />
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
</span>
|
</span>
|
||||||
<span v-if="clickprompt.show" class="title_icon">
|
<span v-if="clickprompt.show" class="title_icon">
|
||||||
<a-tooltip placement="top" trigger="click" :title="clickprompt.value"
|
<a-tooltip
|
||||||
:get-popup-container="getPopupContainer">
|
placement="top"
|
||||||
|
trigger="click"
|
||||||
|
:title="clickprompt.value"
|
||||||
|
:get-popup-container="getPopupContainer"
|
||||||
|
>
|
||||||
<InfoCircleOutlined />
|
<InfoCircleOutlined />
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
</span>
|
</span>
|
||||||
<span v-if="iconmap.show" class="title_icon">
|
<span v-if="iconmap.show" class="title_icon">
|
||||||
<a-tooltip placement="top" :title="iconmap.value" :get-popup-container="getPopupContainer">
|
<a-tooltip
|
||||||
|
placement="top"
|
||||||
|
:title="iconmap.value"
|
||||||
|
:get-popup-container="getPopupContainer"
|
||||||
|
>
|
||||||
<span :class="iconmap.icon"></span>
|
<span :class="iconmap.icon"></span>
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
</span>
|
</span>
|
||||||
<!-- 新增的 clickAction 插槽区域 -->
|
<!-- 新增的 clickAction 插槽区域 -->
|
||||||
<span v-if="clickAction.show" class="title_icon">
|
<span v-if="clickAction.show" class="title_icon">
|
||||||
<a-tooltip placement="top" trigger="click" :get-popup-container="getPopupContainer">
|
<a-tooltip
|
||||||
|
placement="top"
|
||||||
|
trigger="click"
|
||||||
|
:get-popup-container="getPopupContainer"
|
||||||
|
>
|
||||||
<template #title>
|
<template #title>
|
||||||
<slot name="click-action-content"></slot>
|
<slot name="click-action-content"></slot>
|
||||||
</template>
|
</template>
|
||||||
@ -32,45 +48,108 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="title_right">
|
<div class="title_right">
|
||||||
<div v-if="select.show">
|
<div v-if="select.show">
|
||||||
<a-select v-model:value="selectValue" show-search placeholder="请选择" :size="'small'"
|
<a-select
|
||||||
style="width: 120px" :options="select.options" :filter-option="filterOption"
|
v-model:value="selectValue"
|
||||||
@focus="handleFocus" @blur="handleBlur" @change="handleChange"></a-select>
|
show-search
|
||||||
|
placeholder="请选择"
|
||||||
|
:size="'small'"
|
||||||
|
style="width: 120px"
|
||||||
|
:options="select.options"
|
||||||
|
:filter-option="filterOption"
|
||||||
|
@focus="handleFocus"
|
||||||
|
@blur="handleBlur"
|
||||||
|
@change="handleChange"
|
||||||
|
></a-select>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="shrink" class="title_shrink" @click="isExpand = !isExpand">
|
<div v-if="shrink" class="title_shrink" @click="isExpand = !isExpand">
|
||||||
<img v-if="isExpand" src="@/assets/components/arrow-up.png" alt="">
|
<img v-if="isExpand" src="@/assets/components/arrow-up.png" alt="" />
|
||||||
<img v-else src="@/assets/components/arrow-down.png" alt="">
|
<img v-else src="@/assets/components/arrow-down.png" alt="" />
|
||||||
</div>
|
</div>
|
||||||
<div v-if="moreSelect.show">
|
<div v-if="moreSelect.show">
|
||||||
<a-tree-select v-model:value="moreSelectValue" v-model:tree-expanded-keys="treeExpandedKeys"
|
<a-tree-select
|
||||||
show-search :size="'small'" style="width: 110px"
|
v-model:value="moreSelectValue"
|
||||||
:dropdown-style="{ maxHeight: '400px', overflow: 'auto', minWidth: '180px' }" placeholder=" "
|
v-model:tree-expanded-keys="treeExpandedKeys"
|
||||||
|
show-search
|
||||||
|
:size="'small'"
|
||||||
|
style="width: 110px"
|
||||||
|
:dropdown-style="{
|
||||||
|
maxHeight: '400px',
|
||||||
|
overflow: 'auto',
|
||||||
|
minWidth: '200px'
|
||||||
|
}"
|
||||||
|
placeholder=" "
|
||||||
:tree-data="processedMoreSelectOptions"
|
:tree-data="processedMoreSelectOptions"
|
||||||
:field-names="{ label: 'title', value: 'value', children: 'children' }"
|
:field-names="{
|
||||||
tree-node-filter-prop="label" popup-class-name="no-wrap-tree-select" @select="handleTreeSelect"
|
label: 'title',
|
||||||
@expand="handleTreeExpand">
|
value: 'value',
|
||||||
|
children: 'children'
|
||||||
|
}"
|
||||||
|
tree-node-filter-prop="label"
|
||||||
|
popup-class-name="no-wrap-tree-select"
|
||||||
|
@select="handleTreeSelect"
|
||||||
|
@expand="handleTreeExpand"
|
||||||
|
>
|
||||||
</a-tree-select>
|
</a-tree-select>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="datetimePicker.show">
|
<div v-if="datetimePicker.show">
|
||||||
<!-- 添加 locale 属性来设置语言 -->
|
<!-- 添加 locale 属性来设置语言 -->
|
||||||
<a-date-picker v-model:value="datetimeValue"
|
<a-date-picker
|
||||||
|
v-model:value="datetimeValue"
|
||||||
:show-time="timePickerConfig"
|
:show-time="timePickerConfig"
|
||||||
:style="{ width: datetimePicker.picker === 'year' ? '80px' : datetimePicker.picker === 'month' ? '90px' : '130px' }"
|
:style="{
|
||||||
:format="datetimePicker.format !== null ? datetimePicker.format : undefined"
|
width:
|
||||||
:picker="datetimePicker.picker" :allowClear="false" placeholder=" "
|
datetimePicker.picker === 'year'
|
||||||
@change="handleDateTimeChange" :size="'small'"
|
? '80px'
|
||||||
|
: datetimePicker.picker === 'month'
|
||||||
|
? '90px'
|
||||||
|
: '130px'
|
||||||
|
}"
|
||||||
|
:format="
|
||||||
|
datetimePicker.format !== null ? datetimePicker.format : undefined
|
||||||
|
"
|
||||||
|
:picker="datetimePicker.picker"
|
||||||
|
:allowClear="false"
|
||||||
|
placeholder=" "
|
||||||
|
@change="handleDateTimeChange"
|
||||||
|
:size="'small'"
|
||||||
:disabledDate="createDisabledDateFn(datetimePicker.picker)"
|
:disabledDate="createDisabledDateFn(datetimePicker.picker)"
|
||||||
:disabledTime="disabledTimeForSinglePicker" />
|
:disabledTime="disabledTimeForSinglePicker"
|
||||||
|
/>
|
||||||
<!-- 修改为 locale 变量 -->
|
<!-- 修改为 locale 变量 -->
|
||||||
</div>
|
</div>
|
||||||
<div v-if="scopeDate.show" class="title_scopeDate">
|
<div v-if="scopeDate.show" class="title_scopeDate">
|
||||||
<a-range-picker v-model:value="scopeDateValue" :picker="scopeDate.picker" :allowClear="false"
|
<a-range-picker
|
||||||
:style="{ width: scopeDate.picker === 'year' ? '80px' : (scopeDate.picker === 'month' ? '180px' : '') }"
|
v-model:value="scopeDateValue"
|
||||||
:format="scopeDate.format" :range-separator="' 至 '" :size="'small'"
|
:picker="scopeDate.picker"
|
||||||
:presets="computedScopeDatePresets" :disabledDate="createDisabledDateFn(scopeDate.picker)" />
|
:allowClear="false"
|
||||||
|
:style="{
|
||||||
|
width:
|
||||||
|
scopeDate.picker === 'year'
|
||||||
|
? '80px'
|
||||||
|
: scopeDate.picker === 'month'
|
||||||
|
? '180px'
|
||||||
|
: ''
|
||||||
|
}"
|
||||||
|
:format="scopeDate.format"
|
||||||
|
:range-separator="' 至 '"
|
||||||
|
:size="'small'"
|
||||||
|
:presets="computedScopeDatePresets"
|
||||||
|
:disabledDate="createDisabledDateFn(scopeDate.picker)"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="tabs.show" class="typeOne">
|
<div v-if="tabs.show" class="typeOne">
|
||||||
<div @click="handleTabClick('one')" :class="tabsValue == 'one' ? 'typezhong' : ''">图片</div>
|
<div
|
||||||
<div @click="handleTabClick('two')" :class="tabsValue == 'two' ? 'typezhong' : ''">视频</div>
|
@click="handleTabClick('one')"
|
||||||
|
:class="tabsValue == 'one' ? 'typezhong' : ''"
|
||||||
|
>
|
||||||
|
图片
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
@click="handleTabClick('two')"
|
||||||
|
:class="tabsValue == 'two' ? 'typezhong' : ''"
|
||||||
|
>
|
||||||
|
视频
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -112,43 +191,50 @@ defineOptions({
|
|||||||
|
|
||||||
// 定义props
|
// 定义props
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
title: { // 标题
|
title: {
|
||||||
|
// 标题
|
||||||
type: String,
|
type: String,
|
||||||
default: ''
|
default: ''
|
||||||
},
|
},
|
||||||
shrink: { // 是否显示收缩
|
shrink: {
|
||||||
|
// 是否显示收缩
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false
|
default: false
|
||||||
},
|
},
|
||||||
prompt: { // 浮动提示
|
prompt: {
|
||||||
|
// 浮动提示
|
||||||
|
type: Object as () => PromptConfig,
|
||||||
|
default: () => ({
|
||||||
|
show: false,
|
||||||
|
value: ''
|
||||||
|
})
|
||||||
|
},
|
||||||
|
clickprompt: {
|
||||||
|
// 点击提示
|
||||||
|
type: Object as () => PromptConfig,
|
||||||
|
default: () => ({
|
||||||
|
show: false,
|
||||||
|
value: ''
|
||||||
|
})
|
||||||
|
},
|
||||||
|
iconmap: {
|
||||||
|
//自定义图标浮动
|
||||||
type: Object as () => PromptConfig,
|
type: Object as () => PromptConfig,
|
||||||
default: () => ({
|
default: () => ({
|
||||||
show: false,
|
show: false,
|
||||||
value: '',
|
value: '',
|
||||||
|
icon: 'iconfont icon-time'
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
clickprompt: { // 点击提示
|
clickAction: {
|
||||||
type: Object as () => PromptConfig,
|
// 新增:用于控制带交互的提示插槽
|
||||||
default: () => ({
|
|
||||||
show: false,
|
|
||||||
value: '',
|
|
||||||
})
|
|
||||||
},
|
|
||||||
iconmap: {//自定义图标浮动
|
|
||||||
type: Object as () => PromptConfig,
|
|
||||||
default: () => ({
|
|
||||||
show: false,
|
|
||||||
value: '',
|
|
||||||
icon: 'iconfont icon-time',
|
|
||||||
})
|
|
||||||
},
|
|
||||||
clickAction: { // 新增:用于控制带交互的提示插槽
|
|
||||||
type: Object as () => { show: boolean },
|
type: Object as () => { show: boolean },
|
||||||
default: () => ({
|
default: () => ({
|
||||||
show: false
|
show: false
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
select: { // 选择框
|
select: {
|
||||||
|
// 选择框
|
||||||
type: Object as () => SelectConfig,
|
type: Object as () => SelectConfig,
|
||||||
default: () => ({
|
default: () => ({
|
||||||
show: false,
|
show: false,
|
||||||
@ -156,7 +242,8 @@ const props = defineProps({
|
|||||||
options: []
|
options: []
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
moreSelect: {//树选择框
|
moreSelect: {
|
||||||
|
//树选择框
|
||||||
type: Object as () => SelectConfig,
|
type: Object as () => SelectConfig,
|
||||||
default: () => ({
|
default: () => ({
|
||||||
show: false,
|
show: false,
|
||||||
@ -164,7 +251,8 @@ const props = defineProps({
|
|||||||
options: []
|
options: []
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
datetimePicker: { // 时间选择框
|
datetimePicker: {
|
||||||
|
// 时间选择框
|
||||||
type: Object as () => SelectConfig & {
|
type: Object as () => SelectConfig & {
|
||||||
timeFormat?: string; // 新增:时间选择器的格式(控制选择粒度)
|
timeFormat?: string; // 新增:时间选择器的格式(控制选择粒度)
|
||||||
},
|
},
|
||||||
@ -176,7 +264,8 @@ const props = defineProps({
|
|||||||
timeFormat: 'HH:mm:ss' // 默认值:显示时分秒选择器
|
timeFormat: 'HH:mm:ss' // 默认值:显示时分秒选择器
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
scopeDate: { // 时间选择框
|
scopeDate: {
|
||||||
|
// 时间选择框
|
||||||
type: Object as () => SelectConfig,
|
type: Object as () => SelectConfig,
|
||||||
default: () => ({
|
default: () => ({
|
||||||
show: false,
|
show: false,
|
||||||
@ -189,28 +278,33 @@ const props = defineProps({
|
|||||||
type: Object,
|
type: Object,
|
||||||
default: () => ({
|
default: () => ({
|
||||||
show: false,
|
show: false,
|
||||||
value: 'one',
|
value: 'one'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
const emit = defineEmits(['tab-change', 'update-values']);
|
const emit = defineEmits(['tab-change', 'update-values']);
|
||||||
const isExpand = ref(true);
|
const isExpand = ref(true);
|
||||||
const selectValue = ref(props.select.value)
|
const selectValue = ref(props.select.value);
|
||||||
const moreSelectValue = ref(props.moreSelect.value)
|
const moreSelectValue = ref(props.moreSelect.value);
|
||||||
const datetimeValue = ref<Dayjs | null>(props.datetimePicker.value ? dayjs(props.datetimePicker.value) : null);
|
const datetimeValue = ref<Dayjs | null>(
|
||||||
|
props.datetimePicker.value ? dayjs(props.datetimePicker.value) : null
|
||||||
|
);
|
||||||
const scopeDateValue = ref<[Dayjs, Dayjs] | undefined>(
|
const scopeDateValue = ref<[Dayjs, Dayjs] | undefined>(
|
||||||
props.scopeDate.value && Array.isArray(props.scopeDate.value)
|
props.scopeDate.value && Array.isArray(props.scopeDate.value)
|
||||||
? [dayjs(props.scopeDate.value[0]), dayjs(props.scopeDate.value[1])]
|
? [dayjs(props.scopeDate.value[0]), dayjs(props.scopeDate.value[1])]
|
||||||
: undefined
|
: undefined
|
||||||
);
|
);
|
||||||
const tabsValue = ref(props.tabs.value)
|
const tabsValue = ref(props.tabs.value);
|
||||||
|
|
||||||
|
// 保存选中树节点的额外属性(如 lgtd、lttd 等)
|
||||||
|
const selectedNodeExtra = ref<{ lgtd?: any; lttd?: any }>({});
|
||||||
|
|
||||||
// 保存最后一次选择的月份值(用于从year切换回month时恢复月份)
|
// 保存最后一次选择的月份值(用于从year切换回month时恢复月份)
|
||||||
const lastMonthValue = ref<string | null>(null);
|
const lastMonthValue = ref<string | null>(null);
|
||||||
|
|
||||||
// 树形选择器展开状态管理
|
// 树形选择器展开状态管理
|
||||||
const treeExpandedKeys = ref<string[]>([])
|
const treeExpandedKeys = ref<string[]>([]);
|
||||||
const nodeMap = new Map<string, { node: any; parentKey: string | null }>()
|
const nodeMap = new Map<string, { node: any; parentKey: string | null }>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 创建针对不同 picker 类型的日期禁用函数
|
* 创建针对不同 picker 类型的日期禁用函数
|
||||||
@ -219,34 +313,34 @@ const nodeMap = new Map<string, { node: any; parentKey: string | null }>()
|
|||||||
*/
|
*/
|
||||||
const createDisabledDateFn = (pickerType: string) => {
|
const createDisabledDateFn = (pickerType: string) => {
|
||||||
return (current: Dayjs) => {
|
return (current: Dayjs) => {
|
||||||
if (!current) return false
|
if (!current) return false;
|
||||||
|
|
||||||
const now = dayjs()
|
const now = dayjs();
|
||||||
|
|
||||||
switch (pickerType) {
|
switch (pickerType) {
|
||||||
case 'year':
|
case 'year':
|
||||||
// 年份选择器:禁用 > 当前年份
|
// 年份选择器:禁用 > 当前年份
|
||||||
return current.year() > now.year()
|
return current.year() > now.year();
|
||||||
|
|
||||||
case 'month':
|
case 'month':
|
||||||
// 月份选择器:禁用 > 当前年月
|
// 月份选择器:禁用 > 当前年月
|
||||||
return current.isAfter(now, 'month')
|
return current.isAfter(now, 'month');
|
||||||
|
|
||||||
case 'quarter':
|
case 'quarter':
|
||||||
// 季度选择器:禁用 > 当前季度
|
// 季度选择器:禁用 > 当前季度
|
||||||
return current.isAfter(now, 'quarter')
|
return current.isAfter(now, 'quarter');
|
||||||
|
|
||||||
case 'week':
|
case 'week':
|
||||||
// 周选择器:禁用 > 当前周
|
// 周选择器:禁用 > 当前周
|
||||||
return current.isAfter(now, 'week')
|
return current.isAfter(now, 'week');
|
||||||
|
|
||||||
case 'date':
|
case 'date':
|
||||||
default:
|
default:
|
||||||
// 日期选择器:禁用 > 当前日期
|
// 日期选择器:禁用 > 当前日期
|
||||||
return current.isAfter(now, 'day')
|
return current.isAfter(now, 'day');
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 单日期选择器的时间禁用函数(针对 show-time)
|
* 单日期选择器的时间禁用函数(针对 show-time)
|
||||||
@ -256,52 +350,52 @@ const createDisabledDateFn = (pickerType: string) => {
|
|||||||
const disabledTimeForSinglePicker = (current?: Dayjs) => {
|
const disabledTimeForSinglePicker = (current?: Dayjs) => {
|
||||||
// 只在 datetimePicker 显示且为 date 类型时生效
|
// 只在 datetimePicker 显示且为 date 类型时生效
|
||||||
if (!props.datetimePicker.show || props.datetimePicker.picker !== 'date') {
|
if (!props.datetimePicker.show || props.datetimePicker.picker !== 'date') {
|
||||||
return undefined
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
const now = dayjs()
|
const now = dayjs();
|
||||||
|
|
||||||
// 关键修复:使用传入的 current 参数,而不是 datetimeValue.value
|
// 关键修复:使用传入的 current 参数,而不是 datetimeValue.value
|
||||||
// current 是用户当前在面板中操作的日期,会实时响应面板内的日期切换
|
// current 是用户当前在面板中操作的日期,会实时响应面板内的日期切换
|
||||||
const selectedDate = current || datetimeValue.value
|
const selectedDate = current || datetimeValue.value;
|
||||||
|
|
||||||
// 如果未选择日期,或选择的不是今天,则不限制时间
|
// 如果未选择日期,或选择的不是今天,则不限制时间
|
||||||
if (!selectedDate || !selectedDate.isSame(now, 'day')) {
|
if (!selectedDate || !selectedDate.isSame(now, 'day')) {
|
||||||
return {}
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
// 辅助函数:生成从 start 到 end-1 的数组
|
// 辅助函数:生成从 start 到 end-1 的数组
|
||||||
const range = (start: number, end: number) => {
|
const range = (start: number, end: number) => {
|
||||||
const result: number[] = []
|
const result: number[] = [];
|
||||||
for (let i = start; i < end; i++) {
|
for (let i = start; i < end; i++) {
|
||||||
result.push(i)
|
result.push(i);
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
}
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
// 禁用当前小时之后的所有小时
|
// 禁用当前小时之后的所有小时
|
||||||
disabledHours: () => {
|
disabledHours: () => {
|
||||||
return range(now.hour() + 1, 24)
|
return range(now.hour() + 1, 24);
|
||||||
},
|
},
|
||||||
|
|
||||||
// 禁用当前分钟之后的所有分钟(仅在当前小时)
|
// 禁用当前分钟之后的所有分钟(仅在当前小时)
|
||||||
disabledMinutes: (selectedHour: number) => {
|
disabledMinutes: (selectedHour: number) => {
|
||||||
if (selectedHour === now.hour()) {
|
if (selectedHour === now.hour()) {
|
||||||
return range(now.minute() + 1, 60)
|
return range(now.minute() + 1, 60);
|
||||||
}
|
}
|
||||||
return []
|
return [];
|
||||||
},
|
},
|
||||||
|
|
||||||
// 禁用当前秒数之后的所有秒数(仅在当前小时和分钟)
|
// 禁用当前秒数之后的所有秒数(仅在当前小时和分钟)
|
||||||
disabledSeconds: (selectedHour: number, selectedMinute: number) => {
|
disabledSeconds: (selectedHour: number, selectedMinute: number) => {
|
||||||
if (selectedHour === now.hour() && selectedMinute === now.minute()) {
|
if (selectedHour === now.hour() && selectedMinute === now.minute()) {
|
||||||
return range(now.second() + 1, 60)
|
return range(now.second() + 1, 60);
|
||||||
}
|
}
|
||||||
return []
|
return [];
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
}
|
};
|
||||||
|
|
||||||
// // 定义 locale 变量
|
// // 定义 locale 变量
|
||||||
// const locale = zhCN;
|
// const locale = zhCN;
|
||||||
@ -344,21 +438,21 @@ const processedMoreSelectOptions = computed(() => {
|
|||||||
* @param parentKey 父节点key
|
* @param parentKey 父节点key
|
||||||
*/
|
*/
|
||||||
const buildNodeMap = (treeData: any[], parentKey: string | null = null) => {
|
const buildNodeMap = (treeData: any[], parentKey: string | null = null) => {
|
||||||
if (!treeData || !Array.isArray(treeData)) return
|
if (!treeData || !Array.isArray(treeData)) return;
|
||||||
|
|
||||||
treeData.forEach(node => {
|
treeData.forEach(node => {
|
||||||
// 存储当前节点及其父节点信息
|
// 存储当前节点及其父节点信息
|
||||||
nodeMap.set(node.value, {
|
nodeMap.set(node.value, {
|
||||||
node,
|
node,
|
||||||
parentKey
|
parentKey
|
||||||
})
|
});
|
||||||
|
|
||||||
// 递归处理子节点
|
// 递归处理子节点
|
||||||
if (node.children && node.children.length > 0) {
|
if (node.children && node.children.length > 0) {
|
||||||
buildNodeMap(node.children, node.value)
|
buildNodeMap(node.children, node.value);
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取目标节点的所有父节点keys
|
* 获取目标节点的所有父节点keys
|
||||||
@ -366,25 +460,29 @@ const buildNodeMap = (treeData: any[], parentKey: string | null = null) => {
|
|||||||
* @returns 父节点keys数组(从根到直接父节点)
|
* @returns 父节点keys数组(从根到直接父节点)
|
||||||
*/
|
*/
|
||||||
const getParentKeys = (targetValue: string): string[] => {
|
const getParentKeys = (targetValue: string): string[] => {
|
||||||
const parentKeys: string[] = []
|
const parentKeys: string[] = [];
|
||||||
let currentKey: string | null = targetValue
|
let currentKey: string | null = targetValue;
|
||||||
|
|
||||||
// 向上追溯所有父节点
|
// 向上追溯所有父节点
|
||||||
while (currentKey !== null && currentKey !== undefined) {
|
while (currentKey !== null && currentKey !== undefined) {
|
||||||
const nodeInfo = nodeMap.get(currentKey)
|
const nodeInfo = nodeMap.get(currentKey);
|
||||||
|
|
||||||
// 如果节点不存在或已到达根节点,停止追溯
|
// 如果节点不存在或已到达根节点,停止追溯
|
||||||
if (!nodeInfo || nodeInfo.parentKey === null || nodeInfo.parentKey === undefined) {
|
if (
|
||||||
break
|
!nodeInfo ||
|
||||||
|
nodeInfo.parentKey === null ||
|
||||||
|
nodeInfo.parentKey === undefined
|
||||||
|
) {
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 将父节点插入到数组开头(保持从根到叶的顺序)
|
// 将父节点插入到数组开头(保持从根到叶的顺序)
|
||||||
parentKeys.unshift(nodeInfo.parentKey)
|
parentKeys.unshift(nodeInfo.parentKey);
|
||||||
currentKey = nodeInfo.parentKey
|
currentKey = nodeInfo.parentKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
return parentKeys
|
return parentKeys;
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 计算属性:根据 picker 类型动态生成快捷日期选项
|
* 计算属性:根据 picker 类型动态生成快捷日期选项
|
||||||
@ -412,7 +510,10 @@ const computedScopeDatePresets = computed(() => {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: '昨天',
|
label: '昨天',
|
||||||
value: [now.subtract(1, 'day').startOf('day'), now.subtract(1, 'day').endOf('day')]
|
value: [
|
||||||
|
now.subtract(1, 'day').startOf('day'),
|
||||||
|
now.subtract(1, 'day').endOf('day')
|
||||||
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: '最近七天',
|
label: '最近七天',
|
||||||
@ -438,7 +539,10 @@ const computedScopeDatePresets = computed(() => {
|
|||||||
presets.push(
|
presets.push(
|
||||||
{
|
{
|
||||||
label: '最近一季度',
|
label: '最近一季度',
|
||||||
value: [now.subtract(1, 'quarter').startOf('quarter'), now.endOf('quarter')]
|
value: [
|
||||||
|
now.subtract(1, 'quarter').startOf('quarter'),
|
||||||
|
now.endOf('quarter')
|
||||||
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: '今年',
|
label: '今年',
|
||||||
@ -456,7 +560,10 @@ const computedScopeDatePresets = computed(() => {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: '上周',
|
label: '上周',
|
||||||
value: [now.subtract(1, 'week').startOf('week'), now.subtract(1, 'week').endOf('week')]
|
value: [
|
||||||
|
now.subtract(1, 'week').startOf('week'),
|
||||||
|
now.subtract(1, 'week').endOf('week')
|
||||||
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: '最近四周',
|
label: '最近四周',
|
||||||
@ -475,7 +582,10 @@ const computedScopeDatePresets = computed(() => {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: '昨天',
|
label: '昨天',
|
||||||
value: [now.subtract(1, 'day').startOf('day'), now.subtract(1, 'day').endOf('day')]
|
value: [
|
||||||
|
now.subtract(1, 'day').startOf('day'),
|
||||||
|
now.subtract(1, 'day').endOf('day')
|
||||||
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: '最近七天',
|
label: '最近七天',
|
||||||
@ -542,6 +652,14 @@ const handleTreeSelect = (selectedKeys: string | string[], info: any) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
moreSelectValue.value = selectedValue;
|
moreSelectValue.value = selectedValue;
|
||||||
|
|
||||||
|
// 保存选中节点的额外属性(如 lgtd、lttd 等)
|
||||||
|
if (info && typeof info === 'object') {
|
||||||
|
selectedNodeExtra.value = {
|
||||||
|
lgtd: info.lgtd,
|
||||||
|
lttd: info.lttd
|
||||||
|
};
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -604,12 +722,15 @@ const emitAllValues = () => {
|
|||||||
select: selectValue.value,
|
select: selectValue.value,
|
||||||
moreSelect: moreSelectValue.value,
|
moreSelect: moreSelectValue.value,
|
||||||
tabs: tabsValue.value,
|
tabs: tabsValue.value,
|
||||||
|
selectedNodeExtra: selectedNodeExtra.value
|
||||||
};
|
};
|
||||||
|
|
||||||
// 处理单个时间选择器
|
// 处理单个时间选择器
|
||||||
if (datetimeValue.value) {
|
if (datetimeValue.value) {
|
||||||
// 使用传入的 format,如果没有则根据 picker 类型设置默认格式
|
// 使用传入的 format,如果没有则根据 picker 类型设置默认格式
|
||||||
const format = props.datetimePicker.format || getDefaultFormat(props.datetimePicker.picker);
|
const format =
|
||||||
|
props.datetimePicker.format ||
|
||||||
|
getDefaultFormat(props.datetimePicker.picker);
|
||||||
payload.datetime = datetimeValue.value.format(format);
|
payload.datetime = datetimeValue.value.format(format);
|
||||||
} else {
|
} else {
|
||||||
payload.datetime = null;
|
payload.datetime = null;
|
||||||
@ -618,8 +739,11 @@ const emitAllValues = () => {
|
|||||||
// 处理时间范围选择器
|
// 处理时间范围选择器
|
||||||
if (scopeDateValue.value && Array.isArray(scopeDateValue.value)) {
|
if (scopeDateValue.value && Array.isArray(scopeDateValue.value)) {
|
||||||
// 使用传入的 format,如果没有则根据 picker 类型设置默认格式
|
// 使用传入的 format,如果没有则根据 picker 类型设置默认格式
|
||||||
const format = props.scopeDate.format || getDefaultFormat(props.scopeDate.picker);
|
const format =
|
||||||
payload.scopeDate = scopeDateValue.value.map(d => d ? d.format(format) : null);
|
props.scopeDate.format || getDefaultFormat(props.scopeDate.picker);
|
||||||
|
payload.scopeDate = scopeDateValue.value.map(d =>
|
||||||
|
d ? d.format(format) : null
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
payload.scopeDate = [];
|
payload.scopeDate = [];
|
||||||
}
|
}
|
||||||
@ -645,14 +769,17 @@ const getDefaultFormat = (picker: string): string => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// 监听各个变量的变化
|
// 监听各个变量的变化
|
||||||
watch([selectValue, moreSelectValue, datetimeValue, scopeDateValue, tabsValue], () => {
|
watch(
|
||||||
|
[selectValue, moreSelectValue, datetimeValue, scopeDateValue, tabsValue],
|
||||||
|
() => {
|
||||||
emitAllValues();
|
emitAllValues();
|
||||||
});
|
}
|
||||||
|
);
|
||||||
|
|
||||||
// 监听 props 变化,实现父子组件数据同步
|
// 监听 props 变化,实现父子组件数据同步
|
||||||
watch(
|
watch(
|
||||||
() => props.select.value,
|
() => props.select.value,
|
||||||
(newVal) => {
|
newVal => {
|
||||||
if (newVal !== selectValue.value) {
|
if (newVal !== selectValue.value) {
|
||||||
selectValue.value = newVal;
|
selectValue.value = newVal;
|
||||||
}
|
}
|
||||||
@ -662,7 +789,7 @@ watch(
|
|||||||
// 监听 moreSelectValue 的变化,移除了自动展开逻辑
|
// 监听 moreSelectValue 的变化,移除了自动展开逻辑
|
||||||
watch(
|
watch(
|
||||||
() => props.moreSelect.value,
|
() => props.moreSelect.value,
|
||||||
(newVal) => {
|
newVal => {
|
||||||
if (newVal !== moreSelectValue.value) {
|
if (newVal !== moreSelectValue.value) {
|
||||||
moreSelectValue.value = newVal;
|
moreSelectValue.value = newVal;
|
||||||
}
|
}
|
||||||
@ -671,9 +798,13 @@ watch(
|
|||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => props.datetimePicker.value,
|
() => props.datetimePicker.value,
|
||||||
(newVal) => {
|
newVal => {
|
||||||
const newDayjs = newVal ? dayjs(newVal) : null;
|
const newDayjs = newVal ? dayjs(newVal) : null;
|
||||||
if (!datetimeValue.value || !newDayjs || !datetimeValue.value.isSame(newDayjs)) {
|
if (
|
||||||
|
!datetimeValue.value ||
|
||||||
|
!newDayjs ||
|
||||||
|
!datetimeValue.value.isSame(newDayjs)
|
||||||
|
) {
|
||||||
datetimeValue.value = newDayjs;
|
datetimeValue.value = newDayjs;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -681,15 +812,17 @@ watch(
|
|||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => props.scopeDate.value,
|
() => props.scopeDate.value,
|
||||||
(newVal) => {
|
newVal => {
|
||||||
if (newVal && Array.isArray(newVal) && newVal.length === 2) {
|
if (newVal && Array.isArray(newVal) && newVal.length === 2) {
|
||||||
const newRange: [Dayjs, Dayjs] = [dayjs(newVal[0]), dayjs(newVal[1])];
|
const newRange: [Dayjs, Dayjs] = [dayjs(newVal[0]), dayjs(newVal[1])];
|
||||||
const currentRange = scopeDateValue.value;
|
const currentRange = scopeDateValue.value;
|
||||||
|
|
||||||
// 检查是否需要更新(避免不必要的触发)
|
// 检查是否需要更新(避免不必要的触发)
|
||||||
if (!currentRange ||
|
if (
|
||||||
|
!currentRange ||
|
||||||
!currentRange[0].isSame(newRange[0]) ||
|
!currentRange[0].isSame(newRange[0]) ||
|
||||||
!currentRange[1].isSame(newRange[1])) {
|
!currentRange[1].isSame(newRange[1])
|
||||||
|
) {
|
||||||
scopeDateValue.value = newRange;
|
scopeDateValue.value = newRange;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -702,7 +835,7 @@ watch(
|
|||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => props.tabs.value,
|
() => props.tabs.value,
|
||||||
(newVal) => {
|
newVal => {
|
||||||
if (newVal !== tabsValue.value) {
|
if (newVal !== tabsValue.value) {
|
||||||
tabsValue.value = newVal;
|
tabsValue.value = newVal;
|
||||||
}
|
}
|
||||||
@ -714,7 +847,12 @@ watch(
|
|||||||
() => props.datetimePicker.picker,
|
() => props.datetimePicker.picker,
|
||||||
(newPicker, oldPicker) => {
|
(newPicker, oldPicker) => {
|
||||||
// 当从 year 切换到 month 时,恢复之前保存的月份
|
// 当从 year 切换到 month 时,恢复之前保存的月份
|
||||||
if (oldPicker === 'year' && newPicker === 'month' && lastMonthValue.value && datetimeValue.value) {
|
if (
|
||||||
|
oldPicker === 'year' &&
|
||||||
|
newPicker === 'month' &&
|
||||||
|
lastMonthValue.value &&
|
||||||
|
datetimeValue.value
|
||||||
|
) {
|
||||||
const currentYear = datetimeValue.value.year(); // 获取当前选择的年份
|
const currentYear = datetimeValue.value.year(); // 获取当前选择的年份
|
||||||
const lastMonth = dayjs(lastMonthValue.value); // 获取上次保存的月份值
|
const lastMonth = dayjs(lastMonthValue.value); // 获取上次保存的月份值
|
||||||
const lastMonthNum = lastMonth.month(); // 获取月份(0-11)
|
const lastMonthNum = lastMonth.month(); // 获取月份(0-11)
|
||||||
@ -735,39 +873,47 @@ watch(
|
|||||||
);
|
);
|
||||||
|
|
||||||
// 监听 moreSelectValue 变化(包括默认值和用户选择)
|
// 监听 moreSelectValue 变化(包括默认值和用户选择)
|
||||||
watch(() => moreSelectValue.value, (newValue) => {
|
watch(
|
||||||
console.log('moreSelectValue 变化:', newValue)
|
() => moreSelectValue.value,
|
||||||
|
newValue => {
|
||||||
|
console.log('moreSelectValue 变化:', newValue);
|
||||||
|
|
||||||
if (newValue && nodeMap.size > 0) {
|
if (newValue && nodeMap.size > 0) {
|
||||||
// 只有当 nodeMap 已构建时才执行
|
// 只有当 nodeMap 已构建时才执行
|
||||||
const parentKeys = getParentKeys(newValue as string)
|
const parentKeys = getParentKeys(newValue as string);
|
||||||
treeExpandedKeys.value = parentKeys
|
treeExpandedKeys.value = parentKeys;
|
||||||
console.log('自动展开父节点:', parentKeys)
|
console.log('自动展开父节点:', parentKeys);
|
||||||
} else {
|
} else {
|
||||||
treeExpandedKeys.value = []
|
treeExpandedKeys.value = [];
|
||||||
}
|
}
|
||||||
}, { immediate: true }) // immediate: true 确保默认值也能触发
|
},
|
||||||
|
{ immediate: true }
|
||||||
|
); // immediate: true 确保默认值也能触发
|
||||||
|
|
||||||
// 监听树数据变化,重新构建映射
|
// 监听树数据变化,重新构建映射
|
||||||
watch(() => processedMoreSelectOptions.value, (newData) => {
|
watch(
|
||||||
console.log('树数据变化,重新构建映射')
|
() => processedMoreSelectOptions.value,
|
||||||
|
newData => {
|
||||||
|
console.log('树数据变化,重新构建映射');
|
||||||
|
|
||||||
if (newData && newData.length > 0) {
|
if (newData && newData.length > 0) {
|
||||||
// 清空旧映射
|
// 清空旧映射
|
||||||
nodeMap.clear()
|
nodeMap.clear();
|
||||||
// 重新构建映射
|
// 重新构建映射
|
||||||
buildNodeMap(newData)
|
buildNodeMap(newData);
|
||||||
|
|
||||||
// 数据加载完成后,如果已有默认值,重新触发一次
|
// 数据加载完成后,如果已有默认值,重新触发一次
|
||||||
if (moreSelectValue.value) {
|
if (moreSelectValue.value) {
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
const parentKeys = getParentKeys(moreSelectValue.value as string)
|
const parentKeys = getParentKeys(moreSelectValue.value as string);
|
||||||
treeExpandedKeys.value = parentKeys
|
treeExpandedKeys.value = parentKeys;
|
||||||
console.log('数据加载后自动展开父节点:', parentKeys)
|
console.log('数据加载后自动展开父节点:', parentKeys);
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, { deep: true })
|
},
|
||||||
|
{ deep: true }
|
||||||
|
);
|
||||||
|
|
||||||
// 页面加载时执行的逻辑
|
// 页面加载时执行的逻辑
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
@ -826,7 +972,6 @@ onUnmounted(() => {
|
|||||||
margin-left: 5px;
|
margin-left: 5px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.title_right {
|
.title_right {
|
||||||
@ -834,7 +979,6 @@ onUnmounted(() => {
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
||||||
div {
|
div {
|
||||||
|
|
||||||
margin-right: 2px;
|
margin-right: 2px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -854,7 +998,7 @@ onUnmounted(() => {
|
|||||||
text-align: center;
|
text-align: center;
|
||||||
background: #fff;
|
background: #fff;
|
||||||
margin: 0px;
|
margin: 0px;
|
||||||
color: rgba(0, 0, 0, .85);
|
color: rgba(0, 0, 0, 0.85);
|
||||||
}
|
}
|
||||||
|
|
||||||
.typezhong {
|
.typezhong {
|
||||||
@ -866,7 +1010,7 @@ onUnmounted(() => {
|
|||||||
|
|
||||||
.qgc_title:before {
|
.qgc_title:before {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
content: "";
|
content: '';
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
left: 0;
|
left: 0;
|
||||||
width: 2px;
|
width: 2px;
|
||||||
@ -887,7 +1031,6 @@ onUnmounted(() => {
|
|||||||
height: calc(100% - 36px);
|
height: calc(100% - 36px);
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
|
||||||
|
|
||||||
p {
|
p {
|
||||||
text-indent: 2em;
|
text-indent: 2em;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -33,6 +33,7 @@ export class MapClass implements MapClassInterface {
|
|||||||
this.service = new MapOl();
|
this.service = new MapOl();
|
||||||
}
|
}
|
||||||
static getInstance(): MapClass {
|
static getInstance(): MapClass {
|
||||||
|
console.log('getInstance');
|
||||||
if (!this.instance) {
|
if (!this.instance) {
|
||||||
this.instance = new MapClass();
|
this.instance = new MapClass();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -72,6 +72,7 @@ export class MapOl implements MapInterface {
|
|||||||
private hoveredFeatureId: string | number | null = null;
|
private hoveredFeatureId: string | number | null = null;
|
||||||
private popupManager: PopupManager;
|
private popupManager: PopupManager;
|
||||||
private regionMaskManager: RegionMaskManager;
|
private regionMaskManager: RegionMaskManager;
|
||||||
|
private isBatchPopupMode = false;
|
||||||
constructor() {
|
constructor() {
|
||||||
this.pointLayerManager = new PointLayerManager({
|
this.pointLayerManager = new PointLayerManager({
|
||||||
map: null,
|
map: null,
|
||||||
@ -142,7 +143,11 @@ export class MapOl implements MapInterface {
|
|||||||
this.popupManager.setMap(this.map);
|
this.popupManager.setMap(this.map);
|
||||||
this.regionMaskManager.setContext(this.map, this.view);
|
this.regionMaskManager.setContext(this.map, this.view);
|
||||||
|
|
||||||
this.view.on('change:resolution', () => {});
|
this.view.on('change:resolution', () => {
|
||||||
|
this.handleZoomChange();
|
||||||
|
const zoom = this.view.getZoom();
|
||||||
|
console.log('mouse zoom:', zoom);
|
||||||
|
});
|
||||||
this.map.on('click', evt => {
|
this.map.on('click', evt => {
|
||||||
this.popupManager.showPopup(undefined, undefined);
|
this.popupManager.showPopup(undefined, undefined);
|
||||||
this.popupManager.handleMapClick(evt.pixel, detectedFeature => {
|
this.popupManager.handleMapClick(evt.pixel, detectedFeature => {
|
||||||
@ -169,9 +174,24 @@ export class MapOl implements MapInterface {
|
|||||||
targetElement.style.cursor = payload.hoveredId ? 'pointer' : '';
|
targetElement.style.cursor = payload.hoveredId ? 'pointer' : '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 批量 popup 模式下(>= 14 级)不触发 hover popup
|
||||||
|
if (!this.isBatchPopupMode) {
|
||||||
this.showPopup(payload.detectedFeature, payload.coordinate);
|
this.showPopup(payload.detectedFeature, payload.coordinate);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.map.on('pointerdrag', () => {
|
||||||
|
if (this.isBatchPopupMode) {
|
||||||
|
this.updateBatchPopups();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.map.on('moveend', () => {
|
||||||
|
if (this.isBatchPopupMode) {
|
||||||
|
this.updateBatchPopups();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
return Promise.resolve(this.map);
|
return Promise.resolve(this.map);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('OL Init Error', e);
|
console.error('OL Init Error', e);
|
||||||
@ -220,6 +240,52 @@ export class MapOl implements MapInterface {
|
|||||||
showPopup(feature: Feature | undefined, coordinate: number[] | undefined) {
|
showPopup(feature: Feature | undefined, coordinate: number[] | undefined) {
|
||||||
this.popupManager.showPopup(feature, coordinate);
|
this.popupManager.showPopup(feature, coordinate);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 监听缩放层级变化,>= 15 时自动显示可视区域内所有锚点的 popup
|
||||||
|
*/
|
||||||
|
private handleZoomChange() {
|
||||||
|
if (!this.view) return;
|
||||||
|
|
||||||
|
const zoom = this.view.getZoom();
|
||||||
|
if (zoom === undefined) return;
|
||||||
|
|
||||||
|
if (zoom >= 15) {
|
||||||
|
this.enableBatchPopupMode();
|
||||||
|
} else {
|
||||||
|
this.disableBatchPopupMode();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 启用批量 popup 模式:自动显示可视区域内所有锚点的 popup
|
||||||
|
*/
|
||||||
|
private enableBatchPopupMode() {
|
||||||
|
this.isBatchPopupMode = true;
|
||||||
|
this.popupManager.clearBatchPopups();
|
||||||
|
this.updateBatchPopups();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 禁用批量 popup 模式:恢复鼠标悬停显示 popup
|
||||||
|
*/
|
||||||
|
private disableBatchPopupMode() {
|
||||||
|
this.isBatchPopupMode = false;
|
||||||
|
this.popupManager.clearBatchPopups();
|
||||||
|
this.popupManager.showPopup(undefined, undefined);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新批量显示的 popups(拖动地图后调用)
|
||||||
|
*/
|
||||||
|
private updateBatchPopups() {
|
||||||
|
const features = this.pointLayerManager.getFeaturesInViewport();
|
||||||
|
// 传入样式检查器,过滤掉 declutter/距离过滤等隐藏的锚点
|
||||||
|
this.popupManager.showPopupsForFeatures(features, feature => {
|
||||||
|
const style = this.createPointStyle(feature);
|
||||||
|
return style !== null;
|
||||||
|
});
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* 创建点样式 (模拟 Leaflet 的 DivIcon 效果,支持随缩放动态调整大小)
|
* 创建点样式 (模拟 Leaflet 的 DivIcon 效果,支持随缩放动态调整大小)
|
||||||
*/
|
*/
|
||||||
@ -660,6 +726,10 @@ export class MapOl implements MapInterface {
|
|||||||
const layerInstance = this.layerRegistry.get(registryKey as string);
|
const layerInstance = this.layerRegistry.get(registryKey as string);
|
||||||
if (layerInstance) {
|
if (layerInstance) {
|
||||||
layerInstance.setVisible(checked);
|
layerInstance.setVisible(checked);
|
||||||
|
// 图层显隐变化时刷新 batch popup
|
||||||
|
if (this.isBatchPopupMode) {
|
||||||
|
this.updateBatchPopups();
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
console.warn(
|
console.warn(
|
||||||
`未找到标识为 [${registryKey}] 的图层实例,当前注册表 keys:`,
|
`未找到标识为 [${registryKey}] 的图层实例,当前注册表 keys:`,
|
||||||
@ -669,6 +739,10 @@ export class MapOl implements MapInterface {
|
|||||||
}
|
}
|
||||||
mdLayerTreeShowOrHidden(layerType: string, checked?: boolean): void {
|
mdLayerTreeShowOrHidden(layerType: string, checked?: boolean): void {
|
||||||
this.pointLayerManager.setLayerVisible(layerType, checked);
|
this.pointLayerManager.setLayerVisible(layerType, checked);
|
||||||
|
// 图层显隐变化时刷新 batch popup
|
||||||
|
if (this.isBatchPopupMode) {
|
||||||
|
this.updateBatchPopups();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
hasLayer(layerType: string): void {
|
hasLayer(layerType: string): void {
|
||||||
@ -1212,7 +1286,6 @@ export class MapOl implements MapInterface {
|
|||||||
// 使用 Object.assign 批量设置样式,代码更整洁
|
// 使用 Object.assign 批量设置样式,代码更整洁
|
||||||
Object.assign(container.style, {
|
Object.assign(container.style, {
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
backgroundColor: 'rgba(255, 255, 255, 0.9)',
|
|
||||||
border: '1px solid #000',
|
border: '1px solid #000',
|
||||||
padding: '4px 8px',
|
padding: '4px 8px',
|
||||||
borderRadius: '4px',
|
borderRadius: '4px',
|
||||||
@ -1223,7 +1296,6 @@ export class MapOl implements MapInterface {
|
|||||||
display: 'flex',
|
display: 'flex',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
zIndex: '1001',
|
zIndex: '1001',
|
||||||
boxShadow: '0 2px 4px rgba(0,0,0,0.2)',
|
|
||||||
pointerEvents: 'auto' // 确保可以点击
|
pointerEvents: 'auto' // 确保可以点击
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -1309,6 +1381,10 @@ export class MapOl implements MapInterface {
|
|||||||
key || '',
|
key || '',
|
||||||
!!checked
|
!!checked
|
||||||
);
|
);
|
||||||
|
// 图例变化时刷新 batch popup
|
||||||
|
if (this.isBatchPopupMode) {
|
||||||
|
this.updateBatchPopups();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1327,6 +1403,10 @@ export class MapOl implements MapInterface {
|
|||||||
anchoPointState,
|
anchoPointState,
|
||||||
checked
|
checked
|
||||||
);
|
);
|
||||||
|
// 图例变化时刷新 batch popup
|
||||||
|
if (this.isBatchPopupMode) {
|
||||||
|
this.updateBatchPopups();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
switchView(_type): void {}
|
switchView(_type): void {}
|
||||||
|
|||||||
@ -32,6 +32,34 @@ export class PointLayerManager {
|
|||||||
this.map = map;
|
this.map = map;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 备注:获取当前屏幕可视区域内的所有点要素(仅包含图层可见且图例可见的要素)。
|
||||||
|
getFeaturesInViewport(): Feature[] {
|
||||||
|
if (!this.map) return [];
|
||||||
|
|
||||||
|
const extent = this.map.getView().calculateExtent(this.map.getSize());
|
||||||
|
if (!extent) return [];
|
||||||
|
|
||||||
|
const [minX, minY, maxX, maxY] = extent;
|
||||||
|
const result: Feature[] = [];
|
||||||
|
|
||||||
|
this.forEachFeature((feature, layer) => {
|
||||||
|
// 跳过图层本身不可见的要素
|
||||||
|
if (!layer.getVisible()) return;
|
||||||
|
|
||||||
|
const geom = feature.getGeometry();
|
||||||
|
if (!geom || geom.getType() !== 'Point') return;
|
||||||
|
|
||||||
|
const coords = (geom as Point).getCoordinates();
|
||||||
|
const [x, y] = coords;
|
||||||
|
|
||||||
|
if (x >= minX && x <= maxX && y >= minY && y <= maxY) {
|
||||||
|
result.push(feature);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
// 备注:统一返回当前点图层注册表,供外层做遍历或清理。
|
// 备注:统一返回当前点图层注册表,供外层做遍历或清理。
|
||||||
getRegistry() {
|
getRegistry() {
|
||||||
return this.layerRegistry;
|
return this.layerRegistry;
|
||||||
@ -128,6 +156,7 @@ export class PointLayerManager {
|
|||||||
const vectorLayer = this.layerRegistry.get(layerKey);
|
const vectorLayer = this.layerRegistry.get(layerKey);
|
||||||
if (vectorLayer && vectorLayer.getVisible() !== visible) {
|
if (vectorLayer && vectorLayer.getVisible() !== visible) {
|
||||||
vectorLayer.setVisible(visible);
|
vectorLayer.setVisible(visible);
|
||||||
|
vectorLayer.changed();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -204,6 +204,213 @@ export class PopupManager {
|
|||||||
this.popupElement.style.display = 'none';
|
this.popupElement.style.display = 'none';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 备注:批量显示多个要素的 Popup,使用绝对定位的 div 而非 Overlay。
|
||||||
|
showPopupsForFeatures(
|
||||||
|
features: Feature[],
|
||||||
|
styleChecker?: (feature: Feature) => boolean
|
||||||
|
) {
|
||||||
|
if (!this.map || !this.popupElement) return;
|
||||||
|
|
||||||
|
// 清除之前批量显示的 popups
|
||||||
|
this.clearBatchPopups();
|
||||||
|
|
||||||
|
// 过滤:只显示图例可见的要素
|
||||||
|
const visibleFeatures = features.filter(
|
||||||
|
f => f.get('_legendVisible') !== false
|
||||||
|
);
|
||||||
|
|
||||||
|
if (visibleFeatures.length === 0) return;
|
||||||
|
|
||||||
|
const mapElement = this.map.getTargetElement();
|
||||||
|
const batchContainer = document.createElement('div');
|
||||||
|
batchContainer.className = 'batch-popup-container';
|
||||||
|
batchContainer.style.cssText = `
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
pointer-events: none;
|
||||||
|
z-index: 1000;
|
||||||
|
`;
|
||||||
|
batchContainer.id = 'batch-popup-container';
|
||||||
|
|
||||||
|
// 先添加容器到 DOM,以便后续元素能正确测量尺寸
|
||||||
|
mapElement.appendChild(batchContainer);
|
||||||
|
|
||||||
|
// 用于碰撞检测的已放置 popup 列表
|
||||||
|
const placedPopups: Array<{
|
||||||
|
left: number;
|
||||||
|
right: number;
|
||||||
|
top: number;
|
||||||
|
bottom: number;
|
||||||
|
}> = [];
|
||||||
|
let styleHiddenCount = 0;
|
||||||
|
let blockedCount = 0;
|
||||||
|
let renderedCount = 0;
|
||||||
|
const renderedIds: Array<string | number | null> = [];
|
||||||
|
const blockedIds: Array<string | number | null> = [];
|
||||||
|
|
||||||
|
// 用于 declutter 模拟检测的已放置要素边界框列表
|
||||||
|
const placedAnchors: Array<{
|
||||||
|
bbLeft: number;
|
||||||
|
bbRight: number;
|
||||||
|
bbTop: number;
|
||||||
|
bbBottom: number;
|
||||||
|
}> = [];
|
||||||
|
|
||||||
|
visibleFeatures.forEach(feature => {
|
||||||
|
const geom = feature.getGeometry();
|
||||||
|
if (!geom || geom.getType() !== 'Point') return;
|
||||||
|
|
||||||
|
const coords = (geom as Point).getCoordinates();
|
||||||
|
const pixel = this.map?.getPixelFromCoordinate(coords);
|
||||||
|
if (!pixel) return;
|
||||||
|
|
||||||
|
// 检查样式是否隐藏(declutter、距离过滤等)
|
||||||
|
if (styleChecker && !styleChecker(feature)) {
|
||||||
|
styleHiddenCount += 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 模拟 OpenLayers declutter:基于图标+文字的边界框碰撞检测
|
||||||
|
const zoom = this.map.getView().getZoom() ?? 14;
|
||||||
|
let dynamicScale = 0.7 + (zoom - 4.5) * 0.08;
|
||||||
|
dynamicScale = Math.max(0.5, Math.min(3.0, dynamicScale));
|
||||||
|
|
||||||
|
// 计算要素的碰撞边界框
|
||||||
|
const labelText = (feature.get('_labelText') as string) || '';
|
||||||
|
const labelLineCount = labelText.split('\n').length;
|
||||||
|
const labelWidth = Math.min(
|
||||||
|
labelText
|
||||||
|
.split('\n')
|
||||||
|
.reduce((max, line) => Math.max(max, line.length), 0) *
|
||||||
|
7 *
|
||||||
|
dynamicScale,
|
||||||
|
200
|
||||||
|
);
|
||||||
|
const labelHeight = 12 * dynamicScale * labelLineCount;
|
||||||
|
const labelOffsetY =
|
||||||
|
labelLineCount > 1 ? -30 * dynamicScale : -22 * dynamicScale;
|
||||||
|
const iconSize = 32 * dynamicScale; // 假设图标原始大小 32x32
|
||||||
|
|
||||||
|
// 碰撞框 = 图标框 ∪ 文字框
|
||||||
|
const bbLeft = pixel[0] - Math.max(iconSize / 2, labelWidth / 2);
|
||||||
|
const bbRight = pixel[0] + Math.max(iconSize / 2, labelWidth / 2);
|
||||||
|
const bbTop = pixel[1] + labelOffsetY - labelHeight;
|
||||||
|
const bbBottom = pixel[1] + iconSize / 2;
|
||||||
|
|
||||||
|
// 检查是否和已放置的要素碰撞
|
||||||
|
let blocked = false;
|
||||||
|
for (const placed of placedAnchors) {
|
||||||
|
if (
|
||||||
|
bbRight > placed.bbLeft &&
|
||||||
|
bbLeft < placed.bbRight &&
|
||||||
|
bbBottom > placed.bbTop &&
|
||||||
|
bbTop < placed.bbBottom
|
||||||
|
) {
|
||||||
|
blocked = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (blocked) {
|
||||||
|
blockedCount += 1;
|
||||||
|
blockedIds.push((feature.getId?.() as string | number | null) ?? null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = feature.getProperties();
|
||||||
|
const popupHtml = props.popupHtml || generatePopupHtml(props);
|
||||||
|
if (!popupHtml) return;
|
||||||
|
|
||||||
|
// 记录边界框用于 declutter 检测
|
||||||
|
placedAnchors.push({ bbLeft, bbRight, bbTop, bbBottom });
|
||||||
|
|
||||||
|
// 创建与原始 popupElement 完全相同的样式
|
||||||
|
const popupEl = document.createElement('div');
|
||||||
|
popupEl.className = this.popupElement.className;
|
||||||
|
popupEl.innerHTML = popupHtml;
|
||||||
|
// 不设置自定义 cssText,只设置定位(对应原始 popup 的 positioning: 'bottom-center', offset: [0, -10])
|
||||||
|
popupEl.style.position = 'absolute';
|
||||||
|
popupEl.style.display = 'block';
|
||||||
|
popupEl.style.transform = 'translate(-50%, -100%)'; // bottom-center 对齐
|
||||||
|
popupEl.style.pointerEvents = 'none';
|
||||||
|
|
||||||
|
// 临时设置 visibility: hidden 来测量尺寸
|
||||||
|
popupEl.style.visibility = 'hidden';
|
||||||
|
popupEl.style.left = `${pixel[0]}px`;
|
||||||
|
popupEl.style.top = `${pixel[1] - 10}px`;
|
||||||
|
batchContainer.appendChild(popupEl);
|
||||||
|
|
||||||
|
// 获取 popup 尺寸
|
||||||
|
const rect = popupEl.getBoundingClientRect();
|
||||||
|
const popupWidth = rect.width;
|
||||||
|
const popupHeight = rect.height;
|
||||||
|
|
||||||
|
// 计算 popup 的位置(对应原始 positioning: 'bottom-center', offset: [0, -10])
|
||||||
|
const popupX = pixel[0];
|
||||||
|
const popupY = pixel[1] - 10; // bottom-center 对齐 + 10px 偏移
|
||||||
|
const popupRect = {
|
||||||
|
left: popupX - popupWidth / 2,
|
||||||
|
right: popupX + popupWidth / 2,
|
||||||
|
top: popupY - popupHeight,
|
||||||
|
bottom: popupY
|
||||||
|
};
|
||||||
|
|
||||||
|
// 碰撞检测
|
||||||
|
let hasCollision = false;
|
||||||
|
for (const placed of placedPopups) {
|
||||||
|
if (this.checkCollision(popupRect, placed)) {
|
||||||
|
hasCollision = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hasCollision) {
|
||||||
|
// 无碰撞,显示 popup
|
||||||
|
popupEl.style.visibility = 'visible';
|
||||||
|
popupEl.style.left = `${popupX}px`;
|
||||||
|
popupEl.style.top = `${popupY}px`;
|
||||||
|
renderedCount += 1;
|
||||||
|
renderedIds.push((feature.getId?.() as string | number | null) ?? null);
|
||||||
|
|
||||||
|
placedPopups.push({
|
||||||
|
left: popupRect.left,
|
||||||
|
right: popupRect.right,
|
||||||
|
top: popupRect.top,
|
||||||
|
bottom: popupRect.bottom
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// 有碰撞,移除该 popup
|
||||||
|
blockedCount += 1;
|
||||||
|
blockedIds.push((feature.getId?.() as string | number | null) ?? null);
|
||||||
|
batchContainer.removeChild(popupEl);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 备注:清除批量显示的 popups。
|
||||||
|
clearBatchPopups() {
|
||||||
|
const existing = document.getElementById('batch-popup-container');
|
||||||
|
if (existing) {
|
||||||
|
existing.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 备注:检测两个 popup 矩形是否碰撞。
|
||||||
|
private checkCollision(
|
||||||
|
rect1: { left: number; right: number; top: number; bottom: number },
|
||||||
|
rect2: { left: number; right: number; top: number; bottom: number }
|
||||||
|
): boolean {
|
||||||
|
const padding = 4;
|
||||||
|
return !(
|
||||||
|
rect1.right + padding < rect2.left ||
|
||||||
|
rect1.left - padding > rect2.right ||
|
||||||
|
rect1.bottom + padding < rect2.top ||
|
||||||
|
rect1.top - padding > rect2.bottom
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// 备注:统一重置 hover 和 Popup 状态,供地图销毁和切换时复用。
|
// 备注:统一重置 hover 和 Popup 状态,供地图销毁和切换时复用。
|
||||||
reset() {
|
reset() {
|
||||||
if (this.animationFrameId) {
|
if (this.animationFrameId) {
|
||||||
@ -264,6 +471,7 @@ export class PopupManager {
|
|||||||
|
|
||||||
// 备注:销毁 Popup 管理器内部状态和 Overlay 引用。
|
// 备注:销毁 Popup 管理器内部状态和 Overlay 引用。
|
||||||
destroy() {
|
destroy() {
|
||||||
|
this.clearBatchPopups();
|
||||||
this.reset();
|
this.reset();
|
||||||
if (this.popupElement && this.popupMouseEnterHandler) {
|
if (this.popupElement && this.popupMouseEnterHandler) {
|
||||||
this.popupElement.removeEventListener(
|
this.popupElement.removeEventListener(
|
||||||
|
|||||||
@ -1,11 +1,19 @@
|
|||||||
<!-- 垂向水温变化图 -->
|
<!-- 垂向水温变化图 -->
|
||||||
<template>
|
<template>
|
||||||
<SidePanelItem title="垂向水温变化" :moreSelect="select" :datetimePicker="datetimePicker"
|
<SidePanelItem
|
||||||
@update-values="handlePanelChange1">
|
title="垂向水温变化"
|
||||||
|
:moreSelect="select"
|
||||||
|
:datetimePicker="datetimePicker"
|
||||||
|
@update-values="handlePanelChange1"
|
||||||
|
>
|
||||||
<div class="chart-wrapper">
|
<div class="chart-wrapper">
|
||||||
<!-- 图表容器 -->
|
<!-- 图表容器 -->
|
||||||
<a-spin :spinning="loading" tip="加载中...">
|
<a-spin :spinning="loading" tip="加载中...">
|
||||||
<div v-show="showemit && !loading" ref="chartRef" class="chart-container"></div>
|
<div
|
||||||
|
v-show="showemit && !loading"
|
||||||
|
ref="chartRef"
|
||||||
|
class="chart-container"
|
||||||
|
></div>
|
||||||
<div v-show="!showemit && !loading" class="chart-container">
|
<div v-show="!showemit && !loading" class="chart-container">
|
||||||
<a-empty />
|
<a-empty />
|
||||||
</div>
|
</div>
|
||||||
@ -15,18 +23,29 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { ref, computed, onMounted, onBeforeUnmount, watch, nextTick } from 'vue';
|
import {
|
||||||
|
ref,
|
||||||
|
computed,
|
||||||
|
onMounted,
|
||||||
|
onBeforeUnmount,
|
||||||
|
watch,
|
||||||
|
nextTick
|
||||||
|
} from 'vue';
|
||||||
import * as echarts from 'echarts';
|
import * as echarts from 'echarts';
|
||||||
import type { ECharts } from 'echarts';
|
import type { ECharts } from 'echarts';
|
||||||
import SidePanelItem from '@/components/SidePanelItem/index.vue';
|
import SidePanelItem from '@/components/SidePanelItem/index.vue';
|
||||||
import { useJidiSelectEventStore } from "@/store/modules/jidiSelectEvent";
|
import { useJidiSelectEventStore } from '@/store/modules/jidiSelectEvent';
|
||||||
import { getChuiXiangShuiWenTreeStcd, getCxswList } from "@/api/sw";
|
import { getChuiXiangShuiWenTreeStcd, getCxswList } from '@/api/sw';
|
||||||
import { useModelStore } from "@/store/modules/model";
|
import { useModelStore } from '@/store/modules/model';
|
||||||
|
import { useMapModalStore } from '@/store/modules/mapModal';
|
||||||
|
import { MapClass } from '@/components/gis/map.class';
|
||||||
|
|
||||||
const modelStore = useModelStore();
|
const modelStore = useModelStore();
|
||||||
const JidiSelectEventStore = useJidiSelectEventStore();
|
const JidiSelectEventStore = useJidiSelectEventStore();
|
||||||
const baseid = ref('');
|
const baseid = ref('');
|
||||||
|
|
||||||
|
// 选择的时间
|
||||||
|
const year = ref('');
|
||||||
// 定义组件名
|
// 定义组件名
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: 'chuixiangshuiwenChangeMod'
|
name: 'chuixiangshuiwenChangeMod'
|
||||||
@ -179,9 +198,18 @@ function generateColorsAndVisibility(
|
|||||||
selected: Record<string, boolean>;
|
selected: Record<string, boolean>;
|
||||||
} {
|
} {
|
||||||
const baseColors = [
|
const baseColors = [
|
||||||
'#6ca4f7', '#78c300', '#9556a4', '#df91ab',
|
'#6ca4f7',
|
||||||
'#7399c6', '#dbb629', '#56c2e3', '#f56a06',
|
'#78c300',
|
||||||
'#cdba75', '#76523b', '#df75b6', '#00a050'
|
'#9556a4',
|
||||||
|
'#df91ab',
|
||||||
|
'#7399c6',
|
||||||
|
'#dbb629',
|
||||||
|
'#56c2e3',
|
||||||
|
'#f56a06',
|
||||||
|
'#cdba75',
|
||||||
|
'#76523b',
|
||||||
|
'#df75b6',
|
||||||
|
'#00a050'
|
||||||
];
|
];
|
||||||
|
|
||||||
const currentYear = new Date().getFullYear();
|
const currentYear = new Date().getFullYear();
|
||||||
@ -233,7 +261,7 @@ const getCurrentHourTimeTwo = () => {
|
|||||||
const startTime = new Date(endTime);
|
const startTime = new Date(endTime);
|
||||||
startTime.setMonth(startTime.getMonth() - 1);
|
startTime.setMonth(startTime.getMonth() - 1);
|
||||||
|
|
||||||
const formatDateTime = (date) => {
|
const formatDateTime = date => {
|
||||||
const year = date.getFullYear();
|
const year = date.getFullYear();
|
||||||
const month = String(date.getMonth() + 1).padStart(2, '0');
|
const month = String(date.getMonth() + 1).padStart(2, '0');
|
||||||
const day = String(date.getDate()).padStart(2, '0');
|
const day = String(date.getDate()).padStart(2, '0');
|
||||||
@ -253,15 +281,15 @@ const select = ref({
|
|||||||
value: '',
|
value: '',
|
||||||
options: [],
|
options: [],
|
||||||
picker: undefined,
|
picker: undefined,
|
||||||
format: undefined,
|
format: undefined
|
||||||
});
|
});
|
||||||
|
|
||||||
const datetimePicker = ref({
|
const datetimePicker = ref({
|
||||||
show: true,
|
show: true,
|
||||||
value: computed(() => getCurrentHourTime()),
|
value: computed(() => getCurrentHourTime()),
|
||||||
format: "YYYY",
|
format: 'YYYY',
|
||||||
picker: "year" as const,
|
picker: 'year' as const,
|
||||||
options: [],
|
options: []
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -273,58 +301,61 @@ const selectOptions = async () => {
|
|||||||
try {
|
try {
|
||||||
let filters = [
|
let filters = [
|
||||||
{
|
{
|
||||||
"field": "mway",
|
field: 'mway',
|
||||||
"operator": "eq",
|
operator: 'eq',
|
||||||
"dataType": "string",
|
dataType: 'string',
|
||||||
"value": 2
|
value: 2
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"field": "tm",
|
field: 'tm',
|
||||||
"operator": "gte",
|
operator: 'gte',
|
||||||
"dataType": "date",
|
dataType: 'date',
|
||||||
"value": getCurrentHourTimeTwo()[0]
|
value: getCurrentHourTimeTwo()[0]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"field": "tm",
|
field: 'tm',
|
||||||
"operator": "lte",
|
operator: 'lte',
|
||||||
"dataType": "date",
|
dataType: 'date',
|
||||||
"value": getCurrentHourTimeTwo()[1]
|
value: getCurrentHourTimeTwo()[1]
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
if (baseid.value && baseid.value !== 'all') {
|
if (baseid.value && baseid.value !== 'all') {
|
||||||
filters.push({
|
filters.push({
|
||||||
"field": "baseId",
|
field: 'baseId',
|
||||||
"operator": "eq",
|
operator: 'eq',
|
||||||
"dataType": "string",
|
dataType: 'string',
|
||||||
"value": baseid.value
|
value: baseid.value
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
let filter = {
|
let filter = {
|
||||||
"logic": "and",
|
logic: 'and',
|
||||||
"filters": filters
|
filters: filters
|
||||||
};
|
};
|
||||||
|
|
||||||
let sort = baseid.value && baseid.value !== 'all' ? [
|
let sort =
|
||||||
|
baseid.value && baseid.value !== 'all'
|
||||||
|
? [
|
||||||
{
|
{
|
||||||
"field": "rstcdStepSort",
|
field: 'rstcdStepSort',
|
||||||
"dir": "asc"
|
dir: 'asc'
|
||||||
}
|
}
|
||||||
] : [
|
]
|
||||||
|
: [
|
||||||
{
|
{
|
||||||
"field": "baseId",
|
field: 'baseId',
|
||||||
"dir": "asc"
|
dir: 'asc'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"field": "rstcdStepSort",
|
field: 'rstcdStepSort',
|
||||||
"dir": "asc"
|
dir: 'asc'
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
const data = { filter, sort };
|
const data = { filter, sort };
|
||||||
const res = await getChuiXiangShuiWenTreeStcd(data);
|
const res = await getChuiXiangShuiWenTreeStcd(data);
|
||||||
let dataOne = res?.data?.data || res.data
|
let dataOne = res?.data?.data || res.data;
|
||||||
|
|
||||||
if (dataOne.length > 0) {
|
if (dataOne.length > 0) {
|
||||||
const treeData = dataOne.map(item => {
|
const treeData = dataOne.map(item => {
|
||||||
@ -334,27 +365,29 @@ const selectOptions = async () => {
|
|||||||
selectable: false,
|
selectable: false,
|
||||||
children: (item.stcdVo || []).map(child => ({
|
children: (item.stcdVo || []).map(child => ({
|
||||||
title: child.stnm,
|
title: child.stnm,
|
||||||
value: child.stcd
|
value: child.stcd,
|
||||||
|
lgtd: child.lgtd,
|
||||||
|
lttd: child.lttd
|
||||||
}))
|
}))
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
if (baseid.value == 'all') {
|
if (baseid.value == 'all') {
|
||||||
select.value.value = '008640203800001014'
|
select.value.value = '008640203800001014';
|
||||||
select.value.options = treeData;
|
select.value.options = treeData;
|
||||||
} else {
|
} else {
|
||||||
select.value.value = treeData[0].children[0].value
|
select.value.value = treeData[0].children[0].value;
|
||||||
select.value.options = treeData;
|
select.value.options = treeData;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
select.value.options = []
|
select.value.options = [];
|
||||||
select.value.value = ''
|
select.value.value = '';
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('获取站点数据失败:', error);
|
console.error('获取站点数据失败:', error);
|
||||||
} finally {
|
} finally {
|
||||||
loading.value = false; // 关闭loading
|
loading.value = false; // 关闭loading
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
// ==================== 新增图表相关状态 ====================
|
// ==================== 新增图表相关状态 ====================
|
||||||
|
|
||||||
@ -362,14 +395,16 @@ const chartRef = ref<HTMLElement | null>(null);
|
|||||||
let chartInstance: ECharts | null = null;
|
let chartInstance: ECharts | null = null;
|
||||||
|
|
||||||
// 图表数据(对象型格式,包含站点元信息)
|
// 图表数据(对象型格式,包含站点元信息)
|
||||||
const chartData = ref<Array<{
|
const chartData = ref<
|
||||||
|
Array<{
|
||||||
name: string;
|
name: string;
|
||||||
dataXy: Array<{
|
dataXy: Array<{
|
||||||
value: [number, number];
|
value: [number, number];
|
||||||
stcd: string;
|
stcd: string;
|
||||||
stnm: string;
|
stnm: string;
|
||||||
}>;
|
}>;
|
||||||
}>>([]);
|
}>
|
||||||
|
>([]);
|
||||||
|
|
||||||
// Y轴类型(1=水深,2=高程)
|
// Y轴类型(1=水深,2=高程)
|
||||||
const type = ref(1);
|
const type = ref(1);
|
||||||
@ -390,26 +425,26 @@ const fetchChartData = async (stcd: string, year: string | number) => {
|
|||||||
try {
|
try {
|
||||||
const params = {
|
const params = {
|
||||||
filter: {
|
filter: {
|
||||||
logic: "and",
|
logic: 'and',
|
||||||
filters: [
|
filters: [
|
||||||
{
|
{
|
||||||
field: "year",
|
field: 'year',
|
||||||
operator: "eq",
|
operator: 'eq',
|
||||||
dataType: "string",
|
dataType: 'string',
|
||||||
value: String(year)
|
value: String(year)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: "stcd",
|
field: 'stcd',
|
||||||
operator: "eq",
|
operator: 'eq',
|
||||||
dataType: "string",
|
dataType: 'string',
|
||||||
value: stcd
|
value: stcd
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
sort: [
|
sort: [
|
||||||
{
|
{
|
||||||
field: "depth",
|
field: 'depth',
|
||||||
dir: "asc"
|
dir: 'asc'
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
@ -429,10 +464,15 @@ const fetchChartData = async (stcd: string, year: string | number) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// ✅ 转换数据,传递 stcd 和 stnm
|
// ✅ 转换数据,传递 stcd 和 stnm
|
||||||
chartData.value = transformAggregatesToChartData(aggregates, stcd, currentStnm);
|
chartData.value = transformAggregatesToChartData(
|
||||||
|
aggregates,
|
||||||
|
stcd,
|
||||||
|
currentStnm
|
||||||
|
);
|
||||||
|
|
||||||
// ✅ 修改:传入固定值 12,而非 chartData.value.length
|
// ✅ 修改:传入固定值 12,而非 chartData.value.length
|
||||||
const { colors: newColors, selected: newSelected } = generateColorsAndVisibility(
|
const { colors: newColors, selected: newSelected } =
|
||||||
|
generateColorsAndVisibility(
|
||||||
12, // ⚠️ 固定传入 12
|
12, // ⚠️ 固定传入 12
|
||||||
year
|
year
|
||||||
);
|
);
|
||||||
@ -440,7 +480,10 @@ const fetchChartData = async (stcd: string, year: string | number) => {
|
|||||||
selected.value = newSelected;
|
selected.value = newSelected;
|
||||||
|
|
||||||
// 根据数据长度决定showemit
|
// 根据数据长度决定showemit
|
||||||
if (chartData.value.length > 0 && chartData.value.some(item => item.dataXy.length > 0)) {
|
if (
|
||||||
|
chartData.value.length > 0 &&
|
||||||
|
chartData.value.some(item => item.dataXy.length > 0)
|
||||||
|
) {
|
||||||
showemit.value = true; // 有数据则显示图表
|
showemit.value = true; // 有数据则显示图表
|
||||||
} else {
|
} else {
|
||||||
showemit.value = false; // 无数据显示空状态
|
showemit.value = false; // 无数据显示空状态
|
||||||
@ -490,12 +533,26 @@ const initChart = () => {
|
|||||||
console.log('站点名称:', stnm);
|
console.log('站点名称:', stnm);
|
||||||
console.log('水温:', temperature);
|
console.log('水温:', temperature);
|
||||||
console.log('深度:', depth);
|
console.log('深度:', depth);
|
||||||
|
|
||||||
|
const matchMonth = params.seriesName.match(/\d+/);
|
||||||
|
const monthNum = matchMonth ? parseInt(matchMonth[0]) : 1;
|
||||||
|
const startTime = `${year.value}-${String(monthNum).padStart(
|
||||||
|
2,
|
||||||
|
'0'
|
||||||
|
)}-01 00:00:00`;
|
||||||
|
const lastDay = new Date(year.value, monthNum, 0).getDate();
|
||||||
|
const endTime = `${year.value}-${String(monthNum).padStart(
|
||||||
|
2,
|
||||||
|
'0'
|
||||||
|
)}-${lastDay} 23:59:59`;
|
||||||
|
console.log(endTime);
|
||||||
modelStore.modalVisible = true;
|
modelStore.modalVisible = true;
|
||||||
modelStore.params.sttp = "WT";
|
modelStore.params.sttp = 'WT';
|
||||||
modelStore.params.enfc = '1';
|
modelStore.params.enfc = '1';
|
||||||
modelStore.title = stnm ;
|
modelStore.title = stnm;
|
||||||
// modelStore.isBasicEdit = true;
|
// modelStore.isBasicEdit = true;
|
||||||
modelStore.params.stcd = stcd;
|
modelStore.params.stcd = stcd;
|
||||||
|
modelStore.filter.rangeTm = [startTime, endTime];
|
||||||
|
|
||||||
// TODO: 在此处添加您的业务逻辑
|
// TODO: 在此处添加您的业务逻辑
|
||||||
});
|
});
|
||||||
@ -511,7 +568,7 @@ const updateChart = () => {
|
|||||||
const yData: any[] = [];
|
const yData: any[] = [];
|
||||||
const legend: string[] = [];
|
const legend: string[] = [];
|
||||||
|
|
||||||
chartData.value.forEach((item) => {
|
chartData.value.forEach(item => {
|
||||||
if (item.dataXy.length > 0) {
|
if (item.dataXy.length > 0) {
|
||||||
// ✅ 从对象中提取 value[0](温度值)
|
// ✅ 从对象中提取 value[0](温度值)
|
||||||
xData.push(item.dataXy[0].value[0]);
|
xData.push(item.dataXy[0].value[0]);
|
||||||
@ -612,13 +669,17 @@ const handleChartClick = (params: any) => {
|
|||||||
const matchMonth = params.seriesName.match(/\d+/);
|
const matchMonth = params.seriesName.match(/\d+/);
|
||||||
const monthNum = matchMonth ? parseInt(matchMonth[0]) : 1;
|
const monthNum = matchMonth ? parseInt(matchMonth[0]) : 1;
|
||||||
|
|
||||||
const year = typeof datetimePicker.value.value === 'string'
|
const year =
|
||||||
|
typeof datetimePicker.value.value === 'string'
|
||||||
? parseInt(datetimePicker.value.value)
|
? parseInt(datetimePicker.value.value)
|
||||||
: datetimePicker.value.value;
|
: datetimePicker.value.value;
|
||||||
|
|
||||||
const startTime = `${year}-${String(monthNum).padStart(2, '0')}-01 00:00:00`;
|
const startTime = `${year}-${String(monthNum).padStart(2, '0')}-01 00:00:00`;
|
||||||
const lastDay = new Date(year, monthNum, 0).getDate();
|
const lastDay = new Date(year, monthNum, 0).getDate();
|
||||||
const endTime = `${year}-${String(monthNum).padStart(2, '0')}-${lastDay} 23:59:59`;
|
const endTime = `${year}-${String(monthNum).padStart(
|
||||||
|
2,
|
||||||
|
'0'
|
||||||
|
)}-${lastDay} 23:59:59`;
|
||||||
|
|
||||||
const currentStcd = select.value.value;
|
const currentStcd = select.value.value;
|
||||||
let currentStnm = '';
|
let currentStnm = '';
|
||||||
@ -669,7 +730,7 @@ onBeforeUnmount(() => {
|
|||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => JidiSelectEventStore.selectedItem,
|
() => JidiSelectEventStore.selectedItem,
|
||||||
async (newVal) => {
|
async newVal => {
|
||||||
if (!newVal || !newVal.wbsCode) {
|
if (!newVal || !newVal.wbsCode) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -689,20 +750,28 @@ watch(
|
|||||||
// },
|
// },
|
||||||
// { deep: true }
|
// { deep: true }
|
||||||
// );
|
// );
|
||||||
|
const handlePanelChange1 = (data: any, type: string) => {
|
||||||
const handlePanelChange1 = (data: any) => {
|
|
||||||
console.log('当前所有控件状态:', data);
|
console.log('当前所有控件状态:', data);
|
||||||
if (data.moreSelect && data.datetime) {
|
if (data.moreSelect && data.datetime) {
|
||||||
fetchChartData(data.moreSelect, data.datetime)
|
if (type == 'click') {
|
||||||
|
const mapClass = MapClass.getInstance();
|
||||||
|
mapClass.flyTopanto(
|
||||||
|
[
|
||||||
|
Number(data.selectedNodeExtra.lgtd),
|
||||||
|
Number(data.selectedNodeExtra.lttd)
|
||||||
|
],
|
||||||
|
15
|
||||||
|
);
|
||||||
|
}
|
||||||
|
year.value = data.datetime;
|
||||||
|
fetchChartData(data.moreSelect, data.datetime);
|
||||||
} else {
|
} else {
|
||||||
showemit.value = true
|
showemit.value = true;
|
||||||
chartData.value = [];
|
chartData.value = [];
|
||||||
colors.value = [];
|
colors.value = [];
|
||||||
selected.value = {};
|
selected.value = {};
|
||||||
showemit.value = false;
|
showemit.value = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@ -6,21 +6,39 @@
|
|||||||
特性:支持月份选择、站点筛选、数据缩放、空值断开显示
|
特性:支持月份选择、站点筛选、数据缩放、空值断开显示
|
||||||
-->
|
-->
|
||||||
<template>
|
<template>
|
||||||
<SidePanelItem title="出入库水温" :moreSelect="select" :scopeDate="scopeDate" @update-values="handlePanelChange1">
|
<SidePanelItem
|
||||||
|
title="出入库水温"
|
||||||
|
:moreSelect="select"
|
||||||
|
:scopeDate="scopeDate"
|
||||||
|
@update-values="handlePanelChange1"
|
||||||
|
>
|
||||||
<!-- ECharts图表容器 -->
|
<!-- ECharts图表容器 -->
|
||||||
<a-spin :spinning="loading" tip="加载中...">
|
<a-spin :spinning="loading" tip="加载中...">
|
||||||
<div v-show="showemit && !loading" ref="chartContainer" class="chart-container"></div>
|
<div
|
||||||
|
v-show="showemit && !loading"
|
||||||
|
ref="chartContainer"
|
||||||
|
class="chart-container"
|
||||||
|
></div>
|
||||||
<div v-show="!showemit && !loading" class="chart-container">
|
<div v-show="!showemit && !loading" class="chart-container">
|
||||||
<a-empty />
|
<a-empty />
|
||||||
</div>
|
</div>
|
||||||
</a-spin>
|
</a-spin>
|
||||||
|
|
||||||
<!-- 数据点详情弹框 -->
|
<!-- 数据点详情弹框 -->
|
||||||
<a-modal v-model:open="modalVisible" :title="`${stationName}出入库水温`" :width="1536" :footer="null"
|
<a-modal
|
||||||
@cancel="handleModalClose">
|
v-model:open="modalVisible"
|
||||||
|
:title="`${stationName}出入库水温`"
|
||||||
|
:width="1536"
|
||||||
|
:footer="null"
|
||||||
|
@cancel="handleModalClose"
|
||||||
|
>
|
||||||
<!-- 弹框内容区域 - 待完善 -->
|
<!-- 弹框内容区域 - 待完善 -->
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<churukushuiwen v-if="clickDataInfo" :tm="[clickDataInfo.date, clickDataInfo.date]" :stcd="paramsOne.value" />
|
<churukushuiwen
|
||||||
|
v-if="clickDataInfo"
|
||||||
|
:tm="[clickDataInfo.date, clickDataInfo.date]"
|
||||||
|
:stcd="paramsOne.value"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</a-modal>
|
</a-modal>
|
||||||
</SidePanelItem>
|
</SidePanelItem>
|
||||||
@ -32,8 +50,8 @@ import * as echarts from 'echarts';
|
|||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import SidePanelItem from '@/components/SidePanelItem/index.vue';
|
import SidePanelItem from '@/components/SidePanelItem/index.vue';
|
||||||
import { getVmsstbprpt, inOutOneGetKendoListCust } from '@/api/sw';
|
import { getVmsstbprpt, inOutOneGetKendoListCust } from '@/api/sw';
|
||||||
import { useJidiSelectEventStore } from "@/store/modules/jidiSelectEvent";
|
import { useJidiSelectEventStore } from '@/store/modules/jidiSelectEvent';
|
||||||
import churukushuiwen from './churukushuiwen.vue'
|
import churukushuiwen from './churukushuiwen.vue';
|
||||||
// ==================== 组件基础配置 ====================
|
// ==================== 组件基础配置 ====================
|
||||||
// 定义组件名(便于调试和递归)
|
// 定义组件名(便于调试和递归)
|
||||||
defineOptions({
|
defineOptions({
|
||||||
@ -59,16 +77,16 @@ const select = ref({
|
|||||||
value: '', // 当前选中值(站点编码)
|
value: '', // 当前选中值(站点编码)
|
||||||
options: [], // 下拉选项列表
|
options: [], // 下拉选项列表
|
||||||
picker: undefined, // 选择器类型(未使用)
|
picker: undefined, // 选择器类型(未使用)
|
||||||
format: undefined, // 格式化字符串(未使用)
|
format: undefined // 格式化字符串(未使用)
|
||||||
});
|
});
|
||||||
|
|
||||||
// ==================== 日期范围选择器配置 ====================
|
// ==================== 日期范围选择器配置 ====================
|
||||||
const scopeDate = ref({
|
const scopeDate = ref({
|
||||||
show: true, // 是否显示日期选择器
|
show: true, // 是否显示日期选择器
|
||||||
value: [currentMonth, currentMonth] as any, // 默认选中当前月份范围(使用 as any 绕过类型检查)
|
value: [currentMonth, currentMonth] as any, // 默认选中当前月份范围(使用 as any 绕过类型检查)
|
||||||
format: "YYYY-MM", // 显示格式
|
format: 'YYYY-MM', // 显示格式
|
||||||
picker: "month" as const, // 选择器类型:月份选择
|
picker: 'month' as const, // 选择器类型:月份选择
|
||||||
options: [], // 可选日期范围(未使用)
|
options: [] // 可选日期范围(未使用)
|
||||||
});
|
});
|
||||||
|
|
||||||
// ==================== 业务变量 ====================
|
// ==================== 业务变量 ====================
|
||||||
@ -86,34 +104,34 @@ const stationName = ref(''); // 站点名称
|
|||||||
*/
|
*/
|
||||||
function transformChartData(rawData: any[]) {
|
function transformChartData(rawData: any[]) {
|
||||||
// 1. 提取所有唯一日期并排序
|
// 1. 提取所有唯一日期并排序
|
||||||
const dateSet = new Set<string>()
|
const dateSet = new Set<string>();
|
||||||
rawData.forEach(item => {
|
rawData.forEach(item => {
|
||||||
const date = item.tm?.split(' ')[0] // "2026-04-01 00:00:00" -> "2026-04-01"
|
const date = item.tm?.split(' ')[0]; // "2026-04-01 00:00:00" -> "2026-04-01"
|
||||||
if (date) dateSet.add(date)
|
if (date) dateSet.add(date);
|
||||||
})
|
});
|
||||||
const dates = Array.from(dateSet).sort()
|
const dates = Array.from(dateSet).sort();
|
||||||
|
|
||||||
// 2. 构建两条线的数据(入库水温和出库水温)
|
// 2. 构建两条线的数据(入库水温和出库水温)
|
||||||
const iwtData: (number | null)[] = [] // 入库水温(Inlet Water Temperature)
|
const iwtData: (number | null)[] = []; // 入库水温(Inlet Water Temperature)
|
||||||
const dwtData: (number | null)[] = [] // 出库水温(Discharge Water Temperature)
|
const dwtData: (number | null)[] = []; // 出库水温(Discharge Water Temperature)
|
||||||
|
|
||||||
dates.forEach(date => {
|
dates.forEach(date => {
|
||||||
// 查找该日期的入库水温(dwtp='IWT')
|
// 查找该日期的入库水温(dwtp='IWT')
|
||||||
const iwtItem = rawData.find(item =>
|
const iwtItem = rawData.find(
|
||||||
item.tm?.startsWith(date) && item.dwtp === 'IWT'
|
item => item.tm?.startsWith(date) && item.dwtp === 'IWT'
|
||||||
)
|
);
|
||||||
// 保留一位小数,缺失则填null(曲线断开)
|
// 保留一位小数,缺失则填null(曲线断开)
|
||||||
iwtData.push(iwtItem ? Number(iwtItem.wt.toFixed(1)) : null)
|
iwtData.push(iwtItem ? Number(iwtItem.wt.toFixed(1)) : null);
|
||||||
|
|
||||||
// 查找该日期的出库水温(dwtp='DWT')
|
// 查找该日期的出库水温(dwtp='DWT')
|
||||||
const dwtItem = rawData.find(item =>
|
const dwtItem = rawData.find(
|
||||||
item.tm?.startsWith(date) && item.dwtp === 'DWT'
|
item => item.tm?.startsWith(date) && item.dwtp === 'DWT'
|
||||||
)
|
);
|
||||||
// 保留一位小数,缺失则填null(曲线断开)
|
// 保留一位小数,缺失则填null(曲线断开)
|
||||||
dwtData.push(dwtItem ? Number(dwtItem.wt.toFixed(1)) : null)
|
dwtData.push(dwtItem ? Number(dwtItem.wt.toFixed(1)) : null);
|
||||||
})
|
});
|
||||||
|
|
||||||
return { dates, iwtData, dwtData }
|
return { dates, iwtData, dwtData };
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -124,17 +142,17 @@ function transformChartData(rawData: any[]) {
|
|||||||
*/
|
*/
|
||||||
function calculateYAxisRange(data: (number | null)[]) {
|
function calculateYAxisRange(data: (number | null)[]) {
|
||||||
// 过滤出有效数值
|
// 过滤出有效数值
|
||||||
const validValues = data.filter(v => v !== null) as number[]
|
const validValues = data.filter(v => v !== null) as number[];
|
||||||
if (validValues.length === 0) return { min: 0, max: 10 } // 无数据时返回默认范围
|
if (validValues.length === 0) return { min: 0, max: 10 }; // 无数据时返回默认范围
|
||||||
|
|
||||||
const min = Math.min(...validValues)
|
const min = Math.min(...validValues);
|
||||||
const max = Math.max(...validValues)
|
const max = Math.max(...validValues);
|
||||||
|
|
||||||
// 上下各扩展0.2,避免曲线贴边
|
// 上下各扩展0.2,避免曲线贴边
|
||||||
return {
|
return {
|
||||||
min: Number((min - 0.2).toFixed(1)), // 最小值减0.2
|
min: Number((min - 0.2).toFixed(1)), // 最小值减0.2
|
||||||
max: Number((max + 0.1).toFixed(1)) // 最大值加0.2
|
max: Number((max + 0.1).toFixed(1)) // 最大值加0.2
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -144,13 +162,13 @@ function calculateYAxisRange(data: (number | null)[]) {
|
|||||||
* @returns X轴刻度间隔值(interval属性)
|
* @returns X轴刻度间隔值(interval属性)
|
||||||
*/
|
*/
|
||||||
function calculateXAxisInterval(dateCount: number): number {
|
function calculateXAxisInterval(dateCount: number): number {
|
||||||
if (dateCount <= 4) return 0 // 数据点≤4时,显示所有刻度
|
if (dateCount <= 4) return 0; // 数据点≤4时,显示所有刻度
|
||||||
return Math.ceil(dateCount / 4) - 1 // 动态计算间隔,保证最多4个刻度
|
return Math.ceil(dateCount / 4) - 1; // 动态计算间隔,保证最多4个刻度
|
||||||
}
|
}
|
||||||
const paramsOne:any = ref({
|
const paramsOne: any = ref({
|
||||||
value: '',
|
value: '',
|
||||||
tm: [currentMonth, currentMonth]
|
tm: [currentMonth, currentMonth]
|
||||||
})
|
});
|
||||||
/**
|
/**
|
||||||
* 获取图表数据
|
* 获取图表数据
|
||||||
* 调用后端接口获取出入库水温数据,并触发图表更新
|
* 调用后端接口获取出入库水温数据,并触发图表更新
|
||||||
@ -163,42 +181,52 @@ async function fetchChartData() {
|
|||||||
try {
|
try {
|
||||||
// 动态计算时间范围(考虑不同月份的实际天数)
|
// 动态计算时间范围(考虑不同月份的实际天数)
|
||||||
const startDate = paramsOne.value.tm[0] + '-01 00:00:00';
|
const startDate = paramsOne.value.tm[0] + '-01 00:00:00';
|
||||||
const endDate = dayjs(paramsOne.value.tm[1]).endOf('month').format('YYYY-MM-DD') + ' 23:59:59';
|
const endDate =
|
||||||
|
dayjs(paramsOne.value.tm[1]).endOf('month').format('YYYY-MM-DD') +
|
||||||
|
' 23:59:59';
|
||||||
|
|
||||||
// 构建查询参数(固定参数,后续可扩展为动态)
|
// 构建查询参数(固定参数,后续可扩展为动态)
|
||||||
const params = {
|
const params = {
|
||||||
filter: {
|
filter: {
|
||||||
logic: "and",
|
logic: 'and',
|
||||||
filters: [
|
filters: [
|
||||||
{
|
{
|
||||||
// OR逻辑:查询入库或出库水温
|
// OR逻辑:查询入库或出库水温
|
||||||
logic: "or",
|
logic: 'or',
|
||||||
filters: [
|
filters: [
|
||||||
{ field: "engDwtCode", operator: "eq", value: paramsOne.value.value }, // 出库站点编码
|
{
|
||||||
{ field: "engIwtCode", operator: "eq", value: paramsOne.value.value } // 入库站点编码
|
field: 'engDwtCode',
|
||||||
|
operator: 'eq',
|
||||||
|
value: paramsOne.value.value
|
||||||
|
}, // 出库站点编码
|
||||||
|
{
|
||||||
|
field: 'engIwtCode',
|
||||||
|
operator: 'eq',
|
||||||
|
value: paramsOne.value.value
|
||||||
|
} // 入库站点编码
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
// 时间范围过滤
|
// 时间范围过滤
|
||||||
{ field: "dt", operator: "gte", dataType: "date", value: startDate },
|
{ field: 'dt', operator: 'gte', dataType: 'date', value: startDate },
|
||||||
{ field: "dt", operator: "lte", dataType: "date", value: endDate }
|
{ field: 'dt', operator: 'lte', dataType: 'date', value: endDate }
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
// 调用API获取数据
|
// 调用API获取数据
|
||||||
const res = await inOutOneGetKendoListCust(params)
|
const res = await inOutOneGetKendoListCust(params);
|
||||||
const rawData = res?.data?.data || []
|
const rawData = res?.data?.data || [];
|
||||||
|
|
||||||
console.log('原始数据:', rawData)
|
console.log('原始数据:', rawData);
|
||||||
|
|
||||||
// 根据数据长度决定showemit
|
// 根据数据长度决定showemit
|
||||||
if (rawData.length > 0) {
|
if (rawData.length > 0) {
|
||||||
showemit.value = true; // 有数据则显示图表
|
showemit.value = true; // 有数据则显示图表
|
||||||
|
|
||||||
// 转换数据格式
|
// 转换数据格式
|
||||||
const { dates, iwtData, dwtData } = transformChartData(rawData)
|
const { dates, iwtData, dwtData } = transformChartData(rawData);
|
||||||
|
|
||||||
console.log('转换后的数据:', { dates, iwtData, dwtData })
|
console.log('转换后的数据:', { dates, iwtData, dwtData });
|
||||||
|
|
||||||
// 数据就绪后初始化或更新图表
|
// 数据就绪后初始化或更新图表
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
@ -215,13 +243,11 @@ async function fetchChartData() {
|
|||||||
}
|
}
|
||||||
}, 50);
|
}, 50);
|
||||||
}, 50);
|
}, 50);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
showemit.value = false; // 无数据显示空状态
|
showemit.value = false; // 无数据显示空状态
|
||||||
}
|
}
|
||||||
|
|
||||||
chartDataLoaded.value = true; // 标记数据已加载
|
chartDataLoaded.value = true; // 标记数据已加载
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('获取图表数据失败:', error);
|
console.error('获取图表数据失败:', error);
|
||||||
showemit.value = false; // 出错也显示空状态
|
showemit.value = false; // 出错也显示空状态
|
||||||
@ -237,7 +263,11 @@ async function fetchChartData() {
|
|||||||
* @param iwtData - 入库水温数据
|
* @param iwtData - 入库水温数据
|
||||||
* @param dwtData - 出库水温数据
|
* @param dwtData - 出库水温数据
|
||||||
*/
|
*/
|
||||||
const initChartWithData = (dates: string[], iwtData: (number | null)[], dwtData: (number | null)[]) => {
|
const initChartWithData = (
|
||||||
|
dates: string[],
|
||||||
|
iwtData: (number | null)[],
|
||||||
|
dwtData: (number | null)[]
|
||||||
|
) => {
|
||||||
if (!chartContainer.value) return;
|
if (!chartContainer.value) return;
|
||||||
|
|
||||||
const containerHeight = chartContainer.value.offsetHeight;
|
const containerHeight = chartContainer.value.offsetHeight;
|
||||||
@ -258,18 +288,29 @@ const initChartWithData = (dates: string[], iwtData: (number | null)[], dwtData:
|
|||||||
* @param dwtData - 出库水温数据数组
|
* @param dwtData - 出库水温数据数组
|
||||||
* @param yAxisMax - 废弃参数,保留以兼容旧代码
|
* @param yAxisMax - 废弃参数,保留以兼容旧代码
|
||||||
*/
|
*/
|
||||||
function updateChart(dates: string[], iwtData: (number | null)[], dwtData: (number | null)[], yAxisMax: number) {
|
function updateChart(
|
||||||
|
dates: string[],
|
||||||
|
iwtData: (number | null)[],
|
||||||
|
dwtData: (number | null)[],
|
||||||
|
yAxisMax: number
|
||||||
|
) {
|
||||||
if (!chartInstance) {
|
if (!chartInstance) {
|
||||||
console.warn('图表实例未初始化')
|
console.warn('图表实例未初始化');
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// ==================== 动态计算坐标轴范围 ====================
|
// ==================== 动态计算坐标轴范围 ====================
|
||||||
const xAxisInterval = calculateXAxisInterval(dates.length) // X轴刻度间隔
|
const xAxisInterval = calculateXAxisInterval(dates.length); // X轴刻度间隔
|
||||||
const { min: yAxisMin, max: yAxisMaxValue } = calculateYAxisRange([...iwtData, ...dwtData]) // Y轴范围
|
const { min: yAxisMin, max: yAxisMaxValue } = calculateYAxisRange([
|
||||||
|
...iwtData,
|
||||||
|
...dwtData
|
||||||
|
]); // Y轴范围
|
||||||
|
|
||||||
console.log('X轴间隔:', xAxisInterval, 'Y轴范围:', { min: yAxisMin, max: yAxisMaxValue })
|
console.log('X轴间隔:', xAxisInterval, 'Y轴范围:', {
|
||||||
|
min: yAxisMin,
|
||||||
|
max: yAxisMaxValue
|
||||||
|
});
|
||||||
|
|
||||||
// ==================== ECharts配置项 ====================
|
// ==================== ECharts配置项 ====================
|
||||||
const option = {
|
const option = {
|
||||||
@ -297,7 +338,10 @@ function updateChart(dates: string[], iwtData: (number | null)[], dwtData: (numb
|
|||||||
let result = `<div style="font-weight: bold; margin-bottom: 8px; font-size: 14px;">${date}</div>`;
|
let result = `<div style="font-weight: bold; margin-bottom: 8px; font-size: 14px;">${date}</div>`;
|
||||||
params.forEach((item: any) => {
|
params.forEach((item: any) => {
|
||||||
// 空值显示'-',符合项目规范
|
// 空值显示'-',符合项目规范
|
||||||
const value = item.value !== null && item.value !== undefined ? `${item.value}°C` : '-'
|
const value =
|
||||||
|
item.value !== null && item.value !== undefined
|
||||||
|
? `${item.value}°C`
|
||||||
|
: '-';
|
||||||
result += `<div style="display: flex; align-items: center; gap: 6px; margin: 4px 0; font-size: 14px;">
|
result += `<div style="display: flex; align-items: center; gap: 6px; margin: 4px 0; font-size: 14px;">
|
||||||
<span style="display: inline-block; width: 10px; height: 10px; border-radius: 50%; background-color: ${item.color};"></span>
|
<span style="display: inline-block; width: 10px; height: 10px; border-radius: 50%; background-color: ${item.color};"></span>
|
||||||
<span>${item.seriesName}</span>
|
<span>${item.seriesName}</span>
|
||||||
@ -465,7 +509,7 @@ function updateChart(dates: string[], iwtData: (number | null)[], dwtData: (numb
|
|||||||
chartInstance.off('click'); // 先移除旧的事件监听
|
chartInstance.off('click'); // 先移除旧的事件监听
|
||||||
chartInstance.on('click', handleChartClick);
|
chartInstance.on('click', handleChartClick);
|
||||||
|
|
||||||
console.log('图表更新成功')
|
console.log('图表更新成功');
|
||||||
|
|
||||||
// 强制重绘,确保图表尺寸正确适配容器
|
// 强制重绘,确保图表尺寸正确适配容器
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
@ -473,9 +517,8 @@ function updateChart(dates: string[], iwtData: (number | null)[], dwtData: (numb
|
|||||||
chartInstance.resize(); // 重新计算图表尺寸
|
chartInstance.resize(); // 重新计算图表尺寸
|
||||||
}
|
}
|
||||||
}, 50);
|
}, 50);
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('更新图表失败:', error)
|
console.error('更新图表失败:', error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -563,13 +606,23 @@ const handleModalClose = () => {
|
|||||||
* 处理下拉框和日期选择器的值变化
|
* 处理下拉框和日期选择器的值变化
|
||||||
* @param data - 包含moreSelect(下拉框值)和datetime(日期范围)的对象
|
* @param data - 包含moreSelect(下拉框值)和datetime(日期范围)的对象
|
||||||
*/
|
*/
|
||||||
const handlePanelChange1 = (data: any) => {
|
const handlePanelChange1 = (data: any, type: string) => {
|
||||||
// TODO: 后续可扩展联动逻辑
|
// TODO: 后续可扩展联动逻辑
|
||||||
if (data.moreSelect) {
|
if (data.moreSelect) {
|
||||||
paramsOne.value.value = data.moreSelect;
|
paramsOne.value.value = data.moreSelect;
|
||||||
}
|
}
|
||||||
paramsOne.value.tm = data.scopeDate
|
if (type == 'click') {
|
||||||
fetchChartData()
|
const mapClass = MapClass.getInstance();
|
||||||
|
mapClass.flyTopanto(
|
||||||
|
[
|
||||||
|
Number(data.selectedNodeExtra.lgtd),
|
||||||
|
Number(data.selectedNodeExtra.lttd)
|
||||||
|
],
|
||||||
|
13
|
||||||
|
);
|
||||||
|
}
|
||||||
|
paramsOne.value.tm = data.scopeDate;
|
||||||
|
fetchChartData();
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -581,94 +634,114 @@ const getselsectData = async () => {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
let params = {
|
let params = {
|
||||||
"filter": {
|
filter: {
|
||||||
"logic": "and",
|
logic: 'and',
|
||||||
"filters": [
|
filters: [
|
||||||
// 如果baseid不是'all',则添加基地过滤条件
|
// 如果baseid不是'all',则添加基地过滤条件
|
||||||
baseid.value != 'all' ? {
|
baseid.value != 'all'
|
||||||
"field": "baseId",
|
? {
|
||||||
"operator": "eq",
|
field: 'baseId',
|
||||||
"dataType": "string",
|
operator: 'eq',
|
||||||
"value": baseid.value
|
dataType: 'string',
|
||||||
} : null,
|
value: baseid.value
|
||||||
|
}
|
||||||
|
: null,
|
||||||
// 站点类型过滤:只查询ENG类型站点
|
// 站点类型过滤:只查询ENG类型站点
|
||||||
{
|
{
|
||||||
"field": "sttpCode",
|
field: 'sttpCode',
|
||||||
"operator": "contains",
|
operator: 'contains',
|
||||||
"dataType": "string",
|
dataType: 'string',
|
||||||
"value": "ENG"
|
value: 'ENG'
|
||||||
},
|
},
|
||||||
// 只查询有效站点(dtin=1)
|
// 只查询有效站点(dtin=1)
|
||||||
{
|
{
|
||||||
"field": "dtin",
|
field: 'dtin',
|
||||||
"operator": "eq",
|
operator: 'eq',
|
||||||
"dataType": "string",
|
dataType: 'string',
|
||||||
"value": 1
|
value: 1
|
||||||
}
|
}
|
||||||
].filter(Boolean) // 过滤掉null值
|
].filter(Boolean) // 过滤掉null值
|
||||||
},
|
},
|
||||||
"sort": [
|
sort: [
|
||||||
// 如果是'all',按基地排序;否则不排序
|
// 如果是'all',按基地排序;否则不排序
|
||||||
baseid.value == 'all' ? {
|
baseid.value == 'all'
|
||||||
"field": "baseStepSort",
|
? {
|
||||||
"dir": "asc"
|
field: 'baseStepSort',
|
||||||
} : null,
|
dir: 'asc'
|
||||||
|
}
|
||||||
|
: null,
|
||||||
{
|
{
|
||||||
"field": "rvcdStepSort",
|
field: 'rvcdStepSort',
|
||||||
"dir": "asc"
|
dir: 'asc'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"field": "siteStepSort",
|
field: 'siteStepSort',
|
||||||
"dir": "asc"
|
dir: 'asc'
|
||||||
}
|
}
|
||||||
].filter(Boolean), // 过滤掉null值
|
].filter(Boolean), // 过滤掉null值
|
||||||
"select": [
|
select: [
|
||||||
"stcd", // 站点编码
|
'stcd', // 站点编码
|
||||||
"stnm", // 站点名称
|
'stnm', // 站点名称
|
||||||
"lgtd", // 经度
|
'lgtd', // 经度
|
||||||
"lttd", // 纬度
|
'lttd', // 纬度
|
||||||
"siteStepSort", // 站点排序
|
'siteStepSort', // 站点排序
|
||||||
"baseId", // 基地ID
|
'baseId', // 基地ID
|
||||||
"baseName" // 基地名称
|
'baseName' // 基地名称
|
||||||
]
|
]
|
||||||
}
|
};
|
||||||
|
|
||||||
// 调用API获取站点数据
|
// 调用API获取站点数据
|
||||||
const res = await getVmsstbprpt(params)
|
const res = await getVmsstbprpt(params);
|
||||||
console.log('res', res)
|
console.log('res', res);
|
||||||
const data = res?.data?.data || res?.data
|
const data = res?.data?.data || res?.data;
|
||||||
const obj: any = {}
|
const obj: any = {};
|
||||||
|
|
||||||
if (data) {
|
if (data) {
|
||||||
// 将扁平数据转换为树形结构(按基地分组)
|
// 将扁平数据转换为树形结构(按基地分组)
|
||||||
data.map((e) => {
|
data.map(e => {
|
||||||
if (obj[e.baseId]) {
|
if (obj[e.baseId]) {
|
||||||
// 如果基地已存在,添加子节点
|
// 如果基地已存在,添加子节点
|
||||||
obj[e.baseId] = { ...obj[e.baseId], children: [...obj[e.baseId].children, { ...e, value: e.stcd, title: e.stnm }] }
|
obj[e.baseId] = {
|
||||||
|
...obj[e.baseId],
|
||||||
|
children: [
|
||||||
|
...obj[e.baseId].children,
|
||||||
|
{ ...e, value: e.stcd, title: e.stnm }
|
||||||
|
]
|
||||||
|
};
|
||||||
} else {
|
} else {
|
||||||
// 创建新的基地节点
|
// 创建新的基地节点
|
||||||
obj[e.baseId] = { value: e.baseId, title: e.baseName, selectable: false, key: e.baseId, children: [{ ...e, value: e.stcd, title: e.stnm }] }
|
obj[e.baseId] = {
|
||||||
|
value: e.baseId,
|
||||||
|
title: e.baseName,
|
||||||
|
selectable: false,
|
||||||
|
key: e.baseId,
|
||||||
|
children: [{ ...e, value: e.stcd, title: e.stnm }]
|
||||||
|
};
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
|
|
||||||
// 转换为数组并按jiDiList顺序排序
|
// 转换为数组并按jiDiList顺序排序
|
||||||
let treeData: any = []
|
let treeData: any = [];
|
||||||
const dataMapNameArr: any = Object.entries(obj).map(([, v]) => v)
|
const dataMapNameArr: any = Object.entries(obj).map(([, v]) => v);
|
||||||
treeData = dataMapNameArr?.sort((a: any, b: any) => {
|
treeData = dataMapNameArr?.sort((a: any, b: any) => {
|
||||||
const indexA = JidiSelectEventStore.jidiData.findIndex(item => item.wbsCode === a.value);
|
const indexA = JidiSelectEventStore.jidiData.findIndex(
|
||||||
const indexB = JidiSelectEventStore.jidiData.findIndex(item => item.wbsCode === b.value);
|
item => item.wbsCode === a.value
|
||||||
|
);
|
||||||
|
const indexB = JidiSelectEventStore.jidiData.findIndex(
|
||||||
|
item => item.wbsCode === b.value
|
||||||
|
);
|
||||||
return indexA - indexB;
|
return indexA - indexB;
|
||||||
})
|
});
|
||||||
console.log('treeData', treeData)
|
console.log('treeData', treeData);
|
||||||
|
|
||||||
// 更新下拉框选项
|
// 更新下拉框选项
|
||||||
select.value.options = treeData
|
select.value.options = treeData;
|
||||||
|
|
||||||
// 设置默认选中值
|
// 设置默认选中值
|
||||||
if (baseid.value == 'all') {
|
if (baseid.value == 'all') {
|
||||||
select.value.value = '008640203800000001' // 默认选中第一个站点
|
select.value.value = '008640203800000001'; // 默认选中第一个站点
|
||||||
} else {
|
} else {
|
||||||
select.value.value = treeData?.[0]?.children[0]?.value // 选中当前基地的第一个站点
|
select.value.value = treeData?.[0]?.children[0]?.value; // 选中当前基地的第一个站点
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -676,7 +749,7 @@ const getselsectData = async () => {
|
|||||||
} finally {
|
} finally {
|
||||||
loading.value = false; // 关闭loading
|
loading.value = false; // 关闭loading
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
// ==================== Watch监听器 ====================
|
// ==================== Watch监听器 ====================
|
||||||
|
|
||||||
@ -686,9 +759,9 @@ const getselsectData = async () => {
|
|||||||
*/
|
*/
|
||||||
watch(
|
watch(
|
||||||
() => JidiSelectEventStore.selectedItem, // 监听Store中的选中项
|
() => JidiSelectEventStore.selectedItem, // 监听Store中的选中项
|
||||||
(newVal) => {
|
newVal => {
|
||||||
baseid.value = newVal.wbsCode; // 更新基地ID
|
baseid.value = newVal.wbsCode; // 更新基地ID
|
||||||
getselsectData() // 重新获取站点数据
|
getselsectData(); // 重新获取站点数据
|
||||||
},
|
},
|
||||||
{ deep: true, immediate: true } // 深度监听,立即执行
|
{ deep: true, immediate: true } // 深度监听,立即执行
|
||||||
);
|
);
|
||||||
|
|||||||
@ -107,6 +107,8 @@ export const useMapOrchestrator = () => {
|
|||||||
mapDataStore.setLoading(true);
|
mapDataStore.setLoading(true);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
const previousPageKey = mapConfigStore.lastLoadOptions?.pageKey || '';
|
||||||
|
const previousCheckedKeys = mapViewStore.getCheckedLayerKeys();
|
||||||
const { layerConfig, legendOriginal, pageLegend } =
|
const { layerConfig, legendOriginal, pageLegend } =
|
||||||
await mapConfigStore.loadPageMapConfig({
|
await mapConfigStore.loadPageMapConfig({
|
||||||
systemId: SYSTEM_ID,
|
systemId: SYSTEM_ID,
|
||||||
@ -122,11 +124,26 @@ export const useMapOrchestrator = () => {
|
|||||||
if (layerConfig.length > 0) {
|
if (layerConfig.length > 0) {
|
||||||
mapStore.setLayerData(layerConfig);
|
mapStore.setLayerData(layerConfig);
|
||||||
ensureBaseLayersInitialized(layerConfig);
|
ensureBaseLayersInitialized(layerConfig);
|
||||||
const checkedKeys = mapViewStore.getCheckedLayerKeys();
|
let checkedKeys = mapViewStore.getCheckedLayerKeys();
|
||||||
|
if (previousPageKey === pageKey && previousCheckedKeys.length > 0) {
|
||||||
|
const currentLayerKeys = new Set(
|
||||||
|
layerConfig
|
||||||
|
.flatMap((item: any) => layerConfig2Flat([item]))
|
||||||
|
.map((item: any) => item?.key)
|
||||||
|
.filter(Boolean)
|
||||||
|
);
|
||||||
|
const runtimeCheckedKeys = previousCheckedKeys.filter(key =>
|
||||||
|
currentLayerKeys.has(key)
|
||||||
|
);
|
||||||
|
if (runtimeCheckedKeys.length > 0) {
|
||||||
|
mapViewStore.setCheckedLayerKeys(runtimeCheckedKeys);
|
||||||
|
checkedKeys = runtimeCheckedKeys;
|
||||||
|
}
|
||||||
|
}
|
||||||
await mapStore.loadAllLayerData(layerConfig, checkedKeys);
|
await mapStore.loadAllLayerData(layerConfig, checkedKeys);
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
mapDataStore.setLoading(false);
|
// mapDataStore.setLoading(false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -380,7 +397,7 @@ export const useMapOrchestrator = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// 备注:统一处理搜索定位,按点位编码从缓存数据中查找并飞行到目标位置。
|
// 备注:统一处理搜索定位,按点位编码从缓存数据中查找并飞行到目标位置。
|
||||||
const focusPoint = (pointId: string, zoom: number = 14) => {
|
const focusPoint = (pointId: string, zoom: number = 15) => {
|
||||||
if (!pointId) return;
|
if (!pointId) return;
|
||||||
const targetPoint = mapDataStore.pointData.find((item: any) => {
|
const targetPoint = mapDataStore.pointData.find((item: any) => {
|
||||||
return item.stcd === pointId || item._id === pointId;
|
return item.stcd === pointId || item._id === pointId;
|
||||||
|
|||||||
@ -1,36 +1,73 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="monthly-average-container">
|
<div class="monthly-average-container">
|
||||||
<!-- 搜索表单 -->
|
<!-- 搜索表单 -->
|
||||||
<a-form ref="formRef" :model="formValue" layout="inline" class="search-form">
|
<a-form
|
||||||
|
ref="formRef"
|
||||||
|
:model="formValue"
|
||||||
|
layout="inline"
|
||||||
|
class="search-form"
|
||||||
|
>
|
||||||
<a-form-item label="基地名称" name="dataDimensionVal">
|
<a-form-item label="基地名称" name="dataDimensionVal">
|
||||||
<a-select v-model:value="formValue.dataDimensionVal" placeholder="请选择" style="width: 200px"
|
<a-select
|
||||||
@change="handleBaseChange">
|
v-model:value="formValue.dataDimensionVal"
|
||||||
<a-select-option v-for="item in jdList" :key="item.wbsCode" :value="item.wbsCode">
|
placeholder="请选择"
|
||||||
|
style="width: 200px"
|
||||||
|
@change="handleBaseChange"
|
||||||
|
>
|
||||||
|
<a-select-option
|
||||||
|
v-for="item in jdList"
|
||||||
|
:key="item.wbsCode"
|
||||||
|
:value="item.wbsCode"
|
||||||
|
>
|
||||||
{{ item.wbsName }}
|
{{ item.wbsName }}
|
||||||
</a-select-option>
|
</a-select-option>
|
||||||
</a-select>
|
</a-select>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
|
|
||||||
<a-form-item label="所在流域" name="rvcd">
|
<a-form-item label="所在流域" name="rvcd">
|
||||||
<a-select v-model:value="formValue.rvcd" placeholder="请选择" style="width: 200px"
|
<a-select
|
||||||
:disabled="!formValue.dataDimensionVal" @change="handleRvcdChange">
|
v-model:value="formValue.rvcd"
|
||||||
<a-select-option v-for="item in lyList" :key="item.value" :value="item.value">
|
placeholder="请选择"
|
||||||
|
style="width: 200px"
|
||||||
|
:disabled="!formValue.dataDimensionVal"
|
||||||
|
@change="handleRvcdChange"
|
||||||
|
>
|
||||||
|
<a-select-option
|
||||||
|
v-for="item in lyList"
|
||||||
|
:key="item.value"
|
||||||
|
:value="item.value"
|
||||||
|
>
|
||||||
{{ item.label }}
|
{{ item.label }}
|
||||||
</a-select-option>
|
</a-select-option>
|
||||||
</a-select>
|
</a-select>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
|
|
||||||
<a-form-item label="断面名称" name="stcd">
|
<a-form-item label="断面名称" name="stcd">
|
||||||
<a-select v-model:value="formValue.stcd" placeholder="请选择" style="width: 200px" :disabled="!formValue.rvcd">
|
<a-select
|
||||||
<a-select-option v-for="item in dmList" :key="item.value" :value="item.value">
|
v-model:value="formValue.stcd"
|
||||||
|
placeholder="请选择"
|
||||||
|
style="width: 200px"
|
||||||
|
:disabled="!formValue.rvcd"
|
||||||
|
>
|
||||||
|
<a-select-option
|
||||||
|
v-for="item in dmList"
|
||||||
|
:key="item.value"
|
||||||
|
:value="item.value"
|
||||||
|
>
|
||||||
{{ item.label }}
|
{{ item.label }}
|
||||||
</a-select-option>
|
</a-select-option>
|
||||||
</a-select>
|
</a-select>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
|
|
||||||
<a-form-item label="月份" name="tm">
|
<a-form-item label="月份" name="tm">
|
||||||
<a-date-picker v-model:value="formValue.tm" picker="month" :disabled-date="disabledDate" format="YYYY-MM"
|
<a-date-picker
|
||||||
value-format="YYYY-MM-DD HH:mm:ss" :allow-clear="false" />
|
v-model:value="formValue.tm"
|
||||||
|
picker="month"
|
||||||
|
:disabled-date="disabledDate"
|
||||||
|
format="YYYY-MM"
|
||||||
|
value-format="YYYY-MM-DD HH:mm:ss"
|
||||||
|
:allow-clear="false"
|
||||||
|
/>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
|
|
||||||
<a-form-item>
|
<a-form-item>
|
||||||
@ -45,8 +82,14 @@
|
|||||||
<div style="width: 100%; display: flex">
|
<div style="width: 100%; display: flex">
|
||||||
<!-- 图表容器:始终存在,不再使用 v-if -->
|
<!-- 图表容器:始终存在,不再使用 v-if -->
|
||||||
<div class="chart-wrapper" ref="chartRef"></div>
|
<div class="chart-wrapper" ref="chartRef"></div>
|
||||||
<BasicTable ref="tableRef" :scrollY="460" :columns="columns" :list-url="DetGetKendoListCust"
|
<BasicTable
|
||||||
:search-params="{ sort: sort }" :transform-data="customTransform">
|
ref="tableRef"
|
||||||
|
:scrollY="460"
|
||||||
|
:columns="columns"
|
||||||
|
:list-url="DetGetKendoListCust"
|
||||||
|
:search-params="{ sort: sort }"
|
||||||
|
:transform-data="customTransform"
|
||||||
|
>
|
||||||
<template #action="{ record }">
|
<template #action="{ record }">
|
||||||
<a @click="handleViewDetail(record)" class="text_hocer">查看详情</a>
|
<a @click="handleViewDetail(record)" class="text_hocer">查看详情</a>
|
||||||
</template>
|
</template>
|
||||||
@ -816,8 +859,12 @@ const handleViewDetail = (record: any) => {
|
|||||||
});
|
});
|
||||||
modelStore.modalVisible = true;
|
modelStore.modalVisible = true;
|
||||||
modelStore.params.sttp = 'WT';
|
modelStore.params.sttp = 'WT';
|
||||||
modelStore.title = stnm ;
|
modelStore.title = stnm;
|
||||||
modelStore.params.stcd = formValue.stcd;
|
modelStore.params.stcd = formValue.stcd;
|
||||||
|
modelStore.filter.rangeTm = [
|
||||||
|
dayjs(formValue.value.tm).startOf('month').format('YYYY-MM-DD HH:mm:ss'),
|
||||||
|
dayjs(formValue.value.tm).endOf('month').format('YYYY-MM-DD HH:mm:ss')
|
||||||
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
defineExpose({
|
defineExpose({
|
||||||
|
|||||||
@ -1,6 +1,11 @@
|
|||||||
<!-- SidePanelItem.vue -->
|
<!-- SidePanelItem.vue -->
|
||||||
<template>
|
<template>
|
||||||
<SidePanelItem title="水文监测" :select="select" :scopeDate="datetimePicker" @update-values="handlePanelChange1">
|
<SidePanelItem
|
||||||
|
title="水文监测"
|
||||||
|
:select="select"
|
||||||
|
:scopeDate="datetimePicker"
|
||||||
|
@update-values="handlePanelChange1"
|
||||||
|
>
|
||||||
<div class="chart-wrapper">
|
<div class="chart-wrapper">
|
||||||
<a-spin :spinning="loading">
|
<a-spin :spinning="loading">
|
||||||
<!-- 始终渲染图表容器,确保有固定宽高 -->
|
<!-- 始终渲染图表容器,确保有固定宽高 -->
|
||||||
@ -19,9 +24,10 @@ import { ref, onMounted, onUnmounted, nextTick, watch } from 'vue';
|
|||||||
import * as echarts from 'echarts';
|
import * as echarts from 'echarts';
|
||||||
import type { EChartsOption } from 'echarts';
|
import type { EChartsOption } from 'echarts';
|
||||||
import SidePanelItem from '@/components/SidePanelItem/index.vue';
|
import SidePanelItem from '@/components/SidePanelItem/index.vue';
|
||||||
import { useJidiSelectEventStore } from "@/store/modules/jidiSelectEvent";
|
import { useJidiSelectEventStore } from '@/store/modules/jidiSelectEvent';
|
||||||
import { msstbprptGetKendoList, sdriverdaysGetKendoList } from '@/api/qxd';
|
import { msstbprptGetKendoList, sdriverdaysGetKendoList } from '@/api/qxd';
|
||||||
import { useModelStore } from "@/store/modules/model";
|
import { useModelStore } from '@/store/modules/model';
|
||||||
|
import dayjs from 'dayjs';
|
||||||
// 定义组件名
|
// 定义组件名
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: 'qixidiliuliangbianhua'
|
name: 'qixidiliuliangbianhua'
|
||||||
@ -29,13 +35,12 @@ defineOptions({
|
|||||||
|
|
||||||
// ==================== 选择器配置 ====================
|
// ==================== 选择器配置 ====================
|
||||||
const JidiSelectEventStore = useJidiSelectEventStore();
|
const JidiSelectEventStore = useJidiSelectEventStore();
|
||||||
const baseid = ref('')
|
const baseid = ref('');
|
||||||
const modelStore = useModelStore();
|
const modelStore = useModelStore();
|
||||||
|
|
||||||
// Loading 状态和数据状态
|
// Loading 状态和数据状态
|
||||||
const loading = ref(false);
|
const loading = ref(false);
|
||||||
const hasData = ref(true); // 初始化为 true,因为 initChart 时会渲染
|
const hasData = ref(true); // 初始化为 true,因为 initChart 时会渲染
|
||||||
|
|
||||||
const select = ref({
|
const select = ref({
|
||||||
show: true,
|
show: true,
|
||||||
value: undefined,
|
value: undefined,
|
||||||
@ -48,9 +53,13 @@ const datetimePicker: any = ref({
|
|||||||
show: true,
|
show: true,
|
||||||
value: (() => {
|
value: (() => {
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
const currentMonth = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}`;
|
const currentMonth = `${now.getFullYear()}-${String(
|
||||||
|
now.getMonth() + 1
|
||||||
|
).padStart(2, '0')}`;
|
||||||
const lastMonth = new Date(now.getFullYear(), now.getMonth() - 1, 1);
|
const lastMonth = new Date(now.getFullYear(), now.getMonth() - 1, 1);
|
||||||
const lastMonthStr = `${lastMonth.getFullYear()}-${String(lastMonth.getMonth() + 1).padStart(2, '0')}`;
|
const lastMonthStr = `${lastMonth.getFullYear()}-${String(
|
||||||
|
lastMonth.getMonth() + 1
|
||||||
|
).padStart(2, '0')}`;
|
||||||
return [lastMonthStr, currentMonth];
|
return [lastMonthStr, currentMonth];
|
||||||
})(),
|
})(),
|
||||||
format: 'YY-MM',
|
format: 'YY-MM',
|
||||||
@ -207,11 +216,15 @@ const initChart = () => {
|
|||||||
// ✅ 判断是否点击的是数据点(series中的点)
|
// ✅ 判断是否点击的是数据点(series中的点)
|
||||||
if (params.componentType === 'series') {
|
if (params.componentType === 'series') {
|
||||||
modelStore.modalVisible = true;
|
modelStore.modalVisible = true;
|
||||||
modelStore.params.sttp = "fh_zq_point";
|
modelStore.params.sttp = 'fh_zq_point';
|
||||||
modelStore.title = "水文监测";
|
modelStore.title = '水文监测';
|
||||||
modelStore.params.stcd = select.value.value;
|
modelStore.params.stcd = select.value.value;
|
||||||
modelStore.showStcdSelector = true
|
modelStore.showStcdSelector = true;
|
||||||
modelStore.stcdOptions = select.value.options
|
modelStore.stcdOptions = select.value.options;
|
||||||
|
modelStore.filter.rangeTm = [
|
||||||
|
dayjs(params.name).startOf('day').format('YYYY-MM-DD HH:mm:ss'),
|
||||||
|
dayjs(params.name).endOf('day').format('YYYY-MM-DD HH:mm:ss')
|
||||||
|
];
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@ -221,9 +234,15 @@ const updateChart = () => {
|
|||||||
if (!chartInstance) return;
|
if (!chartInstance) return;
|
||||||
|
|
||||||
// ✅ 计算数据统计值(用于Y轴动态范围)
|
// ✅ 计算数据统计值(用于Y轴动态范围)
|
||||||
const qValues = echartsData.value.map(item => item.q).filter(v => v != null && !isNaN(v));
|
const qValues = echartsData.value
|
||||||
const zValues = echartsData.value.map(item => item.z).filter(v => v != null && !isNaN(v));
|
.map(item => item.q)
|
||||||
const vValues = echartsData.value.map(item => item.v).filter(v => v != null && !isNaN(v));
|
.filter(v => v != null && !isNaN(v));
|
||||||
|
const zValues = echartsData.value
|
||||||
|
.map(item => item.z)
|
||||||
|
.filter(v => v != null && !isNaN(v));
|
||||||
|
const vValues = echartsData.value
|
||||||
|
.map(item => item.v)
|
||||||
|
.filter(v => v != null && !isNaN(v));
|
||||||
|
|
||||||
const minQ = qValues.length > 0 ? Math.min(...qValues) : 0;
|
const minQ = qValues.length > 0 ? Math.min(...qValues) : 0;
|
||||||
const maxQ = qValues.length > 0 ? Math.max(...qValues) : 0;
|
const maxQ = qValues.length > 0 ? Math.max(...qValues) : 0;
|
||||||
@ -257,9 +276,17 @@ const updateChart = () => {
|
|||||||
if (item.value !== undefined && item.value !== null) {
|
if (item.value !== undefined && item.value !== null) {
|
||||||
// ✅ 保留两位小数
|
// ✅ 保留两位小数
|
||||||
const formattedValue = Number(item.value).toFixed(2);
|
const formattedValue = Number(item.value).toFixed(2);
|
||||||
res += `<span style="background: ${item.color}; height:10px; width: 10px; border-radius: 50%; display: inline-block; margin-right:10px;"></span>${item.seriesName} ${formattedValue}${unit?.[1] || ''}<br/>`;
|
res += `<span style="background: ${
|
||||||
|
item.color
|
||||||
|
}; height:10px; width: 10px; border-radius: 50%; display: inline-block; margin-right:10px;"></span>${
|
||||||
|
item.seriesName
|
||||||
|
} ${formattedValue}${unit?.[1] || ''}<br/>`;
|
||||||
} else {
|
} else {
|
||||||
res += `<span style="background: ${item.color}; height:10px; width: 10px; border-radius: 50%; display: inline-block; margin-right:10px;"></span>${item.seriesName} -${unit?.[1] || ''}<br/>`;
|
res += `<span style="background: ${
|
||||||
|
item.color
|
||||||
|
}; height:10px; width: 10px; border-radius: 50%; display: inline-block; margin-right:10px;"></span>${
|
||||||
|
item.seriesName
|
||||||
|
} -${unit?.[1] || ''}<br/>`;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return res;
|
return res;
|
||||||
@ -447,7 +474,6 @@ const updateChart = () => {
|
|||||||
|
|
||||||
// 系列配置(按期望顺序:流量、水位、流速)
|
// 系列配置(按期望顺序:流量、水位、流速)
|
||||||
series: [
|
series: [
|
||||||
|
|
||||||
{
|
{
|
||||||
name: '流量(m³/s)',
|
name: '流量(m³/s)',
|
||||||
data: echartsData.value.map(item => item.q),
|
data: echartsData.value.map(item => item.q),
|
||||||
@ -495,7 +521,7 @@ const updateChart = () => {
|
|||||||
width: 2
|
width: 2
|
||||||
},
|
},
|
||||||
yAxisIndex: 2
|
yAxisIndex: 2
|
||||||
},
|
}
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -506,11 +532,14 @@ const updateChart = () => {
|
|||||||
chartInstance.setOption(setting, true);
|
chartInstance.setOption(setting, true);
|
||||||
|
|
||||||
// ✅ 初始化时应用动态布局算法,确保只有流量Y轴显示
|
// ✅ 初始化时应用动态布局算法,确保只有流量Y轴显示
|
||||||
yAxisShowDynamic({
|
yAxisShowDynamic(
|
||||||
|
{
|
||||||
'流量(m³/s)': true,
|
'流量(m³/s)': true,
|
||||||
'水位(m)': false,
|
'水位(m)': false,
|
||||||
'流速(m/s)': false
|
'流速(m/s)': false
|
||||||
}, setting);
|
},
|
||||||
|
setting
|
||||||
|
);
|
||||||
|
|
||||||
// 再次应用调整后的配置
|
// 再次应用调整后的配置
|
||||||
chartInstance.setOption(setting, true);
|
chartInstance.setOption(setting, true);
|
||||||
@ -536,54 +565,50 @@ onUnmounted(() => {
|
|||||||
//获取水温监测下拉框
|
//获取水温监测下拉框
|
||||||
const getselsectData = async () => {
|
const getselsectData = async () => {
|
||||||
let params = {
|
let params = {
|
||||||
"filter": {
|
filter: {
|
||||||
"logic": "and",
|
logic: 'and',
|
||||||
"filters": [
|
filters: [
|
||||||
baseid.value != 'all' ? {
|
baseid.value != 'all'
|
||||||
"field": "baseId",
|
? {
|
||||||
"operator": "eq",
|
field: 'baseId',
|
||||||
"dataType": "string",
|
operator: 'eq',
|
||||||
"value": baseid.value
|
dataType: 'string',
|
||||||
} : null,
|
value: baseid.value
|
||||||
|
}
|
||||||
|
: null,
|
||||||
{
|
{
|
||||||
"field": "fhstcd",
|
field: 'fhstcd',
|
||||||
"operator": "isnotnull"
|
operator: 'isnotnull'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"logic": "or",
|
logic: 'or',
|
||||||
"filters": [
|
filters: [
|
||||||
{
|
{
|
||||||
"field": "sttpCode",
|
field: 'sttpCode',
|
||||||
"operator": "eq",
|
operator: 'eq',
|
||||||
"dataType": "string",
|
dataType: 'string',
|
||||||
"value": "ZQ"
|
value: 'ZQ'
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
].filter(Boolean)
|
].filter(Boolean)
|
||||||
},
|
},
|
||||||
"select": [
|
select: ['stcd', 'stnm', 'fhstcd', 'fhstnm', 'sttpCode']
|
||||||
"stcd",
|
};
|
||||||
"stnm",
|
let res = await msstbprptGetKendoList(params);
|
||||||
"fhstcd",
|
console.log(res);
|
||||||
"fhstnm",
|
|
||||||
"sttpCode"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
let res = await msstbprptGetKendoList(params)
|
|
||||||
console.log(res)
|
|
||||||
if (!res?.data?.data || res.data.data.length == 0) {
|
if (!res?.data?.data || res.data.data.length == 0) {
|
||||||
select.value.value = ''
|
select.value.value = '';
|
||||||
select.value.options = []
|
select.value.options = [];
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
select.value.value = res.data.data[0].stcd
|
select.value.value = res.data.data[0].stcd;
|
||||||
select.value.options = res.data.data.map((item: any) => {
|
select.value.options = res.data.data.map((item: any) => {
|
||||||
return {
|
return {
|
||||||
label: item.stnm,
|
label: item.stnm,
|
||||||
value: item.stcd
|
value: item.stcd
|
||||||
}
|
};
|
||||||
})
|
});
|
||||||
// debugger
|
// debugger
|
||||||
};
|
};
|
||||||
const convertDateRange = (dateRange: string[]) => {
|
const convertDateRange = (dateRange: string[]) => {
|
||||||
@ -614,13 +639,18 @@ const convertDateRange = (dateRange: string[]) => {
|
|||||||
// 需要处理月份天数边界问题(如今天是31号,但2月只有28天)
|
// 需要处理月份天数边界问题(如今天是31号,但2月只有28天)
|
||||||
const startMonthLastDay = new Date(fullStartYear, startMonth, 0).getDate();
|
const startMonthLastDay = new Date(fullStartYear, startMonth, 0).getDate();
|
||||||
const actualStartDay = Math.min(currentDay, startMonthLastDay);
|
const actualStartDay = Math.min(currentDay, startMonthLastDay);
|
||||||
const startDate = `${fullStartYear}-${String(startMonth).padStart(2, '0')}-${String(actualStartDay).padStart(2, '0')} 00:00:00`;
|
const startDate = `${fullStartYear}-${String(startMonth).padStart(
|
||||||
|
2,
|
||||||
|
'0'
|
||||||
|
)}-${String(actualStartDay).padStart(2, '0')} 00:00:00`;
|
||||||
|
|
||||||
// 结束日期:基于第二个值的年月 + 今天的日期 + 23:59:59
|
// 结束日期:基于第二个值的年月 + 今天的日期 + 23:59:59
|
||||||
// 同样需要处理月份天数边界问题
|
// 同样需要处理月份天数边界问题
|
||||||
const endMonthLastDay = new Date(fullEndYear, endMonth, 0).getDate();
|
const endMonthLastDay = new Date(fullEndYear, endMonth, 0).getDate();
|
||||||
const actualEndDay = Math.min(currentDay, endMonthLastDay);
|
const actualEndDay = Math.min(currentDay, endMonthLastDay);
|
||||||
const endDate = `${fullEndYear}-${String(endMonth).padStart(2, '0')}-${String(actualEndDay).padStart(2, '0')} 23:59:59`;
|
const endDate = `${fullEndYear}-${String(endMonth).padStart(2, '0')}-${String(
|
||||||
|
actualEndDay
|
||||||
|
).padStart(2, '0')} 23:59:59`;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
startDate,
|
startDate,
|
||||||
@ -634,7 +664,11 @@ const convertDateRange = (dateRange: string[]) => {
|
|||||||
* @param endDate 结束日期 (格式: YYYY-MM-DD HH:mm:ss)
|
* @param endDate 结束日期 (格式: YYYY-MM-DD HH:mm:ss)
|
||||||
* @param stcd 测站代码
|
* @param stcd 测站代码
|
||||||
*/
|
*/
|
||||||
const getChartData = async (startDate: string, endDate: string, stcd: string) => {
|
const getChartData = async (
|
||||||
|
startDate: string,
|
||||||
|
endDate: string,
|
||||||
|
stcd: string
|
||||||
|
) => {
|
||||||
try {
|
try {
|
||||||
console.log('=== 开始加载图表数据 ===');
|
console.log('=== 开始加载图表数据 ===');
|
||||||
console.log('请求参数:', { startDate, endDate, stcd });
|
console.log('请求参数:', { startDate, endDate, stcd });
|
||||||
@ -644,31 +678,31 @@ const getChartData = async (startDate: string, endDate: string, stcd: string) =>
|
|||||||
hasData.value = false;
|
hasData.value = false;
|
||||||
|
|
||||||
const params = {
|
const params = {
|
||||||
"filter": {
|
filter: {
|
||||||
"logic": "and",
|
logic: 'and',
|
||||||
"filters": [
|
filters: [
|
||||||
{
|
{
|
||||||
"field": "DT",
|
field: 'DT',
|
||||||
"operator": "gte",
|
operator: 'gte',
|
||||||
"value": startDate
|
value: startDate
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"field": "DT",
|
field: 'DT',
|
||||||
"operator": "lte",
|
operator: 'lte',
|
||||||
"value": endDate
|
value: endDate
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"field": "stcd",
|
field: 'stcd',
|
||||||
"operator": "eq",
|
operator: 'eq',
|
||||||
"dataType": "string",
|
dataType: 'string',
|
||||||
"value": stcd
|
value: stcd
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"sort": [
|
sort: [
|
||||||
{
|
{
|
||||||
"field": "dt",
|
field: 'dt',
|
||||||
"dir": "asc"
|
dir: 'asc'
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
@ -732,7 +766,6 @@ const getChartData = async (startDate: string, endDate: string, stcd: string) =>
|
|||||||
await nextTick();
|
await nextTick();
|
||||||
updateChart();
|
updateChart();
|
||||||
console.log('图表已更新');
|
console.log('图表已更新');
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('获取图表数据失败:', error);
|
console.error('获取图表数据失败:', error);
|
||||||
hasData.value = false;
|
hasData.value = false;
|
||||||
@ -744,7 +777,7 @@ const getChartData = async (startDate: string, endDate: string, stcd: string) =>
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handlePanelChange1 = async (data) => {
|
const handlePanelChange1 = async data => {
|
||||||
console.log('=== 用户交互触发 ===');
|
console.log('=== 用户交互触发 ===');
|
||||||
console.log('当前所有控件状态:', data);
|
console.log('当前所有控件状态:', data);
|
||||||
// scopeDate: ['26-05', '26-06']
|
// scopeDate: ['26-05', '26-06']
|
||||||
@ -772,12 +805,12 @@ const handlePanelChange1 = async (data) => {
|
|||||||
select: data.select
|
select: data.select
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
watch(
|
watch(
|
||||||
() => JidiSelectEventStore.selectedItem,
|
() => JidiSelectEventStore.selectedItem,
|
||||||
(newVal) => {
|
newVal => {
|
||||||
baseid.value = newVal.wbsCode;
|
baseid.value = newVal.wbsCode;
|
||||||
getselsectData()
|
getselsectData();
|
||||||
},
|
},
|
||||||
{ deep: true, immediate: true }
|
{ deep: true, immediate: true }
|
||||||
);
|
);
|
||||||
|
|||||||
@ -1,6 +1,11 @@
|
|||||||
<!-- SidePanelItem.vue -->
|
<!-- SidePanelItem.vue -->
|
||||||
<template>
|
<template>
|
||||||
<SidePanelItem title="水温监测" :select="select" :scopeDate="datetimePicker" @update-values="handlePanelChange1">
|
<SidePanelItem
|
||||||
|
title="水温监测"
|
||||||
|
:select="select"
|
||||||
|
:scopeDate="datetimePicker"
|
||||||
|
@update-values="handlePanelChange1"
|
||||||
|
>
|
||||||
<div class="chart-wrapper">
|
<div class="chart-wrapper">
|
||||||
<a-spin :spinning="loading">
|
<a-spin :spinning="loading">
|
||||||
<!-- 始终渲染图表容器,确保有固定宽高 -->
|
<!-- 始终渲染图表容器,确保有固定宽高 -->
|
||||||
@ -19,9 +24,10 @@ import { ref, onMounted, onUnmounted, nextTick, watch } from 'vue';
|
|||||||
import * as echarts from 'echarts';
|
import * as echarts from 'echarts';
|
||||||
import type { EChartsOption } from 'echarts';
|
import type { EChartsOption } from 'echarts';
|
||||||
import SidePanelItem from '@/components/SidePanelItem/index.vue';
|
import SidePanelItem from '@/components/SidePanelItem/index.vue';
|
||||||
import { useJidiSelectEventStore } from "@/store/modules/jidiSelectEvent";
|
import { useJidiSelectEventStore } from '@/store/modules/jidiSelectEvent';
|
||||||
import { msstbprptGetKendoList, sdrvwtsGetKendoList } from '@/api/qxd';
|
import { msstbprptGetKendoList, sdrvwtsGetKendoList } from '@/api/qxd';
|
||||||
import { useModelStore } from "@/store/modules/model";
|
import { useModelStore } from '@/store/modules/model';
|
||||||
|
import dayjs from 'dayjs';
|
||||||
// 定义组件名
|
// 定义组件名
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: 'qixidishuiwenbianhua'
|
name: 'qixidishuiwenbianhua'
|
||||||
@ -32,7 +38,7 @@ const JidiSelectEventStore = useJidiSelectEventStore();
|
|||||||
// 图表容器引用
|
// 图表容器引用
|
||||||
const chartRef = ref<HTMLDivElement>();
|
const chartRef = ref<HTMLDivElement>();
|
||||||
let chartInstance: echarts.ECharts | null = null;
|
let chartInstance: echarts.ECharts | null = null;
|
||||||
const baseid = ref('')
|
const baseid = ref('');
|
||||||
|
|
||||||
// Loading 状态和数据状态
|
// Loading 状态和数据状态
|
||||||
const loading = ref(false);
|
const loading = ref(false);
|
||||||
@ -52,9 +58,13 @@ const datetimePicker: any = ref({
|
|||||||
show: true,
|
show: true,
|
||||||
value: (() => {
|
value: (() => {
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
const currentMonth = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}`;
|
const currentMonth = `${now.getFullYear()}-${String(
|
||||||
|
now.getMonth() + 1
|
||||||
|
).padStart(2, '0')}`;
|
||||||
const lastMonth = new Date(now.getFullYear(), now.getMonth() - 1, 1);
|
const lastMonth = new Date(now.getFullYear(), now.getMonth() - 1, 1);
|
||||||
const lastMonthStr = `${lastMonth.getFullYear()}-${String(lastMonth.getMonth() + 1).padStart(2, '0')}`;
|
const lastMonthStr = `${lastMonth.getFullYear()}-${String(
|
||||||
|
lastMonth.getMonth() + 1
|
||||||
|
).padStart(2, '0')}`;
|
||||||
return [lastMonthStr, currentMonth];
|
return [lastMonthStr, currentMonth];
|
||||||
})(),
|
})(),
|
||||||
format: 'YY-MM',
|
format: 'YY-MM',
|
||||||
@ -69,7 +79,16 @@ const modelStore = useModelStore();
|
|||||||
const unita = ref('℃');
|
const unita = ref('℃');
|
||||||
|
|
||||||
// 颜色配置 - 使用HSL色彩空间生成舒适的颜色
|
// 颜色配置 - 使用HSL色彩空间生成舒适的颜色
|
||||||
const Color = ['#5470c6', '#91cc75', '#fac858', '#ee6666', '#73c0de', '#3ba272', '#fc8452', '#9a60b4'];
|
const Color = [
|
||||||
|
'#5470c6',
|
||||||
|
'#91cc75',
|
||||||
|
'#fac858',
|
||||||
|
'#ee6666',
|
||||||
|
'#73c0de',
|
||||||
|
'#3ba272',
|
||||||
|
'#fc8452',
|
||||||
|
'#9a60b4'
|
||||||
|
];
|
||||||
|
|
||||||
// ==================== 工具函数 ====================
|
// ==================== 工具函数 ====================
|
||||||
|
|
||||||
@ -84,7 +103,10 @@ const generateMockData = () => {
|
|||||||
const month = String(i + 1).padStart(2, '0');
|
const month = String(i + 1).padStart(2, '0');
|
||||||
// 模拟季节性变化:夏季高,冬季低
|
// 模拟季节性变化:夏季高,冬季低
|
||||||
// 正弦波模拟:峰值在7-8月,谷值在1-2月
|
// 正弦波模拟:峰值在7-8月,谷值在1-2月
|
||||||
const temp = baseTemp + Math.sin((i / 12) * Math.PI * 2 - Math.PI / 2) * 10 + Math.random() * 2;
|
const temp =
|
||||||
|
baseTemp +
|
||||||
|
Math.sin((i / 12) * Math.PI * 2 - Math.PI / 2) * 10 +
|
||||||
|
Math.random() * 2;
|
||||||
|
|
||||||
data.push({
|
data.push({
|
||||||
dt: `2024-${month}`,
|
dt: `2024-${month}`,
|
||||||
@ -151,12 +173,17 @@ const updateChart = () => {
|
|||||||
let regx = /\(([^()]+?)\)/;
|
let regx = /\(([^()]+?)\)/;
|
||||||
let unit = seriesName.match(regx);
|
let unit = seriesName.match(regx);
|
||||||
// 数据保留一位小数
|
// 数据保留一位小数
|
||||||
const finalValue = item.value !== undefined && item.value !== null
|
const finalValue =
|
||||||
|
item.value !== undefined && item.value !== null
|
||||||
? Number(item.value).toFixed(1)
|
? Number(item.value).toFixed(1)
|
||||||
: '-';
|
: '-';
|
||||||
|
|
||||||
if (item.value !== undefined && item.value !== null) {
|
if (item.value !== undefined && item.value !== null) {
|
||||||
res += `<span style="background: ${item.color}; height:10px; width: 10px; border-radius: 50%;display: inline-block;margin-right:10px;"></span> ${item.seriesName} ${finalValue}${unit?.[1] || ''} <br/>`;
|
res += `<span style="background: ${
|
||||||
|
item.color
|
||||||
|
}; height:10px; width: 10px; border-radius: 50%;display: inline-block;margin-right:10px;"></span> ${
|
||||||
|
item.seriesName
|
||||||
|
} ${finalValue}${unit?.[1] || ''} <br/>`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
@ -242,14 +269,15 @@ const updateChart = () => {
|
|||||||
nameGap: 8, // 与轴线距离 20px
|
nameGap: 8, // 与轴线距离 20px
|
||||||
nameTextStyle: {
|
nameTextStyle: {
|
||||||
color: '#333333',
|
color: '#333333',
|
||||||
fontSize: 12,
|
fontSize: 12
|
||||||
|
|
||||||
},
|
},
|
||||||
// 计算 Y 轴范围:最小值向下取整,最大值向上取整
|
// 计算 Y 轴范围:最小值向下取整,最大值向上取整
|
||||||
min: hasData && echartsData.value.length > 0
|
min:
|
||||||
|
hasData && echartsData.value.length > 0
|
||||||
? Math.floor(Math.min(...echartsData.value.map(item => item.wt)))
|
? Math.floor(Math.min(...echartsData.value.map(item => item.wt)))
|
||||||
: undefined,
|
: undefined,
|
||||||
max: hasData && echartsData.value.length > 0
|
max:
|
||||||
|
hasData && echartsData.value.length > 0
|
||||||
? Math.ceil(Math.max(...echartsData.value.map(item => item.wt)))
|
? Math.ceil(Math.max(...echartsData.value.map(item => item.wt)))
|
||||||
: undefined,
|
: undefined,
|
||||||
// 确保显示刻度和标签,保持整数
|
// 确保显示刻度和标签,保持整数
|
||||||
@ -281,7 +309,9 @@ const updateChart = () => {
|
|||||||
series: [
|
series: [
|
||||||
{
|
{
|
||||||
name: _legendData[0],
|
name: _legendData[0],
|
||||||
data: hasData ? (echartsData.value?.map((item: any) => item.wt) || []) : [],
|
data: hasData
|
||||||
|
? echartsData.value?.map((item: any) => item.wt) || []
|
||||||
|
: [],
|
||||||
type: 'line',
|
type: 'line',
|
||||||
smooth: true,
|
smooth: true,
|
||||||
symbol: 'circle',
|
symbol: 'circle',
|
||||||
@ -328,11 +358,15 @@ const handleDataPointClick = (params: any) => {
|
|||||||
索引: dataIndex
|
索引: dataIndex
|
||||||
});
|
});
|
||||||
modelStore.modalVisible = true;
|
modelStore.modalVisible = true;
|
||||||
modelStore.params.sttp = "WT";
|
modelStore.params.sttp = 'WT';
|
||||||
modelStore.title = '水温监测';
|
modelStore.title = '水温监测';
|
||||||
modelStore.params.stcd = dataItem.stcd;
|
modelStore.params.stcd = dataItem.stcd;
|
||||||
modelStore.showStcdSelector = true
|
modelStore.showStcdSelector = true;
|
||||||
modelStore.stcdOptions = select.value.options
|
modelStore.stcdOptions = select.value.options;
|
||||||
|
modelStore.filter.rangeTm = [
|
||||||
|
dayjs(dataItem.dt).startOf('day').format('YYYY-MM-DD HH:mm:ss'),
|
||||||
|
dayjs(dataItem.dt).endOf('day').format('YYYY-MM-DD HH:mm:ss')
|
||||||
|
];
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -358,7 +392,11 @@ const loadData = async () => {
|
|||||||
* @param endDate 结束日期 (格式: YYYY-MM-DD HH:mm:ss)
|
* @param endDate 结束日期 (格式: YYYY-MM-DD HH:mm:ss)
|
||||||
* @param stcd 测站代码
|
* @param stcd 测站代码
|
||||||
*/
|
*/
|
||||||
const getChartData = async (startDate: string, endDate: string, stcd: string) => {
|
const getChartData = async (
|
||||||
|
startDate: string,
|
||||||
|
endDate: string,
|
||||||
|
stcd: string
|
||||||
|
) => {
|
||||||
try {
|
try {
|
||||||
console.log('=== 开始加载图表数据 ===');
|
console.log('=== 开始加载图表数据 ===');
|
||||||
console.log('请求参数:', { startDate, endDate, stcd });
|
console.log('请求参数:', { startDate, endDate, stcd });
|
||||||
@ -370,31 +408,31 @@ const getChartData = async (startDate: string, endDate: string, stcd: string) =>
|
|||||||
// TODO: 实现API调用逻辑
|
// TODO: 实现API调用逻辑
|
||||||
// 参考选中的代码结构,构建请求参数
|
// 参考选中的代码结构,构建请求参数
|
||||||
const params = {
|
const params = {
|
||||||
"filter": {
|
filter: {
|
||||||
"logic": "and",
|
logic: 'and',
|
||||||
"filters": [
|
filters: [
|
||||||
{
|
{
|
||||||
"field": "DT",
|
field: 'DT',
|
||||||
"operator": "gte",
|
operator: 'gte',
|
||||||
"value": startDate // 例如: "2026-05-01 00:00:00"
|
value: startDate // 例如: "2026-05-01 00:00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"field": "DT",
|
field: 'DT',
|
||||||
"operator": "lte",
|
operator: 'lte',
|
||||||
"value": endDate // 例如: "2026-06-30 23:59:59"
|
value: endDate // 例如: "2026-06-30 23:59:59"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"field": "stcd",
|
field: 'stcd',
|
||||||
"operator": "eq",
|
operator: 'eq',
|
||||||
"dataType": "string",
|
dataType: 'string',
|
||||||
"value": stcd
|
value: stcd
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"sort": [
|
sort: [
|
||||||
{
|
{
|
||||||
"field": "dt",
|
field: 'dt',
|
||||||
"dir": "asc"
|
dir: 'asc'
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
@ -456,7 +494,6 @@ const getChartData = async (startDate: string, endDate: string, stcd: string) =>
|
|||||||
await nextTick();
|
await nextTick();
|
||||||
updateChart();
|
updateChart();
|
||||||
console.log('✅ 图表数据加载完成');
|
console.log('✅ 图表数据加载完成');
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('获取图表数据失败:', error);
|
console.error('获取图表数据失败:', error);
|
||||||
hasData.value = false;
|
hasData.value = false;
|
||||||
@ -501,13 +538,18 @@ const convertDateRange = (dateRange: string[]) => {
|
|||||||
// 需要处理月份天数边界问题(如今天是31号,但2月只有28天)
|
// 需要处理月份天数边界问题(如今天是31号,但2月只有28天)
|
||||||
const startMonthLastDay = new Date(fullStartYear, startMonth, 0).getDate();
|
const startMonthLastDay = new Date(fullStartYear, startMonth, 0).getDate();
|
||||||
const actualStartDay = Math.min(currentDay, startMonthLastDay);
|
const actualStartDay = Math.min(currentDay, startMonthLastDay);
|
||||||
const startDate = `${fullStartYear}-${String(startMonth).padStart(2, '0')}-${String(actualStartDay).padStart(2, '0')} 00:00:00`;
|
const startDate = `${fullStartYear}-${String(startMonth).padStart(
|
||||||
|
2,
|
||||||
|
'0'
|
||||||
|
)}-${String(actualStartDay).padStart(2, '0')} 00:00:00`;
|
||||||
|
|
||||||
// 结束日期:基于第二个值的年月 + 今天的日期 + 23:59:59
|
// 结束日期:基于第二个值的年月 + 今天的日期 + 23:59:59
|
||||||
// 同样需要处理月份天数边界问题
|
// 同样需要处理月份天数边界问题
|
||||||
const endMonthLastDay = new Date(fullEndYear, endMonth, 0).getDate();
|
const endMonthLastDay = new Date(fullEndYear, endMonth, 0).getDate();
|
||||||
const actualEndDay = Math.min(currentDay, endMonthLastDay);
|
const actualEndDay = Math.min(currentDay, endMonthLastDay);
|
||||||
const endDate = `${fullEndYear}-${String(endMonth).padStart(2, '0')}-${String(actualEndDay).padStart(2, '0')} 23:59:59`;
|
const endDate = `${fullEndYear}-${String(endMonth).padStart(2, '0')}-${String(
|
||||||
|
actualEndDay
|
||||||
|
).padStart(2, '0')} 23:59:59`;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
startDate,
|
startDate,
|
||||||
@ -515,7 +557,7 @@ const convertDateRange = (dateRange: string[]) => {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const handlePanelChange1 = async (data) => {
|
const handlePanelChange1 = async data => {
|
||||||
console.log('=== 用户交互触发 ===');
|
console.log('=== 用户交互触发 ===');
|
||||||
console.log('当前所有控件状态:', data);
|
console.log('当前所有控件状态:', data);
|
||||||
// scopeDate: ['26-05', '26-06']
|
// scopeDate: ['26-05', '26-06']
|
||||||
@ -543,67 +585,63 @@ const handlePanelChange1 = async (data) => {
|
|||||||
select: data.select
|
select: data.select
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
//获取水温监测下拉框
|
//获取水温监测下拉框
|
||||||
const getselsectData = async () => {
|
const getselsectData = async () => {
|
||||||
let params = {
|
let params = {
|
||||||
"filter": {
|
filter: {
|
||||||
"logic": "and",
|
logic: 'and',
|
||||||
"filters": [
|
filters: [
|
||||||
baseid.value != 'all' ? {
|
baseid.value != 'all'
|
||||||
"field": "baseId",
|
? {
|
||||||
"operator": "eq",
|
field: 'baseId',
|
||||||
"dataType": "string",
|
operator: 'eq',
|
||||||
"value": baseid.value
|
dataType: 'string',
|
||||||
} : null,
|
value: baseid.value
|
||||||
|
}
|
||||||
|
: null,
|
||||||
{
|
{
|
||||||
"field": "fhstcd",
|
field: 'fhstcd',
|
||||||
"operator": "isnotnull"
|
operator: 'isnotnull'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"logic": "or",
|
logic: 'or',
|
||||||
"filters": [
|
filters: [
|
||||||
{
|
{
|
||||||
"field": "sttpCode",
|
field: 'sttpCode',
|
||||||
"operator": "eq",
|
operator: 'eq',
|
||||||
"dataType": "string",
|
dataType: 'string',
|
||||||
"value": "WTRV"
|
value: 'WTRV'
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
].filter(Boolean)
|
].filter(Boolean)
|
||||||
},
|
},
|
||||||
"select": [
|
select: ['stcd', 'stnm', 'fhstcd', 'fhstnm', 'sttpCode']
|
||||||
"stcd",
|
};
|
||||||
"stnm",
|
let res = await msstbprptGetKendoList(params);
|
||||||
"fhstcd",
|
console.log(res);
|
||||||
"fhstnm",
|
|
||||||
"sttpCode"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
let res = await msstbprptGetKendoList(params)
|
|
||||||
console.log(res)
|
|
||||||
if (!res?.data?.data || res.data.data.length == 0) {
|
if (!res?.data?.data || res.data.data.length == 0) {
|
||||||
select.value.value = ''
|
select.value.value = '';
|
||||||
select.value.options = []
|
select.value.options = [];
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
select.value.value = res.data.data[0].stcd
|
select.value.value = res.data.data[0].stcd;
|
||||||
select.value.options = res.data.data.map((item: any) => {
|
select.value.options = res.data.data.map((item: any) => {
|
||||||
return {
|
return {
|
||||||
label: item.stnm,
|
label: item.stnm,
|
||||||
value: item.stcd
|
value: item.stcd
|
||||||
}
|
};
|
||||||
})
|
});
|
||||||
// debugger
|
// debugger
|
||||||
};
|
};
|
||||||
////
|
////
|
||||||
watch(
|
watch(
|
||||||
() => JidiSelectEventStore.selectedItem,
|
() => JidiSelectEventStore.selectedItem,
|
||||||
(newVal) => {
|
newVal => {
|
||||||
baseid.value = newVal.wbsCode;
|
baseid.value = newVal.wbsCode;
|
||||||
getselsectData()
|
getselsectData();
|
||||||
},
|
},
|
||||||
{ deep: true, immediate: true }
|
{ deep: true, immediate: true }
|
||||||
);
|
);
|
||||||
|
|||||||
@ -298,8 +298,12 @@ const handleExport = async () => {};
|
|||||||
const handleViewDetail = (record: any) => {
|
const handleViewDetail = (record: any) => {
|
||||||
modelStore.modalVisible = true;
|
modelStore.modalVisible = true;
|
||||||
modelStore.params.sttp = 'WT';
|
modelStore.params.sttp = 'WT';
|
||||||
modelStore.title = record.stnm ;
|
modelStore.title = record.stnm;
|
||||||
modelStore.params.stcd = record.stcd;
|
modelStore.params.stcd = record.stcd;
|
||||||
|
modelStore.filter.rangeTm = [
|
||||||
|
dayjs(record.dt).startOf('month').format('YYYY-MM-DD HH:mm:ss'),
|
||||||
|
dayjs(record.dt).endOf('month').format('YYYY-MM-DD HH:mm:ss')
|
||||||
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
// 关闭弹窗
|
// 关闭弹窗
|
||||||
|
|||||||
@ -1,6 +1,10 @@
|
|||||||
<template>
|
<template>
|
||||||
<SidePanelItem title="水温年内分布" :moreSelect="select" :datetimePicker="datetimePicker"
|
<SidePanelItem
|
||||||
@update-values="handlePanelChange1">
|
title="水温年内分布"
|
||||||
|
:moreSelect="select"
|
||||||
|
:datetimePicker="datetimePicker"
|
||||||
|
@update-values="handlePanelChange1"
|
||||||
|
>
|
||||||
<a-spin :spinning="loading" tip="加载中...">
|
<a-spin :spinning="loading" tip="加载中...">
|
||||||
<div v-show="!loading && showemit" class="water-temp-chart-container">
|
<div v-show="!loading && showemit" class="water-temp-chart-container">
|
||||||
<div ref="chartRef" class="chart-wrapper"></div>
|
<div ref="chartRef" class="chart-wrapper"></div>
|
||||||
@ -12,39 +16,52 @@
|
|||||||
</SidePanelItem>
|
</SidePanelItem>
|
||||||
|
|
||||||
<!-- 数据点详情弹框 -->
|
<!-- 数据点详情弹框 -->
|
||||||
<a-modal v-model:open="modalVisible" title="水温数据详情" :footer="null" width="1536px" @cancel="handleModalClose">
|
<a-modal
|
||||||
<YaerAverage :tm="`${datetimePicker.value}-${String(currentData.monthInt).padStart(2, '0')}-01 00:00:00`"
|
v-model:open="modalVisible"
|
||||||
:dataDimensionVal="baseid" :stcd="select.value" />
|
title="水温数据详情"
|
||||||
|
:footer="null"
|
||||||
|
width="1536px"
|
||||||
|
@cancel="handleModalClose"
|
||||||
|
>
|
||||||
|
<YaerAverage
|
||||||
|
:tm="`${datetimePicker.value}-${String(currentData.monthInt).padStart(
|
||||||
|
2,
|
||||||
|
'0'
|
||||||
|
)}-01 00:00:00`"
|
||||||
|
:dataDimensionVal="baseid"
|
||||||
|
:stcd="select.value"
|
||||||
|
/>
|
||||||
</a-modal>
|
</a-modal>
|
||||||
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, onMounted, onBeforeUnmount, watch } from 'vue'
|
import { ref, onMounted, onBeforeUnmount, watch } from 'vue';
|
||||||
import * as echarts from 'echarts'
|
import * as echarts from 'echarts';
|
||||||
import SidePanelItem from '@/components/SidePanelItem/index.vue';
|
import SidePanelItem from '@/components/SidePanelItem/index.vue';
|
||||||
import { getVmsstbprpt, yearListGetKendoListCust } from "@/api/sw";
|
import { getVmsstbprpt, yearListGetKendoListCust } from '@/api/sw';
|
||||||
import { useJidiSelectEventStore } from "@/store/modules/jidiSelectEvent";
|
import { useJidiSelectEventStore } from '@/store/modules/jidiSelectEvent';
|
||||||
import YaerAverage from "./TwoLayers/yaerAverage.vue"
|
import YaerAverage from './TwoLayers/yaerAverage.vue';
|
||||||
|
|
||||||
|
import { MapClass } from '@/components/gis/map.class';
|
||||||
|
|
||||||
const JidiSelectEventStore = useJidiSelectEventStore();
|
const JidiSelectEventStore = useJidiSelectEventStore();
|
||||||
// 水温年内分布数据(响应式)
|
// 水温年内分布数据(响应式)
|
||||||
const waterTempData = ref<any[]>([])
|
const waterTempData = ref<any[]>([]);
|
||||||
const loading = ref(false)
|
const loading = ref(false);
|
||||||
const showemit = ref(true)
|
const showemit = ref(true);
|
||||||
// 弹框相关状态
|
// 弹框相关状态
|
||||||
const modalVisible = ref(false)
|
const modalVisible = ref(false);
|
||||||
const currentData = ref<any>({})
|
const currentData = ref<any>({});
|
||||||
|
|
||||||
|
const chartRef = ref<HTMLElement | null>(null);
|
||||||
const chartRef = ref<HTMLElement | null>(null)
|
let chartInstance: echarts.ECharts | null = null;
|
||||||
let chartInstance: echarts.ECharts | null = null
|
|
||||||
// 选择器配置
|
// 选择器配置
|
||||||
const select = ref({
|
const select = ref({
|
||||||
show: true,
|
show: true,
|
||||||
value: '',
|
value: '',
|
||||||
options: [],
|
options: [],
|
||||||
picker: undefined,
|
picker: undefined,
|
||||||
format: undefined,
|
format: undefined
|
||||||
});
|
});
|
||||||
|
|
||||||
// 日期选择器配置
|
// 日期选择器配置
|
||||||
@ -55,110 +72,123 @@ const datetimePicker = ref({
|
|||||||
const year = now.getFullYear();
|
const year = now.getFullYear();
|
||||||
return `${year}`;
|
return `${year}`;
|
||||||
})(),
|
})(),
|
||||||
format: "YYYY",
|
format: 'YYYY',
|
||||||
picker: "year" as const,
|
picker: 'year' as const,
|
||||||
options: [],
|
options: []
|
||||||
});
|
});
|
||||||
const transUnit = (value: number | null) => {
|
const transUnit = (value: number | null) => {
|
||||||
if (value === null) return null
|
if (value === null) return null;
|
||||||
return value
|
return value;
|
||||||
}
|
};
|
||||||
|
|
||||||
const getUnitConfigByCode = (_code: string, _type: string) => {
|
const getUnitConfigByCode = (_code: string, _type: string) => {
|
||||||
return {
|
return {
|
||||||
unit: '℃'
|
unit: '℃'
|
||||||
}
|
};
|
||||||
}
|
};
|
||||||
|
|
||||||
const getColorByCodeAndType = (_code: string[], _typeKey: string[]) => {
|
const getColorByCodeAndType = (_code: string[], _typeKey: string[]) => {
|
||||||
return ['#4b79ab', '#78c300']
|
return ['#4b79ab', '#78c300'];
|
||||||
}
|
};
|
||||||
|
|
||||||
// 处理图表数据点点击事件
|
// 处理图表数据点点击事件
|
||||||
const handleChartClick = (params: any) => {
|
const handleChartClick = (params: any) => {
|
||||||
if (!params || !String(params.dataIndex)) return
|
if (!params || !String(params.dataIndex)) return;
|
||||||
|
|
||||||
// 获取点击的数据点索引
|
// 获取点击的数据点索引
|
||||||
const dataIndex = params.dataIndex
|
const dataIndex = params.dataIndex;
|
||||||
const dataItem = waterTempData.value[dataIndex]
|
const dataItem = waterTempData.value[dataIndex];
|
||||||
|
|
||||||
if (dataItem) {
|
if (dataItem) {
|
||||||
currentData.value = {
|
currentData.value = {
|
||||||
...dataItem,
|
...dataItem,
|
||||||
stnm: select.value.options.find((opt: any) =>
|
stnm:
|
||||||
opt.children?.some((child: any) => child.value === select.value.value)
|
select.value.options
|
||||||
)?.children?.find((child: any) => child.value === select.value.value)?.title || '未知站点'
|
.find((opt: any) =>
|
||||||
|
opt.children?.some(
|
||||||
|
(child: any) => child.value === select.value.value
|
||||||
|
)
|
||||||
|
)
|
||||||
|
?.children?.find((child: any) => child.value === select.value.value)
|
||||||
|
?.title || '未知站点'
|
||||||
|
};
|
||||||
|
modalVisible.value = true;
|
||||||
}
|
}
|
||||||
modalVisible.value = true
|
};
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 关闭弹框
|
// 关闭弹框
|
||||||
const handleModalClose = () => {
|
const handleModalClose = () => {
|
||||||
modalVisible.value = false
|
modalVisible.value = false;
|
||||||
currentData.value = {}
|
currentData.value = {};
|
||||||
}
|
};
|
||||||
|
|
||||||
//下拉框数据选择
|
//下拉框数据选择
|
||||||
const getselectData = async () => {
|
const getselectData = async () => {
|
||||||
try {
|
try {
|
||||||
const params = {
|
const params = {
|
||||||
filter: {
|
filter: {
|
||||||
logic: "and",
|
logic: 'and',
|
||||||
filters: [
|
filters: [
|
||||||
{
|
{
|
||||||
field: "sttpFullPath",
|
field: 'sttpFullPath',
|
||||||
operator: "contains",
|
operator: 'contains',
|
||||||
value: "ENV,ENVM,WT,"
|
value: 'ENV,ENVM,WT,'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: "sttpCode",
|
field: 'sttpCode',
|
||||||
operator: "eq",
|
operator: 'eq',
|
||||||
value: "WTRV"
|
value: 'WTRV'
|
||||||
},
|
},
|
||||||
baseid.value !== 'all' ? {
|
baseid.value !== 'all'
|
||||||
field: "baseId",
|
? {
|
||||||
operator: "contains",
|
field: 'baseId',
|
||||||
|
operator: 'contains',
|
||||||
value: baseid.value
|
value: baseid.value
|
||||||
} : null
|
}
|
||||||
|
: null
|
||||||
].filter(Boolean)
|
].filter(Boolean)
|
||||||
},
|
},
|
||||||
select: [
|
select: [
|
||||||
"stcd", "stnm", "lgtd", "lttd",
|
'stcd',
|
||||||
"baseId", "baseName", "siteStepSort"
|
'stnm',
|
||||||
|
'lgtd',
|
||||||
|
'lttd',
|
||||||
|
'baseId',
|
||||||
|
'baseName',
|
||||||
|
'siteStepSort'
|
||||||
]
|
]
|
||||||
}
|
};
|
||||||
|
|
||||||
const res = await getVmsstbprpt(params)
|
const res = await getVmsstbprpt(params);
|
||||||
|
|
||||||
// if (!res?.data?.length) {
|
// if (!res?.data?.length) {
|
||||||
// select.value.options = []
|
// select.value.options = []
|
||||||
// return
|
// return
|
||||||
// }
|
// }
|
||||||
let data = res?.data?.date || res?.data || []
|
let data = res?.data?.date || res?.data || [];
|
||||||
// 设置默认选中值(第一个站点的 stcd)
|
// 设置默认选中值(第一个站点的 stcd)
|
||||||
if (res.data) {
|
if (res.data) {
|
||||||
select.value.value = data?.data[0]?.stcd
|
select.value.value = data?.data[0]?.stcd;
|
||||||
} else {
|
} else {
|
||||||
select.value.value = ''
|
select.value.value = '';
|
||||||
}
|
}
|
||||||
// 按 baseId 分组
|
// 按 baseId 分组
|
||||||
const dataMapNameMap: Record<string, any[]> = {}
|
const dataMapNameMap: Record<string, any[]> = {};
|
||||||
data.data.forEach((item: any) => {
|
data.data.forEach((item: any) => {
|
||||||
const { baseId } = item
|
const { baseId } = item;
|
||||||
if (dataMapNameMap[baseId]) {
|
if (dataMapNameMap[baseId]) {
|
||||||
dataMapNameMap[baseId].push(item)
|
dataMapNameMap[baseId].push(item);
|
||||||
} else {
|
} else {
|
||||||
dataMapNameMap[baseId] = [item]
|
dataMapNameMap[baseId] = [item];
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
|
|
||||||
// 构建树形结构
|
// 构建树形结构
|
||||||
const dataMapNameArr: any[] = []
|
const dataMapNameArr: any[] = [];
|
||||||
const otherArr: any[] = []
|
const otherArr: any[] = [];
|
||||||
|
|
||||||
Object.keys(dataMapNameMap).forEach((baseId: string) => {
|
Object.keys(dataMapNameMap).forEach((baseId: string) => {
|
||||||
const stations = dataMapNameMap[baseId]
|
const stations = dataMapNameMap[baseId];
|
||||||
|
|
||||||
const treeItem = {
|
const treeItem = {
|
||||||
title: stations[0]?.baseName,
|
title: stations[0]?.baseName,
|
||||||
@ -166,31 +196,34 @@ const getselectData = async () => {
|
|||||||
selectable: false,
|
selectable: false,
|
||||||
children: stations
|
children: stations
|
||||||
.sort((a, b) => a.siteStepSort - b.siteStepSort)
|
.sort((a, b) => a.siteStepSort - b.siteStepSort)
|
||||||
.map((station) => ({
|
.map(station => ({
|
||||||
title: station.stnm,
|
title: station.stnm,
|
||||||
value: station.stcd,
|
value: station.stcd,
|
||||||
lgtd: station.lgtd,
|
lgtd: station.lgtd,
|
||||||
lttd: station.lttd
|
lttd: station.lttd
|
||||||
}))
|
}))
|
||||||
}
|
};
|
||||||
|
|
||||||
// 区分普通基地和 other 基地
|
// 区分普通基地和 other 基地
|
||||||
if (+baseId) {
|
if (+baseId) {
|
||||||
dataMapNameArr.push(treeItem)
|
dataMapNameArr.push(treeItem);
|
||||||
} else if (baseId === 'other') {
|
} else if (baseId === 'other') {
|
||||||
otherArr.push(treeItem)
|
otherArr.push(treeItem);
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
|
|
||||||
dataMapNameArr.sort((a, b) => {
|
dataMapNameArr.sort((a, b) => {
|
||||||
const indexA = JidiSelectEventStore.jidiData.findIndex((item: any) => item.wbsCode === a.value)
|
const indexA = JidiSelectEventStore.jidiData.findIndex(
|
||||||
const indexB = JidiSelectEventStore.jidiData.findIndex((item: any) => item.wbsCode === b.value)
|
(item: any) => item.wbsCode === a.value
|
||||||
return indexA - indexB
|
);
|
||||||
})
|
const indexB = JidiSelectEventStore.jidiData.findIndex(
|
||||||
|
(item: any) => item.wbsCode === b.value
|
||||||
|
);
|
||||||
|
return indexA - indexB;
|
||||||
|
});
|
||||||
|
|
||||||
// 合并结果并设置选项
|
// 合并结果并设置选项
|
||||||
select.value.options = [...dataMapNameArr, ...otherArr]
|
select.value.options = [...dataMapNameArr, ...otherArr];
|
||||||
|
|
||||||
|
|
||||||
// if (baseid.value == 'all') {
|
// if (baseid.value == 'all') {
|
||||||
// select.value.value = '008640202300001021'
|
// select.value.value = '008640202300001021'
|
||||||
@ -198,104 +231,107 @@ const getselectData = async () => {
|
|||||||
// else if (data.data.length > 0) {
|
// else if (data.data.length > 0) {
|
||||||
// select.value.value = data.data[0].stcd
|
// select.value.value = data.data[0].stcd
|
||||||
// }
|
// }
|
||||||
getshuiwenList()
|
getshuiwenList();
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('获取站点数据失败:', error)
|
console.error('获取站点数据失败:', error);
|
||||||
select.value.options = []
|
select.value.options = [];
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
//水温年内分布
|
//水温年内分布
|
||||||
const getshuiwenList = async () => {
|
const getshuiwenList = async () => {
|
||||||
if (loading.value) return; // 防止重复请求
|
if (loading.value) return; // 防止重复请求
|
||||||
if (!select.value.value) {
|
if (!select.value.value) {
|
||||||
showemit.value = false
|
showemit.value = false;
|
||||||
waterTempData.value = []
|
waterTempData.value = [];
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
|
|
||||||
const params = {
|
const params = {
|
||||||
"filter": {
|
filter: {
|
||||||
"logic": "and",
|
logic: 'and',
|
||||||
"filters": [
|
filters: [
|
||||||
{
|
{
|
||||||
"field": "stcd",
|
field: 'stcd',
|
||||||
"operator": "eq",
|
operator: 'eq',
|
||||||
"dataType": "string",
|
dataType: 'string',
|
||||||
"value": select.value.value
|
value: select.value.value
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"field": "year",
|
field: 'year',
|
||||||
"operator": "eq",
|
operator: 'eq',
|
||||||
"dataType": "string",
|
dataType: 'string',
|
||||||
"value": datetimePicker.value.value
|
value: datetimePicker.value.value
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"sort": [
|
sort: [
|
||||||
{
|
{
|
||||||
"field": "month",
|
field: 'month',
|
||||||
"dir": "asc"
|
dir: 'asc'
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
let res = await yearListGetKendoListCust(params)
|
let res = await yearListGetKendoListCust(params);
|
||||||
let data = res?.data?.data || res?.data || []
|
let data = res?.data?.data || res?.data || [];
|
||||||
|
|
||||||
// 处理空数据情况
|
// 处理空数据情况
|
||||||
if (data.length === 0) {
|
if (data.length === 0) {
|
||||||
showemit.value = false
|
showemit.value = false;
|
||||||
waterTempData.value = []
|
waterTempData.value = [];
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
showemit.value = true
|
showemit.value = true;
|
||||||
|
|
||||||
// 更新响应式数据
|
// 更新响应式数据
|
||||||
waterTempData.value = data
|
waterTempData.value = data;
|
||||||
|
|
||||||
// 重新渲染图表
|
// 重新渲染图表
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
if (!chartInstance) {
|
if (!chartInstance) {
|
||||||
initChart()
|
initChart();
|
||||||
} else {
|
} else {
|
||||||
const option = getChartOption()
|
const option = getChartOption();
|
||||||
chartInstance.setOption(option, true)
|
chartInstance.setOption(option, true);
|
||||||
}
|
}
|
||||||
// 强制重绘,确保尺寸正确
|
// 强制重绘,确保尺寸正确
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
chartInstance?.resize()
|
chartInstance?.resize();
|
||||||
}, 100)
|
}, 100);
|
||||||
}, 50)
|
}, 50);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('获取水温数据失败:', error)
|
console.error('获取水温数据失败:', error);
|
||||||
showemit.value = false
|
showemit.value = false;
|
||||||
waterTempData.value = []
|
waterTempData.value = [];
|
||||||
} finally {
|
} finally {
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
const getChartOption = () => {
|
const getChartOption = () => {
|
||||||
const { unit } = getUnitConfigByCode('Other', 'ACTUALTEMP')
|
const { unit } = getUnitConfigByCode('Other', 'ACTUALTEMP');
|
||||||
const legendData = ['实测值', '天然']
|
const legendData = ['实测值', '天然'];
|
||||||
const xData: string[] = []
|
const xData: string[] = [];
|
||||||
const actualData: (number | null)[] = []
|
const actualData: (number | null)[] = [];
|
||||||
const naturalData: (number | null)[] = []
|
const naturalData: (number | null)[] = [];
|
||||||
|
|
||||||
// 使用响应式数据waterTempData替代静态data
|
// 使用响应式数据waterTempData替代静态data
|
||||||
waterTempData.value.forEach((item: any) => {
|
waterTempData.value.forEach((item: any) => {
|
||||||
xData.push(`${item.monthInt}月`)
|
xData.push(`${item.monthInt}月`);
|
||||||
actualData.push(item.actualTemp === null ? null : transUnit(item.actualTemp))
|
actualData.push(
|
||||||
naturalData.push(item.naturalTemp === null ? null : transUnit(item.naturalTemp))
|
item.actualTemp === null ? null : transUnit(item.actualTemp)
|
||||||
})
|
);
|
||||||
|
naturalData.push(
|
||||||
|
item.naturalTemp === null ? null : transUnit(item.naturalTemp)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
const code = ['Other']
|
const code = ['Other'];
|
||||||
const typeKey = ['ACTUALTEMP', 'NATURALTEMP']
|
const typeKey = ['ACTUALTEMP', 'NATURALTEMP'];
|
||||||
const colors = getColorByCodeAndType(code, typeKey)
|
const colors = getColorByCodeAndType(code, typeKey);
|
||||||
|
|
||||||
const options: any = {
|
const options: any = {
|
||||||
color: colors,
|
color: colors,
|
||||||
@ -313,7 +349,8 @@ const getChartOption = () => {
|
|||||||
params.forEach((item: any) => {
|
params.forEach((item: any) => {
|
||||||
result += `<div style="display: flex; align-items: center; margin: 4px 0;">`;
|
result += `<div style="display: flex; align-items: center; margin: 4px 0;">`;
|
||||||
result += `<span style="display: inline-block; width: 10px; height: 10px; border-radius: 50%; background-color: ${item.color}; margin-right: 8px;"></span>`;
|
result += `<span style="display: inline-block; width: 10px; height: 10px; border-radius: 50%; background-color: ${item.color}; margin-right: 8px;"></span>`;
|
||||||
const displayValue = item.value !== null && item.value !== undefined ? item.value: '-';
|
const displayValue =
|
||||||
|
item.value !== null && item.value !== undefined ? item.value : '-';
|
||||||
result += `<span>${item.seriesName} ${displayValue}${unit}</span>`;
|
result += `<span>${item.seriesName} ${displayValue}${unit}</span>`;
|
||||||
result += `</div>`;
|
result += `</div>`;
|
||||||
});
|
});
|
||||||
@ -437,87 +474,95 @@ const getChartOption = () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
};
|
||||||
|
|
||||||
return options
|
return options;
|
||||||
}
|
};
|
||||||
|
|
||||||
const initChart = async () => {
|
const initChart = async () => {
|
||||||
// 延迟初始化,确保容器已完成渲染
|
// 延迟初始化,确保容器已完成渲染
|
||||||
await new Promise(resolve => setTimeout(resolve, 100))
|
await new Promise(resolve => setTimeout(resolve, 100));
|
||||||
|
|
||||||
if (!chartRef.value) {
|
if (!chartRef.value) {
|
||||||
console.error('图表容器未找到')
|
console.error('图表容器未找到');
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (!chartInstance) {
|
if (!chartInstance) {
|
||||||
chartInstance = echarts.init(chartRef.value)
|
chartInstance = echarts.init(chartRef.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
const option = getChartOption()
|
const option = getChartOption();
|
||||||
chartInstance.setOption(option, false)
|
chartInstance.setOption(option, false);
|
||||||
|
|
||||||
// 添加点击事件监听
|
// 添加点击事件监听
|
||||||
chartInstance.off('click')
|
chartInstance.off('click');
|
||||||
chartInstance.on('click', handleChartClick)
|
chartInstance.on('click', handleChartClick);
|
||||||
|
|
||||||
// 强制重绘,确保图表尺寸正确
|
// 强制重绘,确保图表尺寸正确
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
chartInstance?.resize()
|
chartInstance?.resize();
|
||||||
}, 100)
|
}, 100);
|
||||||
|
|
||||||
window.addEventListener('resize', handleResize)
|
window.addEventListener('resize', handleResize);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('图表初始化失败:', error)
|
console.error('图表初始化失败:', error);
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
const handleResize = () => {
|
const handleResize = () => {
|
||||||
if (chartInstance) {
|
if (chartInstance) {
|
||||||
chartInstance.resize()
|
chartInstance.resize();
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
// 初始化图表
|
// 初始化图表
|
||||||
initChart()
|
initChart();
|
||||||
// 加载初始数据
|
// 加载初始数据
|
||||||
// if (select.value.value) {
|
// if (select.value.value) {
|
||||||
// getshuiwenList()
|
// getshuiwenList()
|
||||||
// }
|
// }
|
||||||
})
|
});
|
||||||
|
|
||||||
onBeforeUnmount(() => {
|
onBeforeUnmount(() => {
|
||||||
window.removeEventListener('resize', handleResize)
|
window.removeEventListener('resize', handleResize);
|
||||||
if (chartInstance) {
|
if (chartInstance) {
|
||||||
chartInstance.dispose()
|
chartInstance.dispose();
|
||||||
chartInstance = null
|
chartInstance = null;
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
//监听子组件的数据变化
|
//监听子组件的数据变化
|
||||||
const handlePanelChange1 = (data) => {
|
const handlePanelChange1 = (data: any, type: string) => {
|
||||||
console.log('当前所有控件状态:', data);
|
console.log('当前所有控件状态:', data);
|
||||||
|
if (type == 'click') {
|
||||||
|
const mapClass = MapClass.getInstance();
|
||||||
|
mapClass.flyTopanto(
|
||||||
|
[
|
||||||
|
Number(data.selectedNodeExtra.lgtd),
|
||||||
|
Number(data.selectedNodeExtra.lttd)
|
||||||
|
],
|
||||||
|
15
|
||||||
|
);
|
||||||
|
}
|
||||||
// 当选择器或日期变化时,重新加载图表数据
|
// 当选择器或日期变化时,重新加载图表数据
|
||||||
if (data.moreSelect || data.datetime) {
|
if (data.moreSelect || data.datetime) {
|
||||||
select.value.value = data.moreSelect
|
select.value.value = data.moreSelect;
|
||||||
datetimePicker.value.value = data.datetime
|
datetimePicker.value.value = data.datetime;
|
||||||
// debugger
|
// debugger
|
||||||
getshuiwenList()
|
getshuiwenList();
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
const baseid = ref('')
|
const baseid = ref('');
|
||||||
watch(
|
watch(
|
||||||
() => JidiSelectEventStore.selectedItem,
|
() => JidiSelectEventStore.selectedItem,
|
||||||
(newVal) => {
|
newVal => {
|
||||||
baseid.value = newVal.wbsCode;
|
baseid.value = newVal.wbsCode;
|
||||||
getselectData()
|
getselectData();
|
||||||
},
|
},
|
||||||
{ deep: true, immediate: true }
|
{ deep: true, immediate: true }
|
||||||
);
|
);
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|||||||
@ -1,8 +1,13 @@
|
|||||||
<!-- SidePanelItem.vue -->
|
<!-- SidePanelItem.vue -->
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<SidePanelItem title="沿程水质变化" :iconmap="iconmap" :select="select" :datetimePicker="datetimePicker"
|
<SidePanelItem
|
||||||
@update-values="handlePanelChange1">
|
title="沿程水质变化"
|
||||||
|
:iconmap="iconmap"
|
||||||
|
:select="select"
|
||||||
|
:datetimePicker="datetimePicker"
|
||||||
|
@update-values="handlePanelChange1"
|
||||||
|
>
|
||||||
<div class="chart-wrapper">
|
<div class="chart-wrapper">
|
||||||
<a-spin :spinning="loading">
|
<a-spin :spinning="loading">
|
||||||
<!-- 始终渲染图表容器,确保有固定宽高 -->
|
<!-- 始终渲染图表容器,确保有固定宽高 -->
|
||||||
@ -21,10 +26,13 @@ import { ref, onMounted, onBeforeUnmount, nextTick, watch } from 'vue';
|
|||||||
import * as echarts from 'echarts';
|
import * as echarts from 'echarts';
|
||||||
import type { EChartsOption } from 'echarts';
|
import type { EChartsOption } from 'echarts';
|
||||||
import SidePanelItem from '@/components/SidePanelItem/index.vue';
|
import SidePanelItem from '@/components/SidePanelItem/index.vue';
|
||||||
import { qgcGetKendoListCust, wbsbGetKendoList } from '@/api/sz'
|
import { qgcGetKendoListCust, wbsbGetKendoList } from '@/api/sz';
|
||||||
import { useJidiSelectEventStore } from "@/store/modules/jidiSelectEvent";
|
import { useJidiSelectEventStore } from '@/store/modules/jidiSelectEvent';
|
||||||
import { useModelStore } from "@/store/modules/model";
|
import { useModelStore } from '@/store/modules/model';
|
||||||
import { WATER_QUALITY_INDICATORS, type WaterQualityIndicator } from '@/modules/waterQuality/padig';
|
import {
|
||||||
|
WATER_QUALITY_INDICATORS,
|
||||||
|
type WaterQualityIndicator
|
||||||
|
} from '@/modules/waterQuality/padig';
|
||||||
const indicators = WATER_QUALITY_INDICATORS;
|
const indicators = WATER_QUALITY_INDICATORS;
|
||||||
// 定义组件名(便于调试和递归)
|
// 定义组件名(便于调试和递归)
|
||||||
defineOptions({
|
defineOptions({
|
||||||
@ -35,7 +43,7 @@ const JidiSelectEventStore = useJidiSelectEventStore();
|
|||||||
const iconmap = ref({
|
const iconmap = ref({
|
||||||
show: true,
|
show: true,
|
||||||
value: '',
|
value: '',
|
||||||
icon: 'iconfont icon-time',
|
icon: 'iconfont icon-time'
|
||||||
});
|
});
|
||||||
// ==================== 响应式数据 ====================
|
// ==================== 响应式数据 ====================
|
||||||
const chartRef = ref<HTMLElement | null>(null);
|
const chartRef = ref<HTMLElement | null>(null);
|
||||||
@ -47,8 +55,20 @@ const hasData = ref(true); // 初始化为 true,因为 initChart 会使用 moc
|
|||||||
|
|
||||||
// 获取当天早上8:00的时间
|
// 获取当天早上8:00的时间
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
const todayAtEightAM = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 8, 0, 0);
|
const todayAtEightAM = new Date(
|
||||||
const defaultValue = `${todayAtEightAM.getFullYear()}-${String(todayAtEightAM.getMonth() + 1).padStart(2, '0')}-${String(todayAtEightAM.getDate()).padStart(2, '0')} ${String(todayAtEightAM.getHours()).padStart(2, '0')}:00`;
|
now.getFullYear(),
|
||||||
|
now.getMonth(),
|
||||||
|
now.getDate(),
|
||||||
|
8,
|
||||||
|
0,
|
||||||
|
0
|
||||||
|
);
|
||||||
|
const defaultValue = `${todayAtEightAM.getFullYear()}-${String(
|
||||||
|
todayAtEightAM.getMonth() + 1
|
||||||
|
).padStart(2, '0')}-${String(todayAtEightAM.getDate()).padStart(
|
||||||
|
2,
|
||||||
|
'0'
|
||||||
|
)} ${String(todayAtEightAM.getHours()).padStart(2, '0')}:00`;
|
||||||
// 👆 注意这里分钟固定为 :00,与 timeFormat: 'HH' 配置保持一致
|
// 👆 注意这里分钟固定为 :00,与 timeFormat: 'HH' 配置保持一致
|
||||||
|
|
||||||
const datetimePicker = ref({
|
const datetimePicker = ref({
|
||||||
@ -64,14 +84,22 @@ const select = ref({
|
|||||||
value: '',
|
value: '',
|
||||||
options: [],
|
options: [],
|
||||||
picker: undefined,
|
picker: undefined,
|
||||||
format: undefined,
|
format: undefined
|
||||||
});
|
});
|
||||||
|
|
||||||
// ==================== 可见系列队列(最多2个)====================
|
// ==================== 可见系列队列(最多2个)====================
|
||||||
const visibleSeriesQueue = ref<string[]>([]);
|
const visibleSeriesQueue = ref<string[]>([]);
|
||||||
|
|
||||||
// ==================== 动态指标配置 ====================
|
// ==================== 动态指标配置 ====================
|
||||||
const indicatorConfig = ref<Array<{ key: string; name: string; unit: string; color: string; sort: number }>>([]);
|
const indicatorConfig = ref<
|
||||||
|
Array<{
|
||||||
|
key: string;
|
||||||
|
name: string;
|
||||||
|
unit: string;
|
||||||
|
color: string;
|
||||||
|
sort: number;
|
||||||
|
}>
|
||||||
|
>([]);
|
||||||
|
|
||||||
// ==================== HSL 随机颜色生成 ====================
|
// ==================== HSL 随机颜色生成 ====================
|
||||||
const generateRandomColor = (index: number): string => {
|
const generateRandomColor = (index: number): string => {
|
||||||
@ -86,11 +114,13 @@ const generateRandomColor = (index: number): string => {
|
|||||||
const lightness = 45 + Math.random() * 15;
|
const lightness = 45 + Math.random() * 15;
|
||||||
|
|
||||||
// HSL 转 RGB
|
// HSL 转 RGB
|
||||||
const c = (1 - Math.abs(2 * lightness / 100 - 1)) * saturation / 100;
|
const c = ((1 - Math.abs((2 * lightness) / 100 - 1)) * saturation) / 100;
|
||||||
const x = c * (1 - Math.abs((hue / 60) % 2 - 1));
|
const x = c * (1 - Math.abs(((hue / 60) % 2) - 1));
|
||||||
const m = lightness / 100 - c / 2;
|
const m = lightness / 100 - c / 2;
|
||||||
|
|
||||||
let r = 0, g = 0, b = 0;
|
let r = 0,
|
||||||
|
g = 0,
|
||||||
|
b = 0;
|
||||||
|
|
||||||
if (hue < 60) {
|
if (hue < 60) {
|
||||||
[r, g, b] = [c, x, 0];
|
[r, g, b] = [c, x, 0];
|
||||||
@ -123,10 +153,28 @@ const generateIndicatorConfig = (data: StationData[]) => {
|
|||||||
|
|
||||||
// 定义需要排除的元数据字段
|
// 定义需要排除的元数据字段
|
||||||
const excludeKeys = new Set([
|
const excludeKeys = new Set([
|
||||||
'stcd', 'stcd2', 'stnm', 'rstcd', 'sttp', 'tm', 'SORT',
|
'stcd',
|
||||||
'limits', 'minTm', 'wwqtg', 'wqgrd', 'wqgrdName', 'min', 'max',
|
'stcd2',
|
||||||
'_tls', 'id', 'recordUser', 'recordTime', 'modifyTime',
|
'stnm',
|
||||||
'displayRecordUser', 'departmentId', 'displayDepartment'
|
'rstcd',
|
||||||
|
'sttp',
|
||||||
|
'tm',
|
||||||
|
'SORT',
|
||||||
|
'limits',
|
||||||
|
'minTm',
|
||||||
|
'wwqtg',
|
||||||
|
'wqgrd',
|
||||||
|
'wqgrdName',
|
||||||
|
'min',
|
||||||
|
'max',
|
||||||
|
'_tls',
|
||||||
|
'id',
|
||||||
|
'recordUser',
|
||||||
|
'recordTime',
|
||||||
|
'modifyTime',
|
||||||
|
'displayRecordUser',
|
||||||
|
'departmentId',
|
||||||
|
'displayDepartment'
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// 收集所有有效指标的 key
|
// 收集所有有效指标的 key
|
||||||
@ -140,9 +188,16 @@ const generateIndicatorConfig = (data: StationData[]) => {
|
|||||||
const value = station[key as keyof typeof station];
|
const value = station[key as keyof typeof station];
|
||||||
|
|
||||||
// 检查是否为有效数值(不为 null 且不是 NaN)
|
// 检查是否为有效数值(不为 null 且不是 NaN)
|
||||||
if (value !== null && value !== undefined && typeof value === 'number' && !isNaN(value)) {
|
if (
|
||||||
|
value !== null &&
|
||||||
|
value !== undefined &&
|
||||||
|
typeof value === 'number' &&
|
||||||
|
!isNaN(value)
|
||||||
|
) {
|
||||||
// 在 indicators 中查找匹配的项(小写比较)
|
// 在 indicators 中查找匹配的项(小写比较)
|
||||||
const matchedIndicator = indicators.find(ind => ind.key.toLowerCase() === key.toLowerCase());
|
const matchedIndicator = indicators.find(
|
||||||
|
ind => ind.key.toLowerCase() === key.toLowerCase()
|
||||||
|
);
|
||||||
if (matchedIndicator) {
|
if (matchedIndicator) {
|
||||||
validKeysSet.add(matchedIndicator.key);
|
validKeysSet.add(matchedIndicator.key);
|
||||||
}
|
}
|
||||||
@ -321,7 +376,9 @@ const getChartOption = (): EChartsOption => {
|
|||||||
const value = item[config.key as keyof typeof item];
|
const value = item[config.key as keyof typeof item];
|
||||||
return typeof value === 'number' && !isNaN(value) ? value : null;
|
return typeof value === 'number' && !isNaN(value) ? value : null;
|
||||||
}),
|
}),
|
||||||
markLine: index === 0 ? {
|
markLine:
|
||||||
|
index === 0
|
||||||
|
? {
|
||||||
symbol: ['none', 'none'],
|
symbol: ['none', 'none'],
|
||||||
label: { show: false },
|
label: { show: false },
|
||||||
lineStyle: {
|
lineStyle: {
|
||||||
@ -329,7 +386,8 @@ const getChartOption = (): EChartsOption => {
|
|||||||
type: 'dashed' as const
|
type: 'dashed' as const
|
||||||
},
|
},
|
||||||
data: markLineData
|
data: markLineData
|
||||||
} : undefined
|
}
|
||||||
|
: undefined
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -340,7 +398,10 @@ const getChartOption = (): EChartsOption => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const values = data
|
const values = data
|
||||||
.map(item => item[indicatorConfig.value[configIndex].key as keyof typeof item])
|
.map(
|
||||||
|
item =>
|
||||||
|
item[indicatorConfig.value[configIndex].key as keyof typeof item]
|
||||||
|
)
|
||||||
.filter((val): val is number => typeof val === 'number' && !isNaN(val));
|
.filter((val): val is number => typeof val === 'number' && !isNaN(val));
|
||||||
|
|
||||||
if (values.length === 0) {
|
if (values.length === 0) {
|
||||||
@ -444,7 +505,9 @@ const getChartOption = (): EChartsOption => {
|
|||||||
const showOnlyOneSeries = visibleSeriesQueue.value.length === 1;
|
const showOnlyOneSeries = visibleSeriesQueue.value.length === 1;
|
||||||
|
|
||||||
params.forEach((param: any) => {
|
params.forEach((param: any) => {
|
||||||
const config = indicatorConfig.value.find(c => c.name === param.seriesName);
|
const config = indicatorConfig.value.find(
|
||||||
|
c => c.name === param.seriesName
|
||||||
|
);
|
||||||
const unit = config?.unit || '';
|
const unit = config?.unit || '';
|
||||||
|
|
||||||
if (param.value !== null && param.value !== undefined) {
|
if (param.value !== null && param.value !== undefined) {
|
||||||
@ -473,16 +536,22 @@ const getChartOption = (): EChartsOption => {
|
|||||||
|
|
||||||
if (limits.min !== undefined && limits.max !== undefined) {
|
if (limits.min !== undefined && limits.max !== undefined) {
|
||||||
// 既有上限又有下限
|
// 既有上限又有下限
|
||||||
limitText = ` 限值: ${limits.min}-${limits.max}${unit ? unit : ''}`;
|
limitText = ` 限值: ${limits.min}-${
|
||||||
|
limits.max
|
||||||
|
}${unit ? unit : ''}`;
|
||||||
} else if (limits.min !== undefined) {
|
} else if (limits.min !== undefined) {
|
||||||
// 只有下限
|
// 只有下限
|
||||||
limitText = ` 限值: ${limits.min}${unit ? unit : ''}`;
|
limitText = ` 限值: ${limits.min}${
|
||||||
|
unit ? unit : ''
|
||||||
|
}`;
|
||||||
} else if (limits.max !== undefined) {
|
} else if (limits.max !== undefined) {
|
||||||
// 只有上限
|
// 只有上限
|
||||||
limitText = ` 限值: ${limits.max}${unit ? unit : ''}`;
|
limitText = ` 限值: ${limits.max}${
|
||||||
|
unit ? unit : ''
|
||||||
|
}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
result += ('<br/>'+limitText);
|
result += '<br/>' + limitText;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -568,7 +637,9 @@ const handleLegendSelectChanged = (params: any) => {
|
|||||||
const clickedName = name;
|
const clickedName = name;
|
||||||
const isNowSelected = selected[clickedName];
|
const isNowSelected = selected[clickedName];
|
||||||
|
|
||||||
console.log(`图例点击: ${clickedName}, 当前状态: ${isNowSelected ? '显示' : '隐藏'}`);
|
console.log(
|
||||||
|
`图例点击: ${clickedName}, 当前状态: ${isNowSelected ? '显示' : '隐藏'}`
|
||||||
|
);
|
||||||
console.log('当前可见队列:', visibleSeriesQueue.value);
|
console.log('当前可见队列:', visibleSeriesQueue.value);
|
||||||
|
|
||||||
if (isNowSelected) {
|
if (isNowSelected) {
|
||||||
@ -723,10 +794,18 @@ const handleDataPointClick = (params: any) => {
|
|||||||
|
|
||||||
if (stationData && stationData.stcd) {
|
if (stationData && stationData.stcd) {
|
||||||
modelStore.modalVisible = true;
|
modelStore.modalVisible = true;
|
||||||
modelStore.params.sttp = "WQFB";
|
modelStore.params.sttp = 'WQFB';
|
||||||
modelStore.title = stationData.stnm ;
|
modelStore.title = stationData.stnm;
|
||||||
// modelStore.isBasicEdit = true;
|
// modelStore.isBasicEdit = true;
|
||||||
modelStore.params.stcd = stationData.stcd;
|
modelStore.params.stcd = stationData.stcd;
|
||||||
|
modelStore.filter.rangeTm = [
|
||||||
|
dayjs(datetimePicker.value.value)
|
||||||
|
.startOf('month')
|
||||||
|
.format('YYYY-MM-DD HH:mm:ss'),
|
||||||
|
dayjs(datetimePicker.value.value)
|
||||||
|
.endOf('month')
|
||||||
|
.format('YYYY-MM-DD HH:mm:ss')
|
||||||
|
];
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -749,42 +828,42 @@ const getecharts = async () => {
|
|||||||
hasData.value = false;
|
hasData.value = false;
|
||||||
|
|
||||||
let params = {
|
let params = {
|
||||||
"filter": {
|
filter: {
|
||||||
"logic": "and",
|
logic: 'and',
|
||||||
"filters": [
|
filters: [
|
||||||
{
|
{
|
||||||
"field": "baseId",
|
field: 'baseId',
|
||||||
"operator": "eq",
|
operator: 'eq',
|
||||||
"dataType": "string",
|
dataType: 'string',
|
||||||
"value": wbsCode.value
|
value: wbsCode.value
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"field": "rvcd",
|
field: 'rvcd',
|
||||||
"operator": "eq",
|
operator: 'eq',
|
||||||
"dataType": "string",
|
dataType: 'string',
|
||||||
"value": select.value.value
|
value: select.value.value
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"field": "tm",
|
field: 'tm',
|
||||||
"operator": "gte",
|
operator: 'gte',
|
||||||
"dataType": "date",
|
dataType: 'date',
|
||||||
"value": getStartTime()
|
value: getStartTime()
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"field": "tm",
|
field: 'tm',
|
||||||
"operator": "lte",
|
operator: 'lte',
|
||||||
"dataType": "date",
|
dataType: 'date',
|
||||||
"value": getEndTime()
|
value: getEndTime()
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"sort": [
|
sort: [
|
||||||
{
|
{
|
||||||
"field": "sort",
|
field: 'sort',
|
||||||
"dir": "asc"
|
dir: 'asc'
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
console.log('请求参数:', params);
|
console.log('请求参数:', params);
|
||||||
@ -940,7 +1019,13 @@ const getecharts = async () => {
|
|||||||
const option = getChartOption();
|
const option = getChartOption();
|
||||||
chartInstance?.setOption(option, true);
|
chartInstance?.setOption(option, true);
|
||||||
chartInstance?.resize();
|
chartInstance?.resize();
|
||||||
console.log('图表数据已更新,共', chartData.length, '个站点,', indicatorConfig.value.length, '个指标');
|
console.log(
|
||||||
|
'图表数据已更新,共',
|
||||||
|
chartData.length,
|
||||||
|
'个站点,',
|
||||||
|
indicatorConfig.value.length,
|
||||||
|
'个指标'
|
||||||
|
);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
// 如果图表不存在,初始化图表
|
// 如果图表不存在,初始化图表
|
||||||
@ -956,7 +1041,7 @@ const getecharts = async () => {
|
|||||||
console.log('关闭 loading 状态');
|
console.log('关闭 loading 状态');
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
// 获取起始时间(当前选择日期前一个月)
|
// 获取起始时间(当前选择日期前一个月)
|
||||||
const getStartTime = () => {
|
const getStartTime = () => {
|
||||||
@ -998,7 +1083,7 @@ const initChart = async () => {
|
|||||||
if (rect.width === 0 || rect.height === 0) {
|
if (rect.width === 0 || rect.height === 0) {
|
||||||
console.warn('图表容器尺寸为0,等待渲染...');
|
console.warn('图表容器尺寸为0,等待渲染...');
|
||||||
// 使用 requestAnimationFrame 确保在浏览器下一重绘帧再初始化
|
// 使用 requestAnimationFrame 确保在浏览器下一重绘帧再初始化
|
||||||
return new Promise((resolve) => {
|
return new Promise(resolve => {
|
||||||
requestAnimationFrame(() => {
|
requestAnimationFrame(() => {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
initChart().then(resolve);
|
initChart().then(resolve);
|
||||||
@ -1077,39 +1162,39 @@ const getSelect = async () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let params = {
|
let params = {
|
||||||
"filter": {
|
filter: {
|
||||||
"logic": "and",
|
logic: 'and',
|
||||||
"filters": [
|
filters: [
|
||||||
{
|
{
|
||||||
"field": "objId",
|
field: 'objId',
|
||||||
"operator": "eq",
|
operator: 'eq',
|
||||||
"dataType": "string",
|
dataType: 'string',
|
||||||
"value": wbsCode.value
|
value: wbsCode.value
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"field": "wbsType",
|
field: 'wbsType',
|
||||||
"operator": "eq",
|
operator: 'eq',
|
||||||
"dataType": "string",
|
dataType: 'string',
|
||||||
"value": "PSB_RVCD"
|
value: 'PSB_RVCD'
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"group": [
|
group: [
|
||||||
{
|
{
|
||||||
"dir": "asc",
|
dir: 'asc',
|
||||||
"field": "orderIndex"
|
field: 'orderIndex'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"dir": "des",
|
dir: 'des',
|
||||||
"field": "wbsCode"
|
field: 'wbsCode'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"dir": "des",
|
dir: 'des',
|
||||||
"field": "wbsName"
|
field: 'wbsName'
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"groupResultFlat": "true"
|
groupResultFlat: 'true'
|
||||||
}
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
let res = await wbsbGetKendoList(params);
|
let res = await wbsbGetKendoList(params);
|
||||||
@ -1154,23 +1239,23 @@ const getSelect = async () => {
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('获取河段数据失败:', error);
|
console.error('获取河段数据失败:', error);
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
//监听子组件的数据变化
|
//监听子组件的数据变化
|
||||||
const handlePanelChange1 = async (data) => {
|
const handlePanelChange1 = async data => {
|
||||||
console.log('当前所有控件状态:', data);
|
console.log('当前所有控件状态:', data);
|
||||||
// 当选择器或日期变化时,重新加载图表数据
|
// 当选择器或日期变化时,重新加载图表数据
|
||||||
if (data.datetime || data.select) {
|
if (data.datetime || data.select) {
|
||||||
select.value.value = data.select
|
select.value.value = data.select;
|
||||||
datetimePicker.value.value = data.datetime
|
datetimePicker.value.value = data.datetime;
|
||||||
getecharts()
|
getecharts();
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
// 监听 datetimePicker 变化,更新 iconmap 中的时间显示
|
// 监听 datetimePicker 变化,更新 iconmap 中的时间显示
|
||||||
watch(
|
watch(
|
||||||
() => datetimePicker.value.value,
|
() => datetimePicker.value.value,
|
||||||
(newVal) => {
|
newVal => {
|
||||||
if (newVal) {
|
if (newVal) {
|
||||||
iconmap.value.value = `注:最新数据时间为${newVal}`;
|
iconmap.value.value = `注:最新数据时间为${newVal}`;
|
||||||
}
|
}
|
||||||
@ -1181,10 +1266,10 @@ watch(
|
|||||||
const wbsCode = ref('');
|
const wbsCode = ref('');
|
||||||
watch(
|
watch(
|
||||||
() => JidiSelectEventStore.selectedItem,
|
() => JidiSelectEventStore.selectedItem,
|
||||||
(newVal) => {
|
newVal => {
|
||||||
console.log(newVal);
|
console.log(newVal);
|
||||||
wbsCode.value = newVal.wbsCode;
|
wbsCode.value = newVal.wbsCode;
|
||||||
getSelect()
|
getSelect();
|
||||||
},
|
},
|
||||||
{ deep: true, immediate: true }
|
{ deep: true, immediate: true }
|
||||||
);
|
);
|
||||||
@ -1194,15 +1279,17 @@ watch(
|
|||||||
() => visibleSeriesQueue.value.length,
|
() => visibleSeriesQueue.value.length,
|
||||||
(newLength, oldLength) => {
|
(newLength, oldLength) => {
|
||||||
// 只有当长度真正变化时才重新设置
|
// 只有当长度真正变化时才重新设置
|
||||||
if (newLength !== oldLength && chartInstance && indicatorConfig.value.length > 0) {
|
if (
|
||||||
|
newLength !== oldLength &&
|
||||||
|
chartInstance &&
|
||||||
|
indicatorConfig.value.length > 0
|
||||||
|
) {
|
||||||
// 重新生成 option(这会创建新的 formatter 闭包,捕获最新的 visibleSeriesQueue)
|
// 重新生成 option(这会创建新的 formatter 闭包,捕获最新的 visibleSeriesQueue)
|
||||||
const newOption = getChartOption();
|
const newOption = getChartOption();
|
||||||
chartInstance.setOption(newOption, true);
|
chartInstance.setOption(newOption, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
|||||||
@ -1,34 +1,42 @@
|
|||||||
<!-- SidePanelItem.vue -->
|
<!-- SidePanelItem.vue -->
|
||||||
<template>
|
<template>
|
||||||
<SidePanelItem title="沿程水温变化" :prompt="prompts" :moreSelect="select" :datetimePicker="datetimePicker"
|
<SidePanelItem
|
||||||
@update-values="handlePanelChange1">
|
title="沿程水温变化"
|
||||||
|
:prompt="prompts"
|
||||||
|
:moreSelect="select"
|
||||||
|
:datetimePicker="datetimePicker"
|
||||||
|
@update-values="handlePanelChange1"
|
||||||
|
>
|
||||||
<a-spin :spinning="loading" tip="加载中...">
|
<a-spin :spinning="loading" tip="加载中...">
|
||||||
<div v-show="showemit && !loading" ref="chartRef" class="chart-container"></div>
|
<div
|
||||||
<div v-show="!showemit && !loading" class="chart-container"> <a-empty /></div>
|
v-show="showemit && !loading"
|
||||||
|
ref="chartRef"
|
||||||
|
class="chart-container"
|
||||||
|
></div>
|
||||||
|
<div v-show="!showemit && !loading" class="chart-container">
|
||||||
|
<a-empty />
|
||||||
|
</div>
|
||||||
</a-spin>
|
</a-spin>
|
||||||
</SidePanelItem>
|
</SidePanelItem>
|
||||||
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { ref, onMounted, onBeforeUnmount, watch, computed } from "vue";
|
import { ref, onMounted, onBeforeUnmount, watch, computed } from 'vue';
|
||||||
import * as echarts from "echarts";
|
import * as echarts from 'echarts';
|
||||||
import type { ECharts } from "echarts";
|
import type { ECharts } from 'echarts';
|
||||||
import SidePanelItem from "@/components/SidePanelItem/index.vue";
|
import SidePanelItem from '@/components/SidePanelItem/index.vue';
|
||||||
import { getKendoListCust, wbsbGetKendoList } from "@/api/sw";
|
import { getKendoListCust, wbsbGetKendoList } from '@/api/sw';
|
||||||
import { useJidiSelectEventStore } from "@/store/modules/jidiSelectEvent";
|
import { useJidiSelectEventStore } from '@/store/modules/jidiSelectEvent';
|
||||||
|
|
||||||
import { da } from "element-plus/es/locale/index.mjs";
|
import { da } from 'element-plus/es/locale/index.mjs';
|
||||||
import { f } from "vue-router/dist/router-CWoNjPRp.mjs";
|
import { f } from 'vue-router/dist/router-CWoNjPRp.mjs';
|
||||||
import { useModelStore } from "@/store/modules/model";
|
import { useModelStore } from '@/store/modules/model';
|
||||||
|
|
||||||
const modelStore = useModelStore();
|
const modelStore = useModelStore();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// 定义组件名(便于调试和递归)
|
// 定义组件名(便于调试和递归)
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: "yanchengshuiwenChangeMod",
|
name: 'yanchengshuiwenChangeMod'
|
||||||
});
|
});
|
||||||
// 改为动态获取当前时间
|
// 改为动态获取当前时间
|
||||||
const getCurrentHourTime = () => {
|
const getCurrentHourTime = () => {
|
||||||
@ -46,21 +54,21 @@ const baseid: any = ref(null);
|
|||||||
const paramsOne = {
|
const paramsOne = {
|
||||||
rvcd: '',
|
rvcd: '',
|
||||||
tm: []
|
tm: []
|
||||||
}
|
};
|
||||||
const dataOne = ref('')
|
const dataOne = ref('');
|
||||||
// 使用 computed 使 prompts 响应 dataOne.value 的变化
|
// 使用 computed 使 prompts 响应 dataOne.value 的变化
|
||||||
const prompts = computed(() => ({
|
const prompts = computed(() => ({
|
||||||
show: true,
|
show: true,
|
||||||
value: `注:最新数据时间为${dataOne.value || getCurrentHourTime()}`,
|
value: `注:最新数据时间为${dataOne.value || getCurrentHourTime()}`
|
||||||
}));
|
}));
|
||||||
const showemit = ref(false)
|
const showemit = ref(false);
|
||||||
const loading = ref(false)
|
const loading = ref(false);
|
||||||
// const selectOptions: any = ref([])
|
// const selectOptions: any = ref([])
|
||||||
const optionsFmt = (options: any) =>
|
const optionsFmt = (options: any) =>
|
||||||
options.map((item: any) => ({
|
options.map((item: any) => ({
|
||||||
label: item.wbsName,
|
label: item.wbsName,
|
||||||
value: item.wbsCode
|
value: item.wbsCode
|
||||||
}))
|
}));
|
||||||
|
|
||||||
const chartRef = ref<HTMLElement | null>(null);
|
const chartRef = ref<HTMLElement | null>(null);
|
||||||
let chartInstance: ECharts | null = null;
|
let chartInstance: ECharts | null = null;
|
||||||
@ -69,7 +77,9 @@ let chartInstance: ECharts | null = null;
|
|||||||
const stationNames = ref([]);
|
const stationNames = ref([]);
|
||||||
|
|
||||||
// 静态数据 - 水温值 (°C),班多无数据设为 null,使用对象格式携带stcd信息
|
// 静态数据 - 水温值 (°C),班多无数据设为 null,使用对象格式携带stcd信息
|
||||||
const waterTemperatures = ref<{ value: number | null; stcd: string; stnm: string }[]>([]);
|
const waterTemperatures = ref<
|
||||||
|
{ value: number | null; stcd: string; stnm: string }[]
|
||||||
|
>([]);
|
||||||
|
|
||||||
// 使用 computed 使 currentTime 响应 dataOne.value 的变化
|
// 使用 computed 使 currentTime 响应 dataOne.value 的变化
|
||||||
const currentTime = computed(() => dataOne.value || getCurrentHourTime());
|
const currentTime = computed(() => dataOne.value || getCurrentHourTime());
|
||||||
@ -79,16 +89,16 @@ const select = ref({
|
|||||||
value: '',
|
value: '',
|
||||||
options: [],
|
options: [],
|
||||||
picker: undefined,
|
picker: undefined,
|
||||||
format: undefined,
|
format: undefined
|
||||||
});
|
});
|
||||||
|
|
||||||
// 日期选择器配置
|
// 日期选择器配置
|
||||||
const datetimePicker = ref({
|
const datetimePicker = ref({
|
||||||
show: true,
|
show: true,
|
||||||
value: computed(() => getCurrentHourTime()),
|
value: computed(() => getCurrentHourTime()),
|
||||||
format: "YYYY-MM-DD HH",
|
format: 'YYYY-MM-DD HH',
|
||||||
picker: "date" as const,
|
picker: 'date' as const,
|
||||||
options: [],
|
options: []
|
||||||
});
|
});
|
||||||
// 初始化图表
|
// 初始化图表
|
||||||
const initChart = () => {
|
const initChart = () => {
|
||||||
@ -98,14 +108,14 @@ const initChart = () => {
|
|||||||
|
|
||||||
const option = {
|
const option = {
|
||||||
title: {
|
title: {
|
||||||
text: "水温(°C)",
|
text: '水温(°C)',
|
||||||
left: 5,
|
left: 5,
|
||||||
top: 0,
|
top: 0,
|
||||||
textStyle: {
|
textStyle: {
|
||||||
color: "#000000",
|
color: '#000000',
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
fontWeight: "normal",
|
fontWeight: 'normal'
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
dataZoom: [
|
dataZoom: [
|
||||||
{
|
{
|
||||||
@ -118,7 +128,7 @@ const initChart = () => {
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
tooltip: {
|
tooltip: {
|
||||||
trigger: "axis",
|
trigger: 'axis',
|
||||||
formatter: (params: any) => {
|
formatter: (params: any) => {
|
||||||
if (params && params.length > 0) {
|
if (params && params.length > 0) {
|
||||||
const data = params[0];
|
const data = params[0];
|
||||||
@ -129,31 +139,31 @@ const initChart = () => {
|
|||||||
return `${currentTime.value}<br/>${data.name}:-`;
|
return `${currentTime.value}<br/>${data.name}:-`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return "";
|
return '';
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
grid: {
|
grid: {
|
||||||
left: "3%",
|
left: '3%',
|
||||||
right: "4%",
|
right: '4%',
|
||||||
bottom: "3%",
|
bottom: '3%',
|
||||||
top: "15%",
|
top: '15%',
|
||||||
containLabel: true,
|
containLabel: true
|
||||||
},
|
},
|
||||||
xAxis: {
|
xAxis: {
|
||||||
type: "category",
|
type: 'category',
|
||||||
data: stationNames.value,
|
data: stationNames.value,
|
||||||
boundaryGap: true,
|
boundaryGap: true,
|
||||||
axisLine: {
|
axisLine: {
|
||||||
show: true,
|
show: true,
|
||||||
lineStyle: {
|
lineStyle: {
|
||||||
color: "#8f8f8f",
|
color: '#8f8f8f'
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
axisTick: {
|
axisTick: {
|
||||||
show: false,
|
show: false
|
||||||
},
|
},
|
||||||
axisLabel: {
|
axisLabel: {
|
||||||
color: "#333",
|
color: '#333',
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
margin: 8,
|
margin: 8,
|
||||||
interval: 0,
|
interval: 0,
|
||||||
@ -166,76 +176,76 @@ const initChart = () => {
|
|||||||
} else {
|
} else {
|
||||||
return ` \n${value}`; // 下方标签:空格占位 + 换行 + 文字
|
return ` \n${value}`; // 下方标签:空格占位 + 换行 + 文字
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
splitLine: {
|
splitLine: {
|
||||||
show: true,
|
show: true,
|
||||||
lineStyle: {
|
lineStyle: {
|
||||||
color: "#e0e0e0",
|
color: '#e0e0e0',
|
||||||
type: "solid",
|
type: 'solid'
|
||||||
},
|
}
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
yAxis: {
|
yAxis: {
|
||||||
type: "value",
|
type: 'value',
|
||||||
min: 0,
|
min: 0,
|
||||||
// max: 10,
|
// max: 10,
|
||||||
interval: 2,
|
interval: 2,
|
||||||
axisLine: {
|
axisLine: {
|
||||||
show: true,
|
show: true,
|
||||||
lineStyle: {
|
lineStyle: {
|
||||||
color: "#8f8f8f",
|
color: '#8f8f8f'
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
axisTick: {
|
axisTick: {
|
||||||
show: true,
|
show: true,
|
||||||
length: 3,
|
length: 3,
|
||||||
lineStyle: {
|
lineStyle: {
|
||||||
color: "#8f8f8f",
|
color: '#8f8f8f'
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
axisLabel: {
|
axisLabel: {
|
||||||
color: "#333",
|
color: '#333',
|
||||||
fontSize: 12,
|
fontSize: 12
|
||||||
},
|
},
|
||||||
splitLine: {
|
splitLine: {
|
||||||
show: true,
|
show: true,
|
||||||
lineStyle: {
|
lineStyle: {
|
||||||
color: "#e0e0e0",
|
color: '#e0e0e0',
|
||||||
type: "solid",
|
type: 'solid'
|
||||||
},
|
}
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
series: [
|
series: [
|
||||||
{
|
{
|
||||||
name: "水温",
|
name: '水温',
|
||||||
type: "line",
|
type: 'line',
|
||||||
smooth: true,
|
smooth: true,
|
||||||
symbol: "circle",
|
symbol: 'circle',
|
||||||
symbolSize: 6,
|
symbolSize: 6,
|
||||||
connectNulls: true, // 连接空值两侧的线段,避免折线断开
|
connectNulls: true, // 连接空值两侧的线段,避免折线断开
|
||||||
lineStyle: {
|
lineStyle: {
|
||||||
color: "#6ca4f7",
|
color: '#6ca4f7',
|
||||||
width: 2,
|
width: 2
|
||||||
},
|
},
|
||||||
itemStyle: {
|
itemStyle: {
|
||||||
color: "#6ca4f7",
|
color: '#6ca4f7'
|
||||||
},
|
},
|
||||||
areaStyle: {
|
areaStyle: {
|
||||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||||
{
|
{
|
||||||
offset: 0,
|
offset: 0,
|
||||||
color: "rgba(108, 164, 247, 0.3)",
|
color: 'rgba(108, 164, 247, 0.3)'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
offset: 1,
|
offset: 1,
|
||||||
color: "rgba(108, 164, 247, 0.05)",
|
color: 'rgba(108, 164, 247, 0.05)'
|
||||||
|
}
|
||||||
|
])
|
||||||
},
|
},
|
||||||
]),
|
data: waterTemperatures.value
|
||||||
},
|
}
|
||||||
data: waterTemperatures.value,
|
]
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
};
|
||||||
|
|
||||||
chartInstance.setOption(option);
|
chartInstance.setOption(option);
|
||||||
@ -247,11 +257,12 @@ const initChart = () => {
|
|||||||
console.log('stcd:', params.data.stcd);
|
console.log('stcd:', params.data.stcd);
|
||||||
console.log('stnm:', params.data.stnm);
|
console.log('stnm:', params.data.stnm);
|
||||||
console.log('temperature:', params.data.value);
|
console.log('temperature:', params.data.value);
|
||||||
|
console.log('temperature:', paramsOne.tm);
|
||||||
modelStore.modalVisible = true;
|
modelStore.modalVisible = true;
|
||||||
modelStore.params.sttp = "WT";
|
modelStore.params.sttp = 'WT';
|
||||||
modelStore.title = params.data.stnm;
|
modelStore.title = params.data.stnm;
|
||||||
modelStore.params.stcd = params.data.stcd;
|
modelStore.params.stcd = params.data.stcd;
|
||||||
modelStore.params.date = paramsOne.tm;
|
modelStore.filter.rangeTm = [paramsOne.tm[1], paramsOne.tm[0]];
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@ -269,33 +280,48 @@ const init = async () => {
|
|||||||
try {
|
try {
|
||||||
const params = {
|
const params = {
|
||||||
filter: {
|
filter: {
|
||||||
logic: "and",
|
logic: 'and',
|
||||||
filters: [
|
filters: [
|
||||||
{ field: "rvcd", operator: "eq", dataType: "string", value: paramsOne.rvcd },
|
{
|
||||||
{ field: "tm", operator: "gte", dataType: "date", value: paramsOne.tm[1] },
|
field: 'rvcd',
|
||||||
{ field: "tm", operator: "lte", dataType: "date", value: paramsOne.tm[0] },
|
operator: 'eq',
|
||||||
],
|
dataType: 'string',
|
||||||
|
value: paramsOne.rvcd
|
||||||
},
|
},
|
||||||
sort: [{ field: "sort", dir: "asc" }],
|
{
|
||||||
|
field: 'tm',
|
||||||
|
operator: 'gte',
|
||||||
|
dataType: 'date',
|
||||||
|
value: paramsOne.tm[1]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'tm',
|
||||||
|
operator: 'lte',
|
||||||
|
dataType: 'date',
|
||||||
|
value: paramsOne.tm[0]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
sort: [{ field: 'sort', dir: 'asc' }]
|
||||||
};
|
};
|
||||||
|
|
||||||
let res = await getKendoListCust(params);
|
let res = await getKendoListCust(params);
|
||||||
let data = res.data.data || res.data
|
let data = res.data.data || res.data;
|
||||||
|
|
||||||
if (data.length > 0) {
|
if (data.length > 0) {
|
||||||
showemit.value = true
|
showemit.value = true;
|
||||||
} else {
|
} else {
|
||||||
showemit.value = false
|
showemit.value = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
stationNames.value = data
|
stationNames.value = data
|
||||||
.filter((item: any) => item.sttp == "1")
|
.filter((item: any) => item.sttp == '1')
|
||||||
.map((item: any) => item.stnm);
|
.map((item: any) => item.stnm);
|
||||||
console.log(stationNames.value);
|
console.log(stationNames.value);
|
||||||
|
|
||||||
// 修改为对象格式,携带stcd和stnm信息
|
// 修改为对象格式,携带stcd和stnm信息
|
||||||
waterTemperatures.value = data
|
waterTemperatures.value = data
|
||||||
.filter((item: any) => item.sttp == "2")
|
.filter((item: any) => item.sttp == '2')
|
||||||
.map((item: any) => ({
|
.map((item: any) => ({
|
||||||
value: item.temperature,
|
value: item.temperature,
|
||||||
stcd: item.stcd,
|
stcd: item.stcd,
|
||||||
@ -311,13 +337,13 @@ const init = async () => {
|
|||||||
// 如果图表已存在,只更新数据
|
// 如果图表已存在,只更新数据
|
||||||
chartInstance.setOption({
|
chartInstance.setOption({
|
||||||
xAxis: {
|
xAxis: {
|
||||||
data: stationNames.value,
|
data: stationNames.value
|
||||||
},
|
},
|
||||||
series: [
|
series: [
|
||||||
{
|
{
|
||||||
data: waterTemperatures.value,
|
data: waterTemperatures.value
|
||||||
},
|
}
|
||||||
],
|
]
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
// 强制重绘,确保尺寸正确
|
// 强制重绘,确保尺寸正确
|
||||||
@ -336,11 +362,11 @@ const init = async () => {
|
|||||||
const getSelectConfig = async () => {
|
const getSelectConfig = async () => {
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
try {
|
try {
|
||||||
let obj: any = {}
|
let obj: any = {};
|
||||||
if (baseid.value === 'all') {
|
if (baseid.value === 'all') {
|
||||||
obj = { rstcdStepSort: 'asc', siteStepSort: 'asc' }
|
obj = { rstcdStepSort: 'asc', siteStepSort: 'asc' };
|
||||||
} else {
|
} else {
|
||||||
obj = { siteStepSort: 'asc' }
|
obj = { siteStepSort: 'asc' };
|
||||||
}
|
}
|
||||||
const filters = [
|
const filters = [
|
||||||
{
|
{
|
||||||
@ -348,13 +374,13 @@ const getSelectConfig = async () => {
|
|||||||
operator: 'eq',
|
operator: 'eq',
|
||||||
value: 'PSB_RVCD'
|
value: 'PSB_RVCD'
|
||||||
}
|
}
|
||||||
]
|
];
|
||||||
if (baseid.value && baseid.value !== 'all') {
|
if (baseid.value && baseid.value !== 'all') {
|
||||||
filters.push({
|
filters.push({
|
||||||
field: 'objId',
|
field: 'objId',
|
||||||
operator: 'eq', // contains
|
operator: 'eq', // contains
|
||||||
value: baseid.value
|
value: baseid.value
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
let data: any = {
|
let data: any = {
|
||||||
filter: {
|
filter: {
|
||||||
@ -362,10 +388,10 @@ const getSelectConfig = async () => {
|
|||||||
filters
|
filters
|
||||||
}
|
}
|
||||||
// select: ['WBS_CODE', 'WBS_NAME']
|
// select: ['WBS_CODE', 'WBS_NAME']
|
||||||
}
|
};
|
||||||
|
|
||||||
if (obj) {
|
if (obj) {
|
||||||
data.sort = [obj]
|
data.sort = [obj];
|
||||||
} else {
|
} else {
|
||||||
data = {
|
data = {
|
||||||
...data,
|
...data,
|
||||||
@ -373,31 +399,42 @@ const getSelectConfig = async () => {
|
|||||||
{ dir: 'asc', field: 'orderIndex' },
|
{ dir: 'asc', field: 'orderIndex' },
|
||||||
{ dir: 'asc', field: 'wbsCode' },
|
{ dir: 'asc', field: 'wbsCode' },
|
||||||
{ dir: 'asc', field: 'wbsName' },
|
{ dir: 'asc', field: 'wbsName' },
|
||||||
{ dir: 'asc', field: 'objid' },
|
{ dir: 'asc', field: 'objid' }
|
||||||
],
|
],
|
||||||
groupResultFlat: true
|
groupResultFlat: true
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
let res = await wbsbGetKendoList(data);
|
||||||
let res = await wbsbGetKendoList(data)
|
let dataOne = res?.data?.data || res?.data;
|
||||||
let dataOne = res?.data?.data || res?.data
|
|
||||||
|
|
||||||
if (dataOne.length > 0) {
|
if (dataOne.length > 0) {
|
||||||
let dataMapNameMap: any = {}
|
let dataMapNameMap: any = {};
|
||||||
let jiDiListMap: any = {}
|
let jiDiListMap: any = {};
|
||||||
JidiSelectEventStore.jidiData.forEach((item: any) => {
|
JidiSelectEventStore.jidiData.forEach((item: any) => {
|
||||||
jiDiListMap[item.wbsCode] = item.wbsName
|
jiDiListMap[item.wbsCode] = item.wbsName;
|
||||||
})
|
});
|
||||||
|
|
||||||
dataOne.map((item: any, index: number) => {
|
dataOne.map((item: any, index: number) => {
|
||||||
const { stcd, stnm, objId } = item
|
const { stcd, stnm, objId } = item;
|
||||||
if (dataMapNameMap[objId]) {
|
if (dataMapNameMap[objId]) {
|
||||||
dataMapNameMap[objId].push({ ...item, baseName: jiDiListMap[objId], baseId: objId })
|
dataMapNameMap[objId].push({
|
||||||
|
...item,
|
||||||
|
baseName: jiDiListMap[objId],
|
||||||
|
baseId: objId
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
dataMapNameMap[objId] = [{ ...item, baseName: jiDiListMap[objId], baseId: objId }]
|
dataMapNameMap[objId] = [
|
||||||
|
{ ...item, baseName: jiDiListMap[objId], baseId: objId }
|
||||||
|
];
|
||||||
}
|
}
|
||||||
return { label: item.wbsName, value: item.wbsCode, baseId: objId, baseName: jiDiListMap[objId] }
|
return {
|
||||||
})
|
label: item.wbsName,
|
||||||
let dataMapNameArr: any = []
|
value: item.wbsCode,
|
||||||
|
baseId: objId,
|
||||||
|
baseName: jiDiListMap[objId]
|
||||||
|
};
|
||||||
|
});
|
||||||
|
let dataMapNameArr: any = [];
|
||||||
|
|
||||||
Object.keys(dataMapNameMap).forEach((item: any) => {
|
Object.keys(dataMapNameMap).forEach((item: any) => {
|
||||||
dataMapNameArr.push({
|
dataMapNameArr.push({
|
||||||
@ -409,24 +446,29 @@ const getSelectConfig = async () => {
|
|||||||
return {
|
return {
|
||||||
title: item2.wbsName,
|
title: item2.wbsName,
|
||||||
value: item2.wbsCode
|
value: item2.wbsCode
|
||||||
}
|
};
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
});
|
||||||
|
});
|
||||||
dataMapNameArr = dataMapNameArr.sort((a: any, b: any) => {
|
dataMapNameArr = dataMapNameArr.sort((a: any, b: any) => {
|
||||||
const indexA = JidiSelectEventStore.jidiData.findIndex(item => item.wbsCode === a.value);
|
const indexA = JidiSelectEventStore.jidiData.findIndex(
|
||||||
const indexB = JidiSelectEventStore.jidiData.findIndex(item => item.wbsCode === b.value);
|
item => item.wbsCode === a.value
|
||||||
|
);
|
||||||
|
const indexB = JidiSelectEventStore.jidiData.findIndex(
|
||||||
|
item => item.wbsCode === b.value
|
||||||
|
);
|
||||||
return indexA - indexB;
|
return indexA - indexB;
|
||||||
})
|
});
|
||||||
select.value.options = filterSelectOptions(dataMapNameArr)
|
select.value.options = filterSelectOptions(dataMapNameArr);
|
||||||
if (baseid.value === 'all') {
|
if (baseid.value === 'all') {
|
||||||
select.value.value = 'SJLY1U'
|
select.value.value = 'SJLY1U';
|
||||||
} else if (filterSelectOptions(dataMapNameArr)[0]?.children) {
|
} else if (filterSelectOptions(dataMapNameArr)[0]?.children) {
|
||||||
select.value.value = filterSelectOptions(dataMapNameArr)[0]?.children[0]?.value
|
select.value.value =
|
||||||
|
filterSelectOptions(dataMapNameArr)[0]?.children[0]?.value;
|
||||||
} else if (filterSelectOptions(dataMapNameArr)[0]?.value) {
|
} else if (filterSelectOptions(dataMapNameArr)[0]?.value) {
|
||||||
select.value.value = filterSelectOptions(dataMapNameArr)[0]?.value
|
select.value.value = filterSelectOptions(dataMapNameArr)[0]?.value;
|
||||||
} else {
|
} else {
|
||||||
select.value.value = ''
|
select.value.value = '';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -434,31 +476,39 @@ const getSelectConfig = async () => {
|
|||||||
} finally {
|
} finally {
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
const filterSelectOptions = (selectOptions: any) => {
|
const filterSelectOptions = (selectOptions: any) => {
|
||||||
if (selectOptions?.length === 1 && selectOptions?.[0]?.children?.length === 1) {
|
if (
|
||||||
return [{ ...selectOptions?.[0]?.children?.[0], isLeaf: true }]
|
selectOptions?.length === 1 &&
|
||||||
|
selectOptions?.[0]?.children?.length === 1
|
||||||
|
) {
|
||||||
|
return [{ ...selectOptions?.[0]?.children?.[0], isLeaf: true }];
|
||||||
} else if (selectOptions.length) {
|
} else if (selectOptions.length) {
|
||||||
return selectOptions.map((e) => {
|
return selectOptions.map(e => {
|
||||||
if (e?.children?.length > 1) {
|
if (e?.children?.length > 1) {
|
||||||
return { ...e, isLeaf: false, selectable: true, children: e.children.map((item) => ({ ...item, isLeaf: true })) }
|
return {
|
||||||
|
...e,
|
||||||
|
isLeaf: false,
|
||||||
|
selectable: true,
|
||||||
|
children: e.children.map(item => ({ ...item, isLeaf: true }))
|
||||||
|
};
|
||||||
} else {
|
} else {
|
||||||
return { ...e.children[0], isLeaf: true }
|
return { ...e.children[0], isLeaf: true };
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
return []
|
return [];
|
||||||
}
|
};
|
||||||
//监听子组件的数据变化
|
//监听子组件的数据变化
|
||||||
const handlePanelChange1 = (data) => {
|
const handlePanelChange1 = data => {
|
||||||
console.log('当前所有控件状态:', data);
|
console.log('当前所有控件状态:', data);
|
||||||
if (data.moreSelect) {
|
if (data.moreSelect) {
|
||||||
paramsOne.rvcd = data.moreSelect
|
paramsOne.rvcd = data.moreSelect;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data.datetime) {
|
if (data.datetime) {
|
||||||
dataOne.value = data.datetime
|
dataOne.value = data.datetime;
|
||||||
const datetime = data.datetime;
|
const datetime = data.datetime;
|
||||||
|
|
||||||
// 解析日期和时间
|
// 解析日期和时间
|
||||||
@ -466,7 +516,14 @@ const handlePanelChange1 = (data) => {
|
|||||||
const [year, month, day] = datePart.split('-').map(Number);
|
const [year, month, day] = datePart.split('-').map(Number);
|
||||||
|
|
||||||
// 创建当前时间的 Date 对象
|
// 创建当前时间的 Date 对象
|
||||||
const currentDate = new Date(year, month - 1, day, parseInt(hourPart), 0, 0);
|
const currentDate = new Date(
|
||||||
|
year,
|
||||||
|
month - 1,
|
||||||
|
day,
|
||||||
|
parseInt(hourPart),
|
||||||
|
0,
|
||||||
|
0
|
||||||
|
);
|
||||||
|
|
||||||
// 创建一个月前的 Date 对象
|
// 创建一个月前的 Date 对象
|
||||||
const previousDate = new Date(year, month - 2, day, 0, 0, 0);
|
const previousDate = new Date(year, month - 2, day, 0, 0, 0);
|
||||||
@ -487,20 +544,19 @@ const handlePanelChange1 = (data) => {
|
|||||||
formatDate(currentDate), // "2026-05-15 15:00:00"
|
formatDate(currentDate), // "2026-05-15 15:00:00"
|
||||||
formatDate(previousDate) // "2026-04-15 00:00:00"
|
formatDate(previousDate) // "2026-04-15 00:00:00"
|
||||||
];
|
];
|
||||||
paramsOne.tm = result
|
paramsOne.tm = result;
|
||||||
console.log(paramsOne.tm, 'paramsOne.tm')
|
console.log(paramsOne.tm, 'paramsOne.tm');
|
||||||
}
|
}
|
||||||
if (paramsOne.rvcd || paramsOne.tm.length > 0) {
|
if (paramsOne.rvcd || paramsOne.tm.length > 0) {
|
||||||
init();
|
init();
|
||||||
}
|
}
|
||||||
|
};
|
||||||
}
|
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => JidiSelectEventStore.selectedItem,
|
() => JidiSelectEventStore.selectedItem,
|
||||||
(newVal) => {
|
newVal => {
|
||||||
baseid.value = newVal.wbsCode;
|
baseid.value = newVal.wbsCode;
|
||||||
getSelectConfig()
|
getSelectConfig();
|
||||||
},
|
},
|
||||||
{ deep: true, immediate: true }
|
{ deep: true, immediate: true }
|
||||||
);
|
);
|
||||||
@ -508,12 +564,12 @@ watch(
|
|||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
// 延迟初始化,确保 DOM 渲染完成
|
// 延迟初始化,确保 DOM 渲染完成
|
||||||
init();
|
init();
|
||||||
window.addEventListener("resize", handleResize);
|
window.addEventListener('resize', handleResize);
|
||||||
});
|
});
|
||||||
|
|
||||||
// 组件卸载前清理
|
// 组件卸载前清理
|
||||||
onBeforeUnmount(() => {
|
onBeforeUnmount(() => {
|
||||||
window.removeEventListener("resize", handleResize);
|
window.removeEventListener('resize', handleResize);
|
||||||
chartInstance?.dispose();
|
chartInstance?.dispose();
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user