水温接口-线上和弹框

This commit is contained in:
王兴凯 2026-05-26 19:31:33 +08:00
commit a43b35b5a8
30 changed files with 5437 additions and 7339 deletions

View File

@ -9,7 +9,7 @@ VITE_APP_BASE_API = '/dev-api'
# 本地环境
# VITE_APP_BASE_URL = 'http://localhost:8093'
# 测试环境
# VITE_APP_BASE_URL = 'http://172.16.21.142:8096'
# VITE_APP_BASE_URL = 'http://172.16.21.142:8093'
# VITE_APP_BASE_URL = 'http://172.16.21.142:8096'
# 汤伟
VITE_APP_BASE_URL = 'http://10.84.121.21:8093'

View File

@ -0,0 +1,8 @@
import request from '@/utils/request';
export function getStcdDetail(url: string, stcd: string) {
return request({
url: url,
method: 'get',
params: { stcd }
});
}

View File

@ -64,3 +64,91 @@ export function baseEvnmAutoMonitorGetKendoListCust(data: any) {
data
});
}
////水温监测工作开展情况弹框
export function vmsstbprptGetKendoList(data: any) {
return request({
url: '/api/dec-lygk-base-server/base/vmsstbprpt/GetKendoList',
method: 'post',
data
});
}
//出入库水温打开弹框api/wmp-env-server/sw/inOutOne/details
export function inOutOneDetails(data: any) {
return request({
url: '/api/wmp-env-server/sw/inOutOne/details',
method: 'post',
data
});
}
//鱼类适宜性分析 - select
export function infoGetKendoListCust(data: any) {
return request({
url: '/api/wmp-env-server/sw/wtrv/fish/info/GetKendoListCust',
method: 'post',
data
});
}
//鱼类适宜性分析-图表和table
export function fishGetKendoListCust(data: any) {
return request({
url: '/api/wmp-env-server/sw/wtrv/fish/GetKendoListCust',
method: 'post',
data
});
}
//月平均水温历史对比
export function avgMonGetKendoListCust(data: any) {
return request({
url: '/api/wmp-env-server/sw/monthList/avgMon/GetKendoListCust',
method: 'post',
data
});
}
//水温年内分布
export function yearListGetKendoListCust(data: any) {
return request({
url: '/api/wmp-env-server/sw/yearList/GetKendoListCust',
method: 'post',
data
});
}
//设施类型介绍
export function sttpbGetKendoList(data: any) {
return request({
url: '/api/dec-lygk-base-server/base/sttpb/GetKendoList',
method: 'post',
data
});
}
//设施类型及接入情况
export function dwInfoGetKendoListCust(data: any) {
return request({
url: '/api/dec-lygk-base-server/base/dwInfo/GetKendoListCust',
method: 'post',
data
});
}
//月均水温对比,图表,表格 /api/wmp-env-server/sw/monthDetail/Det/GetKendoListCust
export function DetGetKendoListCust(data: any) {
return request({
url: '/api/wmp-env-server/sw/monthDetail/Det/GetKendoListCust',
method: 'post',
data
});
}
//水温年内分布
export function yearDetailGetKendoListCust(data: any) {
return request({
url: '/api/wmp-env-server/sw/yearDetail/GetKendoListCust',
method: 'post',
data
});
}
//获取建设状态
export function dictgetRemoteDictValue(data: any) {
return request({
url: '/api/dec-modules-usm-springcloud-starter/usm/v1/dict/getRemoteDictValue',
method: 'get',
params: data
});
}

View File

@ -82,7 +82,7 @@ const scrollConfig = computed(() => {
else if (tableScrollY.value > 0) {
config.y = tableScrollY.value;
}
console.log(tableScrollY.value);
console.log(config);
return config;
});

View File

@ -2072,7 +2072,7 @@ const DwPointColumns: Array<any> = [
},
{
name: '站址',
filed: 'stlc',
filed: 'addvcdName',
visible: true,
type: 'select',
url: '',
@ -2608,7 +2608,7 @@ const DwOnePointColumns: Array<any> = [
url: '',
},
{
name: '多层式进水口',
name: '夹岩双层取水',
visible: false,
ruleTips: '',
type: '',

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,12 @@
<template>
<div>
监测数据
</div>
</template>
<script lang="ts" setup>
import { ref, watch } from "vue";
</script>
<style lang="scss" scoped></style>

View File

@ -0,0 +1,653 @@
<template>
<div class="body_one">
<div class="search_one">
<div class="search_left">
<div>鱼类选择</div>
<a-select v-model:value="selectValue" mode="multiple" style="width: 260px;" placeholder=" "
:options="selectOption" @change="handleChange"></a-select>
</div>
<div>
<a-range-picker v-model:value="rangePicker" format="YYYY-MM-DD" :disabled-date="disabledDate"
:allow-clear="false" @change="handleDateChange">
<template #renderExtraFooter>
<div class="quick-date-options">
<a-button size="small" @click="setQuickRange(7)">最近七天</a-button>
<a-button size="small" @click="setQuickRange(30)">最近一个月</a-button>
<a-button size="small" @click="setQuickRange(90)">最近三个月</a-button>
<a-button size="small" @click="setQuickRange(180)">最近六个月</a-button>
</div>
</template>
</a-range-picker>
</div>
</div>
<div class="body_body">
<div class="echarts" ref="chartRef"></div>
<div class="table">
<BasicTable ref="tableRef" :scrollY="460" :columns="columns" :list-url="fishGetKendoListCust"
:search-params="{}" :transform-data="customTransform" />
</div>
</div>
</div>
</template>
<script lang="ts" setup>
import { ref, watch, onMounted, onUnmounted, computed } from "vue";
import * as echarts from 'echarts';
import dayjs, { Dayjs } from 'dayjs';
import { useModelStore } from "@/store/modules/model";
import { infoGetKendoListCust, fishGetKendoListCust } from "@/api/sw";
import BasicTable from "@/components/BasicTable/index.vue";
const modelStore = useModelStore();
const selectOption = ref([])
const selectValue = ref([])
const tableRef = ref<any>(null);
const chartRef = ref<HTMLDivElement | null>(null);
let chartInstance: echarts.ECharts | null = null;
const chartData = ref<any[]>([]);
const columns = [
{
title: '时间',
dataIndex: 'dt',
width: 180,
customRender: ({ text }: any) => text ? dayjs(text).format('YYYY-MM-DD') : '-'
},
{
title: '水温°C',
dataIndex: 'wt',
customRender: ({ text }: any) => text !== undefined && text !== null ? text : '-'
},
{
title: '上一年水温',
dataIndex: 'beforeWt',
customRender: ({ text }: any) => text !== undefined && text !== null ? text : '-'
},
{
title: '同期对比',
dataIndex: 'contrast',
width: 120,
customRender: ({ record }: any) => {
if (record.wt && record.beforeWt) {
const diff = (record.wt - record.beforeWt).toFixed(2);
const diffNum = record.wt - record.beforeWt;
return (diffNum > 0) ? `+${diff}` : diff;
}
return '-';
}
},
{
title: '适宜产卵鱼',
dataIndex: 'spawnFish',
width: 200,
customRender: ({ record }: any) => {
if (record.fishList && Array.isArray(record.fishList) && record.fishList.length > 0) {
return record.fishList.map(fish => fish.name).join('、');
}
return '-';
}
},
];
// 7
const initDateRange = () => {
const endDate = dayjs();
const startDate = dayjs().subtract(7, 'day');
return [startDate, endDate] as [Dayjs, Dayjs];
};
const rangePicker = ref<[Dayjs, Dayjs] | undefined>(initDateRange());
//
const disabledDate = (current: Dayjs) => {
//
return current && current.isAfter(dayjs(), 'day');
};
//
const setQuickRange = (days: number) => {
const endDate = dayjs();
const startDate = dayjs().subtract(days, 'day');
rangePicker.value = [startDate, endDate];
//
handleDateChange(rangePicker.value, [
startDate.format('YYYY-MM-DD'),
endDate.format('YYYY-MM-DD')
]);
};
//
const handleDateChange = (dates: any, dateStrings: string[]) => {
console.log('日期改变:', dates, dateStrings);
getEchartsData()
gerTableData()
// TODO:
};
//
const getSelectOption = async () => {
//
// modelStore.params.date
let params = {
"filter": {
"logic": "and",
"filters": [
{
"field": "stcd",
"operator": "eq",
"dataType": "string",
"value": modelStore.params.stcd
}
]
}
}
let res = await infoGetKendoListCust(params)
let data = res?.data?.data || res?.data
console.log(res)
selectOption.value = data.map(item => {
return {
label: item.name,
value: item.id,
pretemp: item.pretemp,
}
})
selectValue.value = data.map(item => item.id)
}
//echarts
const getEchartsData = async () => {
const params = {
"filter": {
"logic": "and",
"filters": [
{
"field": "stcd",
"operator": "eq",
"dataType": "string",
"value": modelStore.params.stcd
},
{
"field": "startTime",
"operator": "gte",
"dataType": "date",
"value": rangePicker.value[0].format('YYYY-MM-DD') + " 00:00:00"
},
{
"field": "endTime",
"operator": "lte",
"dataType": "date",
"value": rangePicker.value[1].format('YYYY-MM-DD') + " 23:59:59"
}
]
},
"sort": [
{
"field": "dt",
"dir": "desc"
}
]
}
let res = await fishGetKendoListCust(params)
console.log('ECharts原始数据:', res)
//
const rawData = res?.data?.data || res?.data?.records || []
chartData.value = rawData
// DOM
setTimeout(() => {
updateChart()
}, 100)
}
//table
const gerTableData = async () => {
const filter = {
logic: 'and',
"filters": [
{
"field": "stcd",
"operator": "eq",
"dataType": "string",
"value": modelStore.params.stcd
},
{
"field": "startTime",
"operator": "gte",
"dataType": "date",
"value": rangePicker.value[0].format('YYYY-MM-DD') + " 00:00:00"
},
{
"field": "endTime",
"operator": "lte",
"dataType": "date",
"value": rangePicker.value[1].format('YYYY-MM-DD') + " 23:59:59"
}
]
};
tableRef.value?.getList(filter);
}
const handleChange = (value: any) => {
console.log(value)
getEchartsData()
// gerTableData()
}
const customTransform = (res: any) => {
console.log('表格数据:', res);
return {
records: res?.data?.data || [],
total: res?.data?.total || 0
};
};
//
const processChartData = (rawData: any[]) => {
if (!rawData || !Array.isArray(rawData)) return [];
// wt beforeWt null
const filtered = rawData.filter(item =>
item.wt !== null && item.wt !== undefined &&
item.beforeWt !== null && item.beforeWt !== undefined
);
// dt
return filtered.sort((a, b) =>
new Date(a.dt).getTime() - new Date(b.dt).getTime()
);
};
// Y
const calculateYAxisMax = (data: any[]) => {
if (!data || data.length === 0) return 10;
const allValues = [
...data.map(item => item.wt),
...data.map(item => item.beforeWt)
].filter(v => v !== null && v !== undefined);
const max = Math.max(...allValues);
return Math.ceil(max);
};
// HSLHEX
const generateRandomColor = () => {
const hue = Math.random() * 360; // 0-360°
const saturation = 40 + Math.random() * 30; // 40%-70%
const lightness = 45 + Math.random() * 20; // 45%-65%
// HSLRGB
const c = (1 - Math.abs(2 * lightness / 100 - 1)) * saturation / 100;
const x = c * (1 - Math.abs((hue / 60) % 2 - 1));
const m = lightness / 100 - c / 2;
let r = 0, g = 0, b = 0;
if (hue < 60) { r = c; g = x; b = 0; }
else if (hue < 120) { r = x; g = c; b = 0; }
else if (hue < 180) { r = 0; g = c; b = x; }
else if (hue < 240) { r = 0; g = x; b = c; }
else if (hue < 300) { r = x; g = 0; b = c; }
else { r = c; g = 0; b = x; }
// RGBHEX
const toHex = (value: number) => {
const hex = Math.round((value + m) * 255).toString(16);
return hex.length === 1 ? '0' + hex : hex;
};
return `#${toHex(r)}${toHex(g)}${toHex(b)}`;
};
//
const calculateTempRanges = () => {
if (!selectValue.value || selectValue.value.length === 0) {
return [];
}
// selectOption
const selectedFish = selectOption.value.filter(fish =>
selectValue.value.includes(fish.value)
);
if (selectedFish.length === 0) {
return [];
}
//
return selectedFish
.map(fish => {
if (!fish.pretemp || !Array.isArray(fish.pretemp) || fish.pretemp.length !== 2) {
return null;
}
const min = parseFloat(fish.pretemp[0]);
const max = parseFloat(fish.pretemp[1]);
if (isNaN(min) || isNaN(max)) {
return null;
}
return {
name: fish.label,
min,
max,
color: generateRandomColor()
};
})
.filter(item => item !== null);
};
// ECharts
const getChartOption = computed(() => {
const data = processChartData(chartData.value);
if (data.length === 0) {
return {};
}
// const yAxisMax = calculateYAxisMax(data);
const tempRanges = calculateTempRanges();
const dataMax = calculateYAxisMax(data);
const tempMax = tempRanges.length > 0
? Math.max(...tempRanges.map(r => r.max))
: 0;
const yAxisMax = Math.max(dataMax, tempMax);
//
const xAxisData = data.map(item => dayjs(item.dt).format('YYYY-MM-DD'));
const wtData = data.map(item => item.wt);
const beforeWtData = data.map(item => item.beforeWt);
// markArea
const markAreaConfig = tempRanges.length > 0 ? {
silent: true,
data: tempRanges.map(range => [
{
yAxis: range.min,
name: `${range.name}: ${range.min}-${range.max}°C`,
itemStyle: { color: `${range.color}26` }
},
{
yAxis: range.max
}
])
} : undefined;
return {
tooltip: {
trigger: 'axis',
backgroundColor: 'rgba(50, 50, 50, 0.9)',
textStyle: {
color: '#fff',
fontSize: 14
},
axisPointer: {
type: 'cross',
label: {
backgroundColor: '#6a7985'
}
},
formatter: (params: any) => {
if (!params || params.length === 0) return '';
const date = params[0].axisValue;
let html = `<div style="font-weight:bold;margin-bottom:8px;">${date}</div>`;
params.forEach((param: any) => {
const dataIndex = param.dataIndex;
const record = data[dataIndex];
html += `
<div style="display:flex;align-items:center;margin:4px 0;">
<span style="display:inline-block;width:10px;height:10px;border-radius:50%;background:${param.color};margin-right:8px;"></span>
<span>${param.seriesName}: ${param.value}°C</span>
</div>
`;
});
return html;
}
},
legend: {
top: 10,
left: 'center',
data: ['现在水温', '上一年同期水温'],
textStyle: {
fontSize: 13
}
},
grid: {
left: 60,
right: 40,
top: 60,
bottom: 50
},
xAxis: {
type: 'category',
data: xAxisData,
boundaryGap: false,
axisLine: {
lineStyle: {
color: '#000000'
}
},
axisTick: {
show: false
},
axisLabel: {
interval: Math.ceil(xAxisData.length / 7),
fontSize: 12,
rotate: 0,
margin: 10
},
splitLine: {
show: true,
lineStyle: {
color: '#bfbfbf',
type: 'solid'
}
}
},
yAxis: {
type: 'value',
name: '水温(°C)',
min: 0,
max: yAxisMax,
scale: false,
axisLine: {
lineStyle: {
color: '#000000',
width: 1
}
},
axisTick: {
show: true,
length: 4
},
axisLabel: {
fontSize: 12,
formatter: '{value}'
},
splitLine: {
show: true,
lineStyle: {
color: '#bfbfbf',
type: 'solid'
}
}
},
series: [
{
name: '现在水温',
type: 'line',
data: wtData,
smooth: true,
symbol: 'circle',
symbolSize: 6,
lineStyle: { color: '#009dff', width: 2 },
itemStyle: { color: '#009dff' },
connectNulls: false,
// markArea
},
{
name: '上一年同期水温',
type: 'line',
data: beforeWtData,
smooth: true,
symbol: 'circle',
symbolSize: 6,
lineStyle: { color: '#fee588', width: 2 },
itemStyle: { color: '#fee588' },
connectNulls: false
},
// markArea
{
name: '温度区间背景', // legend
type: 'line',
data: new Array(xAxisData.length).fill(0), //
showSymbol: false,
symbol: 'none',
lineStyle: { opacity: 0 },
itemStyle: { opacity: 0 },
legendHoverLink: false, //
silent: true, //
markArea: markAreaConfig //
}
],
dataZoom: [
{
type: 'inside',
xAxisIndex: [0],
throttle: 50,
start: 0,
end: 100
}
],
toolbox: {
show: true,
feature: {
saveAsImage: {
title: '导出图片',
type: 'png',
pixelRatio: 2
}
},
right: 20,
top: 10
}
};
});
//
const initChart = () => {
if (!chartRef.value) return;
if (chartInstance) {
chartInstance.dispose();
}
chartInstance = echarts.init(chartRef.value);
updateChart();
// resize
window.addEventListener('resize', handleResize);
};
//
const updateChart = () => {
if (!chartInstance) return;
const option = getChartOption.value;
if (Object.keys(option).length > 0) {
chartInstance.setOption(option, true);
} else {
//
chartInstance.clear();
}
};
// resize
const handleResize = () => {
if (chartInstance) {
chartInstance.resize();
}
};
//
const destroyChart = () => {
if (chartInstance) {
chartInstance.dispose();
chartInstance = null;
}
window.removeEventListener('resize', handleResize);
};
onMounted(() => {
getSelectOption()
getEchartsData()
gerTableData()
//
setTimeout(() => {
initChart()
}, 200)
});
onUnmounted(() => {
destroyChart()
});
</script>
<style lang="scss" scoped>
.body_one {
width: 100%;
min-height: 600px;
.search_one {
width: 100%;
height: 32px;
margin-bottom: 10px;
display: flex;
align-items: center;
justify-content: space-between;
.search_left {
display: flex;
align-items: center;
}
}
.body_body {
width: 100%;
display: flex;
height: 540px;
.echarts {
flex: 1;
height: 100%;
box-sizing: border-box;
padding-top: 10px;
}
.table {
flex: 1;
height: 100%;
box-sizing: border-box;
padding: 10px;
}
}
}
.quick-date-options {
display: flex;
gap: 8px;
padding: 8px 0;
.ant-btn {
border-color: #1890ff !important;
color: #1890ff !important;
background-color: #e6f7ff !important;
font-size: 12px;
&:hover {
border-color: #40a9ff !important;
color: #40a9ff !important;
background-color: #bae7ff !important;
}
}
}
</style>

View File

@ -1,19 +1,15 @@
<template>
<a-modal
:open="visible"
:title="title"
width="80vw"
:footer="null"
:closable="true"
@cancel="handleClose"
:destroyOnClose="true"
class="map-modal"
>
<a-modal :open="visible" :title="title" width="80vw" :footer="null" :closable="true" @cancel="handleClose"
:destroyOnClose="true" class="map-modal">
<a-tabs :active-key="currentActiveKey" @change="onTabChange">
<a-tab-pane v-for="tab in tabsConfig" :key="tab.key" :tab="tab.name">
<div class="content">
<!-- 基本信息组件 -->
<BasicInfo v-if="currentActiveKey === 'basicInfo'" :url="tab.url" />
<!-- 水温-监测数据 -->
<WaterTemperature v-if="currentActiveKey === 'WaterTemperature'" />
<!-- 水温-鱼类繁殖适宜性分析 -->
<WaterTemperatureContrast v-if="currentActiveKey === 'WaterTemperatureRep'" />
<!-- 地图组件 -->
<!-- <MapView v-else-if="currentActiveKey === 'mapView'" :data="modalData" /> -->
<!-- 周边配套组件 -->
@ -28,8 +24,7 @@
<a-button type="primary" :disabled="isEngConfig">
<i class="icon iconfont icon-topic mr-[5px]"></i>
电站专题
</a-button></a-tooltip
>
</a-button></a-tooltip>
</template>
</a-tabs>
</a-modal>
@ -39,6 +34,8 @@
import { ref, watch } from "vue";
// Tab
import BasicInfo from "./components/BasicInfo.vue";
import WaterTemperature from "./components/WaterTemperature.vue";
import WaterTemperatureContrast from "./components/WaterTemperatureContrast.vue";
import { useModelStore } from "@/store/modules/model";
import { handleTabs } from "./setting.config";

View File

@ -17,7 +17,7 @@ const ENGTabs: Array<any> = [
name: '基础信息',
key: 'basicInfo',
type: 'basic',
url: '/bbi/siteBipc/getSiteBasicInfo',
url: '/api/dec-lygk-base-server/base/msstbprpt/getStcdInfo',
default: true // 默认显示
},
{
@ -724,7 +724,7 @@ const WTTabs: Array<any> = [
name: '基础信息',
key: 'basicInfo',
type: 'basic',
url: '/bbi/siteBipc/getSiteBasicInfo'
url: '/api/dec-lygk-base-server/base/msstbprpt/getStcdInfo'
},
{
name: '监测数据',
@ -767,23 +767,24 @@ const DWTabs: Array<any> = [
key: 'basicInfo',
type: 'basic',
default: true, // 默认显示
url: '/api/dec-lygk-base-server/base/msstbprpt/getStcdInfo'
},
{
name: '实时视频',
key: 'videoInfo',
type: 'video',
url: '/video/dataStcdFrame/getVideoMonitorList'
},
{
name: '监测数据',
key: 'VerticalWaterTemperature',
type: 'VerticalWaterTemperature',
},
{
name: '叠梁门运行AI识别',
key: 'AIYXSB',
type: 'AIYXSB',
}
// {
// name: '实时视频',
// key: 'videoInfo',
// type: 'video',
// url: '/video/dataStcdFrame/getVideoMonitorList'
// },
// {
// name: '监测数据',
// key: 'VerticalWaterTemperature',
// type: 'VerticalWaterTemperature',
// },
// {
// name: '叠梁门运行AI识别',
// key: 'AIYXSB',
// type: 'AIYXSB',
// }
]
// //低温水减缓设施这是旧的
// const DWTabs2: Array<any> = [
@ -887,7 +888,7 @@ const DEVICETABS: Array<any> = [
name: '基础信息',
key: 'basicInfo',
type: 'basic',
url: '/bbi/siteBipc/getSiteBasicInfo',
url: '/api/dec-lygk-base-server/base/msstbprpt/getStcdInfo',
default: true, // 默认显示
},
]

View File

@ -16,8 +16,7 @@
</a-tooltip>
</span>
<span v-if="iconmap.show" class="title_icon">
<a-tooltip placement="top" :title="iconmap.value"
:get-popup-container="getPopupContainer">
<a-tooltip placement="top" :title="iconmap.value" :get-popup-container="getPopupContainer">
<span :class="iconmap.icon"></span>
</a-tooltip>
</span>
@ -33,45 +32,31 @@
<img v-else src="@/assets/components/arrow-down.png" alt="">
</div>
<div v-if="moreSelect.show">
<a-tree-select v-model:value="moreSelectValue" show-search :size="'small'" style="width: 110px"
<a-tree-select v-model:value="moreSelectValue" v-model:tree-expanded-keys="treeExpandedKeys"
show-search :size="'small'" style="width: 110px"
:dropdown-style="{ maxHeight: '400px', overflow: 'auto', minWidth: '180px' }" placeholder=" "
:tree-data="processedMoreSelectOptions"
:field-names="{ label: 'title', value: 'value', children: 'children' }"
tree-node-filter-prop="label"
:expanded-keys="expandedKeys"
popup-class-name="no-wrap-tree-select"
@select="handleTreeSelect"
@expand="handleTreeExpand"
@dropdownVisibleChange="handleDropdownVisibleChange">
tree-node-filter-prop="label" popup-class-name="no-wrap-tree-select" @select="handleTreeSelect"
@expand="handleTreeExpand">
</a-tree-select>
</div>
<div v-if="datetimePicker.show">
<!-- 添加 locale 属性来设置语言 -->
<a-date-picker
v-model:value="datetimeValue"
show-time
:style="{ width: datetimePicker.picker === 'year' ? '80px' : '130px' }"
<a-date-picker v-model:value="datetimeValue" show-time
:style="{ width: datetimePicker.picker === 'year' ? '80px' : datetimePicker.picker === 'month' ? '90px' : '130px' }"
:format="datetimePicker.format !== null ? datetimePicker.format : undefined"
:picker="datetimePicker.picker"
:allowClear="false"
placeholder=" "
@change="handleDateTimeChange"
:size="'small'"
:picker="datetimePicker.picker" :allowClear="false" placeholder=" "
@change="handleDateTimeChange" :size="'small'"
:disabledDate="createDisabledDateFn(datetimePicker.picker)"
:disabledTime="disabledTimeForSinglePicker" />
<!-- 修改为 locale 变量 -->
</div>
<div v-if="scopeDate.show" class="title_scopeDate">
<a-range-picker
v-model:value="scopeDateValue"
:picker="scopeDate.picker"
:allowClear="false"
<a-range-picker v-model:value="scopeDateValue" :picker="scopeDate.picker" :allowClear="false"
:style="{ width: scopeDate.picker === 'year' ? '80px' : (scopeDate.picker === 'month' ? '180px' : '') }"
:format="scopeDate.format"
:range-separator="' 至 '"
:size="'small'"
:presets="computedScopeDatePresets"
:disabledDate="createDisabledDateFn(scopeDate.picker)" />
:format="scopeDate.format" :range-separator="' 至 '" :size="'small'"
:presets="computedScopeDatePresets" :disabledDate="createDisabledDateFn(scopeDate.picker)" />
</div>
<div v-if="tabs.show" class="typeOne">
<div @click="handleTabClick('one')" :class="tabsValue == 'one' ? 'typezhong' : ''">图片</div>
@ -86,7 +71,7 @@
</template>
<script lang="ts" setup>
import { ref, onMounted, watch, computed } from 'vue';
import { ref, onMounted, watch, computed, nextTick } from 'vue';
import {
QuestionCircleOutlined,
InfoCircleOutlined
@ -201,8 +186,9 @@ const scopeDateValue = ref<[Dayjs, Dayjs] | undefined>(
);
const tabsValue = ref(props.tabs.value)
//
const expandedKeys = ref<string[]>([]);
//
const treeExpandedKeys = ref<string[]>([])
const nodeMap = new Map<string, { node: any; parentKey: string | null }>()
/**
* 创建针对不同 picker 类型的日期禁用函数
@ -326,6 +312,54 @@ const processedMoreSelectOptions = computed(() => {
return processTreeData(props.moreSelect.options || []);
});
/**
* 构建树节点映射建立父子关系
* @param treeData 树形数据
* @param parentKey 父节点key
*/
const buildNodeMap = (treeData: any[], parentKey: string | null = null) => {
if (!treeData || !Array.isArray(treeData)) return
treeData.forEach(node => {
//
nodeMap.set(node.value, {
node,
parentKey
})
//
if (node.children && node.children.length > 0) {
buildNodeMap(node.children, node.value)
}
})
}
/**
* 获取目标节点的所有父节点keys
* @param targetValue 目标节点value
* @returns 父节点keys数组从根到直接父节点
*/
const getParentKeys = (targetValue: string): string[] => {
const parentKeys: string[] = []
let currentKey: string | null = targetValue
//
while (currentKey !== null && currentKey !== undefined) {
const nodeInfo = nodeMap.get(currentKey)
//
if (!nodeInfo || nodeInfo.parentKey === null || nodeInfo.parentKey === undefined) {
break
}
//
parentKeys.unshift(nodeInfo.parentKey)
currentKey = nodeInfo.parentKey
}
return parentKeys
}
/**
* 计算属性根据 picker 类型动态生成快捷日期选项
*/
@ -469,56 +503,7 @@ const handleTreeSelect = (selectedKeys: string | string[], info: any) => {
* @param keys 展开的节点键值数组
*/
const handleTreeExpand = (keys: string[]) => {
expandedKeys.value = keys;
};
/**
* 查找节点的父节点路径
* @param treeData 树数据
* @param targetValue 目标节点的值
* @returns 父节点值的数组从根到直接父节点
*/
const findParentKeys = (treeData: any[], targetValue: string): string[] => {
const parentKeys: string[] = [];
const search = (nodes: any[], target: string, parents: string[]): boolean => {
for (const node of nodes) {
if (node.value === target) {
//
parentKeys.push(...parents);
return true;
}
if (node.children && node.children.length > 0) {
//
if (search(node.children, target, [...parents, node.value])) {
return true;
}
}
}
return false;
};
search(treeData, targetValue, []);
return parentKeys;
};
/**
* 处理下拉框显示/隐藏变化事件
* 当下拉框打开且有选中值时自动展开该节点的所有父节点
* @param visible 下拉框是否可见
*/
const handleDropdownVisibleChange = (visible: boolean) => {
if (visible && moreSelectValue.value && processedMoreSelectOptions.value.length > 0) {
//
const parentKeys = findParentKeys(processedMoreSelectOptions.value, moreSelectValue.value);
if (parentKeys.length > 0) {
//
const mergedKeys = Array.from(new Set([...expandedKeys.value, ...parentKeys]));
expandedKeys.value = mergedKeys;
}
}
//
};
//
@ -619,19 +604,12 @@ watch(
}
);
// moreSelectValue
watch(
() => props.moreSelect.value,
(newVal) => {
if (newVal !== moreSelectValue.value) {
moreSelectValue.value = newVal;
//
if (newVal && processedMoreSelectOptions.value.length > 0) {
const parentKeys = findParentKeys(processedMoreSelectOptions.value, newVal);
if (parentKeys.length > 0) {
expandedKeys.value = Array.from(new Set([...expandedKeys.value, ...parentKeys]));
}
}
}
}
);
@ -676,18 +654,45 @@ watch(
}
);
// moreSelectValue
watch(() => moreSelectValue.value, (newValue) => {
console.log('moreSelectValue 变化:', newValue)
if (newValue && nodeMap.size > 0) {
// nodeMap
const parentKeys = getParentKeys(newValue as string)
treeExpandedKeys.value = parentKeys
console.log('自动展开父节点:', parentKeys)
} else {
treeExpandedKeys.value = []
}
}, { immediate: true }) // immediate: true
//
watch(() => processedMoreSelectOptions.value, (newData) => {
console.log('树数据变化,重新构建映射')
if (newData && newData.length > 0) {
//
nodeMap.clear()
//
buildNodeMap(newData)
//
if (moreSelectValue.value) {
nextTick(() => {
const parentKeys = getParentKeys(moreSelectValue.value as string)
treeExpandedKeys.value = parentKeys
console.log('数据加载后自动展开父节点:', parentKeys)
})
}
}
}, { deep: true })
//
onMounted(() => {
//
emitAllValues();
// tree-select
if (moreSelectValue.value && processedMoreSelectOptions.value.length > 0) {
const parentKeys = findParentKeys(processedMoreSelectOptions.value, moreSelectValue.value);
if (parentKeys.length > 0) {
expandedKeys.value = parentKeys;
}
}
});
</script>

View File

@ -0,0 +1,270 @@
<!-- ArtsDetail.vue -->
<template>
<div class="arts-detail">
<div class="card-container">
<div class="card_left">
<div class="card_img">
<div class="carousel-wrapper">
<div class="carousel-inner">
<div class="carousel-track" :style="trackStyle">
<div v-for="(item, index) in originalMediaData" :key="index" class="carousel-item">
<img :src="item.image" :alt="item.title" />
</div>
</div>
</div>
</div>
</div>
</div>
<div class="card_right">
<div class="card_text_title"></div>
<div class="card_text_content">
{{ currentDescription }}
</div>
</div>
</div>
<div style="width: 100%;display: flex;justify-content: center;">
<div class="card_button">
<LeftOutlined @click="prevSlide" />
<div class="pagination-dots">
<span v-for="(item, index) in originalMediaData" :key="index" class="dot-item"
:class="{ 'active': currentIndex === index }" @click="jumpToSlide(index)" />
</div>
<RightOutlined @click="nextSlide" />
</div>
</div>
</div>
</template>
<script lang="ts" setup>
import { ref, computed, onMounted, watch } from 'vue';
import { LeftOutlined, RightOutlined } from '@ant-design/icons-vue';
defineOptions({
name: 'ArtsDetail'
});
const props = defineProps<{
dataSource: Array<{
title: string;
description: string;
image: string;
}>;
}>();
interface MediaItem {
title: string;
description: string;
image: string;
}
const originalMediaData = ref<MediaItem[]>([]);
const currentIndex = ref(0);
//
const initOriginalData = () => {
if (props.dataSource && props.dataSource.length > 0) {
originalMediaData.value = props.dataSource.map(item => ({
title: item.title,
description: item.description,
image: item.image
}));
}
};
// - React
const trackStyle = computed(() => {
return {
transform: `translateX(calc((68.8px - 48vw) * ${currentIndex.value}))`
};
});
//
const nextSlide = () => {
if (currentIndex.value < originalMediaData.value.length - 1) {
currentIndex.value++;
}
};
//
const prevSlide = () => {
if (currentIndex.value > 0) {
currentIndex.value--;
}
};
//
const jumpToSlide = (targetIndex: number) => {
currentIndex.value = targetIndex;
};
//
const currentDescription = computed(() => {
return originalMediaData.value[currentIndex.value]?.description || '';
});
//
onMounted(() => {
initOriginalData();
});
// dataSource
watch(
() => props.dataSource,
(newData) => {
if (newData && newData.length > 0) {
currentIndex.value = 0;
initOriginalData();
}
},
{ deep: true }
);
</script>
<style lang="scss" scoped>
.arts-detail {
width: 100%;
box-sizing: border-box;
.card-container {
width: 100%;
display: flex;
justify-content: space-between;
align-items: center;
box-sizing: border-box;
}
.card_left {
// flex: 6;
width: 60%;
height: 630px;
padding: 12px;
border: 1px solid rgba(0, 0, 0, 0.12);
box-sizing: border-box;
.card_img {
width: 100%;
height: 100%;
padding: 12px;
border: 1px solid rgba(0, 0, 0, 0.12);
overflow: hidden;
box-sizing: border-box;
}
.carousel-wrapper {
width: 100%;
height: 100%;
position: relative;
overflow: hidden;
.carousel-inner {
width: 100%;
height: 100%;
overflow: hidden;
}
.carousel-track {
height: 100%;
display: flex;
justify-content: flex-start;
align-items: center;
flex-wrap: nowrap;
transition: transform 0.3s ease-in-out;
.carousel-item {
width: calc(48vw - 80.8px);
flex: 0 0 calc(48vw - 80.8px);
height: 100%;
margin-right: 12px;
display: flex;
justify-content: center;
align-items: center;
img {
max-height: 100%;
max-width: 100%;
border: none;
object-fit: contain;
}
}
}
}
}
.card_right {
// flex: 4;
width: 40%;
height: 630px;
padding-left: 40px;
box-sizing: border-box;
.card_text_title {
width: 6px;
height: 24px;
background-color: rgb(108, 164, 247);
box-sizing: border-box;
}
.card_text_content {
width: 100%;
height: 96%;
padding: 0px 16px 16px 0px;
box-sizing: border-box;
line-height: 1.5;
overflow: auto;
font-size: 20px;
text-indent: 2em;
}
}
.card_button {
display: flex;
// justify-content: space-between;
align-items: center;
padding: 16px 0;
box-sizing: border-box;
.pagination-dots {
display: flex;
align-items: center;
.dot-item {
display: inline-block;
width: 12px;
height: 12px;
border-radius: 50%;
background-color: #adaaaa;
margin: 0 6px;
cursor: pointer;
transition: all 0.3s ease;
&:hover {
background-color: #8a8a8a;
transform: scale(1.1);
}
&.active {
background-color: #5E9BFE;
transform: scale(1.2);
}
}
}
:deep(.anticon) {
font-size: 24px;
cursor: pointer;
color: #6c8cf7;
transition: all 0.3s;
&:hover:not([disabled]) {
color: #4a6fd4;
transform: scale(1.1);
}
&[disabled] {
color: #d9d9d9;
cursor: not-allowed;
}
}
}
}
</style>

View File

@ -21,7 +21,9 @@ import type { ECharts } from 'echarts';
import SidePanelItem from '@/components/SidePanelItem/index.vue';
import { useJidiSelectEventStore } from "@/store/modules/jidiSelectEvent";
import { getChuiXiangShuiWenTreeStcd, getCxswList } from "@/api/sw";
import { useModelStore } from "@/store/modules/model";
const modelStore = useModelStore();
const JidiSelectEventStore = useJidiSelectEventStore();
const baseid = ref('');
@ -109,11 +111,26 @@ function arrMin(arr: number[]): number {
/**
* API 返回的 aggregates 数据转换为图表数据格式
*/
function transformAggregatesToChartData(aggregates: Record<string, any[]>): Array<{
function transformAggregatesToChartData(
aggregates: Record<string, any[]>,
stcd: string,
stnm: string
): Array<{
name: string;
dataXy: Array<[number, number]>;
dataXy: Array<{
value: [number, number];
stcd: string;
stnm: string;
}>;
}> {
const result: Array<{ name: string; dataXy: Array<[number, number]> }> = [];
const result: Array<{
name: string;
dataXy: Array<{
value: [number, number];
stcd: string;
stnm: string;
}>;
}> = [];
for (let month = 1; month <= 12; month++) {
const key = month.toString();
@ -128,11 +145,19 @@ function transformAggregatesToChartData(aggregates: Record<string, any[]>): Arra
continue;
}
// { wthg, vwt } [vwt, wthg]
const dataXy: Array<[number, number]> = monthData.map((item: any) => [
//
const dataXy: Array<{
value: [number, number];
stcd: string;
stnm: string;
}> = monthData.map((item: any) => ({
value: [
parseFloat(item.vwt), //
parseInt(item.wthg) // /
]);
] as [number, number], //
stcd: stcd, //
stnm: stnm //
}));
result.push({
name: `${month}`,
@ -336,8 +361,15 @@ const selectOptions = async () => {
const chartRef = ref<HTMLElement | null>(null);
let chartInstance: ECharts | null = null;
//
const chartData = ref<Array<{ name: string; dataXy: Array<[number, number]> }>>([]);
//
const chartData = ref<Array<{
name: string;
dataXy: Array<{
value: [number, number];
stcd: string;
stnm: string;
}>;
}>>([]);
// Y1=2=
const type = ref(1);
@ -386,8 +418,18 @@ const fetchChartData = async (stcd: string, year: string | number) => {
const aggregates = res?.data?.aggregates;
if (aggregates) {
//
chartData.value = transformAggregatesToChartData(aggregates);
//
let currentStnm = '';
select.value.options.forEach((station: any) => {
station.children?.forEach((child: any) => {
if (child.value === stcd) {
currentStnm = child.title;
}
});
});
// stcd stnm
chartData.value = transformAggregatesToChartData(aggregates, stcd, currentStnm);
// 12 chartData.value.length
const { colors: newColors, selected: newSelected } = generateColorsAndVisibility(
@ -433,6 +475,30 @@ const initChart = () => {
}
chartInstance = echarts.init(chartRef.value);
//
chartInstance.on('click', (params: any) => {
console.log('图表数据点被点击:', params);
// params.data stcd stnm
const stcd = params.data?.stcd;
const stnm = params.data?.stnm;
const temperature = params.data?.value?.[0];
const depth = params.data?.value?.[1];
console.log('站点编码:', stcd);
console.log('站点名称:', stnm);
console.log('水温:', temperature);
console.log('深度:', depth);
modelStore.modalVisible = true;
modelStore.params.sttp = "wt_point";
modelStore.title = stnm + "详情信息";
// modelStore.isBasicEdit = true;
modelStore.params.stcd = stcd;
// TODO:
});
updateChart();
};
@ -446,14 +512,15 @@ const updateChart = () => {
chartData.value.forEach((item) => {
if (item.dataXy.length > 0) {
xData.push(item.dataXy[0][0]);
// value[0]
xData.push(item.dataXy[0].value[0]);
}
legend.push(item.name);
yData.push({
name: item.name,
data: item.dataXy,
data: item.dataXy, // 使
type: 'line',
smooth: true,
connectNulls: true,
@ -483,8 +550,9 @@ const updateChart = () => {
formatter: (params: any) => {
if (!params || !params.seriesName) return '';
const temperature = params.data[0];
const depth = params.data[1];
//
const temperature = params.data?.value?.[0];
const depth = params.data?.value?.[1];
return `<span>${params.seriesName}</span><br />
<span>水温()${temperature}</span><br />

View File

@ -0,0 +1,378 @@
<template>
<div class="body_fu">
<!-- 左侧 ECharts 图表 -->
<a-spin :spinning="loading" tip="加载中...">
<div class="echarts">
<div ref="chartRef" class="echarts"></div>
</div>
</a-spin>
<!-- 右侧 BasicTable 表格 -->
<BasicTable ref="tableRef" :scrollY="460" :columns="columns" :list-url="inOutOneDetails" :search-params="{ sort: sort }"
:transform-data="customTransform" />
</div>
</template>
<script lang="ts" setup>
import { ref, onMounted, onUnmounted, watch, nextTick } from 'vue';
import * as echarts from 'echarts';
import dayjs from 'dayjs';
import { inOutOneDetails } from '@/api/sw';
import BasicTable from '@/components/BasicTable/index.vue';
// ==================== Props ====================
const props = defineProps<{
tm: [string, string]; // [startDate, endDate]
stcd: string; //
}>();
// ==================== DOM ====================
const chartRef = ref<HTMLDivElement>();
let chartInstance: echarts.ECharts | null = null;
const tableRef = ref<any>(null);
const loading = ref(false);
let sort = [
{
field: "tm",
dir: "asc"
}
]
// ==================== ===================
const columns = [
{
title: '监测时间点',
dataIndex: 'dt',
width: 180,
customRender: ({ text }: any) => text ? dayjs(text).format('YYYY-MM-DD HH:mm:ss') : '-'
},
{
title: '入库水温(℃)',
dataIndex: 'iwtValue',
width: 120,
customRender: ({ text }: any) => {
const val = Number(text);
return isNaN(val) ? '-' : val.toFixed(1);
}
},
{
title: '出库水温(℃)',
dataIndex: 'dwtValue',
width: 120,
customRender: ({ text }: any) => {
const val = Number(text);
return isNaN(val) ? '-' : val.toFixed(1);
}
}
];
const customTransform = (res: any) => {
console.log('图表数据:', res);
return {
records: res?.data?.data || [],
total: res?.data?.total || 0
};
};
// ==================== ====================
/**
* 计算Y轴显示范围
* 基于数据最小值和最大值,上下各扩展0.2的缓冲空间
* @param data - 水温数据数组(可能包含null值)
* @returns Y轴的最小值和最大值对象
*/
function calculateYAxisRange(data: (number | null)[]) {
//
const validValues = data.filter(v => v !== null) as number[]
if (validValues.length === 0) return { min: 0, max: 10 } //
const min = Math.min(...validValues)
const max = Math.max(...validValues)
// 0.2,线
return {
min: Number((min - 0.1).toFixed(1)), // 0.2
max: Number((max + 0.1).toFixed(1)) // 0.2
}
}
/**
* 计算X轴刻度间隔
* 确保最多显示4个刻度标签,避免标签重叠
* @param dateCount - 日期总数
* @returns X轴刻度间隔值(interval属性)
*/
function calculateXAxisInterval(dateCount: number): number {
if (dateCount <= 4) return 0 // 4,
return Math.ceil(dateCount / 4) - 1 // ,4
}
// ==================== ====================
const initChart = () => {
if (!chartRef.value) return;
const rect = chartRef.value.getBoundingClientRect();
if (rect.width === 0 || rect.height === 0) {
setTimeout(() => initChart(), 50);
return;
}
chartInstance = echarts.init(chartRef.value);
window.addEventListener('resize', handleResize);
};
const updateChart = (data: any[]) => {
if (!chartInstance) return;
const xData: string[] = [];
const inData: (number | null)[] = [];
const outData: (number | null)[] = [];
data.forEach((item: any) => {
xData.push(item.dt);
inData.push(item.iwtValue !== null && item.iwtValue !== undefined ? Number(item.iwtValue) : null);
outData.push(item.dwtValue !== null && item.dwtValue !== undefined ? Number(item.dwtValue) : null);
});
//
const xAxisInterval = calculateXAxisInterval(xData.length);
const { min: yAxisMin, max: yAxisMaxValue } = calculateYAxisRange([...inData, ...outData]);
const option = {
//
tooltip: {
trigger: 'axis',
backgroundColor: 'rgba(50, 50, 50, 0.9)',
borderColor: 'transparent',
textStyle: {
color: '#fff',
fontSize: 14
},
axisPointer: {
type: 'line',
lineStyle: {
color: 'rgba(91, 143, 249, 0.15)',
width: 30,
type: 'solid'
}
},
formatter: function (params: any) {
if (!params || params.length === 0) return '';
const date = params[0].name;
let result = `<div style="font-weight: bold; margin-bottom: 8px; font-size: 14px;">${date}</div>`;
params.forEach((item: any) => {
const value = item.value !== null && item.value !== undefined ? `${item.value}°C` : '-';
result += `<div style="display: flex; align-items: center; gap: 6px; margin: 4px 0; font-size: 14px;">
<span style="display: inline-block; width: 10px; height: 10px; border-radius: 50%; background-color: ${item.color};"></span>
<span>${item.seriesName}</span>
<span style="font-weight: bold; margin-left: auto;">${value}</span>
</div>`;
});
return result;
}
},
// //
// title: {
// text: '(°C)',
// left: 17,
// top: 13,
// textStyle: {
// fontSize: 13,
// color: '#000000',
// fontWeight: 'normal'
// }
// },
legend: {
data: ['入库水温', '出库水温'],
top: 0
},
grid: {
left: '7%',
right: '4%',
bottom: '3%',
containLabel: true
},
xAxis: {
type: 'category',
boundaryGap: true,
data: xData,
interval: xAxisInterval,
axisLine: {
show: true,
lineStyle: {
color: '#000000'
}
},
axisTick: {
show: false
},
axisLabel: {
color: '#000000',
fontSize: 10,
margin: 5
},
splitLine: {
show: true,
lineStyle: {
color: '#bfbfbf',
type: 'solid'
}
}
},
//
dataZoom: [
{
type: 'inside',
start: 0,
end: 100,
minValueSpan: 2,
zoomOnMouseWheel: true,
moveOnMouseMove: true,
moveOnMouseWheel: false,
zoomLock: false,
throttle: 50
}
],
yAxis: {
type: 'value',
name: '水温(℃)',
min: yAxisMin,
max: yAxisMaxValue,
scale: true,
axisLine: {
show: true,
lineStyle: {
color: '#000000',
width: 1
}
},
axisTick: {
show: true,
lineStyle: {
color: '#000000',
width: 1
},
length: 4
},
axisLabel: {
color: '#000000',
fontSize: 10,
formatter: '{value}'
},
splitLine: {
show: true,
lineStyle: {
color: '#bfbfbf',
type: 'solid'
}
}
},
series: [
{
name: '入库水温',
data: inData,
type: 'line',
smooth: true,
symbol: 'none',
connectNulls: true,
color: '#5470c6'
},
{
name: '出库水温',
data: outData,
type: 'line',
smooth: true,
symbol: 'none',
connectNulls: true,
color: '#91cc75'
}
]
};
chartInstance.setOption(option);
};
const handleResize = () => {
chartInstance?.resize();
};
// ==================== ====================
const fetchData = async () => {
if (!props.stcd || !props.tm || !props.tm[0]) return;
loading.value = true;
try {
//
console.log('图表点击事件:', props.tm);
const startTime = dayjs(props.tm[0]).startOf('day').format('YYYY-MM-DD HH:mm:ss');
const endTime = dayjs(props.tm[1]).endOf('day').format('YYYY-MM-DD HH:mm:ss');
const filter = {
logic: 'and',
filters: [
{
logic: "or",
filters: [
{ field: "engDwtCode", operator: "eq", value: props.stcd },
{ field: "engIwtCode", operator: "eq", value: props.stcd }
]
},
{ field: "tm", operator: 'gte', dataType: "date", value: startTime },
{ field: "tm", operator: 'lte', dataType: "date", value: endTime }
]
};
tableRef.value?.getList(filter);
//
const res = await inOutOneDetails({ filter, sort: [{ field: "tm", dir: "asc" }] });
let dataOne = res?.data?.data || res?.data;
if (dataOne) {
updateChart(dataOne);
//
}
} catch (error) {
console.error('获取出入库水温数据失败:', error);
} finally {
loading.value = false;
}
};
// ==================== ====================
onMounted(() => {
initChart();
fetchData();
});
onUnmounted(() => {
window.removeEventListener('resize', handleResize);
chartInstance?.dispose();
});
// props
watch(() => [props.tm, props.stcd], () => {
fetchData();
}, { deep: true });
</script>
<style lang="scss" scoped>
.body_fu {
width: 100%;
display: flex;
height: 570px;
gap: 10px;
.echarts {
min-width: 541px;
height: 500px;
}
}
</style>

View File

@ -14,6 +14,15 @@
<a-empty />
</div>
</a-spin>
<!-- 数据点详情弹框 -->
<a-modal v-model:open="modalVisible" :title="`${stationName}出入库水温`" :width="1536" :footer="null"
@cancel="handleModalClose">
<!-- 弹框内容区域 - 待完善 -->
<div class="modal-content">
<churukushuiwen v-if="clickDataInfo" :tm="[clickDataInfo.date, clickDataInfo.date]" :stcd="paramsOne.value" />
</div>
</a-modal>
</SidePanelItem>
</template>
@ -24,7 +33,7 @@ import dayjs from 'dayjs';
import SidePanelItem from '@/components/SidePanelItem/index.vue';
import { getVmsstbprpt, inOutOneGetKendoListCust } from '@/api/sw';
import { useJidiSelectEventStore } from "@/store/modules/jidiSelectEvent";
import churukushuiwen from './churukushuiwen.vue'
// ==================== ====================
// (便)
defineOptions({
@ -64,6 +73,9 @@ const scopeDate = ref({
// ==================== ====================
const baseid = ref(''); // ID
const modalVisible = ref(false); //
const clickDataInfo = ref<any>(null); //
const stationName = ref(''); //
// ==================== () ====================
const jiDiList: any = ref([
@ -201,7 +213,7 @@ function calculateYAxisRange(data: (number | null)[]) {
// 0.2,线
return {
min: Number((min - 0.2).toFixed(1)), // 0.2
max: Number((max + 0.2).toFixed(1)) // 0.2
max: Number((max + 0.1).toFixed(1)) // 0.2
}
}
@ -215,7 +227,7 @@ function calculateXAxisInterval(dateCount: number): number {
if (dateCount <= 4) return 0 // 4,
return Math.ceil(dateCount / 4) - 1 // ,4
}
const paramsOne = ref({
const paramsOne:any = ref({
value: '',
tm: [currentMonth, currentMonth]
})
@ -528,6 +540,11 @@ function updateChart(dates: string[], iwtData: (number | null)[], dwtData: (numb
//
chartInstance.setOption(option, true); // true
//
chartInstance.off('click'); //
chartInstance.on('click', handleChartClick);
console.log('图表更新成功')
// ,
@ -582,6 +599,45 @@ const handleResize = () => {
// ==================== ====================
/**
* 图表数据点点击事件处理
* @param params - ECharts点击事件参数
*/
const handleChartClick = (params: any) => {
console.log('图表点击事件:', params);
//
if (params.componentType === 'series') {
const { seriesName, name, value, dataIndex } = params;
//
clickDataInfo.value = {
date: name, //
temperature: value, //
type: seriesName, // /
dataIndex: dataIndex //
};
//
// console.log(':', select.value.options, paramsOne.value.value);
const selectedStation = select.value.options
.flatMap((base: any) => base.children || [])
.find((station: any) => station.stcd === paramsOne.value.value);
stationName.value = selectedStation?.title || '未知站点';
//
modalVisible.value = true;
}
};
/**
* 弹框关闭处理
*/
const handleModalClose = () => {
modalVisible.value = false;
clickDataInfo.value = null;
};
/**
* SidePanelItem面板变化回调
* 处理下拉框和日期选择器的值变化
@ -730,7 +786,14 @@ watch(
align-items: center;
/* 固定高度252px */
}
:deep(.ant-spin-nested-loading) {
height: 252px !important;
}
/* 弹框内容样式 */
.modal-content {
min-height: 100px;
padding: 16px 0;
}
</style>

View File

@ -0,0 +1,525 @@
<template>
<div class="facility-detail-container">
<!-- 搜索区域 -->
<a-form ref="formRef" :model="formState" layout="inline" class="search-form">
<a-form-item label="所属基地" name="dataDimensionVal">
<a-select v-model:value="formState.dataDimensionVal" placeholder="请选择" style="width: 200px"
@change="handleBaseChange">
<a-select-option v-for="item in baseList" :key="item.baseid" :value="item.baseid">
{{ item.basename }}
</a-select-option>
</a-select>
</a-form-item>
<a-form-item label="接入状态" name="hydrodtin">
<a-select v-model:value="formState.hydrodtin" placeholder="请选择" style="width: 200px">
<a-select-option value="">全部</a-select-option>
<a-select-option value="0">未接入</a-select-option>
<a-select-option value="1">已接入</a-select-option>
</a-select>
</a-form-item>
<a-form-item label="建设状态" name="bldstt">
<a-select v-model:value="formState.bldstt" placeholder="请选择" style="width: 200px"
:loading="buildStateLoading">
<a-select-option v-for="item in buildStateList" :key="item.value" :value="item.value">
{{ item.label }}
</a-select-option>
</a-select>
</a-form-item>
<a-form-item label="设施名称" name="stnm">
<a-input v-model:value="formState.stnm" placeholder="请输入设施名称" style="width: 200px" allow-clear />
</a-form-item>
<a-form-item>
<a-space>
<a-button type="primary" @click="handleSearch">查询</a-button>
</a-space>
</a-form-item>
</a-form>
<!-- Tab标签页 -->
<a-tabs v-model:activeKey="activeTab" class="facility-tabs" @change="handleTabChange">
<a-tab-pane v-for="item in facilityStats" :key="item.key" :tab="`${item.name}(${item.count})`">
<!-- 表格区域 -->
<BasicTable style="overflow: visible;" v-show="activeTab === item.key"
:ref="(el) => setTableRef(el, item.key)" :scrollY="360" :columns="columns"
:list-url="vmsstbprptGetKendoList" :search-params="{ sort: sort }"
:transform-data="customTransform">
<template #bodyCell="{ column, record }">
<!-- 名称列 - 可点击 -->
<template v-if="column.key === 'stnm'">
<a @click="handleViewDetail(record, 'DW')" class="text-link">
{{ record.stnm }}
</a>
</template>
<!-- 关联电站列 - 可点击 -->
<template v-if="column.key === 'ennm'">
<a @click="handleViewDetail(record, 'ENG')" class="text-link">
{{ record.ennm || '-' }}
</a>
</template>
</template>
</BasicTable>
</a-tab-pane>
</a-tabs>
</div>
</template>
<script setup lang="ts">
import { ref, reactive, computed, onMounted, watch, nextTick } from 'vue'
import { message } from 'ant-design-vue'
import BasicTable from '@/components/BasicTable/index.vue'
// import { useJidiSelectEventStore } from '@/store/modules/jidiSelectEvent'
import { dictgetRemoteDictValue, dwInfoGetKendoListCust, vmsstbprptGetKendoList } from '@/api/sw'
import { useModelStore } from "@/store/modules/model";
const modelStore = useModelStore();
// Props
const props = defineProps<{
datas?: Array<{ baseid: string; basename: string }> //
BID?: string // ID
data?: { name: string } //
res?: { bldstt: string; hydrodtin: string } //
}>()
// Store ()
// const jidiStore = useJidiSelectEventStore()
// Refs
const formRef = ref()
// ref
const tableRefs = ref<Record<string, any>>({})
// ref
const setTableRef = (el: any, key: string) => {
if (el) {
tableRefs.value[key] = el
}
}
// tab
const getCurrentTable = () => {
return tableRefs.value[activeTab.value]
}
// State
const formState = reactive({
dataDimensionVal: props.BID || '',
hydrodtin: props.res?.hydrodtin || '',
bldstt: props.res?.bldstt || '',
stnm: ''
})
// dwtp
const dwtpMap: Record<string, string> = {
'叠梁门': '2',
'前置挡墙': '5',
'隔水幕墙': '6'
}
const currentDwtp = computed(() => {
return dwtpMap[props.data?.name || ''] || '9'
})
// activeTabkey
const activeTab = ref(currentDwtp.value)
const facilityStats = ref<Array<{ name: string; key: string; count: number }>>([])
const buildStateList = ref<Array<{ label: string; value: string }>>([])
const buildStateLoading = ref(false)
//
// - baseIdReact
const sort = computed(() => {
return formState.dataDimensionVal === 'all'
? [{ field: 'baseStepSort', dir: 'asc' }, { field: 'rvcdStepSort', dir: 'asc' }, { field: 'rstcdStepSort', dir: 'asc' }]
: [{ field: 'rvcdStepSort', dir: 'asc' }, { field: 'rstcdStepSort', dir: 'asc' }]
})
// propsstore
const baseList = computed(() => {
return props.datas || []
// store: return jidiStore.jidiData || []
})
//
const columns = [
{
title: '名称',
dataIndex: 'stnm',
key: 'stnm',
width: 150,
ellipsis: true
},
{
title: '接入状态',
dataIndex: 'dtinName',
key: 'dtinName',
width: 120
},
{
title: '接入日期',
dataIndex: 'dtinTm',
key: 'dtinTm',
width: 120
},
{
title: '建设状态',
dataIndex: 'bldsttCcodeName',
key: 'bldsttCcodeName',
width: 120
},
{
title: '开工日期',
dataIndex: 'ststdt',
key: 'ststdt',
width: 120
},
{
title: '建成日期',
dataIndex: 'jcdt',
key: 'jcdt',
width: 120
},
{
title: '所在河段',
dataIndex: 'hbrvcdName',
key: 'hbrvcdName',
width: 150,
ellipsis: true
},
{
title: '关联电站',
dataIndex: 'ennm',
key: 'ennm',
width: 150,
ellipsis: true
},
{
title: '关联基地',
dataIndex: 'baseName',
key: 'baseName',
width: 150,
ellipsis: true
}
]
// API -
const fetchFacilityStats = async () => {
try {
let params = {
"filter": {
"logic": "and",
"filters": [
formState.dataDimensionVal != 'all' ? {
"field": "baseId",
"operator": "eq",
"dataType": "string",
"value": formState.dataDimensionVal
} : null,
formState.hydrodtin ? {
"field": "dtin",
"operator": "eq",
"dataType": "string",
"value": formState.hydrodtin
} : null,
formState.bldstt ? {
"field": "bldsttCcode",
"operator": "eq",
"dataType": "string",
"value": "2"
} : null,
formState.stnm ? {
"field": "stnm",
"operator": "contains",
"dataType": "string",
"value": formState.stnm
} : null,
].filter(Boolean),
}
}
let res = await dwInfoGetKendoListCust(params)
let mockData = res.data.data
//
facilityStats.value = mockData.map((item: any) => ({
name: item.name,
count: item.totalNum,
key: item.dwtp === '1' ? '2' : item.dwtp === '2' ? '5' : item.dwtp === '3' ? '6' : '9'
}))
// tabtab
if (facilityStats.value.length > 0) {
const hasCurrentTab = facilityStats.value.some(item => item.key === activeTab.value)
if (!hasCurrentTab) {
activeTab.value = facilityStats.value[0].key
}
}
} catch (error) {
console.error('获取设施统计数据失败:', error)
message.error('获取设施统计数据失败')
}
}
// API -
//
const customTransform = (res: any) => {
return {
records: res?.data?.data || [],
total: res?.data?.total || 0
}
}
//
const fetchBuildStateList = async () => {
try {
buildStateLoading.value = true
// TODO: API
const res = await dictgetRemoteDictValue({ dictCode: 'BLDSTT3T' })
buildStateList.value = res.data.map((item: any) => ({
label: item.dictMeaning,
value: item.dictValue
}))
buildStateList.value.push({ label: '全部', value: '' })
// 使
// buildStateList.value = [
// { label: '', value: '' },
// { label: '', value: '0' },
// { label: '', value: '1' },
// { label: '', value: '2' }
// ]
} catch (error) {
console.error('获取建设状态列表失败:', error)
} finally {
buildStateLoading.value = false
}
}
//
const handleBaseChange = async () => {
formState.stnm = ''
await fetchFacilityStats()
handleSearch()
}
// Tab
const handleTabChange = (key: string) => {
activeTab.value = key
//
nextTick(() => {
handleSearch()
})
}
//
const handleSearch = () => {
const filters: any[] = [
activeTab.value != '9' ? {
"field": "sttpCode",
"operator": "eq",
"dataType": "string",
"value": 'DW_' + activeTab.value
} : null,
{
"field": "sttpFullPath",
"operator": "contains",
"dataType": "string",
"value": "ENV,ENVP,DW,DW_"
},
formState.dataDimensionVal != 'all' ? {
"field": "baseId",
"operator": "eq",
"dataType": "string",
"value": "01"
} : null,
formState.hydrodtin ? {
"field": "dtin",
"operator": "eq",
"dataType": "string",
"value": formState.hydrodtin
} : null,
formState.bldstt ? {
"field": "bldsttCcode",
"operator": "eq",
"dataType": "string",
"value": "2"
} : null,
formState.stnm ? {
"field": "stnm",
"operator": "contains",
"dataType": "string",
"value": formState.stnm
} : null,
].filter(Boolean)
const filList = [
{
"field": "sttpCode",
"operator": "neq",
"dataType": "string",
"value": "DW_2"
},
{
"field": "sttpCode",
"operator": "neq",
"dataType": "string",
"value": "DW_5"
},
{
"field": "sttpCode",
"operator": "neq",
"dataType": "string",
"value": "DW_6"
}
]
if (activeTab.value == '9') {
filList.forEach((item) => {
filters.push(item)
})
}
const filter = {
logic: 'and',
filters
}
//
getCurrentTable()?.getList(filter)
}
const typefenbian = (type: string) => {
if (type == '2') {
return 'dw_point'
} else if (type == '5') {
return 'DW_5'
} else if (type == '6') {
return 'DW_6'
} else {
return 'DW_1'
}
}
//
const handleViewDetail = (record: any, type: string) => {
if (type === 'DW') {
modelStore.modalVisible = true;
modelStore.params.sttp = typefenbian(activeTab.value);
modelStore.title = record.stnm;
modelStore.params.stcd = record.stcd;
} else if (type === 'ENG') {
modelStore.modalVisible = true;
modelStore.params.sttp = "ENG";
modelStore.title = record.stnm + " 详情信息";
modelStore.params.stcd = record.rstcd;
}
}
//
watch(
() => [formState.dataDimensionVal, formState.hydrodtin, formState.bldstt],
async () => {
await fetchFacilityStats()
handleSearch()
}
)
// props.datatab
watch(
() => props.data?.name,
async (newNameString) => {
const newDwtp = dwtpMap[newNameString || ''] || '9'
// dwtpactiveTab
if (newDwtp !== activeTab.value) {
activeTab.value = newDwtp
await fetchFacilityStats()
handleSearch()
}
},
{ immediate: false } //
)
// props.BID
watch(
() => props.BID,
(newBid) => {
// BIDformState.dataDimensionVal
if (newBid !== formState.dataDimensionVal) {
formState.dataDimensionVal = newBid || ''
}
},
{ immediate: false } // reactive
)
//
onMounted(async () => {
await fetchBuildStateList()
await fetchFacilityStats()
handleSearch()
})
//
defineExpose({
handleSearch
})
</script>
<style scoped lang="scss">
.facility-detail-container {
height: 100%;
display: flex;
flex-direction: column;
.search-form {
// padding: 16px;
background: #fff;
// margin-bottom: 16px;
border-radius: 4px;
:deep(.ant-form-item) {
margin-bottom: 16px;
}
}
.facility-tabs {
flex: 1;
overflow: hidden;
background: #fff;
border-radius: 4px;
:deep(.ant-tabs-content) {
height: calc(100% - 46px);
}
:deep(.ant-tabs-tabpane) {
height: 100%;
}
}
.text-link {
color: #2f6b98;
&:hover {
color: #40a9ff;
}
}
.modal-content {
padding: 20px;
}
}
:deep(.ant-tabs-content-holder) {
height: 450px !important;
}
</style>

View File

@ -1,49 +1,42 @@
<!-- SidePanelItem.vue -->
<template>
<SidePanelItem title="设施类型及接入情况">
<div class="card-container">
<div v-for="(item) in dataJson" class="facility-card">
<a-spin :spinning="loading">
<div v-if="dataJson.length > 0" class="card-container">
<div v-for="(item) in dataJson" class="facility-card" @click="imgclick(item)">
<div class="img_icon">
<i class="icon iconfont" :class="item?.icon" />
</div>
<div class="img_text">
<div>{{ item.nameString }}</div>
<div class="text_num">{{ item.total + item.zj }}</div>
<div>{{ item.name }}</div>
<div class="text_num">{{ item.totalNum }}</div>
</div>
</div>
<!-- <a-card v-for="(item, index) in dataJson" :key="index" class="facility-card" type="inner" size="small"
@click="handleCardClick(item)">
<template #title>
<i class="icon iconfont" :class="item?.icon" style="
display: block;
margin: 0 auto;
width: 40px;
height: 40px;
border-radius: 40px;
background-color: #5389B5;
line-height: 40px;
color: #FFFFFF;
" />
</template>
<a-tooltip>
<div style="font-size: 14px; color: #333">{{ item.nameString }}</div>
<div style="padding: 4px 0" class="showNum">
<span>{{ item.total + item.zj }}</span>
</div>
</a-tooltip>
</a-card> -->
<div v-else style="width: 100%;height: 158px;display: flex; align-items: center; justify-content: center;">
<a-empty description="暂无数据" />
</div>
<!-- <div v-else>
<a-empty />
</div> -->
</a-spin>
</SidePanelItem>
<!-- 自定义弹框 -->
<a-modal v-model:open="modalVisible" title="设施类型及接入情况" width="1536px" :footer="null">
<div class="modal-content">
<DiwenshuijianhuansheshileixingzuchengjijieruqingkuangTwoLayers :datas="jiDiList" :BID="baseid"
:data="selectedItem" :res="res">
</DiwenshuijianhuansheshileixingzuchengjijieruqingkuangTwoLayers>
</div>
</a-modal>
</template>
<script lang="ts" setup>
import { ref, onMounted } from 'vue';
import { ref, onMounted, watch } from 'vue';
import SidePanelItem from '@/components/SidePanelItem/index.vue';
import { dwInfoGetKendoListCust } from "@/api/sw";
import { useJidiSelectEventStore } from "@/store/modules/jidiSelectEvent";
import DiwenshuijianhuansheshileixingzuchengjijieruqingkuangTwoLayers from "./TwoLayers/diwenshuijianhuansheshileixingzuchengjijieruqingkuangTwoLayers.vue"
const JidiSelectEventStore = useJidiSelectEventStore();
// 便
defineOptions({
name: 'diwenshuijianhuansheshileixingzuchengjijieruqingkuang'
@ -52,45 +45,156 @@ defineOptions({
//
interface DataString {
icon?: string;
nameString: string;
num?: number;
total: number;
zj: number;
name: string;
totalNum?: number;
[key: string]: any;
}
//
const dataJson = ref<DataString[]>([
// dwtpicon
const iconMap: Record<string, string> = {
'1': 'icon-dwsjhDieliangmen',
'2': 'icon-dwsjhQianzhidangqiang',
'3': 'icon-dwsjhGeshuimuqiang',
'4': 'icon-dwsjhQita'
};
const jiDiList: any = ref([
{
icon: 'icon-dwsjhDieliangmen',
nameString: '叠梁门',
num: 18,
total: 18,
zj: 0
"baseid": "all",
"basename": "当前全部",
"id": "9BFEC848-83EA-AD22-6DE2-10E969476693"
},
{
icon: 'icon-dwsjhQianzhidangqiang',
nameString: '前置挡墙',
num: 4,
total: 4,
zj: 0
"baseid": "01",
"basename": "金沙江干流",
"id": "A33040B7-8977-D9F1-5E02-D7F0241AB8AA"
},
{
icon: 'icon-dwsjhGeshuimuqiang',
nameString: '隔水幕墙',
num: 1,
total: 1,
zj: 0
"baseid": "02",
"basename": "雅砻江干流",
"id": "C63D6020-995D-FE97-2F4A-F619A7142C79"
},
{
icon: 'icon-dwsjhQita',
nameString: '其他',
num: 1,
total: 1,
zj: 0
"baseid": "03",
"basename": "大渡河干流",
"id": "CCB0766B-F1D4-7DD6-650F-5C556B7231B3"
},
{
"baseid": "04",
"basename": "乌江干流",
"id": "E8A66641-B4F4-CC85-0815-38C5B2F93DBD"
},
{
"baseid": "05",
"basename": "长江上游干流",
"id": "E02C11E9-CEA5-A030-202F-3BFE84465D03"
},
{
"baseid": "10",
"basename": "湘西",
"id": "B1D1D52D-CEEF-6DC4-27DF-9990EB572F8D"
},
{
"baseid": "08",
"basename": "黄河上游干流",
"id": "BC6AF135-263D-09A9-D5CA-C99B2598FE6E"
},
{
"baseid": "09",
"basename": "黄河中游干流",
"id": "60EEEC28-128F-A2A8-44F3-6EAAC8FD8BB6"
},
{
"baseid": "06",
"basename": "南盘江-红水河",
"id": "7BB9A8F4-34B5-42B4-A7FC-CE910AD7F203"
},
{
"baseid": "12",
"basename": "东北",
"id": "47F8EF06-924E-E161-FCAF-62A66BBF252D"
},
{
"baseid": "07",
"basename": "澜沧江干流",
"id": "A966A4C9-278C-B0DA-2B97-2D10B7A6E96A"
},
{
"baseid": "13",
"basename": "怒江干流",
"id": "FA89E8CB-67A8-76DA-DC1E-23AE4C54F9E4"
},
{
"baseid": "11",
"basename": "闽浙赣",
"id": "CD98F995-EEB2-1021-D807-DA1B1AD9E49A"
},
{
"baseid": "other",
"basename": "其他",
"id": "AFBDFC67-B955-4EFD-959A-014CFB59EBFC"
}
]);
const res = ref({
hydrodtin: '',
bldstt: ''
})
//
const dataJson = ref<DataString[]>([]);
//
const loading = ref(false);
const baseid = ref('')
//
const modalVisible = ref(false);
const selectedItem: any = ref<DataString | null>(null);
const getListData = async () => {
loading.value = true;
try {
const params = {
"filter": {
"logic": "and",
"filters": [
baseid.value == 'all' ? null : {
"field": "baseId",
"operator": "contains",
"dataType": "string",
"value": baseid.value
}
].filter(Boolean),
}
}
let res = await dwInfoGetKendoListCust(params)
let data = res?.data?.data || res?.data || []
// icon
if (Array.isArray(data) && data.length > 0) {
dataJson.value = data.map((item: any) => ({
...item,
icon: iconMap[item.dwtp] || 'icon-dwsjhQita' // dwtpicon使""
}))
} else {
dataJson.value = [];
}
} catch (error) {
console.error('获取设施类型数据失败:', error);
dataJson.value = [];
} finally {
loading.value = false;
}
};
watch(
() => JidiSelectEventStore.selectedItem,
(newVal) => {
baseid.value = newVal.wbsCode;
getListData()
},
{ deep: true, immediate: true }
);
//
// const handleCardClick = (item: DataString) => {
// clickList.value = item;
@ -100,11 +204,17 @@ const dataJson = ref<DataString[]>([
// hydrodtin: ''
// };
// };
const imgclick = (item: DataString) => {
//
console.log(item)
selectedItem.value = item;
modalVisible.value = true;
}
//
onMounted(() => {
//
// loadData();
// getListData();
});
</script>
@ -143,6 +253,7 @@ onMounted(() => {
margin-top: 20px;
font-size: 14px;
color: #333;
.text_num {
width: 100%;
margin-top: 3px;
@ -154,4 +265,12 @@ onMounted(() => {
}
}
}
:deep(.ant-spin-nested-loading) {
height: 158px !important;
}
.modal-content {
padding: 20px;
}
</style>

View File

@ -1,51 +1,429 @@
<!-- NEW_FILE_CODE -->
<template>
<SidePanelItem title="月平均水温历史对比">
<div>
<div class="water-temp-compare-chart" ref="chartRef"></div>
<SidePanelItem title="月平均水温历史对比" :moreSelect="select" :datetimePicker="datetimePicker"
@update-values="handlePanelChange1">
<a-spin :spinning="loading" tip="加载中...">
<div v-show="!loading && showemit" class="water-temp-compare-chart" ref="chartRef"></div>
<div v-show="!loading && !showemit" class="water-temp-compare-chart">
<a-empty description="暂无数据" />
</div>
<!-- <div v-else>
<a-empty />
</div> -->
</a-spin>
</SidePanelItem>
<!-- 数据点详情弹框 -->
<a-modal
v-model:open="modalVisible"
:title="'月均水温对比'"
width="1536px"
:footer="null"
@cancel="handleModalClose"
>
<div v-if="modalData" class="modal-content">
<MonthlyAverage
:tm="modalData.date ? moment(modalData.date).format('YYYY-MM') + '-01 00:00:00' : moment().format('YYYY-MM') + '-01 23:59:59'"
:dataDimensionVal="modalData.baseid"
:rvcd='modalData.rvcd'
:stcd="modalData.stcd"
:jdList="jiDiList"
/>
</div>
</a-modal>
</template>
<script setup lang="ts">
import { ref, onMounted, onBeforeUnmount } from 'vue'
import { ref, onMounted, onBeforeUnmount, watch } from 'vue'
import * as echarts from 'echarts'
import SidePanelItem from '@/components/SidePanelItem/index.vue';
import { wbsbGetKendoList, avgMonGetKendoListCust } from "@/api/sw";
import { useJidiSelectEventStore } from "@/store/modules/jidiSelectEvent";
import MonthlyAverage from './monthlyAverage.vue'
import moment from 'moment'
defineOptions({
name: 'monthlyAvgWaterTemCompareHistory'
});
const JidiSelectEventStore = useJidiSelectEventStore();
const chartRef = ref<HTMLElement | null>(null)
let chartInstance: echarts.ECharts | null = null
const unit = '°C'
const baseid: any = ref(null);
const loading = ref(false);
const showemit = ref(true);
const initChart = () => {
//
const modalVisible = ref(false);
const modalData = ref<any>(null);
const stationName = ref('');
//
const jiDiList: any = ref([
{
"baseid": "all",
"basename": "当前全部",
"id": "9BFEC848-83EA-AD22-6DE2-10E969476693"
},
{
"baseid": "01",
"basename": "金沙江干流",
"id": "A33040B7-8977-D9F1-5E02-D7F0241AB8AA"
},
{
"baseid": "02",
"basename": "雅砻江干流",
"id": "C63D6020-995D-FE97-2F4A-F619A7142C79"
},
{
"baseid": "03",
"basename": "大渡河干流",
"id": "CCB0766B-F1D4-7DD6-650F-5C556B7231B3"
},
{
"baseid": "04",
"basename": "乌江干流",
"id": "E8A66641-B4F4-CC85-0815-38C5B2F93DBD"
},
{
"baseid": "05",
"basename": "长江上游干流",
"id": "E02C11E9-CEA5-A030-202F-3BFE84465D03"
},
{
"baseid": "10",
"basename": "湘西",
"id": "B1D1D52D-CEEF-6DC4-27DF-9990EB572F8D"
},
{
"baseid": "08",
"basename": "黄河上游干流",
"id": "BC6AF135-263D-09A9-D5CA-C99B2598FE6E"
},
{
"baseid": "09",
"basename": "黄河中游干流",
"id": "60EEEC28-128F-A2A8-44F3-6EAAC8FD8BB6"
},
{
"baseid": "06",
"basename": "南盘江-红水河",
"id": "7BB9A8F4-34B5-42B4-A7FC-CE910AD7F203"
},
{
"baseid": "12",
"basename": "东北",
"id": "47F8EF06-924E-E161-FCAF-62A66BBF252D"
},
{
"baseid": "07",
"basename": "澜沧江干流",
"id": "A966A4C9-278C-B0DA-2B97-2D10B7A6E96A"
},
{
"baseid": "13",
"basename": "怒江干流",
"id": "FA89E8CB-67A8-76DA-DC1E-23AE4C54F9E4"
},
{
"baseid": "11",
"basename": "闽浙赣",
"id": "CD98F995-EEB2-1021-D807-DA1B1AD9E49A"
},
{
"baseid": "other",
"basename": "其他",
"id": "AFBDFC67-B955-4EFD-959A-014CFB59EBFC"
}
]);
//
const select = ref({
show: true,
value: '',
options: [],
picker: undefined,
format: undefined,
});
//
const datetimePicker = ref({
show: true,
value: (() => {
const now = new Date();
const year = now.getFullYear();
const month = String(now.getMonth() + 1).padStart(2, '0');
return `${year}-${month}`;
})(),
format: "YYYY-MM",
picker: "month" as const,
options: [],
});
const filterSelectOptions = (selectOptions: any) => {
if (selectOptions?.length === 1 && selectOptions?.[0]?.children?.length === 1) {
return [{ ...selectOptions?.[0]?.children?.[0], isLeaf: true }]
} else if (selectOptions.length) {
return selectOptions.map((e) => {
if (e?.children?.length > 1) {
return { ...e, isLeaf: false, selectable: true, children: e.children.map((item) => ({ ...item, isLeaf: true })) }
} else {
return { ...e.children[0], isLeaf: true }
}
})
}
return []
}
//
const getSelectConfig = async () => {
loading.value = true;
try {
let obj: any = {}
if (baseid.value === 'all') {
obj = { rstcdStepSort: 'asc', siteStepSort: 'asc' }
} else {
obj = { siteStepSort: 'asc' }
}
const filters = [
{
field: 'wbsType',
operator: 'eq',
value: 'PSB_RVCD'
}
]
if (baseid.value && baseid.value !== 'all') {
filters.push({
field: 'objId',
operator: 'eq',
value: baseid.value
})
}
let data: any = {
filter: {
logic: 'and',
filters
}
}
if (obj) {
data.sort = [obj]
} else {
data = {
...data,
group: [
{ dir: 'asc', field: 'orderIndex' },
{ dir: 'asc', field: 'wbsCode' },
{ dir: 'asc', field: 'wbsName' },
{ dir: 'asc', field: 'objid' },
],
groupResultFlat: true
}
}
let res = await wbsbGetKendoList(data)
let dataOne = res?.data?.data || res?.data
if (dataOne.length > 0) {
let dataMapNameMap: any = {}
let jiDiListMap: any = {}
jiDiList.value.forEach((item: any) => {
jiDiListMap[item.baseid] = item.basename
})
dataOne.map((item: any, index: number) => {
const { stcd, stnm, objId } = item
if (dataMapNameMap[objId]) {
dataMapNameMap[objId].push({ ...item, baseName: jiDiListMap[objId], baseId: objId })
} else {
dataMapNameMap[objId] = [{ ...item, baseName: jiDiListMap[objId], baseId: objId }]
}
return { label: item.wbsName, value: item.wbsCode, baseId: objId, baseName: jiDiListMap[objId] }
})
let dataMapNameArr: any = []
Object.keys(dataMapNameMap).forEach((item: any) => {
dataMapNameArr.push({
title: dataMapNameMap[item]?.[0]?.baseName,
value: item,
selectable: false,
children: dataMapNameMap[item].map((item2: any) => {
return {
title: item2.wbsName,
value: item2.wbsCode
}
})
})
})
dataMapNameArr = dataMapNameArr.sort((a: any, b: any) => {
const indexA = jiDiList.value.findIndex(item => item.baseid === a.value);
const indexB = jiDiList.value.findIndex(item => item.baseid === b.value);
return indexA - indexB;
})
select.value.options = filterSelectOptions(dataMapNameArr)
if (baseid.value === 'all') {
select.value.value = 'SJLY176'
} else if (filterSelectOptions(dataMapNameArr)[0]?.children) {
select.value.value = filterSelectOptions(dataMapNameArr)[0]?.children[0]?.value
} else if (filterSelectOptions(dataMapNameArr)[0]?.value) {
select.value.value = filterSelectOptions(dataMapNameArr)[0]?.value
} else {
select.value.value = ''
}
}
getEchartsData()
} catch (error) {
console.error('选择器配置加载失败:', error);
} finally {
loading.value = false;
}
}
//
const getEchartsData = async () => {
if (loading.value) return; //
loading.value = true;
try {
// datetimePicker.value.value
const dateValue = datetimePicker.value.value || '';
const [yearStr, monthStr] = dateValue.split('-');
const year = parseInt(yearStr);
const month = parseInt(monthStr);
//
const firstDay = `${dateValue}-01 00:00:00`;
// 11
const nextMonth = month === 12 ? 1 : month + 1;
const nextYear = month === 12 ? year + 1 : year;
const lastDate = new Date(nextYear, nextMonth - 1, 1);
lastDate.setDate(lastDate.getDate() - 1);
const lastDay = `${year}-${String(month).padStart(2, '0')}-${String(lastDate.getDate()).padStart(2, '0')} 23:59:59`;
const params = {
"filter": {
"logic": "and",
"filters": [
{
"field": "rvcd",
"operator": "eq",
"dataType": "string",
"value": select.value.value
},
{
"field": "tm",
"operator": "gte",
"dataType": "date",
"value": firstDay
},
{
"field": "tm",
"operator": "lte",
"dataType": "date",
"value": lastDay
},
{
"field": "year",
"operator": "eq",
"dataType": "string",
"value": year
},
{
"field": "month",
"operator": "eq",
"dataType": "string",
"value": month
}
]
}
}
let res = await avgMonGetKendoListCust(params)
console.log('接口返回数据:', res)
let data = res?.data?.data || res?.data || []
if (data.length === 0) {
showemit.value = false
return
}
showemit.value = true
// sttp === "1" X
const stationData = data.filter((item: any) => item.sttp === "1")
const xData = stationData.map((item: any) => ({
value: item.stnm,
stnm: item.stnm,
stcd: item.stcd //
}))
// sttp === "2"
const tempData = data.filter((item: any) => item.sttp === "2")
//
const actualData = tempData.map((item: any) => ({
value: item.actualTemp,
stcd: item.stcd,
stnm: item.stnm
}))
const lastData = tempData.map((item: any) => ({
value: item.lastTemp,
stcd: item.stcd,
stnm: item.stnm
}))
const naturalData = stationData.map((item: any) => ({
value: item.naturalTemp,
stcd: item.stcd,
stnm: item.stnm
}))
console.log('X轴数据:', xData)
console.log('实测值:', actualData)
console.log('去年同期:', lastData)
console.log('天然值:', naturalData)
//
setTimeout(() => {
if (!chartInstance) {
initChart(xData, actualData, lastData, naturalData)
} else {
//
chartInstance.setOption({
xAxis: {
data: xData
},
series: [
{ data: actualData },
{ data: lastData },
{ data: naturalData }
]
})
}
//
setTimeout(() => {
chartInstance?.resize()
}, 100)
}, 50)
} catch (error) {
console.error('图表数据加载失败:', error)
showemit.value = false
} finally {
loading.value = false;
}
}
const initChart = (xData: any[], actualData: any[], lastData: any[], naturalData: any[]) => {
if (!chartRef.value) return
chartInstance = echarts.init(chartRef.value)
const legendData = ['实测值', '去年同期', '天然']
// x - { value, stnm }
const xData = [
{ value: '两河口', stnm: '两河口' },
{ value: '锦屏一级', stnm: '锦屏一级' },
{ value: '桐子林', stnm: '桐子林' }
]
//
const actualData = [9, 11.5, 14] // (线)
const lastData = [8.8, 10.5, 13.2] // (线)
const naturalData = [9.2, 12, 15.5] // (绿线)
// Y
const allData = [...actualData, ...lastData, ...naturalData].filter(val => val !== null && val !== undefined)
const dataMin = Math.min(...allData)
const dataMax = Math.max(...allData)
const allData = [...actualData, ...lastData, ...naturalData].map(item => item.value).filter(val => val !== null && val !== undefined)
const dataMin = allData.length > 0 ? Math.min(...allData) : 0
const dataMax = allData.length > 0 ? Math.max(...allData) : 10
const yAxisMin = Math.floor(dataMin)
const yAxisMax = Math.ceil(dataMax)
@ -72,11 +450,12 @@ const initChart = () => {
const label = xData.filter((item: any) => item.value === params[0].axisValueLabel)?.[0]?.stnm
let elm = ``
for (const item of params) {
const value = item.data?.value ?? item.value;
elm += `
<div>
${item.marker}
<span>${item.seriesName}:</span>
<span>${item.value === null || item.value === undefined ? '-' : item.value}${unit}</span>
<span>${value === null || value === undefined ? '-' : value}${unit}</span>
</div>
`
}
@ -204,15 +583,63 @@ const initChart = () => {
options.color = ['#5470c6', '#9b59b6', '#91cc75']
chartInstance.setOption(options)
//
chartInstance.on('click', (params: any) => {
console.log('图表数据点被点击:', params);
//
const dataPoint = params.data;
const stcd = dataPoint?.stcd || '';
const stnm = dataPoint?.stnm || '';
//
stationName.value = stnm;
modalData.value = {
baseid: baseid.value,
rvcd: select.value.value,
date: datetimePicker.value.value,
stcd: stcd,
stnm: stnm
};
//
modalVisible.value = true;
// TODO:
});
}
//
const handleModalClose = () => {
modalVisible.value = false;
modalData.value = null;
stationName.value = '';
};
//
const handlePanelChange1 = (data) => {
console.log('当前所有控件状态:', data);
//
if (data.moreSelect || data.datetime) {
getEchartsData()
}
}
watch(
() => JidiSelectEventStore.selectedItem,
(newVal) => {
baseid.value = newVal.wbsCode;
getSelectConfig()
},
{ deep: true, immediate: true }
);
onMounted(() => {
//
setTimeout(() => {
initChart()
// resize
chartInstance?.resize()
getEchartsData()
}, 100)
window.addEventListener('resize', () => {
@ -231,7 +658,17 @@ onBeforeUnmount(() => {
<style scoped>
.water-temp-compare-chart {
width: 100%;
height: 100%;
height: 260px;
display: flex;
justify-content: center;
align-items: center;
}
:deep(.ant-spin-nested-loading) {
height: 260px !important;
}
.modal-content {
padding: 16px;
}
</style>

View File

@ -0,0 +1,835 @@
<template>
<div class="monthly-average-container">
<!-- 搜索表单 -->
<a-form ref="formRef" :model="formValue" layout="inline" class="search-form">
<a-form-item label="基地名称" name="dataDimensionVal">
<a-select v-model:value="formValue.dataDimensionVal" placeholder="请选择" style="width: 200px"
@change="handleBaseChange">
<a-select-option v-for="item in jdList" :key="item.baseid" :value="item.baseid">
{{ item.basename }}
</a-select-option>
</a-select>
</a-form-item>
<a-form-item label="所在流域" name="rvcd">
<a-select v-model:value="formValue.rvcd" placeholder="请选择" style="width: 200px"
:disabled="!formValue.dataDimensionVal" @change="handleRvcdChange">
<a-select-option v-for="item in lyList" :key="item.value" :value="item.value">
{{ item.label }}
</a-select-option>
</a-select>
</a-form-item>
<a-form-item label="断面名称" name="stcd">
<a-select v-model:value="formValue.stcd" placeholder="请选择" style="width: 200px"
:disabled="!formValue.rvcd">
<a-select-option v-for="item in dmList" :key="item.value" :value="item.value">
{{ item.label }}
</a-select-option>
</a-select>
</a-form-item>
<a-form-item label="月份" name="tm">
<a-date-picker v-model:value="formValue.tm" picker="month" :disabled-date="disabledDate"
format="YYYY-MM" value-format="YYYY-MM-DD HH:mm:ss" :allow-clear="false" />
</a-form-item>
<a-form-item>
<a-space>
<a-button type="primary" @click="handleSearch">
查询
</a-button>
<a-button :loading="exportLoading" @click="handleExport">
导出
</a-button>
</a-space>
</a-form-item>
</a-form>
<div style="width: 100%;display: flex;">
<!-- 图表容器始终存在不再使用 v-if -->
<div class="chart-wrapper" ref="chartRef"></div>
<BasicTable ref="tableRef" :scrollY="460" :columns="columns" :list-url="DetGetKendoListCust"
:search-params="{ sort: sort }" :transform-data="customTransform">
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'action'">
<a @click="handleViewDetail(record)" class="text_hocer">查看详情</a>
</template>
</template>
</BasicTable>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, reactive, watch, onMounted, onBeforeUnmount } from 'vue'
import { message } from 'ant-design-vue'
import dayjs, { Dayjs } from 'dayjs'
import * as echarts from 'echarts'
import type { EChartsOption } from 'echarts'
import moment from 'moment'
import { omit } from 'lodash-es'
import { wbsbGetKendoList, getVmsstbprpt, DetGetKendoListCust } from "@/api/sw";
import BasicTable from '@/components/BasicTable/index.vue';
import { useModelStore } from "@/store/modules/model";
const modelStore = useModelStore();
// ==================== Props ====================
interface Props {
tm?: string
dataDimensionVal?: string
rvcd?: string
stcd?: string
jdList?: any[]
}
const props = withDefaults(defineProps<Props>(), {
tm: '',
dataDimensionVal: '',
rvcd: '',
stcd: '',
jdList: () => []
})
// ==================== ====================
interface DefautState {
dataDimensionType: string
dataDimensionVal: any
tm: string | Dayjs
startDt: string
endDt: string
stcd: string
rvcd: string
}
interface OptionItem {
label: string
value: string
}
// ==================== ====================
const formRef = ref()
const chartRef = ref<HTMLElement | null>(null)
let chartInstance: echarts.ECharts | null = null
const chartLoading = ref(false)
const tableLoading = ref(false)
const exportLoading = ref(false)
const tableRef = ref<any>(null);
const sort = [
{
"field": "dt",
"dir": "desc"
}
]
//
const lyList = ref<OptionItem[]>([])
const dmList = ref<OptionItem[]>([])
//
const formValue = reactive<DefautState>({
dataDimensionType: 'hyBase',
dataDimensionVal: props.dataDimensionVal || 'all',
tm: props.tm ? dayjs(props.tm) : dayjs(),
startDt: '',
endDt: '',
stcd: props.stcd || '',
rvcd: props.rvcd || ''
})
//
const tableData = ref<any[]>([])
const pagination = reactive({
current: 1,
pageSize: 20,
total: 0,
showSizeChanger: true,
showTotal: (total: number) => `${total}`
})
//
const optionData = ref<any[]>([])
// ==================== ====================
/**
* 单位转换
*/
const transUnit = (value: any, code: string, type: string): string => {
if (value === null || value === undefined) return '-'
const numValue = Number(value)
if (isNaN(numValue)) return '-'
return numValue.toFixed(2)
}
/**
* 获取单位配置
*/
const getUnitConfigByCode = (code: string, type: string): { unit: string } => {
return { unit: '℃' }
}
/**
* 获取颜色配置
*/
const getColorByCodeAndType = (code: string[], typeKey: string[]): string[] => {
// ()()(绿)
return ['#5470c6', '#9b59b6', '#91cc75']
}
/**
* 日期格式化
*/
const tmFmt = (d: string) => moment(d).format('YYYY-MM-DD')
/**
* 格式化数值保留一位小数四舍五入空值显示 -
*/
const formatNumber = (value: any): string => {
if (value === null || value === undefined || value === '') return '-'
const numValue = Number(value)
if (isNaN(numValue)) return '-'
return numValue.toFixed(1)
}
/**
* 格式化日期为 YYYY-MM-DD 格式
*/
const formatDate = (dateStr: string): string => {
if (!dateStr) return '-'
try {
return dayjs(dateStr).format('YYYY-MM-DD')
} catch (error) {
return dateStr
}
}
/**
* 计算变化趋势实测值 - 去年同期值
*/
const calculateComparison = (record: any): string => {
const wt = record.wt
const beforeWt = record.beforeWt
if (wt === null || wt === undefined || wt === '' ||
beforeWt === null || beforeWt === undefined || beforeWt === '') {
return '-'
}
const wtNum = Number(wt)
const beforeWtNum = Number(beforeWt)
if (isNaN(wtNum) || isNaN(beforeWtNum)) return '-'
const diff = wtNum - beforeWtNum
return diff.toFixed(1)
}
/**
* 禁用未来日期
*/
const disabledDate = (current: Dayjs) => {
return current && current > dayjs().endOf('day')
}
// ==================== API ====================
/**
* 获取流域下拉列表
*/
const fetchLyData = async () => {
try {
console.log('获取流域列表baseid:', formValue.dataDimensionVal)
let params = {
"filter": {
"logic": "and",
"filters": [
{
"field": "wbsType",
"operator": "eq",
"value": "PSB_RVCD"
},
formValue.dataDimensionVal != 'all' ? {
"field": "fullPath",
"operator": "startswith",
"value": formValue.dataDimensionVal
} : null
].filter(Boolean)
},
"group": [
{ "dir": "asc", "field": "orderIndex" },
{ "dir": "asc", "field": "wbsCode" },
{ "dir": "asc", "field": "wbsName" },
{ "dir": "asc", "field": "objid" }
],
"groupResultFlat": true
}
const res = await wbsbGetKendoList(params)
let data = res?.data?.data || res?.data || []
if (data.length > 0) {
const options = data.map((item: any) => ({
label: item.wbsName,
value: item.wbsCode
}))
lyList.value = options
const rvcd = options.find((i: any) => i.value === props.rvcd)?.value || options[0]?.value
formValue.rvcd = rvcd
await fetchDmData()
} else {
lyList.value = []
formValue.rvcd = ''
await fetchDmData()
}
} catch (error) {
//
}
}
/**
* 获取断面下拉列表
*/
const fetchDmData = async () => {
try {
console.log('获取断面列表baseid:', formValue.dataDimensionVal, 'rvcd:', formValue.rvcd)
let params = {
"filter": {
"logic": "and",
"filters": [
{ "field": "sttpCode", "operator": "eq", "value": "WTRV" },
{ "field": "mway", "operator": "eq", "value": 2 },
formValue.dataDimensionVal == 'all' ? {
"field": "rvcd",
"operator": "contains",
"value": formValue.rvcd
} : {
"field": "baseId",
"operator": "contains",
"value": formValue.dataDimensionVal
}
]
},
"select": [ "stcd", "stnm", "lgtd", "lttd", "rstcdStepSort" ],
"sort": [ { "field": "rstcdStepSort", "dir": "desc" } ]
}
const res = await getVmsstbprpt(params)
let resData = res?.data?.data || res?.data || []
const options = resData.map((item: any) => ({
label: item.stnm,
value: item.stcd
}))
dmList.value = options
const stcd = options.find((i: any) => i.value === props.stcd)?.value || options[0]?.value
formValue.stcd = stcd || ''
handleSearch()
} catch (error) {
console.error('获取断面列表失败:', error)
message.error('获取断面列表失败')
}
}
/**
* 获取图表数据
*/
const getChartData = async (value: any) => {
// loading
if (chartInstance) {
chartInstance.showLoading('default', {
text: '加载中...',
color: '#5470c6',
textColor: '#000',
maskColor: 'rgba(255, 255, 255, 0.8)',
zlevel: 0
});
}
chartLoading.value = true;
try {
const tmValue = dayjs.isDayjs(value.tm) ? value.tm : dayjs(value.tm)
let params = {
"filter": {
"logic": "and",
"filters": [
{
"field": "stcd",
"operator": "eq",
"dataType": "string",
"value": value.stcd
},
{
"field": "startTime",
"operator": "gte",
"dataType": "date",
value: tmValue.startOf('month').format('YYYY-MM-DD HH:mm:ss')
},
{
"field": "endTime",
"operator": "lte",
"dataType": "date",
value: tmValue.endOf('month').format('YYYY-MM-DD HH:mm:ss')
}
]
}
}
const res = await DetGetKendoListCust(params)
let data = res?.data?.data || res?.data || []
if (data.length > 0) {
optionData.value = data
updateChart() //
} else {
optionData.value = []
//
if (chartInstance) {
chartInstance.clear();
chartInstance.setOption({
title: {
show: true,
text: '暂无数据',
left: 'center',
top: 'center',
textStyle: {
color: '#999',
fontSize: 14
}
}
});
}
}
} catch (error) {
console.error('获取图表数据失败:', error)
message.error('获取图表数据失败')
if (chartInstance) {
chartInstance.clear();
chartInstance.setOption({
title: {
show: true,
text: '数据加载失败',
left: 'center',
top: 'center',
textStyle: { color: '#f00' }
}
});
}
} finally {
chartLoading.value = false
if (chartInstance) {
chartInstance.hideLoading();
}
}
}
/**
* 获取表格数据
*/
const getTableData = async (value: any) => {
try {
const tmValue = dayjs.isDayjs(value.tm) ? value.tm : dayjs(value.tm)
const filter = {
"logic": "and",
"filters": [
{
"field": "stcd",
"operator": "eq",
"dataType": "string",
"value": value.stcd
},
{
"field": "startTime",
"operator": "gte",
"dataType": "date",
value: tmValue.startOf('month').format('YYYY-MM-DD HH:mm:ss')
},
{
"field": "endTime",
"operator": "lte",
"dataType": "date",
value: tmValue.endOf('month').format('YYYY-MM-DD HH:mm:ss')
}
]
}
tableRef.value?.getList(filter);
} catch (error) {
//
}
}
// ==================== ====================
/**
* 初始化图表
*/
const initChart = () => {
if (!chartRef.value) return
chartInstance = echarts.init(chartRef.value)
}
/**
* 更新图表有数据时调用
*/
const updateChart = () => {
if (!chartInstance) return
// title
chartInstance.setOption({ title: { show: false } }, false);
const { unit } = getUnitConfigByCode('Other', 'ACTUALTEMP')
const legendData = ['实测值', '去年同期', '天然']
const xData: any[] = []
const actualData: (number | null)[] = []
const lastData: (number | null)[] = []
const naturalData: (number | null)[] = []
optionData.value.forEach((item: any) => {
actualData.push(item.wt === null ? null : Number(transUnit(item.wt, 'Other', 'ACTUALTEMP')))
lastData.push(item.beforeWt === null ? null : Number(transUnit(item.beforeWt, 'Other', 'LASTTEMP')))
naturalData.push(item.actualTemp === null ? null : Number(transUnit(item.actualTemp, 'Other', 'NATURALTEMP')))
xData.push({ value: item.dt })
})
const code = ['Other']
const typeKey = ['ACTUALTEMP', 'LASTTEMP', 'NATURALTEMP']
const colors = getColorByCodeAndType(code, typeKey)
const option: EChartsOption = {
color: colors,
tooltip: {
trigger: 'axis',
formatter: (params: any) => {
const a = params
.map((item: any) => {
const value = item.value !== null && item.value !== undefined ? item.value : '-'
return `<div>${item.marker}${item.seriesName}:&nbsp;&nbsp;&nbsp;&nbsp;<span style="float:right">${value} ${unit}</span></div>`
})
.join('')
return `<h3 style="color:#FFF">${tmFmt(params[0].name)}</h3>${a}`
}
},
legend: {
data: legendData
},
xAxis: {
type: 'category',
boundaryGap: false,
axisTick: { show: true },
data: xData,
axisLabel: {
formatter: (value: string) => tmFmt(value)
}
},
yAxis: {
type: 'value',
name: `水温(${unit})`,
scale: true,
axisLine: { show: true }
},
dataZoom: [
{
type: 'inside',
show: false,
xAxisIndex: [0],
yAxisIndex: [], // Y
zoomOnMouseWheel: true,
moveOnMouseMove: true
},
],
series: [
{
name: legendData[0],
data: actualData,
type: 'line',
connectNulls: true,
smooth: true
},
{
name: legendData[1],
data: lastData,
type: 'line',
connectNulls: true,
smooth: true
},
{
name: legendData[2],
data: naturalData,
type: 'line',
connectNulls: true,
smooth: true
}
]
}
try {
chartInstance.setOption(option, true) // notMerge = true
} catch (error) {
console.error('图表更新失败:', error)
}
}
// ==================== ====================
/**
* 基地变化
*/
const handleBaseChange = async (value: string) => {
formValue.dataDimensionVal = value
formValue.rvcd = ''
formValue.stcd = ''
lyList.value = []
dmList.value = []
await fetchLyData()
}
/**
* 流域变化
*/
const handleRvcdChange = async (value: string) => {
formValue.rvcd = value
formValue.stcd = ''
dmList.value = []
await fetchDmData()
}
/**
* 搜索
*/
const handleSearch = async () => {
// if (!formValue.stcd) {
// message.error('')
// return
// }
const tmValue = dayjs.isDayjs(formValue.tm)
? formValue.tm.format('YYYY-MM-DD 00:00:00')
: moment(formValue.tm).format('YYYY-MM-DD 00:00:00')
const values = {
...formValue,
tm: tmValue
}
await getChartData(values)
await getTableData(values)
}
/**
* 导出
*/
const handleExport = async () => {
exportLoading.value = true
try {
const tmValue = dayjs.isDayjs(formValue.tm)
? formValue.tm.format('YYYY-MM-DD 00:00:00')
: moment(formValue.tm).format('YYYY-MM-DD 00:00:00')
const params = {
...buildFilterParams({ ...formValue, tm: tmValue }),
exportType: 'excel',
exportFileName: `月平均水温历史对比 ${moment().format('YYYY-MM-DD HH-mm-ss')}`
}
message.success('导出功能待实现')
console.log('导出参数:', params)
} catch (error) {
console.error('导出失败:', error)
message.error('导出失败')
} finally {
exportLoading.value = false
}
}
/**
* 表格分页变化
*/
const handleTableChange = (pag: any) => {
pagination.current = pag.current
pagination.pageSize = pag.pageSize
handleSearch()
}
/**
* 构建表格过滤参数
*/
const buildFilterParams = (values: any) => {
let _p: any = { ...values, stcd: values?.stcd || props?.stcd }
if (_p.tm) {
const tmValue = dayjs.isDayjs(_p.tm) ? _p.tm : dayjs(_p.tm)
_p.startDt = [{
field: 'startTime',
operator: 'gte',
dataType: 'date',
value: tmValue.startOf('month').format('YYYY-MM-DD HH:mm:ss')
}]
_p.endDt = [{
field: 'endTime',
operator: 'lte',
dataType: 'date',
value: tmValue.endOf('month').format('YYYY-MM-DD HH:mm:ss')
}]
}
if (!_p?.stcd) {
_p.stcd = ''
}
return omit(_p, ['dataDimensionType', 'dataDimensionVal', 'tm', 'rvcd'])
}
// ==================== ====================
const { unit } = getUnitConfigByCode('Other', 'ACTUALTEMP')
const columns = [
{
title: '日期',
dataIndex: 'dt',
key: 'dt',
width: 120,
customRender: ({ record }: any) => formatDate(record.dt)
},
{
title: '日均水温(℃)实测值',
dataIndex: 'wt',
key: 'wt',
width: 150,
customRender: ({ record }: any) => formatNumber(record.wt)
},
{
title: '日均水温(℃)去年同期',
dataIndex: 'beforeWt',
key: 'beforeWt',
width: 150,
customRender: ({ record }: any) => formatNumber(record.beforeWt)
},
{
title: '天然值',
dataIndex: 'actualTemp',
key: 'actualTemp',
width: 120,
customRender: ({ record }: any) => formatNumber(record.actualTemp)
},
{
title: `变化趋势(${unit}`,
key: 'comparison',
width: 150,
customRender: ({ record }: any) => calculateComparison(record)
},
{
title: '操作',
key: 'action',
width: 100,
align: 'center' as const,
fixed: 'right' as const
}
]
// ==================== ====================
watch(
() => [props.tm, props.dataDimensionVal, props.rvcd, props.stcd],
() => {
if (props.tm) formValue.tm = dayjs(props.tm)
if (props.dataDimensionVal) formValue.dataDimensionVal = props.dataDimensionVal
if (props.rvcd) formValue.rvcd = props.rvcd
if (props.stcd) formValue.stcd = props.stcd
},
{ deep: true }
)
// ==================== ====================
onMounted(() => {
setTimeout(() => {
initChart()
}, 100)
fetchLyData()
window.addEventListener('resize', handleResize)
})
onBeforeUnmount(() => {
window.removeEventListener('resize', handleResize)
chartInstance?.dispose()
})
// ==================== ====================
const handleResize = () => {
chartInstance?.resize()
}
const customTransform = (res: any) => {
console.log('表格数据:', res);
return {
records: res?.data?.data || [],
total: res?.data?.total || 0
};
};
const handleViewDetail = (record: any) => {
let stnm = record.stnm;
dmList.value.forEach((element: any) => {
if (element.value == formValue.stcd) {
stnm = element.label;
}
});
modelStore.modalVisible = true;
modelStore.params.sttp = "wt_point";
modelStore.title = stnm + "详情信息";
modelStore.params.stcd = formValue.stcd;
}
defineExpose({
handleSearch,
getChartData,
getTableData
})
</script>
<style scoped lang="scss">
.monthly-average-container {
width: 100%;
padding: 16px;
.search-form {
margin-bottom: 16px;
padding: 16px;
background: #fff;
border-radius: 4px;
}
.chart-wrapper {
width: 40%;
height: 600px;
background: #fff;
border-radius: 4px;
padding: 16px;
box-sizing: border-box;
}
.table-wrapper {
flex: 7;
background: #fff;
border-radius: 4px;
padding: 16px;
box-sizing: border-box;
}
.text_hocer {
color: #2f6b98;
&:hover {
color: #40a9ff;
}
}
}
:deep(.ant-spin-nested-loading) {
width: 100%;
}
</style>

View File

@ -1,31 +1,46 @@
<!-- SidePanelItem.vue -->
<template>
<SidePanelItem title="设施类型介绍">
<a-spin :spinning="loading">
<div v-if="carouselData.length > 0" class="carousel-wrapper">
<a-carousel v-model:current="currentIndex" autoplay class="tech-carousel" :dot-style="{ bottom: '0px' }">
<!-- -->
<div v-for="(item, index) in carouselData" :key="index" class="carousel-item">
<div class="image-container">
<div class="image-container" @click="handleItemClick(carouselData)">
<img :src="item.image" :alt="item.title" class="carousel-image" />
</div>
</div>
</a-carousel>
<!-- 文字描述区域 -->
<div v-if="carouselData[currentIndex]" class="description-container">
<div v-if="carouselData[currentIndex]" class="description-container" @click="handleItemClick(carouselData)">
<p class="item-description">{{ carouselData[currentIndex].description }}</p>
</div>
</div>
<div v-else>
<a-empty />
<div v-else style="width: 100%;height: 300px;display: flex; align-items: center; justify-content: center;">
<a-empty description="暂无数据" />
</div>
</a-spin>
</SidePanelItem>
<!-- 设施详情弹框 -->
<a-modal
v-model:open="modalVisible"
:title="'设施类型介绍'"
width="1536px"
:footer="null"
>
<!-- 你在这里编写弹框内容 -->
<div v-if="currentItem">
<ArtsDetail :dataSource='currentItem' />
</div>
</a-modal>
</template>
<script lang="ts" setup>
import { ref, onMounted } from 'vue';
import SidePanelItem from '@/components/SidePanelItem/index.vue';
import ArtsDetail from '@/components/carouselIntroduce/ArtsDetail.vue';
import { sttpbGetKendoList } from "@/api/sw";
// 便
defineOptions({
name: 'diwenshuijianhuansheshileixingzuchengjijieruqingkuang'
@ -34,39 +49,78 @@ defineOptions({
//
const currentIndex = ref(0);
//
const loading = ref(false);
//
const carouselData = ref([
const carouselData = ref<any[]>([]);
//
const modalVisible = ref(false);
const currentItem = ref<any>(null);
const getListData = async () => {
loading.value = true;
try {
const params = {
"filter": {
"logic": "and",
"filters": [
{
title: '叠梁门',
description: '叠梁门是一种用于调节水库流量和控制水位的设施。在低温季节,可以通过控制叠梁门的开闭程度来调节流量,减少低温水体的进...',
image: 'https://img.shetu66.com/2024/02/19/170835076078361368.png' //
"field": "fullPath",
"operator": "contains",
"dataType": "string",
"value": "DW,DW"
},
{
title: '环保设施',
description: '现代化环保设施,采用先进技术,有效处理工业废水废气,实现达标排放,保护生态环境...',
image: 'https://img.shetu66.com/2024/02/19/170835076078361368.png'
"field": "logo",
"operator": "isnotnull",
"dataType": "string"
},
{
title: '智能监控',
description: '24 小时智能监控系统,实时监测设备运行状态,确保设施安全稳定运行...',
image: 'https://img.shetu66.com/2024/02/19/170835076078361368.png'
},
{
title: '水处理系统',
description: '高效水处理系统,通过多级过滤和生物处理,实现水资源循环利用...',
image: 'https://img.shetu66.com/2024/02/19/170835076078361368.png'
"field": "introduce",
"operator": "isnotnull",
"dataType": "string"
}
]);
]
}
}
let res = await sttpbGetKendoList(params);
let data = res?.data?.data || res?.data || [];
//
const baseUrl = import.meta.env.VITE_APP_PREVIEW_URL;
carouselData.value = data.map((item: any) => ({
title: item.sttpName,
description: item.introduce,
image: item.logo ? `${baseUrl}/?${item.logo}` : ''
}));
} catch (error) {
console.error('获取设施类型数据失败:', error);
carouselData.value = [];
} finally {
loading.value = false;
}
};
//
const handleItemClick = (item: any) => {
console.log('轮播图项目被点击:', item);
currentItem.value = item;
modalVisible.value = true;
};
//
onMounted(() => {
//
getListData()
});
</script>
<style lang="scss" scoped>
.carousel-wrapper {
width: 414px;
:deep(.slick-slide) {
text-align: center;
@ -77,6 +131,20 @@ onMounted(() => {
}
.image-container {
width: 100%;
height: 250px;
overflow: hidden;
cursor: pointer;
}
.carousel-image {
width: 100%;
height: 100%;
object-fit: cover;
display: block;
}
:deep() {
.ant-carousel .slick-dots li {
width: 5px !important;
@ -85,18 +153,22 @@ onMounted(() => {
background-color: #888e95;
}
.ant-carousel .slick-dots li button {
width: 5px !important;
height: 5px !important;
border-radius: 50% !important;
background-color: #888e95;
}
.ant-carousel .slick-dots li.slick-active {
background-color: #005293 !important;
}
.ant-carousel .slick-dots li.slick-active button {
background-color: #005293 !important;
}
.ant-carousel .slick-dots li :hover {
width: 5px !important;
height: 5px !important;
@ -104,6 +176,7 @@ onMounted(() => {
background-color: #ffffffcc;
}
.ant-carousel .slick-dots li button :hover {
width: 5px !important;
height: 5px !important;
@ -124,7 +197,11 @@ onMounted(() => {
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
text-indent: 0em;
cursor: pointer;
}
}
:deep(.ant-spin-nested-loading) {
height: 300px !important;
}
</style>

View File

@ -0,0 +1,379 @@
<template>
<div class="year-average-container">
<!-- 搜索区域 -->
<a-form ref="formRef" :model="formState" layout="inline" class="search-form">
<a-form-item label="基地名称" name="dataDimensionVal">
<a-select v-model:value="formState.dataDimensionVal" placeholder="请选择" style="width: 200px"
@change="handleJdChange">
<a-select-option v-for="item in jdList" :key="item.wbsCode" :value="item.wbsCode">
{{ item.wbsName }}
</a-select-option>
</a-select>
</a-form-item>
<a-form-item label="断面名称" name="stcd">
<a-select v-model:value="formState.stcd" placeholder="请选择" style="width: 200px"
:loading="selectLoading">
<a-select-option v-for="item in dmList" :key="item.value" :value="item.value">
{{ item.label }}
</a-select-option>
</a-select>
</a-form-item>
<a-form-item label="月份" name="tm">
<a-date-picker v-model:value="formState.tm" picker="month" placeholder="请选择月份" style="width: 200px"
format="YYYY-MM" value-format="YYYY-MM-DD HH:mm:ss" />
</a-form-item>
<a-form-item>
<a-space>
<a-button type="primary" @click="handleSearch">查询</a-button>
<a-button :loading="exportLoading" @click="handleExport">
导出
</a-button>
</a-space>
</a-form-item>
</a-form>
<BasicTable ref="tableRef" :scrollY="460" :columns="columns" :list-url="yearDetailGetKendoListCust"
:search-params="{ sort: sort }" :transform-data="customTransform">
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'action'">
<a @click="handleViewDetail(record)" class="text_hocer">查看详情</a>
</template>
</template>
</BasicTable>
</div>
</template>
<script setup lang="ts">
import { ref, reactive, computed, onMounted, watch } from 'vue'
import dayjs, { Dayjs } from 'dayjs'
import { message } from 'ant-design-vue'
import { getVmsstbprpt, yearDetailGetKendoListCust } from '@/api/sw'
import { useJidiSelectEventStore } from '@/store/modules/jidiSelectEvent'
import BasicTable from '@/components/BasicTable/index.vue';
import { useModelStore } from "@/store/modules/model";
const modelStore = useModelStore();
//
interface FormState {
dataDimensionVal: string | number | null
tm: string | Dayjs | null
stcd: string
}
interface TableFilter {
startDt: string
endDt: string
stcd: string
}
// Props
const props = defineProps<{
tm?: string
dataDimensionVal?: string | number
stcd?: string
}>()
// Store
const jidiStore = useJidiSelectEventStore()
// Refs
const tableRef = ref()
const timeRef = ref(props.tm || '')
// State
const formState = reactive<FormState>({
dataDimensionVal: props.dataDimensionVal || '',
tm: props.tm ? dayjs(props.tm) : null,
stcd: props.stcd || ''
})
const sort = [
{
"field": "tm",
"dir": "desc"
}
]
const selectLoading = ref(false)
const exportLoading = ref(false)
const mapModalVisible = ref(false)
const modalData = ref<any>({})
const dmList = ref<Array<{ label: string; value: string }>>([])
const isFirstLoad = ref(true)
// store
const jdList = computed(() => {
return jidiStore.jidiData || []
})
//
const columns = [
{
title: '日期',
dataIndex: 'dt',
key: 'dt',
width: 120,
customRender: ({ text }: any) => text ? dayjs(text).format('YYYY-MM-DD') : '-'
},
{
title: '日均水温(℃)实测值',
dataIndex: 'wt',
key: 'wt',
width: 160
},
{
title: '日均水温(℃)去年同期',
dataIndex: 'beforeWt',
key: 'beforeWt',
width: 180
},
{
title: '天然值',
dataIndex: 'naturalTemp',
key: 'naturalTemp',
width: 120,
customRender: ({ text }: any) => text ? Number(text).toFixed(1) : '-'
},
{
title: '操作',
key: 'action',
width: 100,
align: 'center' as const,
fixed: 'right' as const
}
]
// API
const getTableDataApi = async (params: any) => {
try {
// const res = await yearListGetKendoListCust(requestParams)
} catch (error) {
}
}
//
const fetchDmData = async () => {
if (!formState.dataDimensionVal) return
try {
selectLoading.value = true
const res = await getVmsstbprpt({
filter: {
logic: 'and',
filters: [
{
"field": "sttpFullPath",
"operator": "contains",
"value": "ENV,ENVM,WT,"
},
{
"field": "sttpCode",
"operator": "eq",
"value": "WTRV"
},
formState.dataDimensionVal != 'all' ? {
"field": "baseId",
"operator": "contains",
"value": formState.dataDimensionVal
} : null
].filter(Boolean)
},
select: [
"stcd",
"stnm",
"lgtd",
"lttd",
"baseId",
"baseName",
"siteStepSort"
],
// sort: [{ field: 'rstcdStepSort', dir: 'desc' }]
})
const resData = res?.data?.data || res?.data || []
if (resData.length > 0) {
dmList.value = resData.map((item: any) => ({
label: item.stnm,
value: item.stcd
}))
// stcd
if (props.stcd) {
formState.stcd = props.stcd
isFirstLoad.value = false
} else {
//
formState.stcd = dmList.value[0].value
}
} else {
dmList.value = []
formState.stcd = ''
}
} catch (error) {
console.error('获取断面列表失败:', error)
message.error('获取断面列表失败')
} finally {
selectLoading.value = false
}
}
//
const handleJdChange = (value: string) => {
formState.stcd = ''
dmList.value = []
fetchDmData()
}
//
const handleSearch = async () => {
const tmValue = dayjs.isDayjs(formState.tm) ? formState.tm : dayjs(formState.tm)
const filter = {
"logic": "and",
"filters": [
{
"field": "stcd",
"operator": "eq",
"dataType": "string",
"value": formState.stcd
},
{
"field": "dt",
"operator": "gte",
"dataType": "date",
value: tmValue.startOf('month').format('YYYY-MM-DD HH:mm:ss')
},
{
"field": "dt",
"operator": "lte",
"dataType": "date",
value: tmValue.endOf('month').format('YYYY-MM-DD HH:mm:ss')
}
]
}
//
tableRef.value?.getList(filter)
}
//
const handleExport = async () => {
}
//
const handleViewDetail = (record: any) => {
modelStore.modalVisible = true;
modelStore.params.sttp = "wt_point";
modelStore.title = record.stnm + "详情信息";
modelStore.params.stcd = record.stcd;
}
//
const handleModalClose = () => {
mapModalVisible.value = false
modalData.value = {}
}
const customTransform = (res: any) => {
console.log('表格数据:', res);
return {
records: res?.data?.data || [],
total: res?.data?.total || 0
};
};
// props
watch(
() => [props.tm, props.dataDimensionVal, props.stcd],
async ([newTm, newDataDimensionVal, newStcd], [oldTm, oldDataDimensionVal, oldStcd]) => {
//
if (newTm !== oldTm || newDataDimensionVal !== oldDataDimensionVal || newStcd !== oldStcd) {
// formState
formState.tm = newTm ? dayjs(newTm) : null
formState.dataDimensionVal = newDataDimensionVal || ''
formState.stcd = String(newStcd || '')
//
if (newDataDimensionVal !== oldDataDimensionVal && newDataDimensionVal) {
await fetchDmData()
}
//
handleSearch()
}
},
{ deep: true }
)
//
watch(
() => formState.dataDimensionVal,
async (newVal) => {
if (newVal) {
await fetchDmData()
handleSearch()
}
}
)
//
onMounted(async () => {
if (formState.dataDimensionVal) {
await fetchDmData()
handleSearch()
}
})
//
defineExpose({
handleSearch,
})
</script>
<style scoped lang="scss">
.year-average-container {
height: 100%;
display: flex;
flex-direction: column;
height: 600px;
.search-form {
// padding: 16px;
background: #fff;
// margin-bottom: 16px;
border-radius: 4px;
:deep(.ant-form-item) {
margin-bottom: 16px;
}
}
.table-container {
flex: 1;
overflow: hidden;
background: #fff;
border-radius: 4px;
}
.text-hocer {
color: #2f6b98;
&:hover {
color: #40a9ff;
}
}
}
.text_hocer{
color: #2f6b98;
}
.text_hocer:hover{
color: #40a9ff;
}
</style>

View File

@ -1,28 +1,140 @@
<template>
<SidePanelItem title="水温年内分布">
<div class="water-temp-chart-container">
<SidePanelItem title="水温年内分布" :moreSelect="select" :datetimePicker="datetimePicker"
@update-values="handlePanelChange1">
<a-spin :spinning="loading" tip="加载中...">
<div v-show="!loading && showemit" class="water-temp-chart-container">
<div ref="chartRef" class="chart-wrapper"></div>
</div>
<!-- <div v-else>
<a-empty />
</div> -->
<div v-show="!loading && !showemit" class="water-temp-chart-container">
<a-empty description="暂无数据" />
</div>
</a-spin>
</SidePanelItem>
<!-- 数据点详情弹框 -->
<a-modal v-model:open="modalVisible" title="水温数据详情" :footer="null" width="1536px" @cancel="handleModalClose">
<YaerAverage :tm="`${datetimePicker.value}-${String(currentData.monthInt).padStart(2, '0')}-01 00:00:00`"
:dataDimensionVal="baseid" :stcd="select.value" />
</a-modal>
</template>
<script setup lang="ts">
import { ref, onMounted, onBeforeUnmount } from 'vue'
import { ref, onMounted, onBeforeUnmount, watch } from 'vue'
import * as echarts from 'echarts'
import SidePanelItem from '@/components/SidePanelItem/index.vue';
const data = [
{ monthInt: 1, actualTemp: 0.9, naturalTemp: 0 },
{ monthInt: 2, actualTemp: 0.9, naturalTemp: 0.1 },
{ monthInt: 3, actualTemp: 1.0, naturalTemp: 2.0 }
]
import { getVmsstbprpt, yearListGetKendoListCust } from "@/api/sw";
import { useJidiSelectEventStore } from "@/store/modules/jidiSelectEvent";
import YaerAverage from "./TwoLayers/yaerAverage.vue"
const JidiSelectEventStore = useJidiSelectEventStore();
//
const waterTempData = ref<any[]>([])
const loading = ref(false)
const showemit = ref(true)
//
const modalVisible = ref(false)
const currentData = ref<any>({})
const jiDiList: any = ref([
{
"baseid": "all",
"basename": "当前全部",
"id": "9BFEC848-83EA-AD22-6DE2-10E969476693"
},
{
"baseid": "01",
"basename": "金沙江干流",
"id": "A33040B7-8977-D9F1-5E02-D7F0241AB8AA"
},
{
"baseid": "02",
"basename": "雅砻江干流",
"id": "C63D6020-995D-FE97-2F4A-F619A7142C79"
},
{
"baseid": "03",
"basename": "大渡河干流",
"id": "CCB0766B-F1D4-7DD6-650F-5C556B7231B3"
},
{
"baseid": "04",
"basename": "乌江干流",
"id": "E8A66641-B4F4-CC85-0815-38C5B2F93DBD"
},
{
"baseid": "05",
"basename": "长江上游干流",
"id": "E02C11E9-CEA5-A030-202F-3BFE84465D03"
},
{
"baseid": "10",
"basename": "湘西",
"id": "B1D1D52D-CEEF-6DC4-27DF-9990EB572F8D"
},
{
"baseid": "08",
"basename": "黄河上游干流",
"id": "BC6AF135-263D-09A9-D5CA-C99B2598FE6E"
},
{
"baseid": "09",
"basename": "黄河中游干流",
"id": "60EEEC28-128F-A2A8-44F3-6EAAC8FD8BB6"
},
{
"baseid": "06",
"basename": "南盘江-红水河",
"id": "7BB9A8F4-34B5-42B4-A7FC-CE910AD7F203"
},
{
"baseid": "12",
"basename": "东北",
"id": "47F8EF06-924E-E161-FCAF-62A66BBF252D"
},
{
"baseid": "07",
"basename": "澜沧江干流",
"id": "A966A4C9-278C-B0DA-2B97-2D10B7A6E96A"
},
{
"baseid": "13",
"basename": "怒江干流",
"id": "FA89E8CB-67A8-76DA-DC1E-23AE4C54F9E4"
},
{
"baseid": "11",
"basename": "闽浙赣",
"id": "CD98F995-EEB2-1021-D807-DA1B1AD9E49A"
},
{
"baseid": "other",
"basename": "其他",
"id": "AFBDFC67-B955-4EFD-959A-014CFB59EBFC"
}
]);
const chartRef = ref<HTMLElement | null>(null)
let chartInstance: echarts.ECharts | null = null
//
const select = ref({
show: true,
value: '',
options: [],
picker: undefined,
format: undefined,
});
//
const datetimePicker = ref({
show: true,
value: (() => {
const now = new Date();
const year = now.getFullYear();
return `${year}`;
})(),
format: "YYYY",
picker: "year" as const,
options: [],
});
const transUnit = (value: number | null) => {
if (value === null) return null
return value
@ -38,6 +150,209 @@ const getColorByCodeAndType = (_code: string[], _typeKey: string[]) => {
return ['#4b79ab', '#78c300']
}
//
const handleChartClick = (params: any) => {
if (!params || !params.dataIndex) return
//
const dataIndex = params.dataIndex
const dataItem = waterTempData.value[dataIndex]
if (dataItem) {
currentData.value = {
...dataItem,
stnm: select.value.options.find((opt: any) =>
opt.children?.some((child: any) => child.value === select.value.value)
)?.children?.find((child: any) => child.value === select.value.value)?.title || '未知站点'
}
modalVisible.value = true
}
}
//
const handleModalClose = () => {
modalVisible.value = false
currentData.value = {}
}
//
const getselectData = async () => {
try {
const params = {
filter: {
logic: "and",
filters: [
{
field: "sttpFullPath",
operator: "contains",
value: "ENV,ENVM,WT,"
},
{
field: "sttpCode",
operator: "eq",
value: "WTRV"
},
baseid.value !== 'all' ? {
field: "baseId",
operator: "contains",
value: baseid.value
} : null
].filter(Boolean)
},
select: [
"stcd", "stnm", "lgtd", "lttd",
"baseId", "baseName", "siteStepSort"
]
}
const res = await getVmsstbprpt(params)
// if (!res?.data?.length) {
// select.value.options = []
// return
// }
let data = res?.data?.date || res?.data || []
// stcd
if (res.data) {
select.value.value = data?.data[0]?.stcd
} else {
select.value.value = ''
}
// baseId
const dataMapNameMap: Record<string, any[]> = {}
data.data.forEach((item: any) => {
const { baseId } = item
if (dataMapNameMap[baseId]) {
dataMapNameMap[baseId].push(item)
} else {
dataMapNameMap[baseId] = [item]
}
})
//
const dataMapNameArr: any[] = []
const otherArr: any[] = []
Object.keys(dataMapNameMap).forEach((baseId: string) => {
const stations = dataMapNameMap[baseId]
const treeItem = {
title: stations[0]?.baseName,
value: baseId,
selectable: false,
children: stations
.sort((a, b) => a.siteStepSort - b.siteStepSort)
.map((station) => ({
title: station.stnm,
value: station.stcd,
lgtd: station.lgtd,
lttd: station.lttd
}))
}
// other
if (+baseId) {
dataMapNameArr.push(treeItem)
} else if (baseId === 'other') {
otherArr.push(treeItem)
}
})
// jiDiList
dataMapNameArr.sort((a, b) => {
const indexA = jiDiList.value.findIndex((item: any) => item.baseid === a.value)
const indexB = jiDiList.value.findIndex((item: any) => item.baseid === b.value)
return indexA - indexB
})
//
select.value.options = [...dataMapNameArr, ...otherArr]
// debugger
// if (baseid.value == 'all') {
// select.value.value = '008640202300001021'
// }
// else if (data.data.length > 0) {
// select.value.value = data.data[0].stcd
// }
getshuiwenList()
} catch (error) {
console.error('获取站点数据失败:', error)
select.value.options = []
}
}
//
const getshuiwenList = async () => {
if (loading.value) return; //
loading.value = true;
const params = {
"filter": {
"logic": "and",
"filters": [
{
"field": "stcd",
"operator": "eq",
"dataType": "string",
"value": select.value.value
},
{
"field": "year",
"operator": "eq",
"dataType": "string",
"value": datetimePicker.value.value
}
]
},
"sort": [
{
"field": "month",
"dir": "asc"
}
]
}
try {
let res = await yearListGetKendoListCust(params)
let data = res?.data?.data || res?.data || []
//
if (data.length === 0) {
showemit.value = false
waterTempData.value = []
return
}
showemit.value = true
//
waterTempData.value = data
//
setTimeout(() => {
if (!chartInstance) {
initChart()
} else {
const option = getChartOption()
chartInstance.setOption(option, true)
}
//
setTimeout(() => {
chartInstance?.resize()
}, 100)
}, 50)
} catch (error) {
console.error('获取水温数据失败:', error)
showemit.value = false
waterTempData.value = []
} finally {
loading.value = false;
}
}
const getChartOption = () => {
const { unit } = getUnitConfigByCode('Other', 'ACTUALTEMP')
const legendData = ['实测值', '天然']
@ -45,7 +360,8 @@ const getChartOption = () => {
const actualData: (number | null)[] = []
const naturalData: (number | null)[] = []
data.forEach((item: any) => {
// 使waterTempDatadata
waterTempData.value.forEach((item: any) => {
xData.push(`${item.monthInt}`)
actualData.push(item.actualTemp === null ? null : transUnit(item.actualTemp))
naturalData.push(item.naturalTemp === null ? null : transUnit(item.naturalTemp))
@ -77,6 +393,18 @@ const getChartOption = () => {
return result;
}
},
dataZoom: [
{
type: 'inside',
start: 0,
end: 100,
xAxisIndex: [0],
filterMode: 'filter',
zoomOnMouseWheel: true,
moveOnMouseMove: false,
moveOnMouseWheel: false
}
],
xAxis: {
type: 'category',
boundaryGap: true,
@ -204,6 +532,10 @@ const initChart = async () => {
const option = getChartOption()
chartInstance.setOption(option, false)
//
chartInstance.off('click')
chartInstance.on('click', handleChartClick)
//
setTimeout(() => {
chartInstance?.resize()
@ -222,7 +554,13 @@ const handleResize = () => {
}
onMounted(() => {
//
initChart()
//
if (select.value.value) {
getshuiwenList()
}
})
onBeforeUnmount(() => {
@ -232,18 +570,43 @@ onBeforeUnmount(() => {
chartInstance = null
}
})
//
const handlePanelChange1 = (data) => {
console.log('当前所有控件状态:', data);
//
if (data.moreSelect || data.datetime) {
select.value.value = data.moreSelect
getshuiwenList()
}
}
const baseid = ref('')
watch(
() => JidiSelectEventStore.selectedItem,
(newVal) => {
baseid.value = newVal.wbsCode;
getselectData()
},
{ deep: true, immediate: true }
);
</script>
<style scoped>
.water-temp-chart-container {
width: 100%;
height: 100%;
min-height: 290px;
height: 290px;
display: flex;
justify-content: center;
align-items: center;
}
.chart-wrapper {
width: 100%;
height: 100%;
min-height: 290px;
}
:deep(.ant-spin-nested-loading) {
height: 290px !important;
}
</style>

View File

@ -3,7 +3,8 @@
<SidePanelItem title="水温监测工作开展情况">
<a-spin :spinning="loading" tip="加载中...">
<div class="facility-grid">
<div v-for="facility in facilities" :key="facility.name" class="facility-card">
<div v-for="facility in facilities" :key="facility.name" class="facility-card"
@click="handleFacilityClick(facility)">
<div style="
width: 60px;
height: 62px;
@ -25,6 +26,11 @@
</div>
</div>
</a-spin>
<!-- 弹框 -->
<a-modal v-model:open="modalVisible" :title="'水温监测工作开展情况'" :footer="null" width="1536px" @cancel="handleCloseModal">
<ShuiwenjiancegongzuoEJ v-if="currentFacility" :activeKey="currentFacility.key" :tabs="facilities" :baseId="baseid" />
</a-modal>
</SidePanelItem>
</template>
@ -33,7 +39,7 @@ import { ref, onMounted, watch } from "vue";
import SidePanelItem from "@/components/SidePanelItem/index.vue";
import { getKendoListCust, baseEvnmAutoMonitorGetKendoListCust } from "@/api/sw";
import { useJidiSelectEventStore } from "@/store/modules/jidiSelectEvent";
import ShuiwenjiancegongzuoEJ from "./shuiwenjiancegongzuoEJ.vue"
const JidiSelectEventStore = useJidiSelectEventStore();
// 便
defineOptions({
@ -46,17 +52,38 @@ const facilities = ref([
name: "表层水温",
count: 0,
icon: "icon iconfont icon-shuizhijiancezhan",
key:'26'
},
{
name: "垂向水温",
count: 0,
icon: "icon iconfont icon-diwenshuijianhuan",
key:'27'
},
]);
// Loading
const loading = ref(false);
//
const modalVisible = ref(false);
const currentFacility = ref<any>(null);
//
const handleFacilityClick = (facility: any) => {
// console.log(facility);
currentFacility.value = facility;
modalVisible.value = true;
};
//
const handleCloseModal = () => {
modalVisible.value = false;
currentFacility.value = null;
};
const init = async () => {
//
if (loading.value) return;

View File

@ -0,0 +1,548 @@
<template>
<div class="shuiwenjiancegongzwo-ej-container">
<!-- Tab切换 -->
<a-tabs v-model:activeKey="tabIndex" @change="handleTabChange">
<a-tab-pane v-for="tab in tabs" :key="tab.key" :tab="tab.name" />
</a-tabs>
<!-- 搜索表单 -->
<div class="search-form">
<a-space size="middle">
<div>选择水电站:</div>
<a-select v-model:value="searchData.stcd.dataDimensionData" placeholder="请选择水电站" style="width: 200px"
@change="handleHydropowerStationChange">
<a-select-option v-for="station in hydropowerStationList" :key="station.wbsCode"
:value="station.wbsCode">
{{ station.wbsName }}
</a-select-option>
</a-select>
<a-select v-model:value="searchData.stnm" placeholder=" " style="width: 200px" allow-clear
@change="handleStationChange">
<a-select-option v-for="station in stationDataList" :key="station.wbsCode" :value="station.stcd">
{{ station.stnm }}
</a-select-option>
</a-select>
<!-- <a-input v-model:value="searchData.stnm" placeholder="请输入测站名称" style="width: 200px" allow-clear /> -->
<a-button type="primary" @click="handleSearch">查询</a-button>
<a-tooltip title="重置">
<a-button @click="handleReset">重置</a-button>
</a-tooltip>
</a-space>
</div>
<!-- 数据表格 -->
<a-table :columns="columns" :data-source="tableData" :loading="loading" :pagination="pagination"
:scroll="{ y: tableHeight }" row-key="stcd" @change="handleTableChange">
<template #bodyCell="{ column, record }">
<!-- 监测状态 -->
<template v-if="column.key === 'coenvwState'">
<a-tooltip placement="top">
<template #title>
<ol>
<li>最近1天有监测数据为在线否则为离线</li>
</ol>
</template>
<a-tag :color="record.coenvwState > 0 ? 'green' : '#999'">
{{ record.coenvwState > 0 ? '在线' : '离线' }}
</a-tag>
</a-tooltip>
</template>
<!-- 操作 -->
<template v-else-if="column.key === 'action'">
<a @click="handleViewDetail(record)" class="text_hocer" >查看详情</a>
</template>
</template>
</a-table>
</div>
</template>
<script lang="ts" setup>
import { ref, computed, watch, onMounted } from 'vue'
import { vmsstbprptGetKendoList, wbsbGetKendoList, getVmsstbprpt } from '@/api/sw'
import { useModelStore } from "@/store/modules/model";
const modelStore = useModelStore();
// ==================== Props ====================
const props = defineProps<{
baseId?: string
tabs?: any[]
activeKey?: string
}>()
// ==================== ====================
const tabIndex = ref(props.activeKey || '26')
const loading = ref(false)
const tableData = ref<any[]>([])
const pagination = ref({
current: 1,
pageSize: 20,
total: 0,
showSizeChanger: true,
showQuickJumper: true,
showTotal: (total: number) => `${total}`,
pageSizeOptions: ['10', '20', '50', '100']
})
const tableHeight = 'calc(48vh)'
//
const hydropowerStationList = ref<any[]>([])
const stationDataList: any = ref([])
const searchData = ref<any>({
stcd: {
dataDimensionType: 'hyBase',
stcdId: '',
dataDimensionData: props.baseId || 'all'
},
stnm: null
})
// ==================== ====================
const getRowSpanInfo = (dataIndex: string) => {
const spanMap = new Map()
if (!tableData.value || tableData.value.length === 0) {
return spanMap
}
let currentGroupStart = 0
let currentValue = tableData.value[0][dataIndex]
for (let i = 0; i < tableData.value.length; i++) {
const value = tableData.value[i][dataIndex]
if (value !== currentValue) {
//
spanMap.set(currentGroupStart, i - currentGroupStart)
currentGroupStart = i
currentValue = value
}
}
//
spanMap.set(currentGroupStart, tableData.value.length - currentGroupStart)
return spanMap
}
// ==================== Props ====================
// const baseId = ref('')
watch(
() => props.baseId,
(newVal) => {
if (newVal && newVal !== searchData.value.stcd.dataDimensionData) {
searchData.value.stcd.dataDimensionData = newVal
fetchData()
}
}
)
watch(
() => props.activeKey,
(newVal) => {
if (newVal && newVal !== tabIndex.value) {
tabIndex.value = newVal
fetchData()
}
}
)
// ==================== ====================
const columns = computed(() => {
const baseColumns = [
{
title: '所属基地',
dataIndex: 'baseName',
key: 'baseName',
width: 150,
customCell: (record: any, index: number) => {
const spanMap = getRowSpanInfo('baseName')
const rowSpan = spanMap.get(index)
if (rowSpan === undefined) {
//
return { rowSpan: 0 }
}
//
return { rowSpan }
}
},
{
title: '测站名称',
dataIndex: 'stnm',
key: 'stnm',
width: 150
},
{
title: '电站',
dataIndex: 'ennm',
key: 'ennm',
width: 150
},
{
title: '建成时间',
dataIndex: 'jcdt',
key: 'jcdt',
width: 120
},
{
title: '监测指标',
dataIndex: 'stindx',
key: 'stindx',
width: 150
},
{
title: '监测状态',
key: 'coenvwState',
dataIndex: 'coenvwState',
width: 100,
align: 'center' as const
},
{
title: '操作',
key: 'action',
width: 100,
align: 'center' as const,
fixed: 'right' as const
}
]
return baseColumns.filter(Boolean)
})
// ==================== ====================
const buildSearchFilter = () => {
const filters: any[] = []
// ID
if (searchData.value?.stcd?.dataDimensionData && searchData.value.stcd.dataDimensionData !== 'all') {
filters.push({
field: 'baseId',
operator: 'eq',
value: searchData.value.stcd.dataDimensionData
})
}
// ID
if (searchData.value?.stcd?.stcdId) {
filters.push({
field: 'rstcd',
operator: 'eq',
value: searchData.value.stcd.stcdId
})
}
//
if (searchData.value?.stnm) {
filters.push({
field: 'rstcd',
operator: 'eq',
value: searchData.value.stnm
})
}
// Tab
switch (tabIndex.value) {
case '26':
filters.push(
{
field: 'mway',
operator: 'eq',
dataType: 'string',
value: 2
},
{
field: 'sttpCode',
operator: 'eq',
dataType: 'string',
value: 'WTRV'
}
)
break
case '27':
filters.push(
{
field: 'mway',
operator: 'eq',
dataType: 'string',
value: 2
},
{
field: 'sttpCode',
operator: 'eq',
dataType: 'string',
value: 'WTVT'
}
)
break
}
return {
logic: 'and',
filters: filters.filter((f) => f.value !== undefined && f.value !== null && f.value !== '')
}
}
// ==================== API ====================
const fetchData = async () => {
loading.value = true
try {
const filter = buildSearchFilter()
const params = {
filter: filter,
select: [
'baseName',
'stnm',
'ennm',
'jcdt',
'stindx',
'coenvwState',
'sttpCode',
'stcd',
'rstcd',
'stCode',
'stName',
'dvtp'
],
sort: [
{ field: 'baseStepSort', dir: 'asc' },
{ field: 'hbrvcd', dir: 'asc' },
{ field: 'rstcdStepSort', dir: 'asc' },
{ field: 'siteStepSort', dir: 'asc' }
]
}
console.log('【请求参数】', params)
const res = await vmsstbprptGetKendoList(params)
console.log('【响应数据】', res)
//
let total = res.data.total
let records = res?.data?.data || res?.data
// debugger
// if (Array.isArray(res?.data?.data)) {
// records = res.data.data
// total = res.data.data.length
// }
tableData.value = records || []
pagination.value.total = total
loading.value = false
} catch (error) {
console.error('获取水文监测数据失败:', error)
tableData.value = []
pagination.value.total = 0
} finally {
loading.value = false
}
}
const getselect = async () => {
try {
const params = {
"filter": {
"logic": "and",
"filters": [
{
"field": "wbsType",
"operator": "eq",
"dataType": "string",
"value": "PSB"
},
{
"field": "treeLevel",
"operator": "eq",
"dataType": "string",
"value": "1"
}
]
},
"sort": [
{
"field": "orderIndex",
"dir": "asc"
}
]
}
const res = await wbsbGetKendoList(params)
console.log('【水电站列表】', res)
//
if (res?.data?.data && Array.isArray(res.data.data)) {
hydropowerStationList.value = res.data.data
console.log('站点数据列表', stationDataList.value)
getselectTwo(searchData.value.stcd.dataDimensionData)
console.log('【水电站数量】', hydropowerStationList.value.length)
} else {
console.warn('【警告】未获取到有效的水电站数据')
hydropowerStationList.value = []
}
} catch (error) {
console.error('【错误】获取水电站列表失败:', error)
hydropowerStationList.value = []
}
}
const getselectTwo = async (code: any) => {
console.log('站点数据列表')
const params = {
"filter": {
"logic": "and",
"filters": [
code != 'all' ? {
"field": "baseId",
"operator": "contains",
"dataType": "string",
"value": code
} : null,
{
"field": "sttpCode",
"operator": "eq",
"dataType": "string",
"value": "ENG"
}
].filter(Boolean)
},
"sort": code !== 'all'
? [
{
"field": "baseId",
"dir": "asc"
},
{
"field": "rvcdStepSort",
"dir": "asc"
},
{
"field": "rstcdStepSort",
"dir": "asc"
},
{
"field": "siteStepSort",
"dir": "asc"
}
]
: [
{
"field": "ttpwr",
"dir": "desc"
}
],
"select": [
"stcd",
"stnm"
]
}
const res = await getVmsstbprpt(params)
// console.log('', res)
console.log('站点数据列表', res?.data?.data)
stationDataList.value = res?.data?.data || res?.data
}
// ==================== ====================
const handleHydropowerStationChange = (value: string) => {
console.log('【水电站选择变化】', value)
//
searchData.value.stnm = null
searchData.value.stcd.stcdId = ''
//
getselectTwo(value)
//
// pagination.value.current = 1
// fetchData()
}
const handleStationChange = (value: string | undefined) => {
console.log('【测站选择变化】', value)
// ID
if (value) {
searchData.value.stcd.stcdId = value
} else {
searchData.value.stcd.stcdId = ''
}
}
const handleSearch = () => {
console.log('【搜索】', searchData.value)
pagination.value.current = 1
fetchData()
}
const handleReset = () => {
console.log('【重置】')
searchData.value = {
stcd: {
dataDimensionType: 'hyBase',
stcdId: '',
dataDimensionData: props.baseId || 'all'
},
stnm: null
}
pagination.value.current = 1
fetchData()
}
const handleTabChange = (key: string) => {
console.log('【Tab切换】', key)
tabIndex.value = key
pagination.value.current = 1
fetchData()
}
const handleTableChange = (pag: any) => {
pagination.value.current = pag.current
pagination.value.pageSize = pag.pageSize
fetchData()
}
const handleViewDetail = (record: any) => {
if (record) {
modelStore.modalVisible = true;
modelStore.params.sttp = "wt_point";
modelStore.title = record.stnm + "详情信息";
// modelStore.isBasicEdit = true;
modelStore.params.stcd = record.stcd;
}
// TODO:
}
// ==================== ====================
onMounted(() => {
console.log('【组件挂载】水文监测工作二级页面已加载')
fetchData()
getselect()
})
</script>
<style scoped lang="scss">
.shuiwenjiancegongzwo-ej-container {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
background-color: #ffffff;
box-sizing: border-box;
.search-form {
margin-bottom: 16px;
border-radius: 4px;
}
:deep(.ant-table-wrapper) {
flex: 1;
overflow: hidden;
}
}
.text_hocer{
color: #2f6b98;
}
.text_hocer:hover{
color: #40a9ff;
}
</style>

View File

@ -17,8 +17,12 @@ import type { ECharts } from "echarts";
import SidePanelItem from "@/components/SidePanelItem/index.vue";
import { getKendoListCust, wbsbGetKendoList } from "@/api/sw";
import { useJidiSelectEventStore } from "@/store/modules/jidiSelectEvent";
import { da } from "element-plus/es/locale/index.mjs";
import { f } from "vue-router/dist/router-CWoNjPRp.mjs";
import { useModelStore } from "@/store/modules/model";
const modelStore = useModelStore();
@ -141,8 +145,8 @@ let chartInstance: ECharts | null = null;
// -
const stationNames = ref([]);
// - (°C) null
const waterTemperatures = ref([]);
// - (°C) null使stcd
const waterTemperatures = ref<{ value: number | null; stcd: string; stnm: string }[]>([]);
// 使 computed 使 currentTime dataOne.value
const currentTime = computed(() => dataOne.value || getCurrentHourTime());
@ -309,6 +313,22 @@ const initChart = () => {
};
chartInstance.setOption(option);
//
chartInstance.on('click', (params: any) => {
if (params.componentType === 'series') {
console.log('点击数据点:', params);
console.log('stcd:', params.data.stcd);
console.log('stnm:', params.data.stnm);
console.log('temperature:', params.data.value);
modelStore.modalVisible = true;
modelStore.params.sttp = "wt_point";
modelStore.title = params.data.stnm + "详情信息";
// modelStore.isBasicEdit = true;
modelStore.params.stcd = params.data.stcd;
modelStore.params.date = paramsOne.tm;
}
});
};
//
@ -348,10 +368,14 @@ const init = async () => {
.map((item: any) => item.stnm);
console.log(stationNames.value);
// stcdstnm
waterTemperatures.value = data
.filter((item: any) => item.sttp == "2")
.map((item: any) => item.temperature);
// waterTemperatures.value = res.data.data.map((item: any) => item.temperature);
.map((item: any) => ({
value: item.temperature,
stcd: item.stcd,
stnm: item.stnm
}));
console.log(waterTemperatures.value);
//
@ -579,6 +603,7 @@ onBeforeUnmount(() => {
justify-content: center;
align-items: center;
}
:deep(.ant-spin-nested-loading) {
height: 252px !important;
}

View File

@ -11,8 +11,12 @@ export const useModelStore = defineStore('model', () => {
const params = ref<{
sttp: string;
stcd: any;
date: any;
}>({
sttp: 'eng'
sttp: 'eng',
stcd: '',
date: '',
});
return {
params,

View File

@ -113,3 +113,6 @@ svg {
.ant-empty-description {
color: rgba(0, 0, 0, 0.85);
}
.ant-select-tree-node-selected{
background-color: #bae7ff !important;
}

View File

@ -22,7 +22,8 @@ service.interceptors.request.use(
if (
config.url.includes('/dec-lygk-base-server') ||
config.url.includes('/wmp-env-server') ||
config.url.includes('/wmp-sys-server')
config.url.includes('/wmp-sys-server') ||
config.url.includes('/dec-modules-usm-springcloud-starter')
) {
config.headers._appid = '974975A6-47FD-4C04-9ACD-68938D2992BD';
config.headers._isolateid = '5b34aecb-adfb-4dfc-ad95-21505a9eb388';

View File

@ -145,7 +145,7 @@ const showMapModal17 = () => {
<div class="rightContent">
<RightDrawer>
<a-button @click="showMapModal">打开电站弹窗</a-button>
<!-- <a-button @click="showMapModal1">打开水温弹窗</a-button> -->
<a-button @click="showMapModal1">打开水温弹窗</a-button>
<!-- <a-button @click="showMapModal2">打开水质弹窗</a-button> -->
<!-- <a-button @click="showMapModal3">打开栖息地弹窗</a-button> -->
<!-- <a-button @click="showMapModal4">打开栖息地流量弹窗</a-button> -->
@ -161,7 +161,7 @@ const showMapModal17 = () => {
<!-- <a-button @click="showMapModal14">打开低温水减缓设施-叠梁门弹窗</a-button> -->
<!-- <a-button @click="showMapModal15">打开前置挡墙弹窗</a-button> -->
<!-- <a-button @click="showMapModal16">打开隔水幕墙弹窗</a-button> -->
<a-button @click="showMapModal17">打开夹岩双层取水弹窗</a-button>
<!-- <a-button @click="showMapModal17">打开夹岩双层取水弹窗</a-button> -->
<jidiInfoMod />
<shuidianhuangjingjieruMod />

View File

@ -74,6 +74,16 @@ export default ({ mode }: ConfigEnv): UserConfig => {
new RegExp('^/api/wmp-sys-server'),
'/api/wmp-sys-server'
)
},
'/api/dec-modules-usm-springcloud-starter': {
target: 'https://211.99.26.225:12122',
changeOrigin: true,
secure: false,
rewrite: path =>
path.replace(
new RegExp('^/api/dec-modules-usm-springcloud-starter'),
'/api/dec-modules-usm-springcloud-starter'
)
}
}
},