修改水温垂向右侧点击,添加录像视频和建设状态,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;
|
||||||
};
|
};
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -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' : '';
|
||||||
}
|
}
|
||||||
|
|
||||||
this.showPopup(payload.detectedFeature, payload.coordinate);
|
// 批量 popup 模式下(>= 14 级)不触发 hover popup
|
||||||
|
if (!this.isBatchPopupMode) {
|
||||||
|
this.showPopup(payload.detectedFeature, payload.coordinate);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.map.on('pointerdrag', () => {
|
||||||
|
if (this.isBatchPopupMode) {
|
||||||
|
this.updateBatchPopups();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.map.on('moveend', () => {
|
||||||
|
if (this.isBatchPopupMode) {
|
||||||
|
this.updateBatchPopups();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
return Promise.resolve(this.map);
|
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'
|
||||||
@ -140,7 +159,7 @@ function transformAggregatesToChartData(
|
|||||||
if (!monthData || monthData.length === 0) {
|
if (!monthData || monthData.length === 0) {
|
||||||
result.push({
|
result.push({
|
||||||
name: `${month}月`,
|
name: `${month}月`,
|
||||||
dataXy: [] // 空数组,ECharts 会显示为断开
|
dataXy: [] // 空数组,ECharts 会显示为断开
|
||||||
});
|
});
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -152,11 +171,11 @@ function transformAggregatesToChartData(
|
|||||||
stnm: string;
|
stnm: string;
|
||||||
}> = monthData.map((item: any) => ({
|
}> = monthData.map((item: any) => ({
|
||||||
value: [
|
value: [
|
||||||
parseFloat(item.vwt), // 温度
|
parseFloat(item.vwt), // 温度
|
||||||
parseInt(item.wthg) // 深度/高程
|
parseInt(item.wthg) // 深度/高程
|
||||||
] as [number, number], // ✅ 显式声明为元组类型
|
] as [number, number], // ✅ 显式声明为元组类型
|
||||||
stcd: stcd, // 站点编码
|
stcd: stcd, // 站点编码
|
||||||
stnm: stnm // 站点名称
|
stnm: stnm // 站点名称
|
||||||
}));
|
}));
|
||||||
|
|
||||||
result.push({
|
result.push({
|
||||||
@ -172,16 +191,25 @@ function transformAggregatesToChartData(
|
|||||||
* 根据当前年月生成颜色和图例显隐状态
|
* 根据当前年月生成颜色和图例显隐状态
|
||||||
*/
|
*/
|
||||||
function generateColorsAndVisibility(
|
function generateColorsAndVisibility(
|
||||||
dataLength: number, // ⚠️ 现在固定传入 12
|
dataLength: number, // ⚠️ 现在固定传入 12
|
||||||
sDate: string | number
|
sDate: string | number
|
||||||
): {
|
): {
|
||||||
colors: string[];
|
colors: string[];
|
||||||
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",
|
? [
|
||||||
"dir": "asc"
|
{
|
||||||
}
|
field: 'rstcdStepSort',
|
||||||
] : [
|
dir: 'asc'
|
||||||
{
|
}
|
||||||
"field": "baseId",
|
]
|
||||||
"dir": "asc"
|
: [
|
||||||
},
|
{
|
||||||
{
|
field: 'baseId',
|
||||||
"field": "rstcdStepSort",
|
dir: 'asc'
|
||||||
"dir": "asc"
|
},
|
||||||
}
|
{
|
||||||
];
|
field: 'rstcdStepSort',
|
||||||
|
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<
|
||||||
name: string;
|
Array<{
|
||||||
dataXy: Array<{
|
name: string;
|
||||||
value: [number, number];
|
dataXy: Array<{
|
||||||
stcd: string;
|
value: [number, number];
|
||||||
stnm: string;
|
stcd: 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,18 +464,26 @@ 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 } =
|
||||||
12, // ⚠️ 固定传入 12
|
generateColorsAndVisibility(
|
||||||
year
|
12, // ⚠️ 固定传入 12
|
||||||
);
|
year
|
||||||
|
);
|
||||||
colors.value = newColors;
|
colors.value = newColors;
|
||||||
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]);
|
||||||
@ -521,7 +578,7 @@ const updateChart = () => {
|
|||||||
|
|
||||||
yData.push({
|
yData.push({
|
||||||
name: item.name,
|
name: item.name,
|
||||||
data: item.dataXy, // ✅ 直接使用对象数组
|
data: item.dataXy, // ✅ 直接使用对象数组
|
||||||
type: 'line',
|
type: 'line',
|
||||||
smooth: true,
|
smooth: true,
|
||||||
connectNulls: true,
|
connectNulls: true,
|
||||||
@ -593,7 +650,7 @@ const updateChart = () => {
|
|||||||
type: 'value' as const,
|
type: 'value' as const,
|
||||||
name: type.value === 2 ? '高程(m)' : '水深(m)',
|
name: type.value === 2 ? '高程(m)' : '水深(m)',
|
||||||
min: 0,
|
min: 0,
|
||||||
scale: false, // ✅ 新增:确保 Y 轴从 0 开始,防止自动调整
|
scale: false, // ✅ 新增:确保 Y 轴从 0 开始,防止自动调整
|
||||||
nameGap: type.value === 2 ? 22 : 10,
|
nameGap: type.value === 2 ? 22 : 10,
|
||||||
inverse: type.value !== 2
|
inverse: type.value !== 2
|
||||||
},
|
},
|
||||||
@ -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 =
|
||||||
? parseInt(datetimePicker.value.value)
|
typeof datetimePicker.value.value === 'string'
|
||||||
: datetimePicker.value.value;
|
? parseInt(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>
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -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>
|
||||||
@ -254,10 +297,10 @@ const fetchLyData = async () => {
|
|||||||
},
|
},
|
||||||
formValue.dataDimensionVal != 'all'
|
formValue.dataDimensionVal != 'all'
|
||||||
? {
|
? {
|
||||||
field: 'fullPath',
|
field: 'fullPath',
|
||||||
operator: 'startswith',
|
operator: 'startswith',
|
||||||
value: formValue.dataDimensionVal
|
value: formValue.dataDimensionVal
|
||||||
}
|
}
|
||||||
: null
|
: null
|
||||||
].filter(Boolean)
|
].filter(Boolean)
|
||||||
},
|
},
|
||||||
@ -311,15 +354,15 @@ const fetchDmData = async () => {
|
|||||||
{ field: 'mway', operator: 'eq', value: 2 },
|
{ field: 'mway', operator: 'eq', value: 2 },
|
||||||
formValue.dataDimensionVal == 'all'
|
formValue.dataDimensionVal == 'all'
|
||||||
? {
|
? {
|
||||||
field: 'rvcd',
|
field: 'rvcd',
|
||||||
operator: 'contains',
|
operator: 'contains',
|
||||||
value: formValue.rvcd
|
value: formValue.rvcd
|
||||||
}
|
}
|
||||||
: {
|
: {
|
||||||
field: 'baseId',
|
field: 'baseId',
|
||||||
operator: 'contains',
|
operator: 'contains',
|
||||||
value: formValue.dataDimensionVal
|
value: formValue.dataDimensionVal
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
select: ['stcd', 'stnm', 'lgtd', 'lttd', 'rstcdStepSort'],
|
select: ['stcd', 'stnm', 'lgtd', 'lttd', 'rstcdStepSort'],
|
||||||
@ -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({
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -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')
|
||||||
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
// 关闭弹窗
|
// 关闭弹窗
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -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
|
||||||
|
},
|
||||||
|
{
|
||||||
|
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" }],
|
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);
|
||||||
@ -484,23 +541,22 @@ const handlePanelChange1 = (data) => {
|
|||||||
|
|
||||||
// 生成结果数组
|
// 生成结果数组
|
||||||
const result = [
|
const result = [
|
||||||
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