Merge branch 'main' of http://121.37.111.42:3000/zhengsl/WholeProcessPlatform into main_hzz
This commit is contained in:
commit
d984fcfbff
@ -7,12 +7,12 @@ VITE_APP_TITLE = '水电水利建设项目全过程环境管理信息平台'
|
|||||||
VITE_APP_PORT = 3000
|
VITE_APP_PORT = 3000
|
||||||
VITE_APP_BASE_API = '/dev-api'
|
VITE_APP_BASE_API = '/dev-api'
|
||||||
# 本地环境
|
# 本地环境
|
||||||
VITE_APP_BASE_URL = 'http://localhost:8093'
|
# VITE_APP_BASE_URL = 'http://localhost:8093'
|
||||||
# 测试环境
|
# 测试环境
|
||||||
# VITE_APP_BASE_URL = 'http://172.16.21.142:8093'
|
# 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://172.16.21.142:8096'
|
||||||
# 汤伟
|
# 汤伟
|
||||||
# VITE_APP_BASE_URL = 'http://10.84.121.21:8093'
|
VITE_APP_BASE_URL = 'http://10.84.121.21:8093'
|
||||||
# VITE_APP_BASE_URL = 'http://192.168.1.162:8093'
|
# VITE_APP_BASE_URL = 'http://192.168.1.162:8093'
|
||||||
|
|
||||||
# 测试环境线上
|
# 测试环境线上
|
||||||
|
|||||||
@ -2,7 +2,7 @@ import request from '@/utils/request';
|
|||||||
//水质监测工作开展情况
|
//水质监测工作开展情况
|
||||||
export function msstbprptGetKendoList(data: any) {
|
export function msstbprptGetKendoList(data: any) {
|
||||||
return request({
|
return request({
|
||||||
url: '/api/dec-lygk-base-server/base/msstbprpt/GetKendoList',
|
url: '/wq/msstbprpt/GetKendoList',
|
||||||
method: 'post',
|
method: 'post',
|
||||||
data
|
data
|
||||||
});
|
});
|
||||||
@ -18,7 +18,7 @@ export function vmsstbprptGetKendoList(data: any) {
|
|||||||
//地表水水质达标率
|
//地表水水质达标率
|
||||||
export function wqGetKendoListCust(data: any) {
|
export function wqGetKendoListCust(data: any) {
|
||||||
return request({
|
return request({
|
||||||
url: '/api/wmp-env-server/env/wq/GetKendoListCust',
|
url: '/wq/GetKendoListCust',
|
||||||
method: 'post',
|
method: 'post',
|
||||||
data
|
data
|
||||||
});
|
});
|
||||||
@ -27,7 +27,7 @@ export function wqGetKendoListCust(data: any) {
|
|||||||
//环境质量满足度 - 表格数据列表
|
//环境质量满足度 - 表格数据列表
|
||||||
export function wqGetKendoList(data: any) {
|
export function wqGetKendoList(data: any) {
|
||||||
return request({
|
return request({
|
||||||
url: '/api/wmp-env-server/env/wq/GetKendoListCust',
|
url: '/wq/GetKendoListCust',
|
||||||
method: 'post',
|
method: 'post',
|
||||||
data
|
data
|
||||||
});
|
});
|
||||||
@ -35,7 +35,7 @@ export function wqGetKendoList(data: any) {
|
|||||||
//沿程水质变化_ 图表数据
|
//沿程水质变化_ 图表数据
|
||||||
export function qgcGetKendoListCust(data: any) {
|
export function qgcGetKendoListCust(data: any) {
|
||||||
return request({
|
return request({
|
||||||
url: '/api/wmp-env-server/env/wq/along/qgc/GetKendoListCust',
|
url: '/wq/along/qgc/GetKendoListCust',
|
||||||
method: 'post',
|
method: 'post',
|
||||||
data
|
data
|
||||||
});
|
});
|
||||||
@ -43,7 +43,46 @@ export function qgcGetKendoListCust(data: any) {
|
|||||||
//沿程水质变化_ 根据流域获取河段 https://211.99.26.225:12122
|
//沿程水质变化_ 根据流域获取河段 https://211.99.26.225:12122
|
||||||
export function wbsbGetKendoList(data: any) {
|
export function wbsbGetKendoList(data: any) {
|
||||||
return request({
|
return request({
|
||||||
url: '/api/dec-lygk-base-server/base/wbsb/GetKendoList',
|
url: '/wq/wbsb/GetKendoList',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// /获取预警规则限值
|
||||||
|
export function ruleGetKendoListCust(data: any) {
|
||||||
|
return request({
|
||||||
|
url: '/wq/rule/GetKendoListCust',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
});
|
||||||
|
}
|
||||||
|
//获取数据范围
|
||||||
|
export function noAuthGetKendoListCust(data: any) {
|
||||||
|
return request({
|
||||||
|
url: '/wq/data/noAuth/GetKendoListCust',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
});
|
||||||
|
}
|
||||||
|
//获取要素配置
|
||||||
|
export function stbprpGetStbprpYsByStcd(data: any) {
|
||||||
|
return request({
|
||||||
|
url: '/wq/stbprp/GetStbprpYsByStcd',
|
||||||
|
method: 'get',
|
||||||
|
params:data
|
||||||
|
});
|
||||||
|
}
|
||||||
|
//表格数据获取函数
|
||||||
|
export function dataGetKendoListCust(data: any) {
|
||||||
|
return request({
|
||||||
|
url: '/wq/data/GetKendoListCust?calculateFlag=1',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
});
|
||||||
|
}
|
||||||
|
export function NodataGetKendoListCust(data: any) {
|
||||||
|
return request({
|
||||||
|
url: '/wq/data/GetKendoListCust',
|
||||||
method: 'post',
|
method: 'post',
|
||||||
data
|
data
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1043,13 +1043,7 @@ const wtPointColumns: Array<any> = [
|
|||||||
];
|
];
|
||||||
//水质基础信息
|
//水质基础信息
|
||||||
const FhWpPointColumns: Array<any> = [
|
const FhWpPointColumns: Array<any> = [
|
||||||
{
|
|
||||||
name: '基本属性',
|
|
||||||
visible: false,
|
|
||||||
ruleTips: '',
|
|
||||||
type: '',
|
|
||||||
url: ''
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
name: '测站名称',
|
name: '测站名称',
|
||||||
filed: 'stnm',
|
filed: 'stnm',
|
||||||
@ -1066,7 +1060,7 @@ const FhWpPointColumns: Array<any> = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: '站址',
|
name: '站址',
|
||||||
filed: 'addvcdName',
|
filed: 'stlc',
|
||||||
visible: true,
|
visible: true,
|
||||||
type: 'input',
|
type: 'input',
|
||||||
url: '',
|
url: '',
|
||||||
@ -1093,6 +1087,14 @@ const FhWpPointColumns: Array<any> = [
|
|||||||
type: 'number',
|
type: 'number',
|
||||||
toFixed: 6,
|
toFixed: 6,
|
||||||
url: '',
|
url: '',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '建成日期',
|
||||||
|
filed: 'jcdt',
|
||||||
|
visible: true,
|
||||||
|
type: 'date',
|
||||||
|
format: 'YYYY-MM-DD',
|
||||||
|
url: '',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: '类别',
|
name: '类别',
|
||||||
@ -1115,14 +1117,6 @@ const FhWpPointColumns: Array<any> = [
|
|||||||
type: 'select',
|
type: 'select',
|
||||||
url: '',
|
url: '',
|
||||||
},
|
},
|
||||||
{
|
|
||||||
name: '',
|
|
||||||
filed: '',
|
|
||||||
visible: true,
|
|
||||||
type: '',
|
|
||||||
url: '',
|
|
||||||
},
|
|
||||||
|
|
||||||
];
|
];
|
||||||
//栖息地基础信息 fh_point
|
//栖息地基础信息 fh_point
|
||||||
const FhPointColumns: Array<any> = [
|
const FhPointColumns: Array<any> = [
|
||||||
|
|||||||
@ -668,6 +668,12 @@ const WQFBTabs: Array<any> = [
|
|||||||
key: 'WaterQuality',
|
key: 'WaterQuality',
|
||||||
type: 'WaterQuality',
|
type: 'WaterQuality',
|
||||||
default: true // 默认显示
|
default: true // 默认显示
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '实时视频',
|
||||||
|
key: 'videoInfo',
|
||||||
|
type: 'video',
|
||||||
|
url: '/video/dataStcdFrame/getVideoMonitorList'
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
// const WQTabs: Array<any> = [
|
// const WQTabs: Array<any> = [
|
||||||
@ -767,7 +773,7 @@ const DWTabs: Array<any> = [
|
|||||||
key: 'basicInfo',
|
key: 'basicInfo',
|
||||||
type: 'basic',
|
type: 'basic',
|
||||||
default: true, // 默认显示
|
default: true, // 默认显示
|
||||||
url: '/api/dec-lygk-base-server/base/msstbprpt/getStcdInfo'
|
url: '/api/dec-lygk-base-server/base/msstbprpt/getStcdInfo'
|
||||||
},
|
},
|
||||||
// {
|
// {
|
||||||
// name: '实时视频',
|
// name: '实时视频',
|
||||||
|
|||||||
@ -43,7 +43,8 @@
|
|||||||
</div>
|
</div>
|
||||||
<div v-if="datetimePicker.show">
|
<div v-if="datetimePicker.show">
|
||||||
<!-- 添加 locale 属性来设置语言 -->
|
<!-- 添加 locale 属性来设置语言 -->
|
||||||
<a-date-picker v-model:value="datetimeValue" show-time
|
<a-date-picker v-model:value="datetimeValue"
|
||||||
|
:show-time="timePickerConfig"
|
||||||
:style="{ width: datetimePicker.picker === 'year' ? '80px' : datetimePicker.picker === 'month' ? '90px' : '130px' }"
|
:style="{ width: datetimePicker.picker === 'year' ? '80px' : datetimePicker.picker === 'month' ? '90px' : '130px' }"
|
||||||
:format="datetimePicker.format !== null ? datetimePicker.format : undefined"
|
:format="datetimePicker.format !== null ? datetimePicker.format : undefined"
|
||||||
:picker="datetimePicker.picker" :allowClear="false" placeholder=" "
|
:picker="datetimePicker.picker" :allowClear="false" placeholder=" "
|
||||||
@ -149,12 +150,15 @@ const props = defineProps({
|
|||||||
})
|
})
|
||||||
},
|
},
|
||||||
datetimePicker: { // 时间选择框
|
datetimePicker: { // 时间选择框
|
||||||
type: Object as () => SelectConfig,
|
type: Object as () => SelectConfig & {
|
||||||
|
timeFormat?: string; // 新增:时间选择器的格式(控制选择粒度)
|
||||||
|
},
|
||||||
default: () => ({
|
default: () => ({
|
||||||
show: false,
|
show: false,
|
||||||
value: undefined,
|
value: undefined,
|
||||||
format: null, //YYYY-MM-DD HH
|
format: null, //YYYY-MM-DD HH
|
||||||
picker: 'date' //date | week | month | quarter | year
|
picker: 'date', //date | week | month | quarter | year
|
||||||
|
timeFormat: 'HH:mm:ss' // 默认值:显示时分秒选择器
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
scopeDate: { // 时间选择框
|
scopeDate: { // 时间选择框
|
||||||
@ -481,6 +485,26 @@ const computedScopeDatePresets = computed(() => {
|
|||||||
return presets;
|
return presets;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算属性:根据 datetimePicker 的 timeFormat 配置生成时间选择器的配置对象
|
||||||
|
*/
|
||||||
|
const timePickerConfig = computed(() => {
|
||||||
|
if (!props.datetimePicker.show || props.datetimePicker.picker !== 'date') {
|
||||||
|
return true; // 默认行为
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果配置了 timeFormat,则使用该格式控制选择粒度
|
||||||
|
if (props.datetimePicker.timeFormat) {
|
||||||
|
return {
|
||||||
|
format: props.datetimePicker.timeFormat,
|
||||||
|
hideDisabledOptions: true, // 隐藏禁用的时间选项
|
||||||
|
defaultValue: dayjs().startOf('hour') // 默认选中整点
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return true; // 默认行为
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 处理树节点选择事件
|
* 处理树节点选择事件
|
||||||
* 由于父节点已设置 selectable: false,这里只会接收到叶子节点
|
* 由于父节点已设置 selectable: false,这里只会接收到叶子节点
|
||||||
@ -542,6 +566,12 @@ const handleDateTimeChange = (date: any | null, dateString: string) => {
|
|||||||
if (props.datetimePicker.picker === 'month' && date) {
|
if (props.datetimePicker.picker === 'month' && date) {
|
||||||
lastMonthValue.value = dayjs(date).format('YYYY-MM');
|
lastMonthValue.value = dayjs(date).format('YYYY-MM');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 如果配置了 timeFormat 为 'HH'(只选择小时),确保分钟和秒为 00
|
||||||
|
if (props.datetimePicker.timeFormat === 'HH' && date) {
|
||||||
|
const normalizedDate = dayjs(date).minute(0).second(0);
|
||||||
|
datetimeValue.value = normalizedDate;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleTabClick = (value: string) => {
|
const handleTabClick = (value: string) => {
|
||||||
|
|||||||
@ -4,35 +4,19 @@
|
|||||||
<a-form ref="formRef" :model="formState" layout="inline" class="search-form">
|
<a-form ref="formRef" :model="formState" layout="inline" class="search-form">
|
||||||
<a-space wrap>
|
<a-space wrap>
|
||||||
<a-form-item label="所属基地" name="dataDimensionVal">
|
<a-form-item label="所属基地" name="dataDimensionVal">
|
||||||
<a-select
|
<a-select v-model:value="formState.dataDimensionVal" placeholder="请选择" style="width: 120px"
|
||||||
v-model:value="formState.dataDimensionVal"
|
show-search :filter-option="filterBaseOption" option-filter-prop="label"
|
||||||
placeholder="请选择"
|
@change="handleBaseChange">
|
||||||
style="width: 120px"
|
<a-select-option v-for="item in baseList" :key="item.baseid" :value="item.baseid"
|
||||||
show-search
|
:label="item.basename">
|
||||||
:filter-option="filterBaseOption"
|
|
||||||
option-filter-prop="label"
|
|
||||||
@change="handleBaseChange"
|
|
||||||
>
|
|
||||||
<a-select-option
|
|
||||||
v-for="item in baseList"
|
|
||||||
:key="item.baseid"
|
|
||||||
:value="item.baseid"
|
|
||||||
:label="item.basename"
|
|
||||||
>
|
|
||||||
{{ item.basename }}
|
{{ item.basename }}
|
||||||
</a-select-option>
|
</a-select-option>
|
||||||
</a-select>
|
</a-select>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
|
|
||||||
<a-form-item v-if="formState.dataDimensionVal === 'all'" label="测站类型" name="dtinType">
|
<a-form-item v-if="typeOne == 'all'" label="测站类型" name="dtinType">
|
||||||
<a-select
|
<a-select v-model:value="formState.dtinType" placeholder="请选择" style="width: 120px" show-search
|
||||||
v-model:value="formState.dtinType"
|
:filter-option="filterOption" option-filter-prop="label">
|
||||||
placeholder="请选择"
|
|
||||||
style="width: 100px"
|
|
||||||
show-search
|
|
||||||
:filter-option="filterOption"
|
|
||||||
option-filter-prop="label"
|
|
||||||
>
|
|
||||||
<a-select-option value="" label="全部">全部</a-select-option>
|
<a-select-option value="" label="全部">全部</a-select-option>
|
||||||
<a-select-option value="0" label="自建水质站">自建水质站</a-select-option>
|
<a-select-option value="0" label="自建水质站">自建水质站</a-select-option>
|
||||||
<a-select-option value="1" label="国家水质站">国家水质站</a-select-option>
|
<a-select-option value="1" label="国家水质站">国家水质站</a-select-option>
|
||||||
@ -40,28 +24,16 @@
|
|||||||
</a-form-item>
|
</a-form-item>
|
||||||
|
|
||||||
<a-form-item label="时间维度" name="types">
|
<a-form-item label="时间维度" name="types">
|
||||||
<a-select
|
<a-select v-model:value="formState.types" placeholder="请选择" style="width: 80px" show-search
|
||||||
v-model:value="formState.types"
|
:filter-option="filterOption" option-filter-prop="label">
|
||||||
placeholder="请选择"
|
|
||||||
style="width: 80px"
|
|
||||||
show-search
|
|
||||||
:filter-option="filterOption"
|
|
||||||
option-filter-prop="label"
|
|
||||||
>
|
|
||||||
<a-select-option value="year" label="年度">年度</a-select-option>
|
<a-select-option value="year" label="年度">年度</a-select-option>
|
||||||
<a-select-option value="month" label="月度">月度</a-select-option>
|
<a-select-option value="month" label="月度">月度</a-select-option>
|
||||||
</a-select>
|
</a-select>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
|
|
||||||
<a-form-item label="达标状态" name="sfdb">
|
<a-form-item label="达标状态" name="sfdb">
|
||||||
<a-select
|
<a-select v-model:value="formState.sfdb" placeholder="请选择" style="width: 100px" show-search
|
||||||
v-model:value="formState.sfdb"
|
:filter-option="filterOption" option-filter-prop="label">
|
||||||
placeholder="请选择"
|
|
||||||
style="width: 100px"
|
|
||||||
show-search
|
|
||||||
:filter-option="filterOption"
|
|
||||||
option-filter-prop="label"
|
|
||||||
>
|
|
||||||
<a-select-option value="" label="全部">全部</a-select-option>
|
<a-select-option value="" label="全部">全部</a-select-option>
|
||||||
<a-select-option value="0" label="不达标">不达标</a-select-option>
|
<a-select-option value="0" label="不达标">不达标</a-select-option>
|
||||||
<a-select-option value="1" label="达标">达标</a-select-option>
|
<a-select-option value="1" label="达标">达标</a-select-option>
|
||||||
@ -70,31 +42,14 @@
|
|||||||
</a-form-item>
|
</a-form-item>
|
||||||
|
|
||||||
<a-form-item label="时间" name="dateValues">
|
<a-form-item label="时间" name="dateValues">
|
||||||
<a-month-picker
|
<a-date-picker v-model:value="formState.dateValues"
|
||||||
v-if="formState.types === 'month'"
|
:format="formState.types === 'month' ? 'YYYY-MM' : 'YYYY'" :picker="formState.types"
|
||||||
v-model:value="formState.dateValues"
|
placeholder="请选择月份" style="width: 140px" :disabled-date="disabledMonthDate" />
|
||||||
format="YYYY-MM"
|
|
||||||
placeholder="请选择月份"
|
|
||||||
style="width: 160px"
|
|
||||||
:disabled-date="disabledMonthDate"
|
|
||||||
/>
|
|
||||||
<a-year-picker
|
|
||||||
v-else
|
|
||||||
v-model:value="formState.dateValues"
|
|
||||||
format="YYYY"
|
|
||||||
placeholder="请选择年份"
|
|
||||||
style="width: 160px"
|
|
||||||
:disabled-date="disabledYearDate"
|
|
||||||
/>
|
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
|
|
||||||
<a-form-item label="断面名称" name="stnm">
|
<a-form-item label="断面名称" name="stnm">
|
||||||
<a-input
|
<a-input v-model:value="formState.stnm" placeholder="请输入断面名称" style="width: 200px" allow-clear />
|
||||||
v-model:value="formState.stnm"
|
|
||||||
placeholder="请输入断面名称"
|
|
||||||
style="width: 200px"
|
|
||||||
allow-clear
|
|
||||||
/>
|
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
|
|
||||||
<a-form-item>
|
<a-form-item>
|
||||||
@ -120,21 +75,9 @@
|
|||||||
|
|
||||||
<!-- 右侧表格区域 -->
|
<!-- 右侧表格区域 -->
|
||||||
<div class="table-section">
|
<div class="table-section">
|
||||||
<BasicTable
|
<BasicTable ref="tableRef" :scrollY="360" :columns="columns" :list-url="wqGetKendoList"
|
||||||
ref="tableRef"
|
:search-params="{ sort: sortConfig }" :transform-data="customTransform">
|
||||||
:scrollY="360"
|
|
||||||
:columns="columns"
|
|
||||||
:list-url="wqGetKendoList"
|
|
||||||
:search-params="{ sort: sortConfig }"
|
|
||||||
:transform-data="customTransform"
|
|
||||||
>
|
|
||||||
<template #bodyCell="{ column, record }">
|
<template #bodyCell="{ column, record }">
|
||||||
<!-- 断面名称列 - 可点击 -->
|
|
||||||
<template v-if="column.key === 'stnm'">
|
|
||||||
<a @click="handleViewStnmDetail(record)" class="text-link">
|
|
||||||
{{ record.stnm }}
|
|
||||||
</a>
|
|
||||||
</template>
|
|
||||||
<!-- 操作列 -->
|
<!-- 操作列 -->
|
||||||
<template v-if="column.key === 'action'">
|
<template v-if="column.key === 'action'">
|
||||||
<a @click="handleViewDetail(record)" class="text-link">
|
<a @click="handleViewDetail(record)" class="text-link">
|
||||||
@ -147,53 +90,44 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 详情弹窗 -->
|
<!-- 详情弹窗 -->
|
||||||
<a-modal
|
<a-modal v-model:open="detailModalVisible" :title="`${detailData.stnm || ''} 详细信息`" width="80%" :footer="null"
|
||||||
v-model:open="detailModalVisible"
|
@cancel="handleDetailModalClose">
|
||||||
:title="`${detailData.stnm || ''} 详细信息`"
|
<HJMZDTwoLays
|
||||||
width="80%"
|
:tc="{
|
||||||
:footer="null"
|
stcd: detailData.stcd || '',
|
||||||
@cancel="handleDetailModalClose"
|
startTime:detailData.tm ,
|
||||||
>
|
endTime: (typeof detailData.tm === 'string' ? detailData.tm.split(' ')[0] : dayjs(detailData.tm).format('YYYY-MM-DD')) + ' 23:59:59',
|
||||||
<div class="modal-content">
|
sfdb: formState.sfdb || ''
|
||||||
<!-- TODO: 这里放置详情组件,待后续实现 -->
|
}"
|
||||||
<p>详情内容待实现...</p>
|
:calculateFlag="{ calculateFlag: 0 }"
|
||||||
<pre>{{ detailData }}</pre>
|
/>
|
||||||
</div>
|
|
||||||
</a-modal>
|
|
||||||
|
|
||||||
<!-- 地图弹窗(断面名称点击) -->
|
|
||||||
<a-modal
|
|
||||||
v-model:open="mapModalVisible"
|
|
||||||
:title="`${mapModalData.titleName || ''} 详情信息`"
|
|
||||||
width="80%"
|
|
||||||
:footer="null"
|
|
||||||
@cancel="handleMapModalClose"
|
|
||||||
>
|
|
||||||
<div class="modal-content">
|
|
||||||
<!-- TODO: 这里放置地图标签页组件,待后续实现 -->
|
|
||||||
<p>地图详情内容待实现...</p>
|
|
||||||
<pre>{{ mapModalData }}</pre>
|
|
||||||
</div>
|
|
||||||
</a-modal>
|
</a-modal>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, reactive, computed, onMounted, onBeforeUnmount, watch, nextTick } from 'vue'
|
import { ref, reactive, computed, onMounted, onBeforeUnmount, watch, nextTick, h } from 'vue'
|
||||||
import { message } from 'ant-design-vue'
|
import { message } from 'ant-design-vue'
|
||||||
import * as echarts from 'echarts'
|
import * as echarts from 'echarts'
|
||||||
import dayjs, { Dayjs } from 'dayjs'
|
import dayjs, { Dayjs } from 'dayjs'
|
||||||
|
import isSameOrAfter from 'dayjs/plugin/isSameOrAfter'
|
||||||
import BasicTable from '@/components/BasicTable/index.vue'
|
import BasicTable from '@/components/BasicTable/index.vue'
|
||||||
import { wqGetKendoList } from '@/api/sz'
|
import { wqGetKendoList } from '@/api/sz'
|
||||||
|
import HJMZDTwoLays from "./HJMZDTwoLays.vue"
|
||||||
|
|
||||||
|
// 注册 dayjs 插件
|
||||||
|
dayjs.extend(isSameOrAfter)
|
||||||
|
|
||||||
// Props 定义
|
// Props 定义
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
datas?: Array<{ baseid: string; basename: string }> // 基地列表
|
datas?: Array<{ baseid: string; basename: string }> // 基地列表
|
||||||
type?: 'month' | 'year' // 时间维度
|
type?: string // 时间维度
|
||||||
modalData?: string // 模态框数据
|
modalData?: string // 模态框数据
|
||||||
dateValue?: string | Dayjs // 日期值
|
dateValue?: string | Dayjs // 日期值
|
||||||
dataDimensionVal?: string // 数据维度值
|
dataDimensionVal?: string // 数据维度值
|
||||||
buildType?: string | number // 建设类型
|
buildType?: string | number // 建设类型
|
||||||
|
typeOne: string //区分基地和站点
|
||||||
|
baseid: string
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
// Refs
|
// Refs
|
||||||
@ -203,19 +137,23 @@ const pieChartRef = ref<HTMLElement | null>(null)
|
|||||||
let pieChartInstance: echarts.ECharts | null = null
|
let pieChartInstance: echarts.ECharts | null = null
|
||||||
|
|
||||||
// State
|
// State
|
||||||
|
const baseid = ref(props.baseid || '')
|
||||||
|
const typeOne = ref(props.typeOne || '')
|
||||||
|
const originalMonth = ref<string>('') // 保存原始月份,用于年度切回月度时恢复
|
||||||
const formState = reactive({
|
const formState = reactive({
|
||||||
dataDimensionVal: props.dataDimensionVal || '',
|
dataDimensionVal: props.dataDimensionVal || '',
|
||||||
dtinType: '',
|
dtinType: props.buildType || '',
|
||||||
types: props.type || 'month',
|
types: props.type || 'month',
|
||||||
sfdb: '',
|
sfdb: '',
|
||||||
dateValues: props.dateValue ? (typeof props.dateValue === 'string' ? dayjs(props.dateValue, props.type === 'month' ? 'YYYY-MM' : 'YYYY') : props.dateValue) : dayjs(),
|
dateValues: props.dateValue ? (typeof props.dateValue === 'string' ? dayjs(props.dateValue, props.type === 'month' ? 'YYYY-MM' : 'YYYY') : props.dateValue) : dayjs(),
|
||||||
stnm: ''
|
stnm: ''
|
||||||
})
|
})
|
||||||
|
const kong = ref(true)
|
||||||
const baseList = computed(() => props.datas || [])
|
const baseList = computed(() => props.datas || [])
|
||||||
const pieLoading = ref(false)
|
const pieLoading = ref(false)
|
||||||
const showPieChart = ref(true)
|
const showPieChart = ref(true)
|
||||||
const pieCode = ref<string[]>([]) // 饼图选中的代码,用于过滤表格
|
const pieCode = ref<string[]>([]) // 饼图选中的代码,用于过滤表格
|
||||||
|
const displayTotal = ref<number>(0) // 饼图中心显示的总数(动态更新)
|
||||||
|
|
||||||
// 弹窗相关
|
// 弹窗相关
|
||||||
const detailModalVisible = ref(false)
|
const detailModalVisible = ref(false)
|
||||||
@ -230,17 +168,7 @@ const sortConfig = computed(() => [
|
|||||||
{ field: 'tm', dir: 'desc' }
|
{ field: 'tm', dir: 'desc' }
|
||||||
])
|
])
|
||||||
|
|
||||||
// 获取列表URL
|
|
||||||
const getListUrl = async (params: any) => {
|
|
||||||
try {
|
|
||||||
const res = await wqGetKendoList(params)
|
|
||||||
return res
|
|
||||||
} catch (error) {
|
|
||||||
console.error('获取表格数据失败:', error)
|
|
||||||
message.error('获取表格数据失败')
|
|
||||||
return { data: { data: [], total: 0 } }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 自定义数据转换
|
// 自定义数据转换
|
||||||
const customTransform = (res: any) => {
|
const customTransform = (res: any) => {
|
||||||
@ -250,6 +178,40 @@ const customTransform = (res: any) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 计算断面名称合并信息
|
||||||
|
const getStnmMergeInfo = (records: any[]) => {
|
||||||
|
if (!records || records.length === 0) return {}
|
||||||
|
|
||||||
|
const mergeInfo: Record<number, { rowSpan: number }> = {}
|
||||||
|
let i = 0
|
||||||
|
|
||||||
|
while (i < records.length) {
|
||||||
|
const currentStnm = records[i].stnm
|
||||||
|
let rowSpan = 1
|
||||||
|
|
||||||
|
// 查找后续相同断面名称的行
|
||||||
|
let j = i + 1
|
||||||
|
while (j < records.length && records[j].stnm === currentStnm) {
|
||||||
|
rowSpan++
|
||||||
|
j++
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置第一行的 rowSpan
|
||||||
|
mergeInfo[i] = { rowSpan }
|
||||||
|
|
||||||
|
// 设置后续行的 rowSpan 为 0(隐藏)
|
||||||
|
for (let k = i + 1; k < j; k++) {
|
||||||
|
mergeInfo[k] = { rowSpan: 0 }
|
||||||
|
}
|
||||||
|
|
||||||
|
i = j
|
||||||
|
}
|
||||||
|
|
||||||
|
return mergeInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// 表格列定义
|
// 表格列定义
|
||||||
const columns = [
|
const columns = [
|
||||||
{
|
{
|
||||||
@ -273,15 +235,13 @@ const columns = [
|
|||||||
title: '断面名称',
|
title: '断面名称',
|
||||||
dataIndex: 'stnm',
|
dataIndex: 'stnm',
|
||||||
key: 'stnm',
|
key: 'stnm',
|
||||||
width: 150,
|
width: 180,
|
||||||
ellipsis: true
|
ellipsis: true,
|
||||||
},
|
customCell: (record: any, rowIndex: number) => {
|
||||||
{
|
const tableData = tableRef.value?.tableData || []
|
||||||
title: '断面编码',
|
const mergeInfo = getStnmMergeInfo(tableData)
|
||||||
dataIndex: 'stcd',
|
return mergeInfo[rowIndex] || { rowSpan: 1 }
|
||||||
key: 'stcd',
|
}
|
||||||
width: 120,
|
|
||||||
visible: false
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '测站类型',
|
title: '测站类型',
|
||||||
@ -293,7 +253,19 @@ const columns = [
|
|||||||
title: '达标状态',
|
title: '达标状态',
|
||||||
dataIndex: 'sfdbName',
|
dataIndex: 'sfdbName',
|
||||||
key: 'sfdbName',
|
key: 'sfdbName',
|
||||||
width: 120
|
width: 120,
|
||||||
|
customRender: ({ record }: any) => {
|
||||||
|
const sfdb = record.sfdbName
|
||||||
|
let color = '#000000'
|
||||||
|
|
||||||
|
if (sfdb === '达标') {
|
||||||
|
color = '#008000' // 达标 - 绿色
|
||||||
|
} else if (sfdb === '不达标') {
|
||||||
|
color = '#FF0000' // 不达标 - 红色
|
||||||
|
}
|
||||||
|
|
||||||
|
return h('span', { style: { color } }, record.sfdbName || '-')
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '水质要求',
|
title: '水质要求',
|
||||||
@ -328,12 +300,6 @@ const disabledMonthDate = (current: Dayjs) => {
|
|||||||
return current.isAfter(now, 'month')
|
return current.isAfter(now, 'month')
|
||||||
}
|
}
|
||||||
|
|
||||||
// 禁用未去年份
|
|
||||||
const disabledYearDate = (current: Dayjs) => {
|
|
||||||
if (!current) return false
|
|
||||||
const now = dayjs()
|
|
||||||
return current.year() > now.year()
|
|
||||||
}
|
|
||||||
|
|
||||||
// 基地选择器搜索过滤函数
|
// 基地选择器搜索过滤函数
|
||||||
const filterBaseOption = (input: string, option: any) => {
|
const filterBaseOption = (input: string, option: any) => {
|
||||||
@ -348,12 +314,12 @@ const filterOption = (input: string, option: any) => {
|
|||||||
// 获取饼图数据
|
// 获取饼图数据
|
||||||
const fetchPieData = async () => {
|
const fetchPieData = async () => {
|
||||||
pieLoading.value = true
|
pieLoading.value = true
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 计算时间参数
|
// 计算时间参数
|
||||||
const timeFormat = formState.types === 'month' ? 'YYYY-MM' : 'YYYY'
|
const timeFormat = formState.types === 'month' ? 'YYYY-MM' : 'YYYY'
|
||||||
const startTime = formState.dateValues ? formState.dateValues.format(timeFormat) : ''
|
const startTime = formState.dateValues ? formState.dateValues.format(timeFormat) : ''
|
||||||
|
|
||||||
if (!startTime || !formState.dataDimensionVal) {
|
if (!startTime || !formState.dataDimensionVal) {
|
||||||
showPieChart.value = false
|
showPieChart.value = false
|
||||||
return
|
return
|
||||||
@ -361,18 +327,12 @@ const fetchPieData = async () => {
|
|||||||
|
|
||||||
// 构建请求参数
|
// 构建请求参数
|
||||||
const filters: any[] = [
|
const filters: any[] = [
|
||||||
{
|
formState.dataDimensionVal != 'all' ? {
|
||||||
field: 'baseId',
|
field: 'baseId',
|
||||||
operator: 'eq',
|
operator: 'eq',
|
||||||
dataType: 'string',
|
dataType: 'string',
|
||||||
value: formState.dataDimensionVal
|
value: typeOne.value == 'all' ? formState.dataDimensionVal : baseid.value
|
||||||
},
|
} : null,
|
||||||
{
|
|
||||||
field: 'type',
|
|
||||||
operator: 'eq',
|
|
||||||
dataType: 'string',
|
|
||||||
value: 'DAY'
|
|
||||||
},
|
|
||||||
formState.types === 'month' ? {
|
formState.types === 'month' ? {
|
||||||
field: 'drMonth',
|
field: 'drMonth',
|
||||||
operator: 'eq',
|
operator: 'eq',
|
||||||
@ -384,25 +344,36 @@ const fetchPieData = async () => {
|
|||||||
dataType: 'string',
|
dataType: 'string',
|
||||||
value: startTime
|
value: startTime
|
||||||
},
|
},
|
||||||
formState.dataDimensionVal !== 'all' ? {
|
{
|
||||||
|
field: 'TYPE',
|
||||||
|
operator: 'eq',
|
||||||
|
dataType: 'string',
|
||||||
|
value: 'DAY'
|
||||||
|
},
|
||||||
|
typeOne.value != 'all' ? {
|
||||||
field: 'rstcd',
|
field: 'rstcd',
|
||||||
operator: 'eq',
|
operator: 'eq',
|
||||||
dataType: 'string',
|
dataType: 'string',
|
||||||
value: formState.dataDimensionVal
|
value: formState.dataDimensionVal
|
||||||
} : null,
|
} : null,
|
||||||
formState.sfdb ? {
|
pieCode.value.length > 0 ? {
|
||||||
|
field: 'sfdb',
|
||||||
|
operator: 'in',
|
||||||
|
dataType: 'string',
|
||||||
|
value: pieCode.value
|
||||||
|
} : (formState.sfdb ? {
|
||||||
field: 'sfdb',
|
field: 'sfdb',
|
||||||
operator: 'eq',
|
operator: 'eq',
|
||||||
dataType: 'string',
|
dataType: 'string',
|
||||||
value: formState.sfdb
|
value: formState.sfdb
|
||||||
} : null,
|
} : null),
|
||||||
formState.stnm ? {
|
formState.stnm ? {
|
||||||
field: 'stnm',
|
field: 'stnm',
|
||||||
operator: 'contains',
|
operator: 'contains',
|
||||||
dataType: 'string',
|
dataType: 'string',
|
||||||
value: formState.stnm
|
value: formState.stnm
|
||||||
} : null,
|
} : null,
|
||||||
formState.dataDimensionVal == 'all' && formState.dtinType ? {
|
typeOne.value == 'all' && formState.dtinType != '' ? {
|
||||||
field: 'dtinType',
|
field: 'dtinType',
|
||||||
operator: 'eq',
|
operator: 'eq',
|
||||||
dataType: 'string',
|
dataType: 'string',
|
||||||
@ -414,12 +385,18 @@ const fetchPieData = async () => {
|
|||||||
filter: {
|
filter: {
|
||||||
logic: 'and',
|
logic: 'and',
|
||||||
filters
|
filters
|
||||||
}
|
},
|
||||||
|
group: [
|
||||||
|
{
|
||||||
|
"dir": "des",
|
||||||
|
"field": "sfdb"
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
// 调用API获取饼图数据
|
// 调用API获取饼图数据
|
||||||
const res = await wqGetKendoList(params)
|
const res = await wqGetKendoList(params)
|
||||||
|
|
||||||
let data = res?.data?.data || res?.data || []
|
let data = res?.data?.data || res?.data || []
|
||||||
|
|
||||||
if (data.length === 0) {
|
if (data.length === 0) {
|
||||||
@ -427,16 +404,30 @@ const fetchPieData = async () => {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 转换数据格式
|
// 转换数据格式 - 为每个数据项设置对应颜色
|
||||||
const dataSource = data.map((item: any) => ({
|
const dataSource = data.map((item: any) => {
|
||||||
name: item.keyName || 'null',
|
const name = item.keyName || 'null'
|
||||||
value: item.count,
|
// 根据名称设置颜色:达标-绿色,不达标-红色,其他-灰色
|
||||||
key: item.keyName === '达标' ? 'DB_STATUS_1' : item.keyName === '不达标' ? 'DB_STATUS_0' : 'CURNODATA',
|
let color = '#d9d9d9'
|
||||||
keyData: item?.key
|
if (name === '达标') {
|
||||||
}))
|
color = '#52c41a' // 绿色
|
||||||
|
} else if (name === '不达标') {
|
||||||
|
color = '#ff4d4f' // 红色
|
||||||
|
}
|
||||||
|
|
||||||
// 过滤掉"无数据"项
|
return {
|
||||||
const filteredDataSource = dataSource.filter(item => item.name !== '无数据')
|
name: name,
|
||||||
|
value: item.count,
|
||||||
|
key: item.keyName === '达标' ? 'DB_STATUS_1' : item.keyName === '不达标' ? 'DB_STATUS_0' : 'CURNODATA',
|
||||||
|
keyData: item?.key,
|
||||||
|
itemStyle: {
|
||||||
|
color: color
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// 使用所有数据项,包括"无数据"
|
||||||
|
const filteredDataSource = dataSource
|
||||||
|
|
||||||
if (filteredDataSource.length === 0) {
|
if (filteredDataSource.length === 0) {
|
||||||
showPieChart.value = false
|
showPieChart.value = false
|
||||||
@ -444,11 +435,11 @@ const fetchPieData = async () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
showPieChart.value = true
|
showPieChart.value = true
|
||||||
|
|
||||||
// 初始化或更新饼图
|
// 初始化或更新饼图
|
||||||
await nextTick()
|
await nextTick()
|
||||||
initPieChart(filteredDataSource)
|
initPieChart(filteredDataSource)
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('获取饼图数据失败:', error)
|
console.error('获取饼图数据失败:', error)
|
||||||
message.error('获取饼图数据失败')
|
message.error('获取饼图数据失败')
|
||||||
@ -470,8 +461,9 @@ const initPieChart = (data: any[]) => {
|
|||||||
|
|
||||||
pieChartInstance = echarts.init(pieChartRef.value)
|
pieChartInstance = echarts.init(pieChartRef.value)
|
||||||
|
|
||||||
// 计算总数
|
// 计算总数并初始化 displayTotal
|
||||||
const total = data.reduce((sum, item) => sum + item.value, 0)
|
const total = data.reduce((sum, item) => sum + item.value, 0)
|
||||||
|
displayTotal.value = total
|
||||||
|
|
||||||
const option = {
|
const option = {
|
||||||
tooltip: {
|
tooltip: {
|
||||||
@ -486,34 +478,36 @@ const initPieChart = (data: any[]) => {
|
|||||||
},
|
},
|
||||||
title: [
|
title: [
|
||||||
{
|
{
|
||||||
text: total.toString(),
|
text: displayTotal.value.toString(),
|
||||||
left: '45%',
|
left: '39%',
|
||||||
top: '50%',
|
top: '38%',
|
||||||
textAlign: 'center',
|
textAlign: 'center',
|
||||||
textStyle: {
|
textStyle: {
|
||||||
fontSize: 20,
|
fontSize: 20,
|
||||||
fontWeight: 'bold',
|
fontWeight: 'bold',
|
||||||
color: '#333'
|
color: '#2f6b98'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: '监测总次数',
|
text: '监测总次数',
|
||||||
left: '45%',
|
left: '39%',
|
||||||
top: '56%',
|
top: '44%',
|
||||||
textAlign: 'center',
|
textAlign: 'center',
|
||||||
textStyle: {
|
textStyle: {
|
||||||
fontSize: 12,
|
fontSize: 13,
|
||||||
color: '#666'
|
color: '#333333',
|
||||||
|
fontWeight: '500'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: '(次)',
|
text: '(次)',
|
||||||
left: '45%',
|
left: '39%',
|
||||||
top: '61%',
|
top: '49%',
|
||||||
textAlign: 'center',
|
textAlign: 'center',
|
||||||
textStyle: {
|
textStyle: {
|
||||||
fontSize: 10,
|
fontSize: 13,
|
||||||
color: '#999'
|
color: '#333333',
|
||||||
|
fontWeight: '500'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
@ -521,8 +515,8 @@ const initPieChart = (data: any[]) => {
|
|||||||
{
|
{
|
||||||
name: '断面总数量',
|
name: '断面总数量',
|
||||||
type: 'pie',
|
type: 'pie',
|
||||||
radius: ['20%', '35%'],
|
radius: ['25%', '35%'],
|
||||||
center: ['45%', '55%'],
|
center: ['40%', '45%'],
|
||||||
avoidLabelOverlap: false,
|
avoidLabelOverlap: false,
|
||||||
itemStyle: {
|
itemStyle: {
|
||||||
borderRadius: 10,
|
borderRadius: 10,
|
||||||
@ -531,7 +525,7 @@ const initPieChart = (data: any[]) => {
|
|||||||
},
|
},
|
||||||
label: {
|
label: {
|
||||||
show: true,
|
show: true,
|
||||||
formatter: '{b}: {c} ({d}%)'
|
formatter: '{b}'
|
||||||
},
|
},
|
||||||
emphasis: {
|
emphasis: {
|
||||||
label: {
|
label: {
|
||||||
@ -545,31 +539,39 @@ const initPieChart = (data: any[]) => {
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
// 颜色配置(根据数据类型动态设置)
|
pieChartInstance.setOption(option)
|
||||||
const colorConfig: any = {
|
|
||||||
color: ['#52c41a', '#ff4d4f', '#d9d9d9']
|
|
||||||
}
|
|
||||||
|
|
||||||
pieChartInstance.setOption({ ...option, ...colorConfig })
|
|
||||||
|
|
||||||
// 绑定点击事件 - 过滤表格数据
|
// 绑定点击事件 - 过滤表格数据
|
||||||
pieChartInstance.on('legendselectchanged', (params: any) => {
|
pieChartInstance.on('legendselectchanged', (params: any) => {
|
||||||
|
kong.value = false
|
||||||
const selectedKeys = Object.keys(params.selected).filter(key => params.selected[key])
|
const selectedKeys = Object.keys(params.selected).filter(key => params.selected[key])
|
||||||
const allFalse = Object.values(params.selected).every(value => value === false)
|
const allFalse = Object.values(params.selected).every(value => value === false)
|
||||||
|
|
||||||
// 映射到后端需要的keyData
|
// 动态计算当前显示的总数
|
||||||
const dataMap: Record<string, string> = {
|
if (allFalse) {
|
||||||
'达标': 'DB_STATUS_1',
|
// 所有图例都取消选中,显示 0
|
||||||
'不达标': 'DB_STATUS_0',
|
displayTotal.value = 0
|
||||||
'无数据': 'CURNODATA'
|
} else {
|
||||||
|
// 只统计被选中图例的数量总和
|
||||||
|
displayTotal.value = data
|
||||||
|
.filter(item => selectedKeys.includes(item.name))
|
||||||
|
.reduce((sum, item) => sum + item.value, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 局部更新 title 文本
|
||||||
|
pieChartInstance.setOption({
|
||||||
|
title: [
|
||||||
|
{
|
||||||
|
text: displayTotal.value.toString()
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|
||||||
const selectedCodes = data
|
const selectedCodes = data
|
||||||
.filter(item => selectedKeys.includes(item.name))
|
.filter(item => selectedKeys.includes(item.name))
|
||||||
.map(item => item.keyData)
|
.map(item => item.keyData)
|
||||||
|
|
||||||
pieCode.value = allFalse ? ['5'] : selectedCodes
|
pieCode.value = allFalse ? ['5'] : selectedCodes
|
||||||
|
|
||||||
// 刷新表格
|
// 刷新表格
|
||||||
handleSearch()
|
handleSearch()
|
||||||
})
|
})
|
||||||
@ -578,7 +580,6 @@ const initPieChart = (data: any[]) => {
|
|||||||
// 基地变化处理
|
// 基地变化处理
|
||||||
const handleBaseChange = () => {
|
const handleBaseChange = () => {
|
||||||
formState.stnm = ''
|
formState.stnm = ''
|
||||||
fetchPieData()
|
|
||||||
handleSearch()
|
handleSearch()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -586,14 +587,17 @@ const handleBaseChange = () => {
|
|||||||
const handleSearch = () => {
|
const handleSearch = () => {
|
||||||
const timeFormat = formState.types === 'month' ? 'YYYY-MM' : 'YYYY'
|
const timeFormat = formState.types === 'month' ? 'YYYY-MM' : 'YYYY'
|
||||||
const startTime = formState.dateValues ? formState.dateValues.format(timeFormat) : ''
|
const startTime = formState.dateValues ? formState.dateValues.format(timeFormat) : ''
|
||||||
|
// console.log( typeOne.value )
|
||||||
|
// console.log( formState.dtinType )
|
||||||
|
// console.log(typeOne.value == 'all' && formState.dtinType !='')
|
||||||
|
// debugger
|
||||||
const filters: any[] = [
|
const filters: any[] = [
|
||||||
{
|
formState.dataDimensionVal != 'all' ? {
|
||||||
field: 'baseId',
|
field: 'baseId',
|
||||||
operator: 'eq',
|
operator: 'eq',
|
||||||
dataType: 'string',
|
dataType: 'string',
|
||||||
value: formState.dataDimensionVal
|
value: typeOne.value == 'all' ? formState.dataDimensionVal : baseid.value
|
||||||
},
|
} : null,
|
||||||
formState.types === 'month' ? {
|
formState.types === 'month' ? {
|
||||||
field: 'drMonth',
|
field: 'drMonth',
|
||||||
operator: 'eq',
|
operator: 'eq',
|
||||||
@ -606,12 +610,12 @@ const handleSearch = () => {
|
|||||||
value: startTime
|
value: startTime
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: 'type',
|
field: 'TYPE',
|
||||||
operator: 'eq',
|
operator: 'eq',
|
||||||
dataType: 'string',
|
dataType: 'string',
|
||||||
value: 'DAY'
|
value: 'DAY'
|
||||||
},
|
},
|
||||||
formState.dataDimensionVal !== 'all' ? {
|
typeOne.value != 'all' ? {
|
||||||
field: 'rstcd',
|
field: 'rstcd',
|
||||||
operator: 'eq',
|
operator: 'eq',
|
||||||
dataType: 'string',
|
dataType: 'string',
|
||||||
@ -634,7 +638,7 @@ const handleSearch = () => {
|
|||||||
dataType: 'string',
|
dataType: 'string',
|
||||||
value: formState.stnm
|
value: formState.stnm
|
||||||
} : null,
|
} : null,
|
||||||
formState.dataDimensionVal === 'all' && formState.dtinType ? {
|
typeOne.value == 'all' && formState.dtinType != '' ? {
|
||||||
field: 'dtinType',
|
field: 'dtinType',
|
||||||
operator: 'eq',
|
operator: 'eq',
|
||||||
dataType: 'string',
|
dataType: 'string',
|
||||||
@ -649,6 +653,12 @@ const handleSearch = () => {
|
|||||||
|
|
||||||
// 刷新表格
|
// 刷新表格
|
||||||
tableRef.value?.getList(filter)
|
tableRef.value?.getList(filter)
|
||||||
|
if (kong.value) {
|
||||||
|
// debugger
|
||||||
|
fetchPieData()
|
||||||
|
}
|
||||||
|
kong.value = true
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 重置处理
|
// 重置处理
|
||||||
@ -660,8 +670,7 @@ const handleReset = () => {
|
|||||||
formState.dateValues = dayjs()
|
formState.dateValues = dayjs()
|
||||||
formState.stnm = ''
|
formState.stnm = ''
|
||||||
pieCode.value = []
|
pieCode.value = []
|
||||||
|
|
||||||
fetchPieData()
|
|
||||||
handleSearch()
|
handleSearch()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -676,55 +685,163 @@ const handleDetailModalClose = () => {
|
|||||||
detailModalVisible.value = false
|
detailModalVisible.value = false
|
||||||
detailData.value = {}
|
detailData.value = {}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查看断面名称详情(地图)
|
|
||||||
const handleViewStnmDetail = (record: any) => {
|
|
||||||
const timeRange = formState.dateValues
|
|
||||||
? [dayjs(formState.dateValues).startOf(formState.types === 'month' ? 'month' : 'year'),
|
|
||||||
dayjs(formState.dateValues).endOf(formState.types === 'month' ? 'month' : 'year')]
|
|
||||||
: []
|
|
||||||
|
|
||||||
mapModalData.value = {
|
|
||||||
sttp: 'wq',
|
|
||||||
stcd: record.stcd,
|
|
||||||
titleName: record.stnm,
|
|
||||||
timeRange,
|
|
||||||
calculateFlag: null // TODO: 根据实际情况设置
|
|
||||||
}
|
|
||||||
mapModalVisible.value = true
|
|
||||||
}
|
|
||||||
|
|
||||||
// 地图弹窗关闭
|
// 地图弹窗关闭
|
||||||
const handleMapModalClose = () => {
|
const handleMapModalClose = () => {
|
||||||
mapModalVisible.value = false
|
mapModalVisible.value = false
|
||||||
mapModalData.value = {}
|
mapModalData.value = {}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 监听表单变化,自动查询
|
|
||||||
watch(
|
|
||||||
() => [formState.dataDimensionVal, formState.types, formState.sfdb, formState.stnm, formState.dtinType],
|
|
||||||
() => {
|
|
||||||
fetchPieData()
|
|
||||||
handleSearch()
|
|
||||||
},
|
|
||||||
{ deep: true }
|
|
||||||
)
|
|
||||||
|
|
||||||
// 监听日期变化
|
// 监听日期变化
|
||||||
watch(
|
watch(
|
||||||
() => formState.dateValues,
|
() => formState.dateValues,
|
||||||
() => {
|
() => {
|
||||||
fetchPieData()
|
|
||||||
handleSearch()
|
handleSearch()
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// 监听 props.dataDimensionVal 变化,重置表单状态并重新加载数据
|
||||||
|
watch(
|
||||||
|
() => props.dataDimensionVal,
|
||||||
|
(newVal) => {
|
||||||
|
// 重置表单状态
|
||||||
|
formState.dataDimensionVal = newVal || ''
|
||||||
|
formState.dtinType = ''
|
||||||
|
formState.sfdb = ''
|
||||||
|
formState.stnm = ''
|
||||||
|
pieCode.value = []
|
||||||
|
|
||||||
|
// 重新加载数据
|
||||||
|
nextTick(() => {
|
||||||
|
handleSearch()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// 监听 props.type 变化,同步更新时间维度和日期格式
|
||||||
|
watch(
|
||||||
|
() => props.type,
|
||||||
|
(newType) => {
|
||||||
|
if (!newType || newType === formState.types) return
|
||||||
|
|
||||||
|
// 转换日期格式以匹配新的时间维度
|
||||||
|
const currentValue: any = formState.dateValues
|
||||||
|
if (currentValue) {
|
||||||
|
if (newType === 'year') {
|
||||||
|
// 切换到年度模式:保存当前月份
|
||||||
|
if (typeof currentValue.year === 'function') {
|
||||||
|
// dayjs 对象,保存月份
|
||||||
|
originalMonth.value = String(currentValue.month() + 1).padStart(2, '0')
|
||||||
|
const year = currentValue.year()
|
||||||
|
formState.dateValues = dayjs(String(year), 'YYYY')
|
||||||
|
} else if (typeof currentValue === 'string') {
|
||||||
|
// 字符串,提取并保存月份
|
||||||
|
const monthMatch = currentValue.match(/^(\d{4})-(\d{2})/)
|
||||||
|
if (monthMatch) {
|
||||||
|
originalMonth.value = monthMatch[2]
|
||||||
|
formState.dateValues = dayjs(monthMatch[1], 'YYYY')
|
||||||
|
} else {
|
||||||
|
const yearMatch = currentValue.match(/^(\d{4})/)
|
||||||
|
if (yearMatch) {
|
||||||
|
formState.dateValues = dayjs(yearMatch[1], 'YYYY')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (newType === 'month') {
|
||||||
|
// 切换到月度模式:恢复原来保存的月份
|
||||||
|
const monthToUse = originalMonth.value || '01'
|
||||||
|
if (typeof currentValue.year === 'function') {
|
||||||
|
const year = currentValue.year()
|
||||||
|
formState.dateValues = dayjs(`${year}-${monthToUse}`, 'YYYY-MM')
|
||||||
|
} else if (typeof currentValue === 'string') {
|
||||||
|
const yearMatch = currentValue.match(/^(\d{4})/)
|
||||||
|
if (yearMatch) {
|
||||||
|
formState.dateValues = dayjs(`${yearMatch[1]}-${monthToUse}`, 'YYYY-MM')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新时间维度
|
||||||
|
formState.types = newType
|
||||||
|
|
||||||
|
// 重新加载数据
|
||||||
|
nextTick(() => {
|
||||||
|
handleSearch()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// 监听 props.typeOne 变化,同步更新本地 state
|
||||||
|
watch(
|
||||||
|
() => props.typeOne,
|
||||||
|
(newVal) => {
|
||||||
|
typeOne.value = newVal
|
||||||
|
// debugger
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// 监听 props.baseid 变化,同步更新本地 state
|
||||||
|
watch(
|
||||||
|
() => props.baseid,
|
||||||
|
(newVal) => {
|
||||||
|
baseid.value = newVal || ''
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// 监听时间维度变化,自动转换日期格式
|
||||||
|
watch(
|
||||||
|
() => formState.types,
|
||||||
|
(newType, oldType) => {
|
||||||
|
if (!formState.dateValues || newType === oldType) return
|
||||||
|
// debugger
|
||||||
|
const currentValue: any = formState.dateValues
|
||||||
|
|
||||||
|
if (newType === 'year') {
|
||||||
|
// 从月度切换到年度:保存当前月份
|
||||||
|
if (currentValue && typeof currentValue.year === 'function') {
|
||||||
|
// dayjs 对象,保存月份
|
||||||
|
originalMonth.value = String(currentValue.month() + 1).padStart(2, '0')
|
||||||
|
const year = currentValue.year()
|
||||||
|
formState.dateValues = dayjs(String(year), 'YYYY')
|
||||||
|
} else if (typeof currentValue === 'string') {
|
||||||
|
// 字符串,提取并保存月份
|
||||||
|
const monthMatch = currentValue.match(/^(\d{4})-(\d{2})/)
|
||||||
|
if (monthMatch) {
|
||||||
|
originalMonth.value = monthMatch[2]
|
||||||
|
formState.dateValues = dayjs(monthMatch[1], 'YYYY')
|
||||||
|
} else {
|
||||||
|
const yearMatch = currentValue.match(/^(\d{4})/)
|
||||||
|
if (yearMatch) {
|
||||||
|
formState.dateValues = dayjs(yearMatch[1], 'YYYY')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (newType === 'month') {
|
||||||
|
// 从年度切换到月度:恢复原来保存的月份
|
||||||
|
const monthToUse = originalMonth.value || '01'
|
||||||
|
if (currentValue && typeof currentValue.year === 'function') {
|
||||||
|
const year = currentValue.year()
|
||||||
|
formState.dateValues = dayjs(`${year}-${monthToUse}`, 'YYYY-MM')
|
||||||
|
} else if (typeof currentValue === 'string') {
|
||||||
|
const yearMatch = currentValue.match(/^(\d{4})/)
|
||||||
|
if (yearMatch) {
|
||||||
|
formState.dateValues = dayjs(`${yearMatch[1]}-${monthToUse}`, 'YYYY-MM')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 日期格式转换后,重新获取数据
|
||||||
|
nextTick(() => {
|
||||||
|
handleSearch()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
// 初始化
|
// 初始化
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
await nextTick()
|
await nextTick()
|
||||||
fetchPieData()
|
|
||||||
handleSearch()
|
handleSearch()
|
||||||
|
|
||||||
// 监听窗口resize
|
// 监听窗口resize
|
||||||
window.addEventListener('resize', () => {
|
window.addEventListener('resize', () => {
|
||||||
pieChartInstance?.resize()
|
pieChartInstance?.resize()
|
||||||
@ -756,7 +873,7 @@ defineExpose({
|
|||||||
.search-form {
|
.search-form {
|
||||||
background: #fff;
|
background: #fff;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
padding: 16px;
|
padding: 0px 0px 16px 0px;
|
||||||
|
|
||||||
:deep(.ant-space) {
|
:deep(.ant-space) {
|
||||||
display: flex;
|
display: flex;
|
||||||
@ -841,4 +958,4 @@ defineExpose({
|
|||||||
:deep(.ant-spin-container) {
|
:deep(.ant-spin-container) {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
@ -0,0 +1,540 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<!-- 搜索表单 - 使用 Ant Design Vue 原生组件 -->
|
||||||
|
<a-form ref="formRef" :model="formState" layout="inline" @finish="onFinish" style="margin-bottom: 16px">
|
||||||
|
<a-form-item label="达标状态" name="sfdb">
|
||||||
|
<a-select v-model:value="formState.sfdb" placeholder="请选择" allow-clear style="width: 150px">
|
||||||
|
<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-option :value="'2'">无目标等级/无数据</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
</a-form-item>
|
||||||
|
|
||||||
|
<a-form-item>
|
||||||
|
<a-space>
|
||||||
|
<a-button type="primary" html-type="submit">查询</a-button>
|
||||||
|
<Tooltip title="重置">
|
||||||
|
<a-button @click="handleReset">重置</a-button>
|
||||||
|
</Tooltip>
|
||||||
|
</a-space>
|
||||||
|
</a-form-item>
|
||||||
|
</a-form>
|
||||||
|
|
||||||
|
<!-- 数据表格 -->
|
||||||
|
<BasicTable ref="tableRef" :columns="columns"
|
||||||
|
:list-url="props.calculateFlag?.calculateFlag == 1 ? dataGetKendoListCust : NodataGetKendoListCust"
|
||||||
|
:scrollY="500" :search-params="{ sort: [{ field: 'tm', dir: 'desc' }] }"
|
||||||
|
:transform-data="customTransform" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, reactive, computed, watch, onMounted, h, nextTick } from 'vue'
|
||||||
|
import { omit } from 'lodash-es'
|
||||||
|
import { Button, Tooltip, Popover } from 'ant-design-vue'
|
||||||
|
import BasicTable from '@/components/BasicTable/index.vue'
|
||||||
|
import { ruleGetKendoListCust, noAuthGetKendoListCust, stbprpGetStbprpYsByStcd, dataGetKendoListCust, NodataGetKendoListCust } from '@/api/sz'
|
||||||
|
|
||||||
|
// ==================== Props 定义 ====================
|
||||||
|
interface Props {
|
||||||
|
tc: {
|
||||||
|
stcd: string
|
||||||
|
startTime: string
|
||||||
|
endTime: string
|
||||||
|
sfdb: string
|
||||||
|
}
|
||||||
|
calculateFlag?: {
|
||||||
|
calculateFlag: number
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = defineProps<Props>()
|
||||||
|
|
||||||
|
// ==================== 响应式状态 ====================
|
||||||
|
const formRef = ref()
|
||||||
|
const tableRef = ref()
|
||||||
|
const col = ref<any[]>([])
|
||||||
|
|
||||||
|
// ✅ 表单状态(替代之前的 sfdb ref)
|
||||||
|
const formState = reactive({
|
||||||
|
sfdb: '1'
|
||||||
|
})
|
||||||
|
|
||||||
|
// ==================== 固定列配置 ====================
|
||||||
|
const columnList = [
|
||||||
|
{
|
||||||
|
dataIndexRaw: '',
|
||||||
|
key: 'tm',
|
||||||
|
title: '时间',
|
||||||
|
dataIndex: 'tm',
|
||||||
|
children: [],
|
||||||
|
visible: true,
|
||||||
|
fixed: '',
|
||||||
|
ellipsis: true,
|
||||||
|
wrap: false,
|
||||||
|
dictType: '',
|
||||||
|
dictCode: '',
|
||||||
|
dictDatas: {},
|
||||||
|
fromType: '',
|
||||||
|
dataType: 'date',
|
||||||
|
dataFormat: '',
|
||||||
|
selectMode: '',
|
||||||
|
unit: '',
|
||||||
|
remark: '',
|
||||||
|
rules: '',
|
||||||
|
ruleTips: '',
|
||||||
|
setting: {},
|
||||||
|
chartColor: '',
|
||||||
|
elementCode: '',
|
||||||
|
disabledDate: true,
|
||||||
|
sorter: true,
|
||||||
|
multiLink: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
dataIndexRaw: '',
|
||||||
|
key: 'wwqtg',
|
||||||
|
title: '水质要求',
|
||||||
|
dataIndex: 'wwqtgName',
|
||||||
|
children: [],
|
||||||
|
visible: true,
|
||||||
|
fixed: '',
|
||||||
|
ellipsis: true,
|
||||||
|
wrap: false,
|
||||||
|
dictType: '',
|
||||||
|
dictCode: '',
|
||||||
|
dictDatas: {},
|
||||||
|
fromType: '',
|
||||||
|
dataFormat: '',
|
||||||
|
selectMode: '',
|
||||||
|
unit: '',
|
||||||
|
remark: '',
|
||||||
|
rules: '',
|
||||||
|
ruleTips: '',
|
||||||
|
setting: {},
|
||||||
|
chartColor: '',
|
||||||
|
elementCode: '',
|
||||||
|
sorter: true,
|
||||||
|
disabledDate: true,
|
||||||
|
multiLink: false,
|
||||||
|
width: 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
dataIndexRaw: '',
|
||||||
|
key: 'wqGrd',
|
||||||
|
title: '水质等级',
|
||||||
|
dataIndex: 'wqgrdName',
|
||||||
|
children: [],
|
||||||
|
visible: true,
|
||||||
|
fixed: '',
|
||||||
|
ellipsis: true,
|
||||||
|
wrap: false,
|
||||||
|
dictType: '',
|
||||||
|
dictCode: '',
|
||||||
|
dictDatas: {},
|
||||||
|
fromType: '',
|
||||||
|
dataFormat: '',
|
||||||
|
selectMode: '',
|
||||||
|
unit: '',
|
||||||
|
remark: '',
|
||||||
|
rules: '',
|
||||||
|
ruleTips: '',
|
||||||
|
setting: {},
|
||||||
|
chartColor: '',
|
||||||
|
elementCode: '',
|
||||||
|
sorter: true,
|
||||||
|
disabledDate: true,
|
||||||
|
multiLink: false,
|
||||||
|
width: 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
dataIndexRaw: 'sfdb',
|
||||||
|
key: 'sfdb',
|
||||||
|
title: '是否达标',
|
||||||
|
dataIndex: 'sfdbName',
|
||||||
|
children: [],
|
||||||
|
visible: true,
|
||||||
|
fixed: '',
|
||||||
|
ellipsis: true,
|
||||||
|
wrap: false,
|
||||||
|
// TODO: 【您需要实现】字典转换逻辑 - BasicTable 不支持 dictType/dictCode
|
||||||
|
// 需要在 customRender 中手动处理
|
||||||
|
dictType: 'dataDict',
|
||||||
|
dictCode: 'sd_wq_r.sfdb',
|
||||||
|
dictDatas: {},
|
||||||
|
fromType: 'select',
|
||||||
|
dataFormat: '',
|
||||||
|
selectMode: '',
|
||||||
|
unit: '',
|
||||||
|
remark: '',
|
||||||
|
rules: '',
|
||||||
|
ruleTips: '',
|
||||||
|
setting: {},
|
||||||
|
chartColor: '',
|
||||||
|
elementCode: '',
|
||||||
|
sorter: true,
|
||||||
|
disabledDate: true,
|
||||||
|
multiLink: false,
|
||||||
|
width: 100,
|
||||||
|
// ✅ Vue3 版本需要添加 customRender
|
||||||
|
customRender: ({ record }: any) => {
|
||||||
|
// TODO: 【您需要实现】字典转换逻辑
|
||||||
|
// 示例:return getDictLabel('sd_wq_r.sfdb', record.sfdbName)
|
||||||
|
return record.sfdbName // 暂时直接显示原始值
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
// ==================== 动态列计算 ====================
|
||||||
|
const columns = computed(() => {
|
||||||
|
if (!col.value.length) return columnList
|
||||||
|
return [...columnList, ...col.value]
|
||||||
|
})
|
||||||
|
|
||||||
|
// ==================== 筛选条件计算 ====================
|
||||||
|
const tableFilter = computed(() => {
|
||||||
|
let res: any = {
|
||||||
|
stcd: props.tc.stcd,
|
||||||
|
sfdb: formState.sfdb === '2' ? '' : formState.sfdb,
|
||||||
|
startTime: props.tc.startTime,
|
||||||
|
endTime: props.tc.endTime
|
||||||
|
}
|
||||||
|
if (res.sfdb === '') res = omit(res, 'sfdb')
|
||||||
|
return res
|
||||||
|
})
|
||||||
|
|
||||||
|
// ==================== 方法定义 ====================
|
||||||
|
|
||||||
|
// 搜索表单提交
|
||||||
|
const onFinish = () => {
|
||||||
|
const filters: any[] = [
|
||||||
|
{
|
||||||
|
"field": "stcd",
|
||||||
|
"operator": "eq",
|
||||||
|
"dataType": "string",
|
||||||
|
"value": tableFilter.value.stcd
|
||||||
|
},
|
||||||
|
formState.sfdb ? {
|
||||||
|
"field": "sfdb",
|
||||||
|
"operator": "eq",
|
||||||
|
"dataType": "string",
|
||||||
|
"value": formState.sfdb
|
||||||
|
} : null,
|
||||||
|
{
|
||||||
|
"field": "tm",
|
||||||
|
"operator": "gte",
|
||||||
|
"dataType": "date",
|
||||||
|
"value": tableFilter.value.startTime
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"field": "tm",
|
||||||
|
"operator": "lte",
|
||||||
|
"dataType": "date",
|
||||||
|
"value": tableFilter.value.endTime
|
||||||
|
}
|
||||||
|
].filter(Boolean)
|
||||||
|
const filter = {
|
||||||
|
logic: 'and',
|
||||||
|
filters
|
||||||
|
}
|
||||||
|
// 触发表格刷新
|
||||||
|
tableRef.value?.getList(filter)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 重置表单
|
||||||
|
const handleReset = () => {
|
||||||
|
formState.sfdb = '1'
|
||||||
|
onFinish()
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: 【预留】获取动态列配置 - 您需要实现的API调用逻辑
|
||||||
|
const getCol = async (params: any) => {
|
||||||
|
try {
|
||||||
|
const lo = await columYaoshu(params)
|
||||||
|
col.value = lo || []
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取列配置失败:', error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: 【预留】处理列配置的核心逻辑 - 您需要实现
|
||||||
|
const columYaoshu = async (params: any) => {
|
||||||
|
|
||||||
|
// API 1: 获取预警规则限值
|
||||||
|
let params3 = {
|
||||||
|
"filter": {
|
||||||
|
"logic": "and",
|
||||||
|
"filters": [
|
||||||
|
{
|
||||||
|
"field": "stcd",
|
||||||
|
"operator": "eq",
|
||||||
|
"dataType": "string",
|
||||||
|
"value": params?.stcd
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const MaxData = await ruleGetKendoListCust(params3)
|
||||||
|
|
||||||
|
// API 2: 获取数据范围
|
||||||
|
let params2 = {
|
||||||
|
"filter": {
|
||||||
|
"logic": "and",
|
||||||
|
"filters": [
|
||||||
|
{
|
||||||
|
"field": "stcd",
|
||||||
|
"operator": "eq",
|
||||||
|
"dataType": "string",
|
||||||
|
"value": params?.stcd
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"field": "tm",
|
||||||
|
"operator": "lte",
|
||||||
|
"dataType": "date",
|
||||||
|
"value": params?.endTime
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"field": "tm",
|
||||||
|
"operator": "gte",
|
||||||
|
"dataType": "date",
|
||||||
|
"value": params?.startTime
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const noAuthRes = await noAuthGetKendoListCust(params2)
|
||||||
|
const noAuthResData = noAuthRes?.data?.data || []
|
||||||
|
|
||||||
|
// API 3: 获取要素配置
|
||||||
|
let params1 = {
|
||||||
|
stcd: params?.stcd,
|
||||||
|
tbCode: 'WQ_R',
|
||||||
|
endTime: params.endTime,
|
||||||
|
startTime: params.startTime
|
||||||
|
}
|
||||||
|
const res = await stbprpGetStbprpYsByStcd(params1)
|
||||||
|
if (res?.data?.data) {
|
||||||
|
const processedColumns = res.data.data
|
||||||
|
.filter((item: any) => item.enable === '1')
|
||||||
|
.map((item: any) => {
|
||||||
|
const _t = item.ysShowName
|
||||||
|
const reg = /[((][^(())]+[))]/
|
||||||
|
|
||||||
|
// TODO: 【您需要实现】单位转换工具函数
|
||||||
|
const _u = getUnitConfigByCode('WQ_R', item.ys)?.unit
|
||||||
|
if (_u) {
|
||||||
|
item.ysShowName = reg.test(_t)
|
||||||
|
? _t.replace(reg, `(${_u})`)
|
||||||
|
: `${_t}(${_u})`
|
||||||
|
}
|
||||||
|
|
||||||
|
let showValue = ''
|
||||||
|
if (noAuthResData.length) {
|
||||||
|
const { max, min } = noAuthResData[0]
|
||||||
|
const ys = item.ys
|
||||||
|
const maxValue = max.filter((e: any) => e[ys])
|
||||||
|
const minValue = min.filter((e: any) => e[ys])
|
||||||
|
|
||||||
|
if (maxValue.length && minValue.length) {
|
||||||
|
showValue = `(${minValue[0]?.[ys]}-${maxValue[0]?.[ys]})`
|
||||||
|
} else {
|
||||||
|
if (minValue.length) {
|
||||||
|
showValue = `(>${minValue[0]?.[ys]})`
|
||||||
|
}
|
||||||
|
if (maxValue.length) {
|
||||||
|
showValue = `(<${maxValue[0]?.[ys]})`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
dataIndexRaw: '',
|
||||||
|
key: item.ys.toLowerCase(),
|
||||||
|
// ✅ Vue3 版本:title 支持函数返回 VNode
|
||||||
|
title: () => h('div', [
|
||||||
|
h('p', {
|
||||||
|
style: {
|
||||||
|
overflow: 'hidden',
|
||||||
|
textOverflow: 'ellipsis',
|
||||||
|
wordBreak: 'keep-all'
|
||||||
|
}
|
||||||
|
}, item.ysShowName),
|
||||||
|
h('p', showValue)
|
||||||
|
]),
|
||||||
|
dataIndex: item.ys.toLowerCase(),
|
||||||
|
children: [],
|
||||||
|
visible: true,
|
||||||
|
fixed: '',
|
||||||
|
sorter: true,
|
||||||
|
ellipsis: true,
|
||||||
|
wrap: false,
|
||||||
|
dictType: '',
|
||||||
|
dictCode: '',
|
||||||
|
dictDatas: {},
|
||||||
|
// ✅ Vue3 版本:使用 customRender 替代 render
|
||||||
|
customRender: ({ record }: any) => {
|
||||||
|
// TODO: 【您需要传入】MaxData 数据
|
||||||
|
const datalist = MaxData?.data?.data
|
||||||
|
const dynamicDataIndex = item.ys
|
||||||
|
const dynamicDataName = item.ysShowName
|
||||||
|
const matchedItem = datalist.find((m: any) => m.ys === dynamicDataIndex)
|
||||||
|
|
||||||
|
// 判断是否超标
|
||||||
|
if (
|
||||||
|
matchedItem &&
|
||||||
|
(matchedItem.isAsc == 1 ? matchedItem?.maxVal : matchedItem.minVal) &&
|
||||||
|
record[dynamicDataIndex?.toLowerCase()] &&
|
||||||
|
((matchedItem.isAsc == 1 && record[dynamicDataIndex?.toLowerCase()] > matchedItem?.maxVal) ||
|
||||||
|
(matchedItem.isAsc != 1 && record[dynamicDataIndex?.toLowerCase()] < matchedItem.minVal))
|
||||||
|
) {
|
||||||
|
// ✅ Vue3 版本:使用 h() 函数创建 VNode
|
||||||
|
return h(Popover, {
|
||||||
|
content: h('div', `预警标准: ${matchedItem.isAsc === 1 ? `>${matchedItem.maxVal}` : `<${matchedItem.minVal}`}`),
|
||||||
|
trigger: 'hover'
|
||||||
|
}, () =>
|
||||||
|
h('span', {
|
||||||
|
style: { color: 'red', cursor: 'pointer' },
|
||||||
|
// TODO: 【您需要实现】单位转换渲染
|
||||||
|
}, transUnitRender(record[dynamicDataIndex?.toLowerCase()], 'WQ_R', dynamicDataIndex))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 正常显示
|
||||||
|
// TODO: 【您需要实现】单位转换渲染
|
||||||
|
return transUnitRender(record[dynamicDataIndex?.toLowerCase()], 'WQ_R', dynamicDataIndex)
|
||||||
|
},
|
||||||
|
fromType: '',
|
||||||
|
dataType: '',
|
||||||
|
dataFormat: '',
|
||||||
|
selectMode: '',
|
||||||
|
unit: '',
|
||||||
|
remark: '',
|
||||||
|
rules: '',
|
||||||
|
ruleTips: '',
|
||||||
|
setting: {},
|
||||||
|
chartColor: '',
|
||||||
|
elementCode: '',
|
||||||
|
disabledDate: true,
|
||||||
|
multiLink: false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
console.log(processedColumns)
|
||||||
|
onFinish()
|
||||||
|
return processedColumns || []
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// TODO: 【预留】单位转换工具函数 - 您需要实现
|
||||||
|
// ==================== 单位转换工具函数区域 ====================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据表代码和要素代码获取单位配置
|
||||||
|
* @param tbCode 表代码 (如 'WQ_R')
|
||||||
|
* @param ys 要素代码 (如 'DOX', 'CODCR')
|
||||||
|
* @returns 单位配置对象 { unit: string }
|
||||||
|
*/
|
||||||
|
const getUnitConfigByCode = (tbCode: string, ys: string) => {
|
||||||
|
// TODO: 【您需要实现】单位配置映射
|
||||||
|
const unitConfig: Record<string, Record<string, { unit: string }>> = {
|
||||||
|
'WQ_R': {
|
||||||
|
'PH': { unit: '' },
|
||||||
|
'DOX': { unit: 'mg/L' },
|
||||||
|
'CODMN': { unit: 'mg/L' },
|
||||||
|
'CODCR': { unit: 'mg/L' },
|
||||||
|
'NH3N': { unit: 'mg/L' },
|
||||||
|
'TP': { unit: 'mg/L' },
|
||||||
|
'TN': { unit: 'mg/L' },
|
||||||
|
'WTMP': { unit: '℃' },
|
||||||
|
'COND': { unit: 'μS/cm' },
|
||||||
|
'CLARITY': { unit: 'm' }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return unitConfig[tbCode]?.[ys] || { unit: '' }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 带单位的值渲染(根据要素类型设置不同小数位数,不带单位)
|
||||||
|
* @param value 数值
|
||||||
|
* @param tbCode 表代码
|
||||||
|
* @param ys 要素代码
|
||||||
|
* @returns 格式化后的字符串(不带单位)
|
||||||
|
*/
|
||||||
|
const transUnitRender = (value: any, tbCode: string, ys: string) => {
|
||||||
|
if (value === null || value === undefined || value === '') return '-'
|
||||||
|
|
||||||
|
const numValue = Number(value)
|
||||||
|
if (isNaN(numValue)) return String(value)
|
||||||
|
|
||||||
|
// 根据不同要素类型设置小数位数
|
||||||
|
let formattedValue: string
|
||||||
|
|
||||||
|
if (ys === 'WTMP' || ys === 'PH') {
|
||||||
|
// 水温和PH保留一位小数
|
||||||
|
formattedValue = numValue.toFixed(1)
|
||||||
|
} else if (ys === 'COND') {
|
||||||
|
// 电导率不要小数
|
||||||
|
formattedValue = Math.round(numValue).toString()
|
||||||
|
} else {
|
||||||
|
// 其他要素保留两位小数
|
||||||
|
formattedValue = numValue.toFixed(2)
|
||||||
|
}
|
||||||
|
|
||||||
|
return formattedValue
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 纯单位转换(不带渲染)
|
||||||
|
* @param value 数值
|
||||||
|
* @param tbCode 表代码
|
||||||
|
* @param ys 要素代码
|
||||||
|
* @returns 转换后的数值
|
||||||
|
*/
|
||||||
|
const transUnit = (value: any, tbCode: string, ys: string) => {
|
||||||
|
if (value === null || value === undefined) return null
|
||||||
|
return Number(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================== 监听器 ====================
|
||||||
|
|
||||||
|
// 监听 tc 和 formState.sfdb 变化,重新获取列配置
|
||||||
|
watch(
|
||||||
|
() => [props.tc.stcd, props.tc.startTime, props.tc.endTime, formState.sfdb],
|
||||||
|
([stcd, startTime, endTime]) => {
|
||||||
|
if(!stcd){
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// debugger
|
||||||
|
tableRef.value.tableData = []
|
||||||
|
getCol({ stcd, startTime, endTime })
|
||||||
|
},
|
||||||
|
{ immediate: false } // 不在初始化时立即执行,由 onMounted 处理
|
||||||
|
)
|
||||||
|
|
||||||
|
// 初始化
|
||||||
|
onMounted(() => {
|
||||||
|
nextTick(() => {
|
||||||
|
})
|
||||||
|
getCol({
|
||||||
|
stcd: props.tc.stcd,
|
||||||
|
startTime: props.tc.startTime,
|
||||||
|
endTime: props.tc.endTime
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
||||||
|
// 自定义数据转换
|
||||||
|
const customTransform = (res: any) => {
|
||||||
|
return {
|
||||||
|
records: res?.data?.data || [],
|
||||||
|
total: res?.data?.total || 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
/* 如需添加样式,请在此处编写 */
|
||||||
|
</style>
|
||||||
@ -1,18 +1,18 @@
|
|||||||
<!-- SidePanelItem.vue -->
|
<!-- SidePanelItem.vue -->
|
||||||
<template>
|
<template>
|
||||||
<SidePanelItem title="地表水水质达标率" :select="select" :datetimePicker="datetimePicker"
|
<SidePanelItem title="地表水水质达标率" :select="select" :datetimePicker="datetimePicker"
|
||||||
@update-values="handlePanelChange1" style="height: 650px;">
|
@update-values="handlePanelChange1" :style="chartData.length === 0 && !loading && baseid != 'all' ? 'height: 300px' : 'height: 650px'">
|
||||||
<div class="body_item">
|
<div class="body_item">
|
||||||
<div v-if="baseid == 'all'" class="tabs_all">
|
<div v-if="baseid == 'all'" class="tabs_all">
|
||||||
<div :class="tabs == 0 ? 'zhong_tabs' : 'no_tabs'" @click="handleTabChange(0)">自建水质站</div>
|
<div :class="tabs == '0' ? 'zhong_tabs' : 'no_tabs'" @click="handleTabChange(0)">自建水质站</div>
|
||||||
<div :class="tabs == 1 ? 'zhong_tabs' : 'no_tabs'" @click="handleTabChange(1)">国家水质站</div>
|
<div :class="tabs == '1' ? 'zhong_tabs' : 'no_tabs'" @click="handleTabChange(1)">国家水质站</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-show="tabs == 0 || tabs == 1" class="tabs_body">
|
<div v-show="tabs == '0' || tabs == '1'" class="tabs_body">
|
||||||
<a-spin :spinning="loading">
|
<a-spin :spinning="loading">
|
||||||
<!-- 始终渲染图表容器,确保有固定宽高 -->
|
<!-- 始终渲染图表容器,确保有固定宽高 -->
|
||||||
<div ref="chartRef" class="chart-container"></div>
|
<div ref="chartRef" class="chart-container"></div>
|
||||||
<!-- 无数据时显示Empty,但不影响容器尺寸 -->
|
<!-- 无数据时显示Empty,但不影响容器尺寸 -->
|
||||||
<div v-if="chartData.length === 0 && !loading" class="empty-overlay">
|
<div v-if="chartData.length === 0 && !loading" class="empty-overlay" :style="chartData.length === 0 && !loading && baseid != 'all' ? 'height: 300px' : 'height: 650px'">
|
||||||
<a-empty description="暂无数据" />
|
<a-empty description="暂无数据" />
|
||||||
</div>
|
</div>
|
||||||
</a-spin>
|
</a-spin>
|
||||||
@ -22,20 +22,10 @@
|
|||||||
</SidePanelItem>
|
</SidePanelItem>
|
||||||
|
|
||||||
<!-- 环境质量满足度弹框 -->
|
<!-- 环境质量满足度弹框 -->
|
||||||
<a-modal
|
<a-modal v-model:open="modalVisible" title="环境质量满足度" width="1536px" :footer="null" centered>
|
||||||
v-model:open="modalVisible"
|
<EnvironmentalQualityTwoLayers :datas="(baseid == 'all' ? jiDiList : stationListForModal)" :type="select.value"
|
||||||
title="环境质量满足度"
|
:dateValue="datetimePicker.value" :dataDimensionVal="(clickedBarData?.selectedId || baseid)"
|
||||||
width="1536px"
|
:buildType="tabs" :typeOne="fieldOne" :baseid="baseid" />
|
||||||
:footer="null"
|
|
||||||
centered
|
|
||||||
>
|
|
||||||
<EnvironmentalQualityTwoLayers
|
|
||||||
:datas="jiDiList"
|
|
||||||
:type="select.value "
|
|
||||||
:dateValue="datetimePicker.value"
|
|
||||||
:dataDimensionVal="baseid"
|
|
||||||
:buildType="tabs"
|
|
||||||
/>
|
|
||||||
</a-modal>
|
</a-modal>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -51,14 +41,20 @@ defineOptions({
|
|||||||
name: 'EnvironmentalQuality'
|
name: 'EnvironmentalQuality'
|
||||||
});
|
});
|
||||||
const JidiSelectEventStore = useJidiSelectEventStore();
|
const JidiSelectEventStore = useJidiSelectEventStore();
|
||||||
const tabs = ref(0);
|
const tabs: any = ref('0');
|
||||||
const chartRef = ref<HTMLElement | null>(null);
|
const chartRef = ref<HTMLElement | null>(null);
|
||||||
let chartInstance: echarts.ECharts | null = null;
|
let chartInstance: echarts.ECharts | null = null;
|
||||||
const loading = ref(false);
|
const loading = ref(false);
|
||||||
|
|
||||||
// 响应式图表数据
|
// 响式图表数据
|
||||||
const chartData = ref<Array<{ name: string; current: number | null; lastYear: number | null }>>([]);
|
const chartData = ref<Array<{ name: string; current: number | null; lastYear: number | null }>>([]);
|
||||||
|
|
||||||
|
// 新增:存储当前基地下的电站列表(用于弹框传递)
|
||||||
|
const stationListForModal = ref<Array<{ baseid: string; basename: string }>>([]);
|
||||||
|
|
||||||
|
// 新增:存储图表数据索引与 baseid/rstcd 的映射关系
|
||||||
|
const chartDataIdMap = ref<Array<string>>([]);
|
||||||
|
|
||||||
const select = ref({
|
const select = ref({
|
||||||
show: true,
|
show: true,
|
||||||
value: 'month',
|
value: 'month',
|
||||||
@ -161,24 +157,16 @@ const jiDiList: any = ref([
|
|||||||
"id": "AFBDFC67-B955-4EFD-959A-014CFB59EBFC"
|
"id": "AFBDFC67-B955-4EFD-959A-014CFB59EBFC"
|
||||||
}
|
}
|
||||||
])
|
])
|
||||||
// 基地列表(从 Store 或其他来源获取)
|
const fieldOne = ref('')
|
||||||
const baseList = computed(() => {
|
|
||||||
// TODO: 根据实际情况返回基地列表
|
|
||||||
// 这里需要根据项目实际情况从 Store 或 API 获取
|
|
||||||
// 示例结构:
|
|
||||||
// return [
|
|
||||||
// { baseid: 'all', basename: '全部' },
|
|
||||||
// { baseid: '01', basename: '金沙江干流' },
|
|
||||||
// ...
|
|
||||||
// ]
|
|
||||||
|
|
||||||
// 临时返回空数组,待后续补充实际数据源
|
|
||||||
return []
|
|
||||||
})
|
|
||||||
|
|
||||||
// 解析API返回的嵌套数据(支持两种结构)
|
// 解析API返回的嵌套数据(支持两种结构)
|
||||||
const parseApiResponse = (items: any[], currentValueForApi: string, lastYearValue: string) => {
|
const parseApiResponse = (items: any[], currentValueForApi: string, lastYearValue: string) => {
|
||||||
const result: Array<{ name: string; current: number | null; lastYear: number | null }> = [];
|
const result: Array<{ name: string; current: number | null; lastYear: number | null }> = [];
|
||||||
|
fieldOne.value = ''
|
||||||
|
// 新增:清空 ID 映射数组
|
||||||
|
chartDataIdMap.value = [];
|
||||||
|
|
||||||
|
// 新增:提取电站列表(仅当 baseid != 'all' 时)
|
||||||
|
const stations: Array<{ baseid: string; basename: string }> = [];
|
||||||
|
|
||||||
console.log('=== parseApiResponse 开始解析 ===');
|
console.log('=== parseApiResponse 开始解析 ===');
|
||||||
console.log('currentValueForApi:', currentValueForApi);
|
console.log('currentValueForApi:', currentValueForApi);
|
||||||
@ -189,25 +177,25 @@ const parseApiResponse = (items: any[], currentValueForApi: string, lastYearValu
|
|||||||
console.log(`\n--- 处理第${groupIndex + 1}个group ---`);
|
console.log(`\n--- 处理第${groupIndex + 1}个group ---`);
|
||||||
console.log('group.key:', group.key);
|
console.log('group.key:', group.key);
|
||||||
console.log('group.items数量:', group.items?.length);
|
console.log('group.items数量:', group.items?.length);
|
||||||
|
|
||||||
group.items?.forEach((mainGroup, mainIndex) => {
|
group.items?.forEach((mainGroup, mainIndex) => {
|
||||||
console.log(` → 第${mainIndex + 1}个mainGroup:`);
|
console.log(` → 第${mainIndex + 1}个mainGroup:`);
|
||||||
console.log(' field:', mainGroup.field);
|
console.log(' field:', mainGroup.field);
|
||||||
console.log(' key:', mainGroup.key);
|
console.log(' key:', mainGroup.key);
|
||||||
|
|
||||||
// 判断是哪种结构:检查第二层的field字段
|
// 判断是哪种结构:检查第二层的field字段
|
||||||
const field = mainGroup.field;
|
const field = mainGroup.field;
|
||||||
|
|
||||||
if (field === 'ennm' || field === 'baseId') {
|
if (field === 'ennm' || field === 'baseId') {
|
||||||
// 提取中文名称:从mainGroup.key获取(站点名或基地ID)
|
// 提取中文名称:从mainGroup.key获取(站点名或基地ID)
|
||||||
let displayName = '';
|
let displayName = '';
|
||||||
|
|
||||||
// mainGroup.key 可能是站点名(如"两河口")或基地ID(如"02")
|
// mainGroup.key 可能是站点名(如"两河口")或基地ID(如"02")
|
||||||
if (mainGroup.key && typeof mainGroup.key === 'string') {
|
if (mainGroup.key && typeof mainGroup.key === 'string') {
|
||||||
displayName = mainGroup.key;
|
displayName = mainGroup.key;
|
||||||
console.log(' 从mainGroup.key获取名称:', displayName);
|
console.log(' 从mainGroup.key获取名称:', displayName);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 如果mainGroup.key不是有效的中文名称,尝试从subGroup.key获取
|
// 如果mainGroup.key不是有效的中文名称,尝试从subGroup.key获取
|
||||||
if (!displayName || !/[\u4e00-\u9fa5]/.test(displayName)) {
|
if (!displayName || !/[\u4e00-\u9fa5]/.test(displayName)) {
|
||||||
const subGroup = mainGroup.items?.[0];
|
const subGroup = mainGroup.items?.[0];
|
||||||
@ -217,7 +205,7 @@ const parseApiResponse = (items: any[], currentValueForApi: string, lastYearValu
|
|||||||
console.log(' 从subGroup.key获取名称:', displayName);
|
console.log(' 从subGroup.key获取名称:', displayName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 如果还是没有,尝试从最内层数据获取
|
// 如果还是没有,尝试从最内层数据获取
|
||||||
if (!displayName) {
|
if (!displayName) {
|
||||||
const subGroup = mainGroup.items?.[0];
|
const subGroup = mainGroup.items?.[0];
|
||||||
@ -232,25 +220,26 @@ const parseApiResponse = (items: any[], currentValueForApi: string, lastYearValu
|
|||||||
console.log(' 从最内层数据获取名称:', displayName);
|
console.log(' 从最内层数据获取名称:', displayName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!displayName) {
|
if (!displayName) {
|
||||||
console.log(' ⚠️ 跳过:没有名称');
|
console.log(' ⚠️ 跳过:没有名称');
|
||||||
return; // 跳过没有名称的数据
|
return; // 跳过没有名称的数据
|
||||||
}
|
}
|
||||||
|
|
||||||
let currentPeriodValue: number | null = null;
|
let currentPeriodValue: number | null = null;
|
||||||
let lastYearPeriodValue: number | null = null;
|
let lastYearPeriodValue: number | null = null;
|
||||||
|
|
||||||
// 遍历时间分组:需要深入到第3层
|
// 遍历时间分组:需要深入到第3层
|
||||||
const subGroup = mainGroup.items?.[0];
|
const subGroup = mainGroup.items?.[0];
|
||||||
console.log(' subGroup.items数量:', subGroup?.items?.length);
|
console.log(' subGroup.items数量:', subGroup?.items?.length);
|
||||||
|
|
||||||
subGroup?.items?.forEach(monthGroup => {
|
subGroup?.items?.forEach(monthGroup => {
|
||||||
const periodKey = monthGroup.key; // "2026-05" 或 "2025-05"
|
const periodKey = monthGroup.key; // "2026-05" 或 "2025-05" 或 "2026" 或 "2025"
|
||||||
const dataItem = monthGroup.items?.[0];
|
const dataItem = monthGroup.items?.[0];
|
||||||
|
|
||||||
if (dataItem) {
|
if (dataItem) {
|
||||||
const value = dataItem.COUNT_DRMONTH ?? dataItem.wqRate;
|
// 兼容月度数据(COUNT_DRMONTH/wqRate)和年度数据(COUNT_DRYEAR)
|
||||||
|
const value = dataItem.COUNT_DRMONTH ?? dataItem.COUNT_DRYEAR ?? dataItem.wqRate;
|
||||||
console.log(` 时间:${periodKey}, 值:${value}`);
|
console.log(` 时间:${periodKey}, 值:${value}`);
|
||||||
|
|
||||||
// 判断是当前期还是去年同期
|
// 判断是当前期还是去年同期
|
||||||
@ -268,7 +257,29 @@ const parseApiResponse = (items: any[], currentValueForApi: string, lastYearValu
|
|||||||
const finalDisplayName = displayName.replace(/干流$/, '\n干流');
|
const finalDisplayName = displayName.replace(/干流$/, '\n干流');
|
||||||
|
|
||||||
console.log(` → 最终数据: {name: "${finalDisplayName}", current: ${currentPeriodValue}, lastYear: ${lastYearPeriodValue}}`);
|
console.log(` → 最终数据: {name: "${finalDisplayName}", current: ${currentPeriodValue}, lastYear: ${lastYearPeriodValue}}`);
|
||||||
|
|
||||||
|
// 新增:记录 ID 映射关系
|
||||||
|
if (field === 'baseId') {
|
||||||
|
fieldOne.value = 'all'
|
||||||
|
// baseid == 'all' 时,记录基地 ID
|
||||||
|
chartDataIdMap.value.push(mainGroup.key); // "02", "03" 等
|
||||||
|
console.log(' 记录基地ID:', mainGroup.key);
|
||||||
|
} else if (field === 'ennm') {
|
||||||
|
fieldOne.value = 'other'
|
||||||
|
// baseid != 'all' 时,记录电站编码
|
||||||
|
const stationCode = subGroup?.key; // "008660306300000001"
|
||||||
|
chartDataIdMap.value.push(stationCode || '');
|
||||||
|
console.log(' 记录电站编码:', stationCode);
|
||||||
|
|
||||||
|
// 同时提取电站列表
|
||||||
|
if (displayName && stationCode) {
|
||||||
|
stations.push({
|
||||||
|
baseid: stationCode,
|
||||||
|
basename: displayName
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
result.push({
|
result.push({
|
||||||
name: finalDisplayName,
|
name: finalDisplayName,
|
||||||
current: currentPeriodValue,
|
current: currentPeriodValue,
|
||||||
@ -278,6 +289,15 @@ const parseApiResponse = (items: any[], currentValueForApi: string, lastYearValu
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 新增:如果提取到了电站列表,更新响应式变量
|
||||||
|
if (stations.length > 0) {
|
||||||
|
stationListForModal.value = stations;
|
||||||
|
console.log('提取到的电站列表:', stations);
|
||||||
|
} else {
|
||||||
|
stationListForModal.value = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('ID 映射关系:', chartDataIdMap.value);
|
||||||
console.log('\n=== parseApiResponse 解析完成 ===');
|
console.log('\n=== parseApiResponse 解析完成 ===');
|
||||||
console.log('result数量:', result.length);
|
console.log('result数量:', result.length);
|
||||||
console.log('result:', result);
|
console.log('result:', result);
|
||||||
@ -293,7 +313,7 @@ const handleTabChange = (tab: number) => {
|
|||||||
|
|
||||||
//监听tabs变化,处理图表刷新
|
//监听tabs变化,处理图表刷新
|
||||||
watch(tabs, (newVal) => {
|
watch(tabs, (newVal) => {
|
||||||
if (newVal === 0 || newVal === 1) {
|
if (newVal === '0' || newVal === '1') {
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
getEchartsData();
|
getEchartsData();
|
||||||
@ -414,6 +434,11 @@ const getEchartsData = async () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 3. 构建请求参数
|
// 3. 构建请求参数
|
||||||
|
console.log(baseid.value)
|
||||||
|
if( ! baseid.value){
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// debugger
|
||||||
let params = {
|
let params = {
|
||||||
"filter": {
|
"filter": {
|
||||||
"logic": "and",
|
"logic": "and",
|
||||||
@ -428,11 +453,14 @@ const getEchartsData = async () => {
|
|||||||
"operator": "eq",
|
"operator": "eq",
|
||||||
"value": select.value.value == "year" ? "YEAR" : "MON"
|
"value": select.value.value == "year" ? "YEAR" : "MON"
|
||||||
},
|
},
|
||||||
{
|
baseid.value == 'all' ? {
|
||||||
"field": "dtinType",
|
"field": "dtinType",
|
||||||
"operator": "eq",
|
"operator": "eq",
|
||||||
"value": tabs.value
|
"value": 0
|
||||||
},
|
} : {
|
||||||
|
"field": "rstcd",
|
||||||
|
"operator": "isnotnull"
|
||||||
|
}, ,
|
||||||
{
|
{
|
||||||
"logic": "or",
|
"logic": "or",
|
||||||
"filters": [
|
"filters": [
|
||||||
@ -453,10 +481,13 @@ const getEchartsData = async () => {
|
|||||||
].filter(Boolean)
|
].filter(Boolean)
|
||||||
},
|
},
|
||||||
"group": [
|
"group": [
|
||||||
{
|
baseid.value == 'all' ? {
|
||||||
"dir": "des",
|
"dir": "des",
|
||||||
"field": "baseStepSort"
|
"field": "baseStepSort"
|
||||||
},
|
} : {
|
||||||
|
"dir": "asc",
|
||||||
|
"field": "rstcdStepSort"
|
||||||
|
}, ,
|
||||||
baseid.value == 'all' ? {
|
baseid.value == 'all' ? {
|
||||||
"dir": "des",
|
"dir": "des",
|
||||||
"field": "baseId"
|
"field": "baseId"
|
||||||
@ -475,7 +506,7 @@ const getEchartsData = async () => {
|
|||||||
"dir": "desc",
|
"dir": "desc",
|
||||||
"field": select.value.value == "year" ? "drYear" : "drMonth"
|
"field": select.value.value == "year" ? "drYear" : "drMonth"
|
||||||
}
|
}
|
||||||
],
|
].filter(Boolean),
|
||||||
"groupResultFlat": false,
|
"groupResultFlat": false,
|
||||||
"aggregate": [
|
"aggregate": [
|
||||||
{
|
{
|
||||||
@ -484,7 +515,8 @@ const getEchartsData = async () => {
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
console.log('请求参数:', params);
|
||||||
|
// debugger
|
||||||
// 4. 调用API并处理数据
|
// 4. 调用API并处理数据
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
try {
|
try {
|
||||||
@ -611,7 +643,7 @@ const updateChartData = (currentLabel: string, lastYearLabel: string) => {
|
|||||||
const name = params[0].name.replace('\n', '');
|
const name = params[0].name.replace('\n', '');
|
||||||
let result = `<div style="font-weight: bold; margin-bottom: 8px;">${name}</div>`;
|
let result = `<div style="font-weight: bold; margin-bottom: 8px;">${name}</div>`;
|
||||||
params.forEach((item: any) => {
|
params.forEach((item: any) => {
|
||||||
const value = item.value != null ? Number(item.value).toFixed(1) : '-';
|
const value = item.value != null ? Number(item.value) : '-';
|
||||||
result += `<div style="display: flex; align-items: center; margin-bottom: 4px;">
|
result += `<div style="display: flex; align-items: center; margin-bottom: 4px;">
|
||||||
<span style="display: inline-block; width: 10px; height: 10px; border-radius: 50%; background: ${item.color}; margin-right: 8px;"></span>
|
<span style="display: inline-block; width: 10px; height: 10px; border-radius: 50%; background: ${item.color}; margin-right: 8px;"></span>
|
||||||
<span>${item.seriesName}:</span>
|
<span>${item.seriesName}:</span>
|
||||||
@ -695,7 +727,7 @@ const updateChartData = (currentLabel: string, lastYearLabel: string) => {
|
|||||||
{
|
{
|
||||||
name: legendCurrentLabel,
|
name: legendCurrentLabel,
|
||||||
type: 'bar',
|
type: 'bar',
|
||||||
data: chartData.value.map(item => item.current != null ? Number(item.current).toFixed(1) : null),
|
data: chartData.value.map(item => item.current != null ? Math.floor(Number(item.current)) : null),
|
||||||
barWidth: 10,
|
barWidth: 10,
|
||||||
barGap: '20%',
|
barGap: '20%',
|
||||||
itemStyle: {
|
itemStyle: {
|
||||||
@ -706,7 +738,7 @@ const updateChartData = (currentLabel: string, lastYearLabel: string) => {
|
|||||||
{
|
{
|
||||||
name: legendLastYearLabel,
|
name: legendLastYearLabel,
|
||||||
type: 'bar',
|
type: 'bar',
|
||||||
data: chartData.value.map(item => item.lastYear != null ? Number(item.lastYear).toFixed(1) : null),
|
data: chartData.value.map(item => item.lastYear != null ? Math.floor(Number(item.lastYear)) : null),
|
||||||
barWidth: 10,
|
barWidth: 10,
|
||||||
barGap: '20%',
|
barGap: '20%',
|
||||||
itemStyle: {
|
itemStyle: {
|
||||||
@ -718,22 +750,26 @@ const updateChartData = (currentLabel: string, lastYearLabel: string) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
chartInstance.setOption(option, { notMerge: true });
|
chartInstance.setOption(option, { notMerge: true });
|
||||||
|
|
||||||
// 移除旧的点击事件监听
|
// 移除旧的点击事件监听
|
||||||
chartInstance.off('click');
|
chartInstance.off('click');
|
||||||
|
|
||||||
// 绑定点击事件
|
// 绑定点击事件
|
||||||
chartInstance.on('click', (params: any) => {
|
chartInstance.on('click', (params: any) => {
|
||||||
if (params.componentType === 'series' && params.seriesType === 'bar') {
|
if (params.componentType === 'series' && params.seriesType === 'bar') {
|
||||||
// 获取点击的柱子对应的数据
|
// 获取点击的柱子对应的数据
|
||||||
const dataIndex = params.dataIndex;
|
const dataIndex = params.dataIndex;
|
||||||
const barData = chartData.value[dataIndex];
|
const barData = chartData.value[dataIndex];
|
||||||
|
|
||||||
if (barData) {
|
if (barData) {
|
||||||
|
// 新增:获取对应的 baseid 或 rstcd
|
||||||
|
const selectedId = chartDataIdMap.value[dataIndex] || '';
|
||||||
|
// debugger
|
||||||
clickedBarData.value = {
|
clickedBarData.value = {
|
||||||
name: barData.name.replace('\n', ''), // 去除换行符
|
name: barData.name.replace('\n', ''), // 去除换行符
|
||||||
current: barData.current,
|
current: barData.current,
|
||||||
lastYear: barData.lastYear
|
lastYear: barData.lastYear,
|
||||||
|
selectedId: selectedId // 新增:存储选中的 ID
|
||||||
};
|
};
|
||||||
modalVisible.value = true;
|
modalVisible.value = true;
|
||||||
}
|
}
|
||||||
@ -756,11 +792,25 @@ watch(
|
|||||||
//监听子组件的数据变化
|
//监听子组件的数据变化
|
||||||
const handlePanelChange1 = async (data) => {
|
const handlePanelChange1 = async (data) => {
|
||||||
console.log('当前所有控件状态:', data);
|
console.log('当前所有控件状态:', data);
|
||||||
// debugger
|
// 当选择器或日期变化时,重新加载图表数据
|
||||||
// 当选择器或日期变化时,重新加载图表数据
|
|
||||||
if (data.select || data.datetime) {
|
if (data.select || data.datetime) {
|
||||||
await (select.value.value = data.select)
|
await (select.value.value = data.select)
|
||||||
await (datetimePicker.value.value = data.datetime)
|
|
||||||
|
// 根据 select 的值决定 datetime 的格式
|
||||||
|
let formattedDatetime = data.datetime;
|
||||||
|
if (data.select === 'year') {
|
||||||
|
// 如果是年度模式,将 datetime 转换为 YYYY 格式
|
||||||
|
if (data.datetime && typeof data.datetime === 'string') {
|
||||||
|
// 提取年份部分(假设原格式为 YYYY-MM)
|
||||||
|
const yearMatch = data.datetime.match(/^(\d{4})/);
|
||||||
|
if (yearMatch) {
|
||||||
|
formattedDatetime = yearMatch[1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 如果是 month 模式,保持 datetime 原值不变
|
||||||
|
|
||||||
|
await (datetimePicker.value.value = formattedDatetime)
|
||||||
await (datetimePicker.value.picker = data.select)
|
await (datetimePicker.value.picker = data.select)
|
||||||
await (datetimePicker.value.format = data.select == 'month' ? 'YYYY-MM' : 'YYYY')
|
await (datetimePicker.value.format = data.select == 'month' ? 'YYYY-MM' : 'YYYY')
|
||||||
getEchartsData()
|
getEchartsData()
|
||||||
@ -800,45 +850,9 @@ onUnmounted(() => {
|
|||||||
|
|
||||||
// 弹框相关状态
|
// 弹框相关状态
|
||||||
const modalVisible = ref(false);
|
const modalVisible = ref(false);
|
||||||
const clickedBarData = ref<{ name: string; current: number | null; lastYear: number | null } | null>(null);
|
const clickedBarData = ref<{ name: string; current: number | null; lastYear: number | null; selectedId?: string } | null>(null);
|
||||||
|
|
||||||
// 计算弹框中的时间信息
|
|
||||||
const modalTimeInfo = computed(() => {
|
|
||||||
if (!datetimePicker.value.value) return '';
|
|
||||||
|
|
||||||
const currentValue = datetimePicker.value.value;
|
|
||||||
const modeText = select.value.value === 'year' ? '年度' : '月度';
|
|
||||||
|
|
||||||
if (select.value.value === 'year') {
|
|
||||||
return `${currentValue}年 ${modeText}`;
|
|
||||||
} else {
|
|
||||||
const [year, month] = currentValue.split('-');
|
|
||||||
return `${year}年${month}月 ${modeText}`;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// 计算同比变化值
|
|
||||||
const getChangeValue = () => {
|
|
||||||
if (!clickedBarData.value || clickedBarData.value.current == null || clickedBarData.value.lastYear == null) {
|
|
||||||
return '-';
|
|
||||||
}
|
|
||||||
|
|
||||||
const change = Number(clickedBarData.value.current) - Number(clickedBarData.value.lastYear);
|
|
||||||
const symbol = change > 0 ? '+' : '';
|
|
||||||
return `${symbol}${change.toFixed(1)}%`;
|
|
||||||
};
|
|
||||||
|
|
||||||
// 获取变化值的样式类
|
|
||||||
const getChangeClass = () => {
|
|
||||||
if (!clickedBarData.value || clickedBarData.value.current == null || clickedBarData.value.lastYear == null) {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
|
|
||||||
const change = Number(clickedBarData.value.current) - Number(clickedBarData.value.lastYear);
|
|
||||||
if (change > 0) return 'positive';
|
|
||||||
if (change < 0) return 'negative';
|
|
||||||
return 'neutral';
|
|
||||||
};
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@ -912,7 +926,7 @@ const getChangeClass = () => {
|
|||||||
top: 0;
|
top: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 300px;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
@ -924,27 +938,27 @@ const getChangeClass = () => {
|
|||||||
|
|
||||||
.modal-content {
|
.modal-content {
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
|
|
||||||
.detail-info {
|
.detail-info {
|
||||||
.info-header {
|
.info-header {
|
||||||
margin-bottom: 24px;
|
margin-bottom: 24px;
|
||||||
padding-bottom: 16px;
|
padding-bottom: 16px;
|
||||||
border-bottom: 2px solid #e8e8e8;
|
border-bottom: 2px solid #e8e8e8;
|
||||||
|
|
||||||
h3 {
|
h3 {
|
||||||
margin: 0 0 8px 0;
|
margin: 0 0 8px 0;
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
color: #333;
|
color: #333;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
.time-info {
|
.time-info {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
color: #666;
|
color: #666;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.info-body {
|
.info-body {
|
||||||
.info-item {
|
.info-item {
|
||||||
display: flex;
|
display: flex;
|
||||||
@ -953,54 +967,54 @@ const getChangeClass = () => {
|
|||||||
padding: 12px 16px;
|
padding: 12px 16px;
|
||||||
background: #f5f7fa;
|
background: #f5f7fa;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
|
|
||||||
.label {
|
.label {
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
color: #666;
|
color: #666;
|
||||||
min-width: 140px;
|
min-width: 140px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.value {
|
.value {
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
|
|
||||||
&.current {
|
&.current {
|
||||||
color: #4A8BC2;
|
color: #4A8BC2;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.last-year {
|
&.last-year {
|
||||||
color: #9B59B6;
|
color: #9B59B6;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.change {
|
&.change {
|
||||||
&.positive {
|
&.positive {
|
||||||
color: #52c41a;
|
color: #52c41a;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.negative {
|
&.negative {
|
||||||
color: #ff4d4f;
|
color: #ff4d4f;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.neutral {
|
&.neutral {
|
||||||
color: #666;
|
color: #666;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.custom-section {
|
.custom-section {
|
||||||
margin-top: 24px;
|
margin-top: 24px;
|
||||||
padding: 16px;
|
padding: 16px;
|
||||||
background: #fafafa;
|
background: #fafafa;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
border: 1px solid #e8e8e8;
|
border: 1px solid #e8e8e8;
|
||||||
|
|
||||||
h4 {
|
h4 {
|
||||||
margin: 0 0 12px 0;
|
margin: 0 0 12px 0;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
color: #333;
|
color: #333;
|
||||||
}
|
}
|
||||||
|
|
||||||
p {
|
p {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
|
|||||||
@ -22,12 +22,12 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { ref, onMounted, onUnmounted, watch, nextTick } from 'vue';
|
import { ref, onMounted, onUnmounted, watch, nextTick } from 'vue';
|
||||||
import SidePanelItem from '@/components/SidePanelItem/index.vue';
|
import SidePanelItem from '@/components/SidePanelItem/index.vue';
|
||||||
|
import { useJidiSelectEventStore } from "@/store/modules/jidiSelectEvent";
|
||||||
// 定义组件名(便于调试和递归)
|
// 定义组件名(便于调试和递归)
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: 'qixidibaohugongzuokaizhanQK'
|
name: 'qixidibaohugongzuokaizhanQK'
|
||||||
});
|
});
|
||||||
|
const JidiSelectEventStore = useJidiSelectEventStore();
|
||||||
// 设施数据
|
// 设施数据
|
||||||
const facilities = ref([
|
const facilities = ref([
|
||||||
{
|
{
|
||||||
@ -52,7 +52,15 @@ const facilities = ref([
|
|||||||
},
|
},
|
||||||
|
|
||||||
]);
|
]);
|
||||||
|
const baseid = ref('')
|
||||||
|
watch(
|
||||||
|
() => JidiSelectEventStore.selectedItem,
|
||||||
|
(newVal) => {
|
||||||
|
baseid.value = newVal.wbsCode;
|
||||||
|
|
||||||
|
},
|
||||||
|
{ deep: true, immediate: true }
|
||||||
|
);
|
||||||
// 页面加载时执行
|
// 页面加载时执行
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
// 延迟初始化,确保容器已渲染
|
// 延迟初始化,确保容器已渲染
|
||||||
|
|||||||
@ -87,7 +87,7 @@ const getDataList = async () => {
|
|||||||
"field": "baseId",
|
"field": "baseId",
|
||||||
"operator": "eq",
|
"operator": "eq",
|
||||||
"dataType": "string",
|
"dataType": "string",
|
||||||
"value": "01"
|
"value": baseid.value
|
||||||
} : null,
|
} : null,
|
||||||
{
|
{
|
||||||
"field": "isDeleted",
|
"field": "isDeleted",
|
||||||
@ -166,7 +166,7 @@ const getDataList = async () => {
|
|||||||
"field": "baseId",
|
"field": "baseId",
|
||||||
"operator": "eq",
|
"operator": "eq",
|
||||||
"dataType": "string",
|
"dataType": "string",
|
||||||
"value": "01"
|
"value": baseid.value
|
||||||
} : null,
|
} : null,
|
||||||
{
|
{
|
||||||
"field": "isDeleted",
|
"field": "isDeleted",
|
||||||
@ -208,25 +208,30 @@ const getDataList = async () => {
|
|||||||
}
|
}
|
||||||
// const params = [data1, data2]
|
// const params = [data1, data2]
|
||||||
const res1 = await msstbprptGetKendoList(data1);
|
const res1 = await msstbprptGetKendoList(data1);
|
||||||
|
// debugger
|
||||||
let finalData = facilities.value.map((item) => ({ ...item, count: 0 }))
|
let finalData = facilities.value.map((item) => ({ ...item, count: 0 }))
|
||||||
if (res1.data) {
|
if (res1.data) {
|
||||||
const { data } = res1
|
const { data } = res1
|
||||||
data.data.forEach((item: any, index: number) => {
|
data.data.forEach((item: any, index: number) => {
|
||||||
const target = finalData.find((n) => n.key == item.key)
|
const target = finalData.find((n) => n.key == item.key)
|
||||||
if (target) {
|
if (target) {
|
||||||
target.count = item.count
|
target.count = item.count || 0
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
console.log(finalData)
|
||||||
|
// debugger/
|
||||||
const res2 = await msstbprptGetKendoList(data2);
|
const res2 = await msstbprptGetKendoList(data2);
|
||||||
if (res2.data) {
|
if (res2.data) {
|
||||||
const responseData = res2?.data?.data
|
const responseData = res2?.data?.data
|
||||||
const target1 = finalData.find((n) => n.key == '3')
|
const target1 = finalData.find((n) => n.key == '3')
|
||||||
if (target1) {
|
if (target1) {
|
||||||
target1.count = responseData[0].count || 0
|
target1.count = responseData[0]?.count || 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
facilities.value = finalData
|
facilities.value = finalData
|
||||||
|
console.log(finalData)
|
||||||
|
// debugger
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('获取数据失败:', error);
|
console.error('获取数据失败:', error);
|
||||||
} finally {
|
} finally {
|
||||||
|
|||||||
@ -24,8 +24,8 @@ import SidePanelItem from '@/components/SidePanelItem/index.vue';
|
|||||||
import { qgcGetKendoListCust, wbsbGetKendoList } from '@/api/sz'
|
import { qgcGetKendoListCust, wbsbGetKendoList } from '@/api/sz'
|
||||||
import { useJidiSelectEventStore } from "@/store/modules/jidiSelectEvent";
|
import { useJidiSelectEventStore } from "@/store/modules/jidiSelectEvent";
|
||||||
import { useModelStore } from "@/store/modules/model";
|
import { useModelStore } from "@/store/modules/model";
|
||||||
|
import { WATER_QUALITY_INDICATORS, type WaterQualityIndicator } from '@/modules/waterQuality/padig';
|
||||||
|
const indicators = WATER_QUALITY_INDICATORS;
|
||||||
// 定义组件名(便于调试和递归)
|
// 定义组件名(便于调试和递归)
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: 'waterQuality'
|
name: 'waterQuality'
|
||||||
@ -34,7 +34,7 @@ const modelStore = useModelStore();
|
|||||||
const JidiSelectEventStore = useJidiSelectEventStore();
|
const JidiSelectEventStore = useJidiSelectEventStore();
|
||||||
const iconmap = ref({
|
const iconmap = ref({
|
||||||
show: true,
|
show: true,
|
||||||
value: '注:最新数据时间为2026-05-14 23:00',
|
value: '',
|
||||||
icon: 'iconfont icon-time',
|
icon: 'iconfont icon-time',
|
||||||
});
|
});
|
||||||
// ==================== 响应式数据 ====================
|
// ==================== 响应式数据 ====================
|
||||||
@ -48,13 +48,15 @@ const hasData = ref(true); // 初始化为 true,因为 initChart 会使用 moc
|
|||||||
// 获取当天早上8:00的时间
|
// 获取当天早上8:00的时间
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
const todayAtEightAM = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 8, 0, 0);
|
const todayAtEightAM = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 8, 0, 0);
|
||||||
const defaultValue = `${todayAtEightAM.getFullYear()}-${String(todayAtEightAM.getMonth() + 1).padStart(2, '0')}-${String(todayAtEightAM.getDate()).padStart(2, '0')} ${String(todayAtEightAM.getHours()).padStart(2, '0')}:${String(todayAtEightAM.getMinutes()).padStart(2, '0')}`;
|
const defaultValue = `${todayAtEightAM.getFullYear()}-${String(todayAtEightAM.getMonth() + 1).padStart(2, '0')}-${String(todayAtEightAM.getDate()).padStart(2, '0')} ${String(todayAtEightAM.getHours()).padStart(2, '0')}:00`;
|
||||||
|
// 👆 注意这里分钟固定为 :00,与 timeFormat: 'HH' 配置保持一致
|
||||||
|
|
||||||
const datetimePicker = ref({
|
const datetimePicker = ref({
|
||||||
show: true,
|
show: true,
|
||||||
value: defaultValue,
|
value: defaultValue,
|
||||||
format: 'YYYY-MM-DD hh:mm',
|
format: 'YYYY-MM-DD HH:mm', // 显示格式:包含分钟
|
||||||
picker: 'date',
|
picker: 'date',
|
||||||
|
timeFormat: 'HH', // 新增:选择格式:只选择小时
|
||||||
options: []
|
options: []
|
||||||
});
|
});
|
||||||
const select = ref({
|
const select = ref({
|
||||||
@ -68,6 +70,109 @@ const select = ref({
|
|||||||
// ==================== 可见系列队列(最多2个)====================
|
// ==================== 可见系列队列(最多2个)====================
|
||||||
const visibleSeriesQueue = ref<string[]>([]);
|
const visibleSeriesQueue = ref<string[]>([]);
|
||||||
|
|
||||||
|
// ==================== 动态指标配置 ====================
|
||||||
|
const indicatorConfig = ref<Array<{ key: string; name: string; unit: string; color: string; sort: number }>>([]);
|
||||||
|
|
||||||
|
// ==================== HSL 随机颜色生成 ====================
|
||||||
|
const generateRandomColor = (index: number): string => {
|
||||||
|
// 使用黄金角度确保颜色均匀分布
|
||||||
|
const goldenAngle = 137.508;
|
||||||
|
const hue = (index * goldenAngle) % 360;
|
||||||
|
|
||||||
|
// 饱和度控制在 45%-65% 之间
|
||||||
|
const saturation = 45 + Math.random() * 20;
|
||||||
|
|
||||||
|
// 亮度控制在 45%-60% 之间,避免太亮或太暗
|
||||||
|
const lightness = 45 + Math.random() * 15;
|
||||||
|
|
||||||
|
// HSL 转 RGB
|
||||||
|
const c = (1 - Math.abs(2 * lightness / 100 - 1)) * saturation / 100;
|
||||||
|
const x = c * (1 - Math.abs((hue / 60) % 2 - 1));
|
||||||
|
const m = lightness / 100 - c / 2;
|
||||||
|
|
||||||
|
let r = 0, g = 0, b = 0;
|
||||||
|
|
||||||
|
if (hue < 60) {
|
||||||
|
[r, g, b] = [c, x, 0];
|
||||||
|
} else if (hue < 120) {
|
||||||
|
[r, g, b] = [x, c, 0];
|
||||||
|
} else if (hue < 180) {
|
||||||
|
[r, g, b] = [0, c, x];
|
||||||
|
} else if (hue < 240) {
|
||||||
|
[r, g, b] = [0, x, c];
|
||||||
|
} else if (hue < 300) {
|
||||||
|
[r, g, b] = [x, 0, c];
|
||||||
|
} else {
|
||||||
|
[r, g, b] = [c, 0, x];
|
||||||
|
}
|
||||||
|
|
||||||
|
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 generateIndicatorConfig = (data: StationData[]) => {
|
||||||
|
if (!data || data.length === 0) {
|
||||||
|
indicatorConfig.value = [];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 定义需要排除的元数据字段
|
||||||
|
const excludeKeys = new Set([
|
||||||
|
'stcd', 'stcd2', 'stnm', 'rstcd', 'sttp', 'tm', 'SORT',
|
||||||
|
'limits', 'minTm', 'wwqtg', 'wqgrd', 'wqgrdName', 'min', 'max',
|
||||||
|
'_tls', 'id', 'recordUser', 'recordTime', 'modifyTime',
|
||||||
|
'displayRecordUser', 'departmentId', 'displayDepartment'
|
||||||
|
]);
|
||||||
|
|
||||||
|
// 收集所有有效指标的 key
|
||||||
|
const validKeysSet = new Set<string>();
|
||||||
|
|
||||||
|
data.forEach(station => {
|
||||||
|
Object.keys(station).forEach(key => {
|
||||||
|
// 跳过排除字段
|
||||||
|
if (excludeKeys.has(key)) return;
|
||||||
|
|
||||||
|
const value = station[key as keyof typeof station];
|
||||||
|
|
||||||
|
// 检查是否为有效数值(不为 null 且不是 NaN)
|
||||||
|
if (value !== null && value !== undefined && typeof value === 'number' && !isNaN(value)) {
|
||||||
|
// 在 indicators 中查找匹配的项(小写比较)
|
||||||
|
const matchedIndicator = indicators.find(ind => ind.key.toLowerCase() === key.toLowerCase());
|
||||||
|
if (matchedIndicator) {
|
||||||
|
validKeysSet.add(matchedIndicator.key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// 如果没有有效指标,返回空数组
|
||||||
|
if (validKeysSet.size === 0) {
|
||||||
|
indicatorConfig.value = [];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 根据 indicators 中的 sort 排序,并生成配置
|
||||||
|
const validIndicators = indicators
|
||||||
|
.filter(ind => validKeysSet.has(ind.key))
|
||||||
|
.sort((a, b) => a.sort - b.sort);
|
||||||
|
|
||||||
|
// 生成最终配置
|
||||||
|
indicatorConfig.value = validIndicators.map((ind, index) => ({
|
||||||
|
key: ind.key,
|
||||||
|
name: ind.name,
|
||||||
|
unit: ind.description || '',
|
||||||
|
color: generateRandomColor(index),
|
||||||
|
sort: ind.sort
|
||||||
|
}));
|
||||||
|
|
||||||
|
console.log('动态生成指标配置:', indicatorConfig.value);
|
||||||
|
};
|
||||||
|
|
||||||
// ==================== 模拟数据 ====================
|
// ==================== 模拟数据 ====================
|
||||||
const generateMockData = () => {
|
const generateMockData = () => {
|
||||||
const stations = [
|
const stations = [
|
||||||
@ -82,6 +187,16 @@ const generateMockData = () => {
|
|||||||
|
|
||||||
const tm = '2024-01-15 08:00:00';
|
const tm = '2024-01-15 08:00:00';
|
||||||
|
|
||||||
|
// 模拟限值数据
|
||||||
|
const mockLimits = {
|
||||||
|
ph: { min: 6, max: 9 },
|
||||||
|
dox: { min: 6 },
|
||||||
|
codmn: { max: 4 },
|
||||||
|
nh3n: { max: 0.5 },
|
||||||
|
tp: { max: 0.1 },
|
||||||
|
tn: { max: 1.0 }
|
||||||
|
};
|
||||||
|
|
||||||
return stations.map((station, index) => ({
|
return stations.map((station, index) => ({
|
||||||
...station,
|
...station,
|
||||||
tm,
|
tm,
|
||||||
@ -91,27 +206,65 @@ const generateMockData = () => {
|
|||||||
codmn: 1.5 + Math.random() * 2.0,
|
codmn: 1.5 + Math.random() * 2.0,
|
||||||
nh3n: 0.15 + Math.random() * 0.35,
|
nh3n: 0.15 + Math.random() * 0.35,
|
||||||
tp: 0.05 + Math.random() * 0.15,
|
tp: 0.05 + Math.random() * 0.15,
|
||||||
tn: 0.8 + Math.random() * 1.2
|
tn: 0.8 + Math.random() * 1.2,
|
||||||
|
limits: mockLimits // 新增:存储该站点的所有限值
|
||||||
}));
|
}));
|
||||||
};
|
};
|
||||||
|
|
||||||
const mockData = ref(generateMockData());
|
// 定义数据类型接口
|
||||||
|
interface StationData {
|
||||||
|
stnm: string;
|
||||||
|
stcd: string;
|
||||||
|
type: string;
|
||||||
|
tm: string;
|
||||||
|
SORT: number;
|
||||||
|
// 所有水质指标字段(动态)
|
||||||
|
ph?: number | null;
|
||||||
|
dox?: number | null;
|
||||||
|
codmn?: number | null;
|
||||||
|
codcr?: number | null;
|
||||||
|
bod5?: number | null;
|
||||||
|
nh3n?: number | null;
|
||||||
|
tp?: number | null;
|
||||||
|
tn?: number | null;
|
||||||
|
cu?: number | null;
|
||||||
|
zn?: number | null;
|
||||||
|
f?: number | null;
|
||||||
|
se?: number | null;
|
||||||
|
ars?: number | null;
|
||||||
|
hg?: number | null;
|
||||||
|
cd?: number | null;
|
||||||
|
cr6?: number | null;
|
||||||
|
pb?: number | null;
|
||||||
|
cn?: number | null;
|
||||||
|
vlph?: number | null;
|
||||||
|
oil?: number | null;
|
||||||
|
las?: number | null;
|
||||||
|
s2?: number | null;
|
||||||
|
fcg?: number | null;
|
||||||
|
cl?: number | null;
|
||||||
|
so4?: number | null;
|
||||||
|
no3?: number | null;
|
||||||
|
thrd?: number | null;
|
||||||
|
cond?: number | null;
|
||||||
|
fe?: number | null;
|
||||||
|
mn?: number | null;
|
||||||
|
al?: number | null;
|
||||||
|
chla?: number | null;
|
||||||
|
clarity?: number | null;
|
||||||
|
tu?: number | null;
|
||||||
|
wtmp?: number | null;
|
||||||
|
limits: Record<string, { min?: number; max?: number }>;
|
||||||
|
[key: string]: any; // 允许任意水质指标字段
|
||||||
|
}
|
||||||
|
|
||||||
// ==================== 指标配置 ====================
|
const mockData = ref<StationData[]>([]);
|
||||||
const indicatorConfig = [
|
|
||||||
{ key: 'ph', name: 'pH', unit: '', color: '#5470C6' },
|
|
||||||
{ key: 'dox', name: '溶解氧', unit: 'mg/L', color: '#91CC75' },
|
|
||||||
{ key: 'codmn', name: '高锰酸盐指数', unit: 'mg/L', color: '#FAC858' },
|
|
||||||
{ key: 'nh3n', name: '氨氮', unit: 'mg/L', color: '#EE6666' },
|
|
||||||
{ key: 'tp', name: '总磷', unit: 'mg/L', color: '#73C0DE' },
|
|
||||||
{ key: 'tn', name: '总氮', unit: 'mg/L', color: '#3BA272' }
|
|
||||||
];
|
|
||||||
|
|
||||||
// ==================== 图表配置生成 ====================
|
// ==================== 图表配置生成 ====================
|
||||||
const getChartOption = (): EChartsOption => {
|
const getChartOption = (): EChartsOption => {
|
||||||
const data = mockData.value;
|
const data = mockData.value;
|
||||||
|
|
||||||
if (!data || data.length === 0) {
|
if (!data || data.length === 0 || indicatorConfig.value.length === 0) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -142,16 +295,16 @@ const getChartOption = (): EChartsOption => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// 图例数据
|
// 图例数据
|
||||||
const legendData = indicatorConfig.map(item => item.name);
|
const legendData = indicatorConfig.value.map(item => item.name);
|
||||||
|
|
||||||
// 根据可见队列构建选中状态
|
// 根据可见队列构建选中状态
|
||||||
const selectedState: Record<string, boolean> = {};
|
const selectedState: Record<string, boolean> = {};
|
||||||
indicatorConfig.forEach(config => {
|
indicatorConfig.value.forEach(config => {
|
||||||
selectedState[config.name] = visibleSeriesQueue.value.includes(config.name);
|
selectedState[config.name] = visibleSeriesQueue.value.includes(config.name);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Series 数据
|
// Series 数据
|
||||||
const seriesData = indicatorConfig.map((config, index) => {
|
const seriesData = indicatorConfig.value.map((config, index) => {
|
||||||
const _key = config.key === 'dox' ? 'do' : config.key;
|
const _key = config.key === 'dox' ? 'do' : config.key;
|
||||||
return {
|
return {
|
||||||
name: config.name,
|
name: config.name,
|
||||||
@ -182,12 +335,12 @@ const getChartOption = (): EChartsOption => {
|
|||||||
|
|
||||||
// 计算每个Y轴的数据范围(用于自适应,使用整数)
|
// 计算每个Y轴的数据范围(用于自适应,使用整数)
|
||||||
const calculateYAxisRange = (configIndex: number) => {
|
const calculateYAxisRange = (configIndex: number) => {
|
||||||
if (!selectedState[indicatorConfig[configIndex].name]) {
|
if (!selectedState[indicatorConfig.value[configIndex].name]) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const values = data
|
const values = data
|
||||||
.map(item => item[indicatorConfig[configIndex].key as keyof typeof item])
|
.map(item => item[indicatorConfig.value[configIndex].key as keyof typeof item])
|
||||||
.filter((val): val is number => typeof val === 'number' && !isNaN(val));
|
.filter((val): val is number => typeof val === 'number' && !isNaN(val));
|
||||||
|
|
||||||
if (values.length === 0) {
|
if (values.length === 0) {
|
||||||
@ -204,10 +357,38 @@ const getChartOption = (): EChartsOption => {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
// Y轴配置(左1右N原则)
|
// Y轴配置(左1右1原则,最多显示2个Y轴)
|
||||||
const yAxisData = indicatorConfig.map((config, index) => {
|
const yAxisData = indicatorConfig.value.map((config, index) => {
|
||||||
const isLeft = index === 0;
|
const isShow = selectedState[config.name];
|
||||||
const offset = index < 2 ? 0 : (index - 1) * 60;
|
|
||||||
|
// 如果不显示,直接返回隐藏配置
|
||||||
|
if (!isShow) {
|
||||||
|
return {
|
||||||
|
type: 'value' as const,
|
||||||
|
name: config.name,
|
||||||
|
show: false
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算当前是第几个显示的Y轴
|
||||||
|
let displayIndex = 0;
|
||||||
|
for (let i = 0; i < index; i++) {
|
||||||
|
if (selectedState[indicatorConfig.value[i].name]) {
|
||||||
|
displayIndex++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 最多只显示2个Y轴(索引0和1)
|
||||||
|
if (displayIndex >= 2) {
|
||||||
|
return {
|
||||||
|
type: 'value' as const,
|
||||||
|
name: config.name,
|
||||||
|
show: false
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const isLeft = displayIndex === 0;
|
||||||
|
const offset = displayIndex === 1 ? 0 : 0;
|
||||||
const range = calculateYAxisRange(index);
|
const range = calculateYAxisRange(index);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@ -237,7 +418,7 @@ const getChartOption = (): EChartsOption => {
|
|||||||
type: 'solid' as const
|
type: 'solid' as const
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
show: selectedState[config.name]
|
show: true
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -259,8 +440,11 @@ const getChartOption = (): EChartsOption => {
|
|||||||
const tm = data.find(item => item.stnm === params[0].name)?.tm;
|
const tm = data.find(item => item.stnm === params[0].name)?.tm;
|
||||||
result += `${params[0].name}<br/>`;
|
result += `${params[0].name}<br/>`;
|
||||||
|
|
||||||
|
// 判断是否只显示一个系列
|
||||||
|
const showOnlyOneSeries = visibleSeriesQueue.value.length === 1;
|
||||||
|
|
||||||
params.forEach((param: any) => {
|
params.forEach((param: any) => {
|
||||||
const config = indicatorConfig.find(c => c.name === param.seriesName);
|
const config = indicatorConfig.value.find(c => c.name === param.seriesName);
|
||||||
const unit = config?.unit || '';
|
const unit = config?.unit || '';
|
||||||
|
|
||||||
if (param.value !== null && param.value !== undefined) {
|
if (param.value !== null && param.value !== undefined) {
|
||||||
@ -272,7 +456,38 @@ const getChartOption = (): EChartsOption => {
|
|||||||
displayValue = numValue.toFixed(1);
|
displayValue = numValue.toFixed(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
result += `${param.marker}${param.seriesName}:${displayValue}${unit}<br/>`;
|
result += `${param.marker}${param.seriesName}:${displayValue}${unit}`;
|
||||||
|
|
||||||
|
// 如果只显示一个系列,且该指标有限值,则显示限值
|
||||||
|
if (showOnlyOneSeries && config) {
|
||||||
|
// 从 data 中获取当前站点的限值
|
||||||
|
const stationData = data.find(item => item.stnm === param.name);
|
||||||
|
if (stationData && stationData.limits) {
|
||||||
|
const limitKey = config.key; // 如 'ph', 'dox'
|
||||||
|
const limits = stationData.limits[limitKey];
|
||||||
|
|
||||||
|
if (limits) {
|
||||||
|
// 构建限值显示字符串
|
||||||
|
let limitText = '';
|
||||||
|
const unit = config.unit || ''; // 获取单位
|
||||||
|
|
||||||
|
if (limits.min !== undefined && limits.max !== undefined) {
|
||||||
|
// 既有上限又有下限
|
||||||
|
limitText = ` 限值: ${limits.min}-${limits.max}${unit ? unit : ''}`;
|
||||||
|
} else if (limits.min !== undefined) {
|
||||||
|
// 只有下限
|
||||||
|
limitText = ` 限值: ${limits.min}${unit ? unit : ''}`;
|
||||||
|
} else if (limits.max !== undefined) {
|
||||||
|
// 只有上限
|
||||||
|
limitText = ` 限值: ${limits.max}${unit ? unit : ''}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
result += ('<br/>'+limitText);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result += '<br/>';
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -282,12 +497,18 @@ const getChartOption = (): EChartsOption => {
|
|||||||
legend: {
|
legend: {
|
||||||
type: 'scroll',
|
type: 'scroll',
|
||||||
top: 0,
|
top: 0,
|
||||||
|
right: 10, // 右侧留白,避免箭头按钮被遮挡
|
||||||
data: legendData,
|
data: legendData,
|
||||||
selected: selectedState,
|
selected: selectedState,
|
||||||
inactiveColor: '#ccc',
|
inactiveColor: '#ccc',
|
||||||
itemWidth: 16,
|
itemWidth: 16,
|
||||||
itemHeight: 12,
|
itemHeight: 12,
|
||||||
itemGap: 8,
|
itemGap: 8,
|
||||||
|
pageButtonItemGap: 5, // 分页按钮与图例项的间距
|
||||||
|
pageIconColor: '#2f4554', // 分页按钮颜色
|
||||||
|
pageIconInactiveColor: '#aaa', // 分页按钮禁用时的颜色
|
||||||
|
pageIconSize: 12, // 分页按钮大小
|
||||||
|
pageFormatter: '{current}/{total}', // 分页格式:当前页/总页数
|
||||||
textStyle: {
|
textStyle: {
|
||||||
fontSize: 14
|
fontSize: 14
|
||||||
}
|
}
|
||||||
@ -374,7 +595,7 @@ const handleLegendSelectChanged = (params: any) => {
|
|||||||
|
|
||||||
// 根据队列构建最终的 selected 状态
|
// 根据队列构建最终的 selected 状态
|
||||||
const finalSelected: Record<string, boolean> = {};
|
const finalSelected: Record<string, boolean> = {};
|
||||||
indicatorConfig.forEach(config => {
|
indicatorConfig.value.forEach(config => {
|
||||||
finalSelected[config.name] = visibleSeriesQueue.value.includes(config.name);
|
finalSelected[config.name] = visibleSeriesQueue.value.includes(config.name);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -387,13 +608,26 @@ const handleLegendSelectChanged = (params: any) => {
|
|||||||
// 根据图例状态更新 Y 轴显隐
|
// 根据图例状态更新 Y 轴显隐
|
||||||
const newYAxis = currentOption.yAxis.map((item: any) => {
|
const newYAxis = currentOption.yAxis.map((item: any) => {
|
||||||
let isShow = false;
|
let isShow = false;
|
||||||
|
let displayIndex = 0;
|
||||||
|
|
||||||
|
// 计算这是第几个显示的Y轴
|
||||||
for (const key in finalSelected) {
|
for (const key in finalSelected) {
|
||||||
if (key === item.name) {
|
if (finalSelected[key]) {
|
||||||
isShow = finalSelected[key];
|
const config = indicatorConfig.value.find(c => c.name === key);
|
||||||
break;
|
if (config && config.name === item.name) {
|
||||||
|
isShow = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
displayIndex++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return { ...item, show: isShow };
|
|
||||||
|
// 最多只显示2个Y轴
|
||||||
|
if (!isShow || displayIndex >= 2) {
|
||||||
|
return { ...item, show: false };
|
||||||
|
}
|
||||||
|
|
||||||
|
return { ...item, show: true };
|
||||||
});
|
});
|
||||||
|
|
||||||
currentOption.yAxis = newYAxis;
|
currentOption.yAxis = newYAxis;
|
||||||
@ -410,24 +644,6 @@ const handleLegendSelectChanged = (params: any) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// ==================== 数据点点击事件处理 ====================
|
|
||||||
const handleDataPointClick = (params: any) => {
|
|
||||||
if (!params || !params.dataIndex) return;
|
|
||||||
|
|
||||||
// 获取点击的数据点对应的站点信息
|
|
||||||
const dataIndex = params.dataIndex;
|
|
||||||
const stationData = mockData.value[dataIndex];
|
|
||||||
|
|
||||||
if (stationData && stationData.stcd) {
|
|
||||||
modelStore.modalVisible = true;
|
|
||||||
modelStore.params.sttp = "fh_wq_point";
|
|
||||||
modelStore.title = stationData.stnm + "详情信息";
|
|
||||||
// modelStore.isBasicEdit = true;
|
|
||||||
modelStore.params.stcd = stationData.stcd;
|
|
||||||
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// ==================== Y轴动态布局调整 ====================
|
// ==================== Y轴动态布局调整 ====================
|
||||||
const yAxisShowDynamic = (selected: Record<string, boolean>, options: any) => {
|
const yAxisShowDynamic = (selected: Record<string, boolean>, options: any) => {
|
||||||
const allShow = options.yAxis?.filter((item: any) => item.show);
|
const allShow = options.yAxis?.filter((item: any) => item.show);
|
||||||
@ -456,10 +672,10 @@ const yAxisShowDynamic = (selected: Record<string, boolean>, options: any) => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 两个及以上Y轴:左侧1个,右侧其余
|
// 两个Y轴:左侧1个,右侧1个
|
||||||
if (showCount >= 2) {
|
if (showCount >= 2) {
|
||||||
let leftIndex = 0;
|
let leftAssigned = false;
|
||||||
let rightIndex = 0;
|
let rightAssigned = false;
|
||||||
|
|
||||||
options.yAxis = options.yAxis.map((item: any) => {
|
options.yAxis = options.yAxis.map((item: any) => {
|
||||||
if (!item.show) {
|
if (!item.show) {
|
||||||
@ -467,8 +683,8 @@ const yAxisShowDynamic = (selected: Record<string, boolean>, options: any) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 第一个可见的Y轴放在左侧
|
// 第一个可见的Y轴放在左侧
|
||||||
if (leftIndex === 0) {
|
if (!leftAssigned) {
|
||||||
leftIndex++;
|
leftAssigned = true;
|
||||||
delete options.grid.right;
|
delete options.grid.right;
|
||||||
options.grid.left = '80px';
|
options.grid.left = '80px';
|
||||||
|
|
||||||
@ -477,108 +693,45 @@ const yAxisShowDynamic = (selected: Record<string, boolean>, options: any) => {
|
|||||||
position: 'left',
|
position: 'left',
|
||||||
offset: 0
|
offset: 0
|
||||||
};
|
};
|
||||||
|
} else if (!rightAssigned) {
|
||||||
|
// 第二个Y轴放在右侧
|
||||||
|
rightAssigned = true;
|
||||||
|
options.grid.right = '60px';
|
||||||
|
return {
|
||||||
|
...item,
|
||||||
|
position: 'right',
|
||||||
|
offset: 0
|
||||||
|
};
|
||||||
} else {
|
} else {
|
||||||
// 其余Y轴放在右侧
|
// 第三个及以后的Y轴隐藏(理论上不应该到这里)
|
||||||
rightIndex++;
|
return {
|
||||||
|
...item,
|
||||||
if (rightIndex > 1) {
|
show: false
|
||||||
options.grid.right = '100px';
|
};
|
||||||
return {
|
|
||||||
...item,
|
|
||||||
position: 'right',
|
|
||||||
offset: 60
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
options.grid.right = '60px';
|
|
||||||
return {
|
|
||||||
...item,
|
|
||||||
position: 'right',
|
|
||||||
offset: 0
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// ==================== 数据点点击事件处理 ====================
|
||||||
|
const handleDataPointClick = (params: any) => {
|
||||||
|
if (!params || !params.dataIndex) return;
|
||||||
|
|
||||||
|
// 获取点击的数据点对应的站点信息
|
||||||
|
const dataIndex = params.dataIndex;
|
||||||
|
const stationData = mockData.value[dataIndex];
|
||||||
|
|
||||||
|
if (stationData && stationData.stcd) {
|
||||||
|
modelStore.modalVisible = true;
|
||||||
|
modelStore.params.sttp = "fh_wq_point";
|
||||||
|
modelStore.title = stationData.stnm + "详情信息";
|
||||||
|
// modelStore.isBasicEdit = true;
|
||||||
|
modelStore.params.stcd = stationData.stcd;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// ==================== 图表初始化 ====================
|
// ==================== 图表初始化 ====================
|
||||||
const initChart = async () => {
|
|
||||||
if (!chartRef.value) {
|
|
||||||
console.warn('图表容器未就绪');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 检查容器尺寸
|
|
||||||
const rect = chartRef.value.getBoundingClientRect();
|
|
||||||
if (rect.width === 0 || rect.height === 0) {
|
|
||||||
console.warn('图表容器尺寸为0,等待渲染...');
|
|
||||||
// 使用 requestAnimationFrame 确保在浏览器下一重绘帧再初始化
|
|
||||||
return new Promise((resolve) => {
|
|
||||||
requestAnimationFrame(() => {
|
|
||||||
setTimeout(() => {
|
|
||||||
initChart().then(resolve);
|
|
||||||
}, 50);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// 如果图表实例已存在,先销毁
|
|
||||||
if (chartInstance) {
|
|
||||||
chartInstance.dispose();
|
|
||||||
chartInstance = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 初始化可见队列:默认只显示第一个系列
|
|
||||||
visibleSeriesQueue.value = [indicatorConfig[0].name];
|
|
||||||
console.log('初始化可见队列:', visibleSeriesQueue.value);
|
|
||||||
|
|
||||||
// 初始化 ECharts 实例
|
|
||||||
chartInstance = echarts.init(chartRef.value);
|
|
||||||
|
|
||||||
// 设置初始配置
|
|
||||||
const option = getChartOption();
|
|
||||||
chartInstance.setOption(option);
|
|
||||||
|
|
||||||
// 监听图例选择变化事件
|
|
||||||
chartInstance.on('legendselectchanged', handleLegendSelectChanged);
|
|
||||||
|
|
||||||
// 监听数据点点击事件
|
|
||||||
chartInstance.on('click', handleDataPointClick);
|
|
||||||
|
|
||||||
// 强制 resize 确保正确渲染
|
|
||||||
setTimeout(() => {
|
|
||||||
chartInstance?.resize();
|
|
||||||
}, 50);
|
|
||||||
|
|
||||||
console.log('图表初始化成功');
|
|
||||||
};
|
|
||||||
|
|
||||||
// ==================== 窗口大小变化处理 ====================
|
|
||||||
const handleResize = () => {
|
|
||||||
chartInstance?.resize();
|
|
||||||
};
|
|
||||||
|
|
||||||
// ==================== 生命周期钩子 ====================
|
|
||||||
onMounted(async () => {
|
|
||||||
await nextTick();
|
|
||||||
// 增加额外延迟确保容器完全渲染
|
|
||||||
setTimeout(() => {
|
|
||||||
initChart();
|
|
||||||
}, 50);
|
|
||||||
|
|
||||||
window.addEventListener('resize', handleResize);
|
|
||||||
});
|
|
||||||
|
|
||||||
onBeforeUnmount(() => {
|
|
||||||
window.removeEventListener('resize', handleResize);
|
|
||||||
|
|
||||||
if (chartInstance) {
|
|
||||||
chartInstance.off('legendselectchanged', handleLegendSelectChanged);
|
|
||||||
chartInstance.off('click', handleDataPointClick);
|
|
||||||
chartInstance.dispose();
|
|
||||||
chartInstance = null;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
//获取图参数
|
//获取图参数
|
||||||
const getecharts = async () => {
|
const getecharts = async () => {
|
||||||
if (!select.value.value) {
|
if (!select.value.value) {
|
||||||
@ -595,15 +748,6 @@ const getecharts = async () => {
|
|||||||
loading.value = true;
|
loading.value = true;
|
||||||
hasData.value = false;
|
hasData.value = false;
|
||||||
|
|
||||||
// 确保图表实例存在
|
|
||||||
if (!chartInstance) {
|
|
||||||
console.log('图表实例不存在,重新初始化...');
|
|
||||||
await nextTick();
|
|
||||||
await initChart();
|
|
||||||
// 等待图表初始化完成
|
|
||||||
await new Promise(resolve => setTimeout(resolve, 100));
|
|
||||||
}
|
|
||||||
|
|
||||||
let params = {
|
let params = {
|
||||||
"filter": {
|
"filter": {
|
||||||
"logic": "and",
|
"logic": "and",
|
||||||
@ -684,6 +828,35 @@ const getecharts = async () => {
|
|||||||
if (stnm.includes('出库水质站')) {
|
if (stnm.includes('出库水质站')) {
|
||||||
stnm = stnm.replace('出库水质站', '');
|
stnm = stnm.replace('出库水质站', '');
|
||||||
}
|
}
|
||||||
|
if (stnm.includes('出库水质_')) {
|
||||||
|
stnm = stnm.replace('出库水质_', '');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解析 min 和 max 数组,构建限值对象
|
||||||
|
const limits: Record<string, { min?: number; max?: number }> = {};
|
||||||
|
|
||||||
|
// 处理下限
|
||||||
|
if (item.min && Array.isArray(item.min)) {
|
||||||
|
item.min.forEach((limitItem: any) => {
|
||||||
|
Object.keys(limitItem).forEach(key => {
|
||||||
|
// key 是大写,如 "PH", "DOX",转为小写以匹配 indicatorConfig
|
||||||
|
const lowerKey = key.toLowerCase();
|
||||||
|
limits[lowerKey] = limits[lowerKey] || {};
|
||||||
|
limits[lowerKey].min = limitItem[key];
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理上限
|
||||||
|
if (item.max && Array.isArray(item.max)) {
|
||||||
|
item.max.forEach((limitItem: any) => {
|
||||||
|
Object.keys(limitItem).forEach(key => {
|
||||||
|
const lowerKey = key.toLowerCase();
|
||||||
|
limits[lowerKey] = limits[lowerKey] || {};
|
||||||
|
limits[lowerKey].max = limitItem[key];
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
stnm: stnm,
|
stnm: stnm,
|
||||||
@ -694,9 +867,39 @@ const getecharts = async () => {
|
|||||||
ph: item.ph != null ? Number(item.ph) : null,
|
ph: item.ph != null ? Number(item.ph) : null,
|
||||||
dox: item.dox != null ? Number(item.dox) : null,
|
dox: item.dox != null ? Number(item.dox) : null,
|
||||||
codmn: item.codmn != null ? Number(item.codmn) : null,
|
codmn: item.codmn != null ? Number(item.codmn) : null,
|
||||||
|
codcr: item.codcr != null ? Number(item.codcr) : null,
|
||||||
|
bod5: item.bod5 != null ? Number(item.bod5) : null,
|
||||||
nh3n: item.nh3n != null ? Number(item.nh3n) : null,
|
nh3n: item.nh3n != null ? Number(item.nh3n) : null,
|
||||||
tp: item.tp != null ? Number(item.tp) : null,
|
tp: item.tp != null ? Number(item.tp) : null,
|
||||||
tn: item.tn != null ? Number(item.tn) : null
|
tn: item.tn != null ? Number(item.tn) : null,
|
||||||
|
cu: item.cu != null ? Number(item.cu) : null,
|
||||||
|
zn: item.zn != null ? Number(item.zn) : null,
|
||||||
|
f: item.f != null ? Number(item.f) : null,
|
||||||
|
se: item.se != null ? Number(item.se) : null,
|
||||||
|
ars: item.ars != null ? Number(item.ars) : null,
|
||||||
|
hg: item.hg != null ? Number(item.hg) : null,
|
||||||
|
cd: item.cd != null ? Number(item.cd) : null,
|
||||||
|
cr6: item.cr6 != null ? Number(item.cr6) : null,
|
||||||
|
pb: item.pb != null ? Number(item.pb) : null,
|
||||||
|
cn: item.cn != null ? Number(item.cn) : null,
|
||||||
|
vlph: item.vlph != null ? Number(item.vlph) : null,
|
||||||
|
oil: item.oil != null ? Number(item.oil) : null,
|
||||||
|
las: item.las != null ? Number(item.las) : null,
|
||||||
|
s2: item.s2 != null ? Number(item.s2) : null,
|
||||||
|
fcg: item.fcg != null ? Number(item.fcg) : null,
|
||||||
|
cl: item.cl != null ? Number(item.cl) : null,
|
||||||
|
so4: item.so4 != null ? Number(item.so4) : null,
|
||||||
|
no3: item.no3 != null ? Number(item.no3) : null,
|
||||||
|
thrd: item.thrd != null ? Number(item.thrd) : null,
|
||||||
|
cond: item.cond != null ? Number(item.cond) : null,
|
||||||
|
fe: item.fe != null ? Number(item.fe) : null,
|
||||||
|
mn: item.mn != null ? Number(item.mn) : null,
|
||||||
|
al: item.al != null ? Number(item.al) : null,
|
||||||
|
chla: item.chla != null ? Number(item.chla) : null,
|
||||||
|
clarity: item.clarity != null ? Number(item.clarity) : null,
|
||||||
|
tu: item.tu != null ? Number(item.tu) : null,
|
||||||
|
wtmp: item.wtmp != null ? Number(item.wtmp) : null,
|
||||||
|
limits: limits // 新增:存储该站点的所有限值
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -707,25 +910,46 @@ const getecharts = async () => {
|
|||||||
hasData.value = chartData.length > 0;
|
hasData.value = chartData.length > 0;
|
||||||
console.log('hasData 设置为:', hasData.value);
|
console.log('hasData 设置为:', hasData.value);
|
||||||
|
|
||||||
|
if (!hasData.value) {
|
||||||
|
// 无数据时清空图表
|
||||||
|
console.log('无数据,清空图表');
|
||||||
|
chartInstance?.clear();
|
||||||
|
indicatorConfig.value = [];
|
||||||
|
loading.value = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 动态生成指标配置
|
||||||
|
generateIndicatorConfig(chartData);
|
||||||
|
|
||||||
|
// 如果没有有效指标,显示空状态
|
||||||
|
if (indicatorConfig.value.length === 0) {
|
||||||
|
console.log('没有有效指标,显示空状态');
|
||||||
|
hasData.value = false;
|
||||||
|
chartInstance?.clear();
|
||||||
|
loading.value = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// 重新渲染图表 - 使用 nextTick 确保 DOM 更新完成
|
// 重新渲染图表 - 使用 nextTick 确保 DOM 更新完成
|
||||||
await nextTick();
|
await nextTick();
|
||||||
|
|
||||||
if (chartInstance && hasData.value) {
|
if (chartInstance) {
|
||||||
// 使用 requestAnimationFrame 确保容器尺寸正确
|
// 如果图表已存在,更新配置
|
||||||
requestAnimationFrame(() => {
|
requestAnimationFrame(() => {
|
||||||
const option = getChartOption();
|
const option = getChartOption();
|
||||||
chartInstance?.setOption(option, true);
|
chartInstance?.setOption(option, true);
|
||||||
chartInstance?.resize();
|
chartInstance?.resize();
|
||||||
console.log('图表数据已更新,共', chartData.length, '个站点');
|
console.log('图表数据已更新,共', chartData.length, '个站点,', indicatorConfig.value.length, '个指标');
|
||||||
});
|
});
|
||||||
} else if (!hasData.value) {
|
} else {
|
||||||
// 无数据时清空图表
|
// 如果图表不存在,初始化图表
|
||||||
console.log('无数据,清空图表');
|
await initChart();
|
||||||
chartInstance?.clear();
|
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('获取图表数据失败:', error);
|
console.error('获取图表数据失败:', error);
|
||||||
hasData.value = false;
|
hasData.value = false;
|
||||||
|
indicatorConfig.value = [];
|
||||||
} finally {
|
} finally {
|
||||||
// 关闭 loading 状态 - 使用 nextTick 确保在下一个 tick 关闭
|
// 关闭 loading 状态 - 使用 nextTick 确保在下一个 tick 关闭
|
||||||
await nextTick();
|
await nextTick();
|
||||||
@ -761,6 +985,90 @@ const getEndTime = () => {
|
|||||||
return `${year}-${month}-${day} 23:59:59`;
|
return `${year}-${month}-${day} 23:59:59`;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// ==================== 图表初始化 ====================
|
||||||
|
|
||||||
|
const initChart = async () => {
|
||||||
|
if (!chartRef.value) {
|
||||||
|
console.warn('图表容器未就绪');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查容器尺寸
|
||||||
|
const rect = chartRef.value.getBoundingClientRect();
|
||||||
|
if (rect.width === 0 || rect.height === 0) {
|
||||||
|
console.warn('图表容器尺寸为0,等待渲染...');
|
||||||
|
// 使用 requestAnimationFrame 确保在浏览器下一重绘帧再初始化
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
setTimeout(() => {
|
||||||
|
initChart().then(resolve);
|
||||||
|
}, 50);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果图表实例已存在,先销毁
|
||||||
|
if (chartInstance) {
|
||||||
|
chartInstance.dispose();
|
||||||
|
chartInstance = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果没有有效指标配置,不初始化图表
|
||||||
|
if (indicatorConfig.value.length === 0) {
|
||||||
|
console.warn('没有有效的指标配置,跳过图表初始化');
|
||||||
|
hasData.value = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始化可见队列:默认只显示第一个系列(按sort排序后的第一个)
|
||||||
|
visibleSeriesQueue.value = [indicatorConfig.value[0].name];
|
||||||
|
console.log('初始化可见队列:', visibleSeriesQueue.value);
|
||||||
|
|
||||||
|
// 初始化 ECharts 实例
|
||||||
|
chartInstance = echarts.init(chartRef.value);
|
||||||
|
|
||||||
|
// 设置初始配置
|
||||||
|
const option = getChartOption();
|
||||||
|
chartInstance.setOption(option);
|
||||||
|
|
||||||
|
// 监听图例选择变化事件
|
||||||
|
chartInstance.on('legendselectchanged', handleLegendSelectChanged);
|
||||||
|
|
||||||
|
// 监听数据点点击事件
|
||||||
|
chartInstance.on('click', handleDataPointClick);
|
||||||
|
|
||||||
|
// 强制 resize 确保正确渲染
|
||||||
|
setTimeout(() => {
|
||||||
|
chartInstance?.resize();
|
||||||
|
}, 50);
|
||||||
|
|
||||||
|
console.log('图表初始化成功');
|
||||||
|
};
|
||||||
|
|
||||||
|
// ==================== 窗口大小变化处理 ====================
|
||||||
|
const handleResize = () => {
|
||||||
|
chartInstance?.resize();
|
||||||
|
};
|
||||||
|
|
||||||
|
// ==================== 生命周期钩子 ====================
|
||||||
|
onMounted(async () => {
|
||||||
|
await nextTick();
|
||||||
|
// 不再立即初始化图表,等待数据加载完成
|
||||||
|
|
||||||
|
window.addEventListener('resize', handleResize);
|
||||||
|
});
|
||||||
|
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
window.removeEventListener('resize', handleResize);
|
||||||
|
|
||||||
|
if (chartInstance) {
|
||||||
|
chartInstance.off('legendselectchanged', handleLegendSelectChanged);
|
||||||
|
chartInstance.off('click', handleDataPointClick);
|
||||||
|
chartInstance.dispose();
|
||||||
|
chartInstance = null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
//根据流域获取河段
|
//根据流域获取河段
|
||||||
const getSelect = async () => {
|
const getSelect = async () => {
|
||||||
if (!wbsCode.value) {
|
if (!wbsCode.value) {
|
||||||
@ -847,15 +1155,29 @@ const getSelect = async () => {
|
|||||||
console.error('获取河段数据失败:', error);
|
console.error('获取河段数据失败:', error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//监听子组件的数据变化
|
//监听子组件的数据变化
|
||||||
const handlePanelChange1 = async (data) => {
|
const handlePanelChange1 = async (data) => {
|
||||||
console.log('当前所有控件状态:', data);
|
console.log('当前所有控件状态:', data);
|
||||||
// 当选择器或日期变化时,重新加载图表数据
|
// 当选择器或日期变化时,重新加载图表数据
|
||||||
if (data.datetime || data.select) {
|
if (data.datetime || data.select) {
|
||||||
|
select.value.value = data.select
|
||||||
|
datetimePicker.value.value = data.datetime
|
||||||
getecharts()
|
getecharts()
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 监听 datetimePicker 变化,更新 iconmap 中的时间显示
|
||||||
|
watch(
|
||||||
|
() => datetimePicker.value.value,
|
||||||
|
(newVal) => {
|
||||||
|
if (newVal) {
|
||||||
|
iconmap.value.value = `注:最新数据时间为${newVal}`;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ immediate: true }
|
||||||
|
);
|
||||||
|
|
||||||
const wbsCode = ref('');
|
const wbsCode = ref('');
|
||||||
watch(
|
watch(
|
||||||
() => JidiSelectEventStore.selectedItem,
|
() => JidiSelectEventStore.selectedItem,
|
||||||
@ -867,6 +1189,19 @@ watch(
|
|||||||
{ deep: true, immediate: true }
|
{ deep: true, immediate: true }
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// 监听可见系列数量变化,重新设置图表配置以确保 tooltip formatter 使用最新的 visibleSeriesQueue 值
|
||||||
|
watch(
|
||||||
|
() => visibleSeriesQueue.value.length,
|
||||||
|
(newLength, oldLength) => {
|
||||||
|
// 只有当长度真正变化时才重新设置
|
||||||
|
if (newLength !== oldLength && chartInstance && indicatorConfig.value.length > 0) {
|
||||||
|
// 重新生成 option(这会创建新的 formatter 闭包,捕获最新的 visibleSeriesQueue)
|
||||||
|
const newOption = getChartOption();
|
||||||
|
chartInstance.setOption(newOption, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
399
frontend/src/modules/waterQuality/padig.ts
Normal file
399
frontend/src/modules/waterQuality/padig.ts
Normal file
@ -0,0 +1,399 @@
|
|||||||
|
export interface WaterQualityIndicator {
|
||||||
|
name: string;
|
||||||
|
key: string;
|
||||||
|
pkey: string;
|
||||||
|
fixed: number;
|
||||||
|
sort: number;
|
||||||
|
group: string;
|
||||||
|
groupSort: string;
|
||||||
|
description: string;
|
||||||
|
children: any[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export const WATER_QUALITY_INDICATORS: WaterQualityIndicator[] = [
|
||||||
|
{
|
||||||
|
"name": "水温",
|
||||||
|
"key": "wtmp",
|
||||||
|
"pkey": "",
|
||||||
|
"fixed": 0,
|
||||||
|
"sort": 0,
|
||||||
|
"group": "",
|
||||||
|
"groupSort": "",
|
||||||
|
"description": "℃",
|
||||||
|
"children": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "PH",
|
||||||
|
"key": "ph",
|
||||||
|
"pkey": "",
|
||||||
|
"fixed": 0,
|
||||||
|
"sort": 1,
|
||||||
|
"group": "",
|
||||||
|
"groupSort": "",
|
||||||
|
"description": "",
|
||||||
|
"children": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "溶解氧",
|
||||||
|
"key": "dox",
|
||||||
|
"pkey": "",
|
||||||
|
"fixed": 0,
|
||||||
|
"sort": 2,
|
||||||
|
"group": "",
|
||||||
|
"groupSort": "",
|
||||||
|
"description": "mg/L",
|
||||||
|
"children": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "高锰酸盐指数",
|
||||||
|
"key": "codmn",
|
||||||
|
"pkey": "",
|
||||||
|
"fixed": 0,
|
||||||
|
"sort": 3,
|
||||||
|
"group": "",
|
||||||
|
"groupSort": "",
|
||||||
|
"description": "",
|
||||||
|
"children": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "化学需氧量",
|
||||||
|
"key": "codcr",
|
||||||
|
"pkey": "",
|
||||||
|
"fixed": 0,
|
||||||
|
"sort": 4,
|
||||||
|
"group": "",
|
||||||
|
"groupSort": "",
|
||||||
|
"description": "",
|
||||||
|
"children": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "五日生化需氧量BODS",
|
||||||
|
"key": "bod5",
|
||||||
|
"pkey": "",
|
||||||
|
"fixed": 0,
|
||||||
|
"sort": 5,
|
||||||
|
"group": "",
|
||||||
|
"groupSort": "",
|
||||||
|
"description": "",
|
||||||
|
"children": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "氨氮",
|
||||||
|
"key": "nh3n",
|
||||||
|
"pkey": "",
|
||||||
|
"fixed": 0,
|
||||||
|
"sort": 6,
|
||||||
|
"group": "",
|
||||||
|
"groupSort": "",
|
||||||
|
"description": "",
|
||||||
|
"children": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "总磷",
|
||||||
|
"key": "tp",
|
||||||
|
"pkey": "",
|
||||||
|
"fixed": 0,
|
||||||
|
"sort": 7,
|
||||||
|
"group": "",
|
||||||
|
"groupSort": "",
|
||||||
|
"description": "",
|
||||||
|
"children": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "总氮",
|
||||||
|
"key": "tn",
|
||||||
|
"pkey": "",
|
||||||
|
"fixed": 0,
|
||||||
|
"sort": 8,
|
||||||
|
"group": "",
|
||||||
|
"groupSort": "",
|
||||||
|
"description": "",
|
||||||
|
"children": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "铜",
|
||||||
|
"key": "cu",
|
||||||
|
"pkey": "",
|
||||||
|
"fixed": 0,
|
||||||
|
"sort": 9,
|
||||||
|
"group": "",
|
||||||
|
"groupSort": "",
|
||||||
|
"description": "",
|
||||||
|
"children": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "锌",
|
||||||
|
"key": "zn",
|
||||||
|
"pkey": "",
|
||||||
|
"fixed": 0,
|
||||||
|
"sort": 10,
|
||||||
|
"group": "",
|
||||||
|
"groupSort": "",
|
||||||
|
"description": "",
|
||||||
|
"children": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "氟化物",
|
||||||
|
"key": "f",
|
||||||
|
"pkey": "",
|
||||||
|
"fixed": 0,
|
||||||
|
"sort": 11,
|
||||||
|
"group": "",
|
||||||
|
"groupSort": "",
|
||||||
|
"description": "",
|
||||||
|
"children": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "硒",
|
||||||
|
"key": "se",
|
||||||
|
"pkey": "",
|
||||||
|
"fixed": 0,
|
||||||
|
"sort": 12,
|
||||||
|
"group": "",
|
||||||
|
"groupSort": "",
|
||||||
|
"description": "",
|
||||||
|
"children": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "砷",
|
||||||
|
"key": "ars",
|
||||||
|
"pkey": "",
|
||||||
|
"fixed": 0,
|
||||||
|
"sort": 13,
|
||||||
|
"group": "",
|
||||||
|
"groupSort": "",
|
||||||
|
"description": "",
|
||||||
|
"children": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "汞",
|
||||||
|
"key": "hg",
|
||||||
|
"pkey": "",
|
||||||
|
"fixed": 0,
|
||||||
|
"sort": 14,
|
||||||
|
"group": "",
|
||||||
|
"groupSort": "",
|
||||||
|
"description": "",
|
||||||
|
"children": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "镉",
|
||||||
|
"key": "cd",
|
||||||
|
"pkey": "",
|
||||||
|
"fixed": 0,
|
||||||
|
"sort": 15,
|
||||||
|
"group": "",
|
||||||
|
"groupSort": "",
|
||||||
|
"description": "",
|
||||||
|
"children": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "铬(六价)",
|
||||||
|
"key": "cr6",
|
||||||
|
"pkey": "",
|
||||||
|
"fixed": 0,
|
||||||
|
"sort": 16,
|
||||||
|
"group": "",
|
||||||
|
"groupSort": "",
|
||||||
|
"description": "",
|
||||||
|
"children": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "铅",
|
||||||
|
"key": "pb",
|
||||||
|
"pkey": "",
|
||||||
|
"fixed": 0,
|
||||||
|
"sort": 17,
|
||||||
|
"group": "",
|
||||||
|
"groupSort": "",
|
||||||
|
"description": "",
|
||||||
|
"children": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "氰化物",
|
||||||
|
"key": "cn",
|
||||||
|
"pkey": "",
|
||||||
|
"fixed": 0,
|
||||||
|
"sort": 18,
|
||||||
|
"group": "",
|
||||||
|
"groupSort": "",
|
||||||
|
"description": "",
|
||||||
|
"children": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "挥发酚",
|
||||||
|
"key": "vlph",
|
||||||
|
"pkey": "",
|
||||||
|
"fixed": 0,
|
||||||
|
"sort": 19,
|
||||||
|
"group": "",
|
||||||
|
"groupSort": "",
|
||||||
|
"description": "",
|
||||||
|
"children": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "石油类",
|
||||||
|
"key": "oil",
|
||||||
|
"pkey": "",
|
||||||
|
"fixed": 0,
|
||||||
|
"sort": 20,
|
||||||
|
"group": "",
|
||||||
|
"groupSort": "",
|
||||||
|
"description": "",
|
||||||
|
"children": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "阴离子表面活性剂",
|
||||||
|
"key": "las",
|
||||||
|
"pkey": "",
|
||||||
|
"fixed": 0,
|
||||||
|
"sort": 21,
|
||||||
|
"group": "",
|
||||||
|
"groupSort": "",
|
||||||
|
"description": "",
|
||||||
|
"children": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "硫化物",
|
||||||
|
"key": "s2",
|
||||||
|
"pkey": "",
|
||||||
|
"fixed": 0,
|
||||||
|
"sort": 22,
|
||||||
|
"group": "",
|
||||||
|
"groupSort": "",
|
||||||
|
"description": "",
|
||||||
|
"children": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "粪大肠菌群",
|
||||||
|
"key": "fcg",
|
||||||
|
"pkey": "",
|
||||||
|
"fixed": 0,
|
||||||
|
"sort": 23,
|
||||||
|
"group": "",
|
||||||
|
"groupSort": "",
|
||||||
|
"description": "",
|
||||||
|
"children": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "氯化物",
|
||||||
|
"key": "cl",
|
||||||
|
"pkey": "",
|
||||||
|
"fixed": 0,
|
||||||
|
"sort": 24,
|
||||||
|
"group": "",
|
||||||
|
"groupSort": "",
|
||||||
|
"description": "",
|
||||||
|
"children": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "硫酸盐",
|
||||||
|
"key": "so4",
|
||||||
|
"pkey": "",
|
||||||
|
"fixed": 0,
|
||||||
|
"sort": 25,
|
||||||
|
"group": "",
|
||||||
|
"groupSort": "",
|
||||||
|
"description": "",
|
||||||
|
"children": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "硝酸盐氮",
|
||||||
|
"key": "no3",
|
||||||
|
"pkey": "",
|
||||||
|
"fixed": 0,
|
||||||
|
"sort": 26,
|
||||||
|
"group": "",
|
||||||
|
"groupSort": "",
|
||||||
|
"description": "",
|
||||||
|
"children": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "总硬度",
|
||||||
|
"key": "thrd",
|
||||||
|
"pkey": "",
|
||||||
|
"fixed": 0,
|
||||||
|
"sort": 27,
|
||||||
|
"group": "",
|
||||||
|
"groupSort": "",
|
||||||
|
"description": "",
|
||||||
|
"children": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "电导率",
|
||||||
|
"key": "cond",
|
||||||
|
"pkey": "",
|
||||||
|
"fixed": 0,
|
||||||
|
"sort": 28,
|
||||||
|
"group": "",
|
||||||
|
"groupSort": "",
|
||||||
|
"description": "",
|
||||||
|
"children": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "铁",
|
||||||
|
"key": "fe",
|
||||||
|
"pkey": "",
|
||||||
|
"fixed": 0,
|
||||||
|
"sort": 29,
|
||||||
|
"group": "",
|
||||||
|
"groupSort": "",
|
||||||
|
"description": "",
|
||||||
|
"children": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "锰",
|
||||||
|
"key": "mn",
|
||||||
|
"pkey": "",
|
||||||
|
"fixed": 0,
|
||||||
|
"sort": 30,
|
||||||
|
"group": "",
|
||||||
|
"groupSort": "",
|
||||||
|
"description": "",
|
||||||
|
"children": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "铝",
|
||||||
|
"key": "al",
|
||||||
|
"pkey": "",
|
||||||
|
"fixed": 0,
|
||||||
|
"sort": 31,
|
||||||
|
"group": "",
|
||||||
|
"groupSort": "",
|
||||||
|
"description": "",
|
||||||
|
"children": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "叶绿素a",
|
||||||
|
"key": "chla",
|
||||||
|
"pkey": "",
|
||||||
|
"fixed": 0,
|
||||||
|
"sort": 32,
|
||||||
|
"group": "",
|
||||||
|
"groupSort": "",
|
||||||
|
"description": "",
|
||||||
|
"children": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "透明度",
|
||||||
|
"key": "clarity",
|
||||||
|
"pkey": "",
|
||||||
|
"fixed": 0,
|
||||||
|
"sort": 33,
|
||||||
|
"group": "",
|
||||||
|
"groupSort": "",
|
||||||
|
"description": "",
|
||||||
|
"children": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "浊度",
|
||||||
|
"key": "tu",
|
||||||
|
"pkey": "",
|
||||||
|
"fixed": 0,
|
||||||
|
"sort": 34,
|
||||||
|
"group": "",
|
||||||
|
"groupSort": "",
|
||||||
|
"description": "",
|
||||||
|
"children": []
|
||||||
|
}
|
||||||
|
];
|
||||||
Loading…
Reference in New Issue
Block a user