栖息地线上接口

This commit is contained in:
王兴凯 2026-06-03 17:17:51 +08:00
commit bbfea93b20
14 changed files with 1622 additions and 241 deletions

View File

@ -0,0 +1,41 @@
import request from '@/utils/request';
//栖息地保护工作开展情况
export function fhGetKendoListCust(data: any) {
return request({
url: '/api/wmp-env-server/env/fh/GetKendoListCust',
method: 'post',
data
});
}
//获取所属栖息地
export function msstbprptGetKendoList(data: any) {
return request({
url: '/api/dec-lygk-base-server/base/msstbprpt/GetKendoList',
method: 'post',
data
});
}
//水温监测数据
export function sdrvwtsGetKendoList(data: any) {
return request({
url: '/api/wmp-env-server/base/sdrvwts/GetKendoList',
method: 'post',
data
});
}
//水文监测
export function sdriverdaysGetKendoList(data: any) {
return request({
url: '/api/wmp-swqx-server/zq/sdriverdays/GetKendoList',
method: 'post',
data
});
}
//主要栖息地基本信息
export function fhGetKendoListCustom(data: any) {
return request({
url: '/api/wmp-env-server/env/fh/GetKendoListCustom',
method: 'post',
data
});
}

View File

@ -1143,7 +1143,7 @@ const FhPointColumns: Array<any> = [
}, },
{ {
name: '所属流域', name: '所属流域',
filed: 'baseName', filed: 'hbrvcdName',
visible: true, visible: true,
type: 'select', type: 'select',
url: '', url: '',
@ -1179,7 +1179,7 @@ const FhPointColumns: Array<any> = [
url: '', url: '',
}, },
{ {
name: '保护外围长度', name: '保护外围长度(km)',
filed: 'gsmqcd', filed: 'gsmqcd',
visible: true, visible: true,
type: 'number', type: 'number',
@ -1210,14 +1210,14 @@ const FhPointColumns: Array<any> = [
}, },
{ {
name: '保护措施', name: '保护措施',
filed: 'prottyp', filed: 'bhcs',
visible: true, visible: true,
type: 'select', type: 'select',
url: '', url: '',
}, },
{ {
name: '保护方式', name: '保护方式',
filed: 'protmthd', filed: 'bhfs',
visible: true, visible: true,
type: 'select', type: 'select',
url: '', url: '',
@ -1228,6 +1228,7 @@ const FhPointColumns: Array<any> = [
visible: true, visible: true,
type: 'number', type: 'number',
url: '', url: '',
toFixed: 3,
}, },
@ -1257,7 +1258,7 @@ const FhZQPointColumns: Array<any> = [
}, },
{ {
name: '所属流域', name: '所属流域',
filed: 'baseName', filed: 'hbrvcdName',
visible: true, visible: true,
type: 'select', type: 'select',
url: '', url: '',
@ -1327,7 +1328,7 @@ const FPPointColumns: Array<any> = [
}, },
{ {
name: '所属流域', name: '所属流域',
filed: 'baseName', filed: 'hbrvcdName',
visible: true, visible: true,
type: 'select', type: 'select',
url: '', url: '',
@ -1500,7 +1501,7 @@ const FBPointColumns: Array<any> = [
}, },
{ {
name: '所属流域', name: '所属流域',
filed: 'baseName', filed: 'hbrvcdName',
visible: true, visible: true,
type: 'select', type: 'select',
url: '', url: '',
@ -1667,7 +1668,7 @@ const VaPointColumns: Array<any> = [
}, },
{ {
name: '所属流域', name: '所属流域',
filed: 'baseName', filed: 'hbrvcdName',
visible: true, visible: true,
type: 'select', type: 'select',
url: '', url: '',
@ -1728,7 +1729,7 @@ const VaPointColumns: Array<any> = [
}, },
{ {
name: '保护方式', name: '保护方式',
filed: 'protmthd', filed: 'bhfs',
visible: true, visible: true,
type: 'input', type: 'input',
url: '', url: '',
@ -1836,7 +1837,7 @@ const VPPointColumns: Array<any> = [
}, },
{ {
name: '保护方式', name: '保护方式',
filed: 'protmthd', filed: 'bhfs',
visible: true, visible: true,
type: 'input', type: 'input',
url: '', url: '',

View File

@ -490,7 +490,7 @@ const FHTabs: Array<any> = [
name: '基础信息', name: '基础信息',
key: 'basicInfo', key: 'basicInfo',
type: 'basic', type: 'basic',
url: '/bbi/siteBipc/getSiteBasicInfo', url: '/api/dec-lygk-base-server/base/msstbprpt/getStcdInfo',
default: true // 默认显示 default: true // 默认显示
}, },
{ {
@ -591,31 +591,31 @@ const FHTabs: Array<any> = [
name: '流量监测', name: '流量监测',
key: 'FlowMeasure', key: 'FlowMeasure',
type: 'FlowMeasure', type: 'FlowMeasure',
tabs: [ // tabs: [
{ // {
url: '/wmp-eng-server/eng/river/GetKendoListCust', // url: '/wmp-eng-server/eng/river/GetKendoListCust',
name: '流量', // name: '流量',
type: 'monitor', // type: 'monitor',
tableUrl: '/wmp-eng-server/eng/river/GetKendoListCust', // tableUrl: '/wmp-eng-server/eng/river/GetKendoListCust',
chartType: 'line', // chartType: 'line',
// filter: NormalStcdFilter, // // filter: NormalStcdFilter,
filterProps: { // filterProps: {
params: { // params: {
ftype: 'WE', // ftype: 'WE',
sttp: 'ZQ', // sttp: 'ZQ',
dataDimensionType: 'hyBase', // dataDimensionType: 'hyBase',
dataDimensionVal: 'all', // dataDimensionVal: 'all',
maptype: '2' // maptype: '2'
} // }
} // }
} // }
] // ]
}, },
{ {
name: '实时视频', name: '实时视频',
key: 'videoInfo', key: 'videoInfo',
type: 'video', type: 'video',
url: '/video/dataStcdFrame/getVideoMonitorList' url: ''
} }
// { // {
// name: "全景影像", // name: "全景影像",
@ -715,7 +715,7 @@ const FLOWTabs: Array<any> = [
name: '基础信息', name: '基础信息',
key: 'basicInfo', key: 'basicInfo',
type: 'basic', type: 'basic',
url: '/bbi/siteBipc/getSiteBasicInfo', url: '/api/dec-lygk-base-server/base/msstbprpt/getStcdInfo',
default: true // 默认显示 default: true // 默认显示
}, },
{ {
@ -1193,7 +1193,6 @@ const WATabs: Array<any> = [
] ]
// //mway : 1-是人工 2-是自动 dtinType: 0-自建 1-国家 2-人工 // //mway : 1-是人工 2-是自动 dtinType: 0-自建 1-国家 2-人工
const handleTabs = (modaldata: any) => { const handleTabs = (modaldata: any) => {
console.log('modaldata', modaldata);
if (!modaldata?.sttp) return; if (!modaldata?.sttp) return;
const sttp = modaldata?.sttp ? modaldata?.sttp.toUpperCase() : ''; const sttp = modaldata?.sttp ? modaldata?.sttp.toUpperCase() : '';
console.log('sttp', sttp); console.log('sttp', sttp);
@ -1205,7 +1204,12 @@ const handleTabs = (modaldata: any) => {
case 'FH_WQ_POINT': case 'FH_WQ_POINT':
return WQFBTabs; return WQFBTabs;
case 'FH_POINT': case 'FH_POINT':
if (modaldata.show) {
return FHTabs; return FHTabs;
}else{
let FHTabsTwo = FHTabs.slice(0, 1);
return FHTabsTwo;
}
case 'FH_ZQ_POINT': case 'FH_ZQ_POINT':
return FLOWTabs; return FLOWTabs;
case 'FP_POINT': case 'FP_POINT':

View File

@ -17,9 +17,9 @@
<a-form-item v-if="typeOne == 'all'" label="测站类型" name="dtinType"> <a-form-item v-if="typeOne == 'all'" label="测站类型" name="dtinType">
<a-select v-model:value="formState.dtinType" placeholder="请选择" style="width: 120px" show-search <a-select v-model:value="formState.dtinType" placeholder="请选择" style="width: 120px" show-search
:filter-option="filterOption" option-filter-prop="label"> :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>
</a-select> </a-select>
</a-form-item> </a-form-item>
@ -92,15 +92,12 @@
<!-- 详情弹窗 --> <!-- 详情弹窗 -->
<a-modal v-model:open="detailModalVisible" :title="`${detailData.stnm || ''} 详细信息`" width="80%" :footer="null" <a-modal v-model:open="detailModalVisible" :title="`${detailData.stnm || ''} 详细信息`" width="80%" :footer="null"
@cancel="handleDetailModalClose"> @cancel="handleDetailModalClose">
<HJMZDTwoLays <HJMZDTwoLays :tc="{
:tc="{
stcd: detailData.stcd || '', stcd: detailData.stcd || '',
startTime:detailData.tm , startTime: detailData.tm,
endTime: (typeof detailData.tm === 'string' ? detailData.tm.split(' ')[0] : dayjs(detailData.tm).format('YYYY-MM-DD')) + ' 23:59:59', endTime: (typeof detailData.tm === 'string' ? detailData.tm.split(' ')[0] : dayjs(detailData.tm).format('YYYY-MM-DD')) + ' 23:59:59',
sfdb: formState.sfdb || '' sfdb: formState.sfdb || ''
}" }" :calculateFlag="{ calculateFlag: 0 }" />
:calculateFlag="{ calculateFlag: 0 }"
/>
</a-modal> </a-modal>
</div> </div>
</template> </template>
@ -142,7 +139,7 @@ const typeOne = ref(props.typeOne || '')
const originalMonth = ref<string>('') // const originalMonth = ref<string>('') //
const formState = reactive({ const formState = reactive({
dataDimensionVal: props.dataDimensionVal || '', dataDimensionVal: props.dataDimensionVal || '',
dtinType: props.buildType || '', dtinType: props.buildType.toString() || '',
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(),
@ -400,7 +397,17 @@ const fetchPieData = async () => {
let data = res?.data?.data || res?.data || [] let data = res?.data?.data || res?.data || []
if (data.length === 0) { if (data.length === 0) {
//
if (pieChartInstance) {
pieChartInstance.dispose()
pieChartInstance = null
}
//
displayTotal.value = 0
pieCode.value = []
showPieChart.value = false showPieChart.value = false
return return
} }
@ -705,11 +712,11 @@ watch(
(newVal) => { (newVal) => {
// //
formState.dataDimensionVal = newVal || '' formState.dataDimensionVal = newVal || ''
formState.dtinType = '' formState.dtinType = props.buildType.toString() || ''
formState.sfdb = '' formState.sfdb = ''
formState.stnm = '' formState.stnm = ''
pieCode.value = [] pieCode.value = []
debugger
// //
nextTick(() => { nextTick(() => {
handleSearch() handleSearch()

View File

@ -456,7 +456,7 @@ const getEchartsData = async () => {
baseid.value == 'all' ? { baseid.value == 'all' ? {
"field": "dtinType", "field": "dtinType",
"operator": "eq", "operator": "eq",
"value": 0 "value":tabs.value
} : { } : {
"field": "rstcd", "field": "rstcd",
"operator": "isnotnull" "operator": "isnotnull"

View File

@ -1,8 +1,10 @@
<!-- SidePanelItem.vue --> <!-- SidePanelItem.vue -->
<template> <template>
<SidePanelItem title="栖息地保护工作开展情况"> <SidePanelItem title="栖息地保护工作开展情况">
<a-spin :spinning="loading" tip="加载中...">
<div class="facility-grid"> <div class="facility-grid">
<div v-for="facility in facilities" :key="facility.name" class="facility-card"> <div v-for="facility in facilities" :key="facility.name" class="facility-card"
@click="handleFacilityClick(facility)">
<div style="width: 60px;height: 62px;display: flex;align-items: center;justify-content: center;"> <div style="width: 60px;height: 62px;display: flex;align-items: center;justify-content: center;">
<div class="facility-icon"> <div class="facility-icon">
<i style="color: #fff;" :class="facility.icon" type="icon-shengtailiuliang2"></i> <i style="color: #fff;" :class="facility.icon" type="icon-shengtailiuliang2"></i>
@ -16,48 +18,120 @@
</div> </div>
</div> </div>
</div> </div>
</a-spin>
</SidePanelItem> </SidePanelItem>
<!-- 弹框 -->
<a-modal v-model:open="modalVisible" :title="'水质监测工作开展情况'" :footer="null" width="1536px" @cancel="handleCloseModal">
<QixidiheduanjianceqingkuangEJ v-if="currentFacility" :activeKey="currentFacility.sttpCode" :tabs="facilities"
:baseId="baseid" />
</a-modal>
</template> </template>
<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"; import { useJidiSelectEventStore } from "@/store/modules/jidiSelectEvent";
import { fhGetKendoListCust } from '@/api/qxd'
import QixidiheduanjianceqingkuangEJ from "./qixidiheduanjianceqingkuangEJ/index.vue"
// 便 // 便
defineOptions({ defineOptions({
name: 'qixidibaohugongzuokaizhanQK' name: 'qixidibaohugongzuokaizhanQK'
}); });
const JidiSelectEventStore = useJidiSelectEventStore(); const JidiSelectEventStore = useJidiSelectEventStore();
// loading
const loading = ref(false);
// //
const facilities = ref([ const facilities = ref([
{ {
name: '栖息地', name: '栖息地',
count: 56, count: 0,
icon: 'icon iconfont icon-qixidi' icon: 'icon iconfont icon-qixidi',
sttpCode: 'FH'
}, },
{ {
name: '水温监测', name: '水温监测',
count: 1722, count: 0,
icon: 'icon iconfont icon-diwenshuijianhuan' icon: 'icon iconfont icon-diwenshuijianhuan',
sttpCode: 'WTRV'
}, },
{ {
name: '水文监测', name: '水文监测',
count: 135, count: 0,
icon: 'icon iconfont icon-shuiwen-line' icon: 'icon iconfont icon-shuiwen-line',
sttpCode: 'ZQ'
}, },
{ {
name: '视频监控', name: '视频监控',
count: 135, count: 0,
icon: 'icon iconfont icon-shipinjiankongshebei' icon: 'icon iconfont icon-shipinjiankongshebei',
sttpCode: 'VD'
}, },
]); ]);
const baseid = ref('') const baseid = ref('')
//
const modalVisible = ref(false);
const currentFacility = ref<any>(null);
//
const getData = async () => {
loading.value = true;
try {
let params = {
"filter": {
"logic": "and",
"filters": [
baseid.value != 'all' ? {
"field": "baseId",
"operator": "eq",
"dataType": "string",
"value": baseid.value
} : null
].filter(Boolean)
},
"group": [
{
"dir": "asc",
"field": "sttpCode"
}
],
"groupResultFlat": true
}
const res = await fhGetKendoListCust(params); //-VD
let data = res?.data?.data || res?.data || [];
// facilities count
facilities.value.forEach(facility => {
const matchedItem = data.find(item => item.sttpCode === facility.sttpCode);
if (matchedItem) {
facility.count = matchedItem.count_sttpCode || 0;
} else {
facility.count = 0;
}
});
} catch (error) {
console.error('获取数据失败:', error);
} finally {
loading.value = false;
}
}
//
const handleFacilityClick = (facility: any) => {
currentFacility.value = facility;
modalVisible.value = true;
};
//
const handleCloseModal = () => {
modalVisible.value = false;
currentFacility.value = null;
};
watch( watch(
() => JidiSelectEventStore.selectedItem, () => JidiSelectEventStore.selectedItem,
(newVal) => { (newVal) => {
baseid.value = newVal.wbsCode; baseid.value = newVal.wbsCode;
getData()
}, },
{ deep: true, immediate: true } { deep: true, immediate: true }
); );

View File

@ -0,0 +1,424 @@
<template>
<div class="shuiwenjiancegongzwo-ej-container">
<!-- Tab切换 -->
<a-tabs v-model:activeKey="tabIndex" @change="handleTabChange">
<a-tab-pane v-for="tab in tabs" :key="tab.sttpCode" :tab="tab.name" />
</a-tabs>
<!-- 搜索表单 -->
<div class="search-form">
<a-space size="middle">
<div>所属基地:</div>
<a-select v-model:value="searchData.baseId" placeholder="请选择所属基地" style="width: 200px" show-search
:filter-option="filterOption" :options="jiDiList" @change="handleHydropowerStationChange" />
<div>所属栖息地:</div>
<a-select v-model:value="searchData.fhstcd" placeholder=" " style="width: 200px" allow-clear show-search
:filter-option="filterOption" :options="stationOptions" />
<a-input v-model:value="searchData.stnm" placeholder="请输入电站或设施名称" style="width: 200px" allow-clear />
<a-button type="primary" @click="handleSearch">查询</a-button>
<a-tooltip title="重置">
<a-button @click="handleReset">重置</a-button>
</a-tooltip>
</a-space>
</div>
<!-- 列表 -->
<BasicTable ref="tableRef" :scrollY="360" :columns="columns" :list-url="fhGetKendoListCust"
:search-params="{ sort: sortConfig }" :transform-data="customTransform">
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'ennm'">
<a @click="handleViewDetail(record, 'dz')" class="text-link">
{{ record.ennm }}
</a>
</template>
<template v-if="column.key === 'action'">
<a @click="handleViewDetail(record, 'qxd')" class="text-link">
查看详情
</a>
</template>
</template>
</BasicTable>
</div>
</template>
<script lang="ts" setup>
import { ref, computed, watch, onMounted } from 'vue'
import { msstbprptGetKendoList, fhGetKendoListCust } from '@/api/qxd'
import BasicTable from '@/components/BasicTable/index.vue'
import { useModelStore } from "@/store/modules/model";
// ==================== Props ====================
const props = defineProps<{
baseId?: string
tabs?: any[]
activeKey?: string
}>()
// ==================== ====================
const tabIndex = ref(props.activeKey || 'FH')
const searchData = ref<any>({
baseId: props.baseId || 'all',
fhstcd: 'all',
stnm: null
})
const modelStore = useModelStore();
const tableRef = ref()
const stationOptions: any = ref([])
// ==================== () ====================
const jiDiList: any = ref([
{
"value": "all",
"label": "当前全部",
},
{
"value": "01",
"label": "金沙江干流",
},
{
"value": "02",
"label": "雅砻江干流",
},
{
"value": "03",
"label": "大渡河干流",
},
{
"value": "04",
"label": "乌江干流",
},
{
"value": "05",
"label": "长江上游干流",
},
{
"value": "10",
"label": "湘西",
},
{
"value": "08",
"label": "黄河上游干流",
},
{
"value": "09",
"label": "黄河中游干流",
},
{
"value": "06",
"label": "南盘江-红水河",
},
{
"value": "12",
"label": "东北",
},
{
"value": "07",
"label": "澜沧江干流",
},
{
"value": "13",
"label": "怒江干流",
},
{
"value": "11",
"label": "闽浙赣",
},
{
"value": "other",
"label": "其他",
}
])
//
const columns = [
{
key: 'stnm',
title: '设施名称',
dataIndex: 'stnm',
width: '176px'
},
{
key: 'baseName',
title: '所在基地',
dataIndex: 'baseName',
width: '150px'
},
{
key: 'ennm',
title: '所属电站',
dataIndex: 'ennm',
width: '150px'
},
{
key: 'fhstnm',
title: '所属栖息地',
dataIndex: 'fhstnm',
width: '176px'
},
{
key: 'ststdt',
title: '开工日期',
dataIndex: 'ststdt',
width: '150px'
},
{
key: 'esstdt',
title: '建成日期',
dataIndex: 'esstdt',
width: '150px'
},
{
key: 'mway',
title: '监测方式',
dataIndex: 'mway',
width: '150px'
},
{
key: 'vlsr',
title: '数据接入来源',
dataIndex: 'vlsr',
width: '150px'
},
{
key: 'hydrodtin_type',
title: '数据接入类型',
dataIndex: 'hydrodtinType',
width: '150px'
},
{
key: 'enable',
title: '是否启用',
dataIndex: 'enable',
width: '150px'
},
{
key: 'dtin',
title: '是否接入',
dataIndex: 'dtin',
width: '150px'
},
{
title: '操作',
key: 'action',
width: 100,
align: 'center' as const,
fixed: 'right' as const
}
];
//
const sortConfig = computed(() => [
{
"field": "baseStepSort",
"dir": "asc"
},
{
"field": "rvcdStepSort",
"dir": "asc"
},
{
"field": "rstcdStepSort",
"dir": "asc"
}
])
const handleTabChange = (key: string) => {
console.log('【Tab切换】', key)
tabIndex.value = key
handleSearch()
}
const filterOption = (input: string, option: any) => {
// 使 options option { label, value }
return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
}
//
const handleHydropowerStationChange = (value: string) => {
console.log('【水电站选择变化】', value)
//
searchData.value.fhstcd = 'all'
getselectTwo()
}
//
const getselectTwo = async () => {
let params = {
"filter": {
"logic": "and",
"filters": [
{
"field": "sttpCode",
"operator": "eq",
"dataType": "string",
"value": tabIndex.value
},
searchData.value.baseId != 'all' ? {
"field": "baseId",
"operator": "eq",
"dataType": "string",
"value": searchData.value.baseId
} : null
].filter(Boolean)
},
"select": [
"stcd",
"stnm"
]
}
let res = await msstbprptGetKendoList(params)
console.log('【获取栖息地】', res)
stationOptions.value = []
if (res?.data?.data.length > 0) {
stationOptions.value = res.data.data.map((item: any) => {
return {
label: item.stnm,
value: item.stcd
}
})
}
stationOptions.value.unshift({
label: '当前全部',
value: 'all'
})
}
const handleSearch = () => {
console.log('【搜索】', searchData.value)
const filters: any[] = [
searchData.value.baseId != 'all' ? {
"field": "baseId",
"operator": "eq",
"value": "01"
} : null,
searchData.value.fhstcd != 'all' ? {
"field": "fhstcd",
"operator": "eq",
"value": searchData.value.fhstcd
} : null,
{
"field": "sttpCode",
"operator": "eq",
"value": tabIndex.value
},
searchData.value.stnm ? {
"logic": "or",
"filters": [
{
"field": "stnm",
"operator": "contains",
"value": searchData.value.stnm
},
{
"field": "ennm",
"operator": "contains",
"value": searchData.value.stnm
}
]
} : null
].filter(Boolean)
const filter = {
logic: 'and',
filters
}
//
tableRef.value?.getList(filter)
}
const handleReset = () => {
console.log('【重置】')
searchData.value = {
baseId: 'all',
fhstcd: 'all',
stnm: null
}
handleSearch()
}
//
const customTransform = (res: any) => {
return {
records: res?.data?.data || [],
total: res?.data?.total || 0
}
}
//
const handleViewDetail = (record: any, type: any) => {
console.log(tabIndex.value)
debugger
if (type == 'dz') {
modelStore.modalVisible = true;
modelStore.params.sttp = "ENG";
modelStore.title = record.ennm + " 详情信息";
modelStore.params.stcd = record.rstcd;
} else {
if (tabIndex.value == 'FH') {
modelStore.modalVisible = true;
modelStore.params.sttp = "fh_point";
modelStore.title = record.stnm + " 详情信息";
modelStore.params.stcd = record.stcd;
modelStore.params.show = false
} else if (tabIndex.value == 'WTRV') {
modelStore.modalVisible = true;
modelStore.params.sttp = "wt_point";
modelStore.title = record.stnm + " 详情信息";
modelStore.params.stcd = record.stcd;
} else if (tabIndex.value == 'ZQ') {
modelStore.modalVisible = true;
modelStore.params.sttp = "fh_zq_point";
modelStore.title = record.stnm + " 详情信息";
modelStore.params.stcd = record.stcd;
} else if (tabIndex.value == 'VD') {
// modelStore.modalVisible = true;
// modelStore.params.sttp = "fh_zq_point";
// modelStore.title = record.stnm + " ";
// modelStore.params.stcd = record.stcd;
}
}
}
// ==================== ====================
onMounted(() => {
getselectTwo()
handleSearch()
})
</script>
<style scoped lang="scss">
.shuiwenjiancegongzwo-ej-container {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
background-color: #ffffff;
box-sizing: border-box;
.search-form {
margin-bottom: 16px;
border-radius: 4px;
}
:deep(.ant-table-wrapper) {
flex: 1;
overflow: hidden;
}
}
.text-link {
color: #2f6b98;
&:hover {
color: #40a9ff;
}
}
</style>

View File

@ -1,9 +1,9 @@
<!-- SidePanelItem.vue --> <!-- SidePanelItem.vue -->
<template> <template>
<SidePanelItem title="主要栖息地基本信息" :select="select"> <SidePanelItem title="主要栖息地基本信息" :select="select" @update-values="handlePanelChange1">
<div class="table-container"> <div class="table-container">
<a-table :columns="columns" :data-source="tableData" :pagination="pagination" :scroll="{ y: 450 }" <a-table :columns="columns" :data-source="tableData" :pagination="pagination" :scroll="{ y: 420 }"
size="small"> :loading="loading" size="small" @change="handleTableChange">
<template #bodyCell="{ column, record }"> <template #bodyCell="{ column, record }">
<!-- 仅栖息地列需要自定义渲染 --> <!-- 仅栖息地列需要自定义渲染 -->
<template v-if="column.key === 'fhstnm'"> <template v-if="column.key === 'fhstnm'">
@ -26,22 +26,91 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref, onMounted, onUnmounted } from 'vue'; import { ref, onMounted, onUnmounted, watch } from 'vue';
import SidePanelItem from '@/components/SidePanelItem/index.vue'; import SidePanelItem from '@/components/SidePanelItem/index.vue';
import { useJidiSelectEventStore } from "@/store/modules/jidiSelectEvent";
import { fhGetKendoListCustom } from '@/api/qxd';
import { useModelStore } from "@/store/modules/model";
// 便 // 便
defineOptions({ defineOptions({
name: 'qixidijchuXx' name: 'qixidijchuXx'
}); });
// ==================== ====================
const JidiSelectEventStore = useJidiSelectEventStore();
// ==================== () ====================
const jiDiList: any = ref([
{
"value": "all",
"label": "当前全部",
},
{
"value": "01",
"label": "金沙江干流",
},
{
"value": "02",
"label": "雅砻江干流",
},
{
"value": "03",
"label": "大渡河干流",
},
{
"value": "04",
"label": "乌江干流",
},
{
"value": "05",
"label": "长江上游干流",
},
{
"value": "10",
"label": "湘西",
},
{
"value": "08",
"label": "黄河上游干流",
},
{
"value": "09",
"label": "黄河中游干流",
},
{
"value": "06",
"label": "南盘江-红水河",
},
{
"value": "12",
"label": "东北",
},
{
"value": "07",
"label": "澜沧江干流",
},
{
"value": "13",
"label": "怒江干流",
},
{
"value": "11",
"label": "闽浙赣",
},
{
"value": "other",
"label": "其他",
}
])
const select = ref({ const select = ref({
show: true, show: true,
value: undefined, value: 'all',
options: [], options: jiDiList.value,
picker: undefined, picker: undefined,
format: undefined format: undefined
}) })
const baseid = ref('');
//
const loading = ref(false);
const modelStore = useModelStore();
// //
const columns = [ const columns = [
{ {
@ -83,11 +152,16 @@ const columns = [
] ]
// //
const pagination = { const pagination = ref({
current: 1,
pageSize: 20, pageSize: 20,
total: 0,
size: 'small',
showSizeChanger: true,
showQuickJumper: true,
showTotal: (total: number) => `${total}`, showTotal: (total: number) => `${total}`,
showSizeChanger: false pageSizeOptions: ['10', '20', '50', '100']
} })
// //
const tableData = ref( const tableData = ref(
@ -259,12 +333,76 @@ const formatValue = (value: any): string => {
return value === null || value === undefined || value === '' ? '-' : String(value); return value === null || value === undefined || value === '' ? '-' : String(value);
} }
// // ()
const handleHabitatClick = (record: any) => { const handleHabitatClick = (record: any) => {
console.log('点击栖息地:', record) console.log('点击栖息地:', record)
// TODO: // TODO:
modelStore.modalVisible = true;
modelStore.params.sttp = "fh_point";
modelStore.title = record.stnm + " 详情信息";
modelStore.params.stcd = record.stcd;
modelStore.params.show = true;
} }
//
const handleTableChange = (pag: any, filters: any, sorter: any) => {
pagination.value.current = pag.current
pagination.value.pageSize = pag.pageSize
gettableData(baseid.value)
}
const gettableData = async (basid: any) => {
loading.value = true;
try {
let params = {
"take": pagination.value.pageSize,
"skip": (pagination.value.current - 1) * pagination.value.pageSize,
"filter": {
"logic": "and",
"filters": [
basid != 'all' ? {
"field": "baseId",
"operator": "eq",
"dataType": "string",
"value": basid
} : null,
].filter(Boolean),
}
}
let res = await fhGetKendoListCustom(params)
//
if (res?.data) {
tableData.value = res.data.data || []
pagination.value.total = res.data.total || 0
}
} finally {
loading.value = false;
}
}
watch(
() => JidiSelectEventStore.selectedItem,
(newVal) => {
baseid.value = newVal.wbsCode;
//
pagination.value.current = 1
gettableData(newVal.wbsCode)
if (newVal.wbsCode != 'all') {
select.value.show = false
} else {
select.value.show = true
}
},
{ deep: true, immediate: true }
);
const handlePanelChange1 = async (data) => {
if (data.select) {
//
pagination.value.current = 1
gettableData(data.select)
}
}
// //
onMounted(() => { onMounted(() => {
// //

View File

@ -1,24 +1,41 @@
<!-- SidePanelItem.vue --> <!-- SidePanelItem.vue -->
<template> <template>
<SidePanelItem title="水文监测" :select="select" :scopeDate="datetimePicker" > <SidePanelItem title="水文监测" :select="select" :scopeDate="datetimePicker" @update-values="handlePanelChange1">
<div class="chart-container"> <div class="chart-wrapper">
<a-spin :spinning="loading">
<!-- 始终渲染图表容器确保有固定宽高 -->
<div ref="chartRef" class="echarts-chart"></div> <div ref="chartRef" class="echarts-chart"></div>
<!-- 无数据时显示Empty但不影响容器尺寸 -->
<div v-if="!loading && !hasData" class="empty-overlay">
<a-empty description="暂无数据" />
</div>
</a-spin>
</div> </div>
</SidePanelItem> </SidePanelItem>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref, onMounted, onUnmounted, nextTick } from 'vue'; import { ref, onMounted, onUnmounted, nextTick, watch } from 'vue';
import * as echarts from 'echarts'; import * as echarts from 'echarts';
import type { EChartsOption } from 'echarts'; import type { EChartsOption } from 'echarts';
import SidePanelItem from '@/components/SidePanelItem/index.vue'; import SidePanelItem from '@/components/SidePanelItem/index.vue';
import { useJidiSelectEventStore } from "@/store/modules/jidiSelectEvent";
import { msstbprptGetKendoList, sdriverdaysGetKendoList } from '@/api/qxd';
import { useModelStore } from "@/store/modules/model";
// //
defineOptions({ defineOptions({
name: 'qixidiliuliangbianhua' name: 'qixidiliuliangbianhua'
}); });
// ==================== ==================== // ==================== ====================
const JidiSelectEventStore = useJidiSelectEventStore();
const baseid = ref('')
const modelStore = useModelStore();
// Loading
const loading = ref(false);
const hasData = ref(true); // true initChart
const select = ref({ const select = ref({
show: true, show: true,
value: undefined, value: undefined,
@ -27,7 +44,7 @@ const select = ref({
format: undefined format: undefined
}); });
// //
const datetimePicker = ref({ const datetimePicker: any = ref({
show: true, show: true,
value: (() => { value: (() => {
const now = new Date(); const now = new Date();
@ -45,21 +62,8 @@ const datetimePicker = ref({
const chartRef = ref<HTMLElement | null>(null); const chartRef = ref<HTMLElement | null>(null);
let chartInstance: echarts.ECharts | null = null; let chartInstance: echarts.ECharts | null = null;
// - 12 //
const echartsData = ref<any[]>([ const echartsData = ref<any[]>([]);
{ dt: '2024-01', wt: 8.5, q: 120.5, z: 45.2 },
{ dt: '2024-02', wt: 9.2, q: 135.8, z: 46.1 },
{ dt: '2024-03', wt: 12.8, q: 180.3, z: 47.5 },
{ dt: '2024-04', wt: 16.3, q: 220.6, z: 48.8 },
{ dt: '2024-05', wt: 20.5, q: 280.2, z: 50.3 },
{ dt: '2024-06', wt: 24.8, q: 350.5, z: 52.1 },
{ dt: '2024-07', wt: 28.3, q: 420.8, z: 53.6 },
{ dt: '2024-08', wt: 27.5, q: 390.3, z: 52.9 },
{ dt: '2024-09', wt: 23.2, q: 310.7, z: 51.2 },
{ dt: '2024-10', wt: 18.6, q: 240.5, z: 49.5 },
{ dt: '2024-11', wt: 13.4, q: 165.2, z: 47.8 },
{ dt: '2024-12', wt: 9.8, q: 130.6, z: 46.3 }
]);
// legendselectchanged // legendselectchanged
const echartOptionRef = ref<EChartsOption>({}); const echartOptionRef = ref<EChartsOption>({});
@ -93,7 +97,7 @@ const yAxisShowDynamic = (selected: Record<string, boolean>, options: any) => {
// Y // Y
if (showCount === 1) { if (showCount === 1) {
options.grid = omit(options.grid, 'right'); options.grid = omit(options.grid, 'right');
options.grid.left = '80px'; options.grid.left = '60px';
options.yAxis = options.yAxis.map((item: any) => { options.yAxis = options.yAxis.map((item: any) => {
if (item.show) { if (item.show) {
@ -122,7 +126,7 @@ const yAxisShowDynamic = (selected: Record<string, boolean>, options: any) => {
if (leftIndex === 0) { if (leftIndex === 0) {
leftIndex++; leftIndex++;
options.grid = omit(options.grid, 'right'); options.grid = omit(options.grid, 'right');
options.grid.left = '80px'; options.grid.left = '60px';
return { return {
...item, ...item,
@ -168,24 +172,27 @@ const initChart = () => {
chartInstance.on('legendselectchanged', (params: any) => { chartInstance.on('legendselectchanged', (params: any) => {
const { selected } = params; const { selected } = params;
// // Aformatter
const options = JSON.parse(JSON.stringify(echartOptionRef.value)); const currentOptions = echartOptionRef.value as any;
// const options: any = {
options.legend.selected = selected; ...currentOptions,
legend: {
// Y ...currentOptions.legend,
const newYAxis = options.yAxis.map((item: any) => { selected: selected
},
yAxis: Array.isArray(currentOptions.yAxis)
? currentOptions.yAxis.map((item: any) => {
let isShow = true; let isShow = true;
for (const key in selected) { for (const key in selected) {
if (key === item.name) { if (key === item.name) {
isShow = selected[key]; isShow = selected[key];
} }
} }
return { ...item, show: isShow }; return { ...item, show: isShow }; // formatter
}); })
: currentOptions.yAxis
options.yAxis = newYAxis; };
// //
yAxisShowDynamic(selected, options); yAxisShowDynamic(selected, options);
@ -197,12 +204,13 @@ const initChart = () => {
// //
chartInstance.on('click', (params: any) => { chartInstance.on('click', (params: any) => {
console.log('点击数据点:', { // series
date: params.name, if (params.componentType === 'series') {
seriesName: params.seriesName, modelStore.modalVisible = true;
value: params.value modelStore.params.sttp = "fh_zq_point";
}); modelStore.title = "水文监测";
// TODO: modelStore.params.stcd = select.value.value;
}
}); });
}; };
@ -210,10 +218,22 @@ const initChart = () => {
const updateChart = () => { const updateChart = () => {
if (!chartInstance) return; if (!chartInstance) return;
// // Y
const Color = ['#5470c6', '#91cc75', '#fac858']; const qValues = echartsData.value.map(item => item.q).filter(v => v != null && !isNaN(v));
const zValues = echartsData.value.map(item => item.z).filter(v => v != null && !isNaN(v));
const vValues = echartsData.value.map(item => item.v).filter(v => v != null && !isNaN(v));
const _legendData = ['水温(℃)', '流量(m³/s)', '水位(m)']; const minQ = qValues.length > 0 ? Math.min(...qValues) : 0;
const maxQ = qValues.length > 0 ? Math.max(...qValues) : 0;
const minZ = zValues.length > 0 ? Math.min(...zValues) : 0;
const maxZ = zValues.length > 0 ? Math.max(...zValues) : 0;
const minV = vValues.length > 0 ? Math.min(...vValues) : 0;
const maxV = vValues.length > 0 ? Math.max(...vValues) : 0;
// Y
const Color = ['#9556a4', '#56c2e3', '#78c300'];
const _legendData = ['流量(m³/s)', '水位(m)', '流速(m/s)'];
const setting: EChartsOption = { const setting: EChartsOption = {
// Tooltip // Tooltip
@ -231,17 +251,20 @@ const updateChart = () => {
const seriesName = item.seriesName ?? ''; const seriesName = item.seriesName ?? '';
const regx = /\(([^()]+?)\)/; const regx = /\(([^()]+?)\)/;
const unit = seriesName.match(regx); const unit = seriesName.match(regx);
const finalValue = item.value !== undefined && item.value !== null ? item.value : '-';
if (item.value !== undefined && item.value !== null) { if (item.value !== undefined && item.value !== null) {
res += `<span style="background: ${item.color}; height:10px; width: 10px; border-radius: 50%; display: inline-block; margin-right:10px;"></span>${item.seriesName} ${finalValue}${unit?.[1] || ''}<br/>`; //
const formattedValue = Number(item.value).toFixed(2);
res += `<span style="background: ${item.color}; height:10px; width: 10px; border-radius: 50%; display: inline-block; margin-right:10px;"></span>${item.seriesName} ${formattedValue}${unit?.[1] || ''}<br/>`;
} else {
res += `<span style="background: ${item.color}; height:10px; width: 10px; border-radius: 50%; display: inline-block; margin-right:10px;"></span>${item.seriesName} -${unit?.[1] || ''}<br/>`;
} }
}); });
return res; return res;
} }
}, },
// // ---绿
color: Color, color: Color,
// //
@ -253,8 +276,8 @@ const updateChart = () => {
itemGap: 15, itemGap: 15,
// "" // ""
selected: { selected: {
'水温(℃)': true, '流速(m/s)': false,
'流量(m³/s)': false, '流量(m³/s)': true,
'水位(m)': false '水位(m)': false
} }
}, },
@ -288,7 +311,17 @@ const updateChart = () => {
data: echartsData.value.map(item => item.dt), data: echartsData.value.map(item => item.dt),
boundaryGap: true, boundaryGap: true,
axisLabel: { axisLabel: {
show: true show: true,
// X5
interval: (index: number) => {
const total = echartsData.value.length;
if (total <= 5) return true; // 5
if (index === 0 || index === total - 1) return true; //
//
const step = Math.floor((total - 1) / 3); // 3
return index % step === 0;
}
}, },
axisLine: { axisLine: {
show: true show: true
@ -308,14 +341,17 @@ const updateChart = () => {
} }
}, },
// Y - Y // Y - Y
yAxis: [ yAxis: [
{ {
type: 'value', type: 'value',
name: '水温(℃)', name: '流量(m³/s)',
position: 'left', position: 'left',
offset: 0, offset: 0,
show: true, show: true,
// floor(min) ~ ceil(max)+5
min: qValues.length > 0 ? Math.floor(minQ) : undefined,
max: qValues.length > 0 ? Math.ceil(maxQ) : undefined,
splitLine: { splitLine: {
show: true, show: true,
lineStyle: { lineStyle: {
@ -329,7 +365,7 @@ const updateChart = () => {
axisLabel: { axisLabel: {
show: true, show: true,
formatter: '{value}', formatter: '{value}',
color: '#5470c6' color: '#9556a4'
}, },
axisLine: { axisLine: {
show: true, show: true,
@ -339,51 +375,28 @@ const updateChart = () => {
} }
}, },
nameTextStyle: { nameTextStyle: {
color: '#5470c6', color: '#9556a4',
fontSize: 12
}
},
{
type: 'value',
name: '流量(m³/s)',
position: 'left',
offset: 60,
show: false,
splitLine: { show: false },
axisTick: {
show: true
},
axisLabel: {
show: true,
formatter: '{value}',
color: '#91cc75'
},
axisLine: {
show: true,
lineStyle: {
color: '#6e7079',
width: 2
}
},
nameTextStyle: {
color: '#91cc75',
fontSize: 12 fontSize: 12
} }
}, },
{ {
type: 'value', type: 'value',
name: '水位(m)', name: '水位(m)',
position: 'right', position: 'left',
offset: 0, offset: 60,
show: false, show: false,
// min-0.01 ~ max+0.04
min: zValues.length > 0 ? minZ : undefined,
max: zValues.length > 0 ? maxZ : undefined,
splitLine: { show: false }, splitLine: { show: false },
axisTick: { axisTick: {
show: true show: true
}, },
axisLabel: { axisLabel: {
show: true, show: true,
formatter: '{value}', // Y使
color: '#fac858' formatter: (value: any) => value.toFixed(2),
color: '#56c2e3'
}, },
axisLine: { axisLine: {
show: true, show: true,
@ -393,62 +406,94 @@ const updateChart = () => {
} }
}, },
nameTextStyle: { nameTextStyle: {
color: '#fac858', color: '#56c2e3',
fontSize: 12
}
},
{
type: 'value',
name: '流速(m/s)',
position: 'right',
offset: 0,
show: false,
// min-0.2 ~ max+0.5
min: vValues.length > 0 ? minV - 0.02 : undefined,
max: vValues.length > 0 ? maxV + 0.02 : undefined,
splitLine: { show: false },
axisTick: {
show: true
},
axisLabel: {
show: true,
// Y
formatter: (value: any) => value.toFixed(3),
color: '#78c300'
},
axisLine: {
show: true,
lineStyle: {
color: '#6e7079',
width: 2
}
},
nameTextStyle: {
color: '#78c300',
fontSize: 12 fontSize: 12
} }
} }
], ],
// //
series: [ series: [
{
name: '水温(℃)',
data: echartsData.value.map(item => item.wt),
type: 'line',
smooth: true,
connectNulls: true,
symbolSize: 4,
symbol: 'circle',
itemStyle: {
color: '#5470c6'
},
lineStyle: {
width: 2.5
},
yAxisIndex: 0
},
{ {
name: '流量(m³/s)', name: '流量(m³/s)',
data: echartsData.value.map(item => item.q), data: echartsData.value.map(item => item.q),
type: 'line', type: 'line',
smooth: true, smooth: true,
connectNulls: true, // connectNulls: true,
symbolSize: 4, symbolSize: 3,
symbol: 'circle', symbol: 'circle',
itemStyle: { itemStyle: {
color: '#91cc75' color: '#9556a4'
}, },
lineStyle: { lineStyle: {
width: 2.5 width: 2
}, },
yAxisIndex: 1 yAxisIndex: 0
}, },
{ {
name: '水位(m)', name: '水位(m)',
data: echartsData.value.map(item => item.z), data: echartsData.value.map(item => item.z),
type: 'line', type: 'line',
smooth: true, smooth: true,
connectNulls: true, // connectNulls: true,
symbolSize: 4, symbolSize: 3,
symbol: 'circle', symbol: 'circle',
itemStyle: { itemStyle: {
color: '#fac858' color: '#56c2e3'
}, },
lineStyle: { lineStyle: {
width: 2.5 width: 2
},
yAxisIndex: 1
},
{
name: '流速(m/s)',
data: echartsData.value.map(item => item.v),
type: 'line',
smooth: true,
// connectNulls: true,
symbolSize: 3,
symbol: 'circle',
itemStyle: {
color: '#78c300'
},
lineStyle: {
width: 2
}, },
yAxisIndex: 2 yAxisIndex: 2
} },
] ]
}; };
@ -458,11 +503,11 @@ const updateChart = () => {
// //
chartInstance.setOption(setting, true); chartInstance.setOption(setting, true);
// Y // Y
yAxisShowDynamic({ yAxisShowDynamic({
'水温(℃)': true, '流量(m³/s)': true,
'流量(m³/s)': false, '水位(m)': false,
'水位(m)': false '流速(m/s)': false
}, setting); }, setting);
// //
@ -486,18 +531,283 @@ onUnmounted(() => {
chartInstance = null; chartInstance = null;
} }
}); });
//
const getselsectData = async () => {
let params = {
"filter": {
"logic": "and",
"filters": [
baseid.value != 'all' ? {
"field": "baseId",
"operator": "eq",
"dataType": "string",
"value": baseid.value
} : null,
{
"field": "fhstcd",
"operator": "isnotnull"
},
{
"logic": "or",
"filters": [
{
"field": "sttpCode",
"operator": "eq",
"dataType": "string",
"value": "ZQ"
}
]
}
].filter(Boolean)
},
"select": [
"stcd",
"stnm",
"fhstcd",
"fhstnm",
"sttpCode"
]
}
let res = await msstbprptGetKendoList(params)
console.log(res)
if (!res?.data?.data) {
select.value.value = ''
select.value.options = []
return
}
select.value.value = res.data.data[0].stcd
select.value.options = res.data.data.map((item: any) => {
return {
label: item.stnm,
value: item.stcd
}
})
// debugger
};
const convertDateRange = (dateRange: string[]) => {
if (!dateRange || dateRange.length !== 2) {
return null;
}
// debugger
const [startStr, endStr] = dateRange;
//
const today = new Date();
const currentDay = today.getDate();
// YY-MM
const [startYearStr, startMonthStr] = startStr.split('-');
const startYear = parseInt(startYearStr);
const startMonth = parseInt(startMonthStr);
const fullStartYear = startYear < 100 ? 2000 + startYear : startYear;
// YY-MM
const [endYearStr, endMonthStr] = endStr.split('-');
const endYear = parseInt(endYearStr);
const endMonth = parseInt(endMonthStr);
const fullEndYear = endYear < 100 ? 2000 + endYear : endYear;
// + + 00:00:00
// 31228
const startMonthLastDay = new Date(fullStartYear, startMonth, 0).getDate();
const actualStartDay = Math.min(currentDay, startMonthLastDay);
const startDate = `${fullStartYear}-${String(startMonth).padStart(2, '0')}-${String(actualStartDay).padStart(2, '0')} 00:00:00`;
// + + 23:59:59
//
const endMonthLastDay = new Date(fullEndYear, endMonth, 0).getDate();
const actualEndDay = Math.min(currentDay, endMonthLastDay);
const endDate = `${fullEndYear}-${String(endMonth).padStart(2, '0')}-${String(actualEndDay).padStart(2, '0')} 23:59:59`;
return {
startDate,
endDate
};
};
/**
* 加载图表数据
* @param startDate 开始日期 (格式: YYYY-MM-DD HH:mm:ss)
* @param endDate 结束日期 (格式: YYYY-MM-DD HH:mm:ss)
* @param stcd 测站代码
*/
const getChartData = async (startDate: string, endDate: string, stcd: string) => {
try {
console.log('=== 开始加载图表数据 ===');
console.log('请求参数:', { startDate, endDate, stcd });
// loading
loading.value = true;
hasData.value = false;
const params = {
"filter": {
"logic": "and",
"filters": [
{
"field": "DT",
"operator": "gte",
"value": startDate
},
{
"field": "DT",
"operator": "lte",
"value": endDate
},
{
"field": "stcd",
"operator": "eq",
"dataType": "string",
"value": stcd
}
]
},
"sort": [
{
"field": "dt",
"dir": "asc"
}
]
};
// API
const res = await sdriverdaysGetKendoList(params);
console.log('API返回结果:', res);
let data = res?.data?.data || [];
console.log('原始数据条数:', data.length);
//
if (!data || data.length === 0) {
echartsData.value = [];
hasData.value = false;
await nextTick();
updateChart();
console.warn('未查询到水文数据');
return;
}
//
const processedData = data
.map(item => {
//
if (!item.dt || typeof item.dt !== 'string') {
console.warn('无效的日期数据:', item);
return null;
}
//
const q = typeof item.q === 'number' ? item.q : parseFloat(item.q);
const z = typeof item.z === 'number' ? item.z : parseFloat(item.z);
const v = typeof item.v === 'number' ? item.v : parseFloat(item.v);
if (isNaN(q) || isNaN(z) || isNaN(v)) {
console.warn('无效的水文数据:', item);
return null;
}
return {
dt: item.dt.split(' ')[0], // "2026-05-03"
v: v, //
q: q,
z: z
};
})
.filter(Boolean); //
console.log('处理后数据条数:', processedData.length);
console.log('处理后数据示例:', processedData[0]);
//
echartsData.value = processedData;
//
hasData.value = echartsData.value.length > 0;
console.log('hasData 设置为:', hasData.value);
// DOM
await nextTick();
updateChart();
console.log('图表已更新');
} catch (error) {
console.error('获取图表数据失败:', error);
hasData.value = false;
} finally {
// loading
await nextTick();
console.log('关闭 loading 状态');
loading.value = false;
}
};
const handlePanelChange1 = async (data) => {
console.log('=== 用户交互触发 ===');
console.log('当前所有控件状态:', data);
// scopeDate: ['26-05', '26-06']
// select: "0836000011"
if (data.scopeDate && data.scopeDate.length > 0 && data.select) {
console.log('参数验证通过,开始加载数据');
//
const dateRange = convertDateRange(data.scopeDate);
if (dateRange) {
console.log('日期转换结果:', dateRange);
await getChartData(dateRange.startDate, dateRange.endDate, data.select);
} else {
console.error('日期转换失败');
}
} else {
loading.value = false;
hasData.value = false;
//
echartsData.value = [];
//
await nextTick();
updateChart();
console.warn('参数不完整:', {
scopeDate: data.scopeDate,
select: data.select
});
}
}
watch(
() => JidiSelectEventStore.selectedItem,
(newVal) => {
baseid.value = newVal.wbsCode;
getselsectData()
},
{ deep: true, immediate: true }
);
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.chart-container { .chart-wrapper {
width: 100%;
height: 231px; height: 231px;
min-height: 231px; min-height: 231px;
width: 100%;
position: relative; position: relative;
/* 为empty-overlay提供定位上下文 */
.echarts-chart { .echarts-chart {
height: 100%; width: 100% !important;
height: 100% !important;
min-width: 100%;
min-height: 231px;
}
.empty-overlay {
position: absolute;
top: 0;
left: 0;
width: 100%; width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
background: rgba(255, 255, 255);
z-index: 10;
} }
} }
</style> </style>

View File

@ -1,26 +1,42 @@
<!-- SidePanelItem.vue --> <!-- SidePanelItem.vue -->
<template> <template>
<SidePanelItem title="水温监测" :select="select" :scopeDate="datetimePicker"> <SidePanelItem title="水温监测" :select="select" :scopeDate="datetimePicker" @update-values="handlePanelChange1">
<div class="chart-wrapper">
<a-spin :spinning="loading">
<!-- 始终渲染图表容器确保有固定宽高 -->
<div ref="chartRef" class="water-temp-chart"></div> <div ref="chartRef" class="water-temp-chart"></div>
<!-- 无数据时显示Empty但不影响容器尺寸 -->
<div v-if="!loading && !hasData" class="empty-overlay">
<a-empty description="暂无数据" />
</div>
</a-spin>
</div>
</SidePanelItem> </SidePanelItem>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref, onMounted, onUnmounted, nextTick } from 'vue'; import { ref, onMounted, onUnmounted, nextTick, watch } from 'vue';
import * as echarts from 'echarts'; import * as echarts from 'echarts';
import type { EChartsOption } from 'echarts'; import type { EChartsOption } from 'echarts';
import SidePanelItem from '@/components/SidePanelItem/index.vue'; import SidePanelItem from '@/components/SidePanelItem/index.vue';
import { useJidiSelectEventStore } from "@/store/modules/jidiSelectEvent";
import { msstbprptGetKendoList, sdrvwtsGetKendoList } from '@/api/qxd';
import { useModelStore } from "@/store/modules/model";
// //
defineOptions({ defineOptions({
name: 'qixidishuiwenbianhua' name: 'qixidishuiwenbianhua'
}); });
// ==================== ==================== // ==================== ====================
const JidiSelectEventStore = useJidiSelectEventStore();
// //
const chartRef = ref<HTMLDivElement>(); const chartRef = ref<HTMLDivElement>();
let chartInstance: echarts.ECharts | null = null; let chartInstance: echarts.ECharts | null = null;
const baseid = ref('')
// Loading
const loading = ref(false);
const hasData = ref(true); // true initChart
// //
const select = ref({ const select = ref({
@ -32,7 +48,7 @@ const select = ref({
}); });
// //
const datetimePicker = ref({ const datetimePicker: any = ref({
show: true, show: true,
value: (() => { value: (() => {
const now = new Date(); const now = new Date();
@ -48,7 +64,7 @@ const datetimePicker = ref({
// //
const echartsData = ref<any[]>([]); const echartsData = ref<any[]>([]);
const modelStore = useModelStore();
// //
const unita = ref('℃'); const unita = ref('℃');
@ -96,6 +112,9 @@ const initChart = () => {
// //
chartInstance = echarts.init(chartRef.value); chartInstance = echarts.init(chartRef.value);
//
chartInstance.on('click', handleDataPointClick);
// //
updateChart(); updateChart();
@ -111,6 +130,9 @@ const updateChart = () => {
const _legendData = [`水温(${unita.value})`]; const _legendData = [`水温(${unita.value})`];
//
const hasData = echartsData.value && echartsData.value.length > 0;
const option: EChartsOption = { const option: EChartsOption = {
tooltip: { tooltip: {
trigger: 'axis', trigger: 'axis',
@ -128,7 +150,10 @@ const updateChart = () => {
const seriesName = item.seriesName ?? ''; const seriesName = item.seriesName ?? '';
let regx = /\(([^()]+?)\)/; let regx = /\(([^()]+?)\)/;
let unit = seriesName.match(regx); let unit = seriesName.match(regx);
const finalValue = item.value !== undefined && item.value !== null ? item.value : '-'; //
const finalValue = item.value !== undefined && item.value !== null
? Number(item.value).toFixed(1)
: '-';
if (item.value !== undefined && item.value !== null) { if (item.value !== undefined && item.value !== null) {
res += `<span style="background: ${item.color}; height:10px; width: 10px; border-radius: 50%;display: inline-block;margin-right:10px;"></span> ${item.seriesName} ${finalValue}${unit?.[1] || ''} <br/>`; res += `<span style="background: ${item.color}; height:10px; width: 10px; border-radius: 50%;display: inline-block;margin-right:10px;"></span> ${item.seriesName} ${finalValue}${unit?.[1] || ''} <br/>`;
@ -150,15 +175,15 @@ const updateChart = () => {
color: Color, color: Color,
legend: { legend: {
data: _legendData, data: _legendData,
top: 10, top: 0,
left: 'center', left: 'center',
selectedMode: 'multiple' selectedMode: 'multiple'
}, },
grid: { grid: {
top: 45, top: 25,
left: '10%', left: '3%',
right: '10px', right: '3%',
bottom: '10%', bottom: '3%',
containLabel: true containLabel: true
}, },
dataZoom: { dataZoom: {
@ -171,6 +196,26 @@ const updateChart = () => {
type: 'category', type: 'category',
data: echartsData.value?.map((item: any) => item.dt) || [], data: echartsData.value?.map((item: any) => item.dt) || [],
boundaryGap: true, boundaryGap: true,
// X - 4
axisLabel: {
show: true,
rotate: 0, //
interval: (index: number, value: string): boolean => {
const total = echartsData.value?.length || 0;
if (total <= 4) return true; // 4
//
if (index === 0) return true;
// 34
const step = Math.floor((total - 1) / 3);
return index % step === 0 && index < total;
},
formatter: function (value: string) {
// "YYYY-MM-DD"
return value;
}
},
// X线 // X线
splitLine: { splitLine: {
show: true, show: true,
@ -191,11 +236,27 @@ const updateChart = () => {
yAxis: [ yAxis: [
{ {
type: 'value', type: 'value',
name: _legendData[0], // name legend
// name: _legendData[0], // "()"
nameLocation: 'end', // Y
nameGap: 8, // 线 20px
nameTextStyle: {
color: '#333333',
fontSize: 12,
},
// Y
min: hasData && echartsData.value.length > 0
? Math.floor(Math.min(...echartsData.value.map(item => item.wt)))
: undefined,
max: hasData && echartsData.value.length > 0
? Math.ceil(Math.max(...echartsData.value.map(item => item.wt)))
: undefined,
//
axisLabel: { axisLabel: {
show: true, show: true,
color: '#333' // color: '#333',
formatter: (value: number) => `${Math.round(value)}` //
}, },
axisTick: { axisTick: {
show: true show: true
@ -203,33 +264,34 @@ const updateChart = () => {
axisLine: { axisLine: {
show: true, show: true,
lineStyle: { lineStyle: {
color: '#333' // Y线 color: '#333'
} }
}, },
splitLine: { splitLine: {
show: true, show: true,
lineStyle: { lineStyle: {
type: 'solid' // 线线 type: 'solid'
} }
}, },
splitNumber: 3 // Y
minInterval: 1,
maxInterval: 1
} }
], ],
series: [ series: [
{ {
name: _legendData[0], name: _legendData[0],
data: echartsData.value?.map((item: any) => item.wt) || [], data: hasData ? (echartsData.value?.map((item: any) => item.wt) || []) : [],
type: 'line', type: 'line',
smooth: true, smooth: true,
symbol: 'circle', symbol: 'circle',
symbolSize: 6, symbolSize: 4,
lineStyle: { lineStyle: {
width: 2 width: 2
} }
} }
] ]
}; };
// 使 // 使
chartInstance.setOption(option, true); chartInstance.setOption(option, true);
}; };
@ -243,6 +305,35 @@ const handleResize = () => {
} }
}; };
/**
* 处理数据点点击事件
*/
const handleDataPointClick = (params: any) => {
console.log('=== 数据点点击事件 ===');
console.log('点击参数:', params);
if (!params || !params.dataIndex) {
console.warn('无效的数据点点击');
return;
}
const dataIndex = params.dataIndex;
const dataItem = echartsData.value[dataIndex];
if (dataItem) {
console.log('点击的数据:', {
日期: dataItem.dt,
水温: dataItem.wt,
测站代码: dataItem.stcd,
索引: dataIndex
});
modelStore.modalVisible = true;
modelStore.params.sttp = "wt_point";
modelStore.title = '水温监测';
modelStore.params.stcd = dataItem.stcd;
}
};
/** /**
* 加载模拟数据 * 加载模拟数据
*/ */
@ -259,6 +350,255 @@ const loadData = async () => {
} }
}; };
/**
* 加载图表数据
* @param startDate 开始日期 (格式: YYYY-MM-DD HH:mm:ss)
* @param endDate 结束日期 (格式: YYYY-MM-DD HH:mm:ss)
* @param stcd 测站代码
*/
const getChartData = async (startDate: string, endDate: string, stcd: string) => {
try {
console.log('=== 开始加载图表数据 ===');
console.log('请求参数:', { startDate, endDate, stcd });
// loading
loading.value = true;
hasData.value = false;
// TODO: API
//
const params = {
"filter": {
"logic": "and",
"filters": [
{
"field": "DT",
"operator": "gte",
"value": startDate // : "2026-05-01 00:00:00"
},
{
"field": "DT",
"operator": "lte",
"value": endDate // : "2026-06-30 23:59:59"
},
{
"field": "STCD",
"operator": "eq",
"dataType": "string",
"value": stcd
}
]
}
};
// API
const res = await sdrvwtsGetKendoList(params);
console.log('API返回结果:', res);
let data = res?.data?.data || [];
console.log('原始数据条数:', data.length);
//
if (!data || data.length === 0) {
echartsData.value = [];
hasData.value = false;
await nextTick();
updateChart();
console.warn('未查询到水温数据');
return;
}
// X
echartsData.value = data
.map(item => {
//
if (!item.dt || typeof item.dt !== 'string') {
console.warn('无效的日期数据:', item);
return null;
}
// wt
const wt = typeof item.wt === 'number' ? item.wt : parseFloat(item.wt);
if (isNaN(wt)) {
console.warn('无效的水温数据:', item);
return null;
}
return {
dt: item.dt.substring(0, 10), // "2026-05-02"
wt: wt, // number
stcd: item.stcd || stcd // stcd API 使 stcd
};
})
.filter(Boolean); //
console.log('转换后的图表数据:', echartsData.value);
console.log('数据类型检查:', {
第一条数据: echartsData.value[0],
dt类型: typeof echartsData.value[0]?.dt,
wt类型: typeof echartsData.value[0]?.wt,
wt값: echartsData.value[0]?.wt
});
//
hasData.value = echartsData.value.length > 0;
console.log('hasData 设置为:', hasData.value);
//
await nextTick();
updateChart();
console.log('✅ 图表数据加载完成');
} catch (error) {
console.error('获取图表数据失败:', error);
hasData.value = false;
} finally {
// loading
await nextTick();
console.log('关闭 loading 状态');
loading.value = false;
}
};
/**
* YY-MM 格式转换为完整的日期时间范围
* @param dateRange ['26-05', '26-06'] 格式的数组
* @returns { startDate: string, endDate: string } 格式化后的日期范围
*/
const convertDateRange = (dateRange: string[]) => {
if (!dateRange || dateRange.length !== 2) {
return null;
}
// debugger
const [startStr, endStr] = dateRange;
//
const today = new Date();
const currentDay = today.getDate();
// YY-MM
const [startYearStr, startMonthStr] = startStr.split('-');
const startYear = parseInt(startYearStr);
const startMonth = parseInt(startMonthStr);
const fullStartYear = startYear < 100 ? 2000 + startYear : startYear;
// YY-MM
const [endYearStr, endMonthStr] = endStr.split('-');
const endYear = parseInt(endYearStr);
const endMonth = parseInt(endMonthStr);
const fullEndYear = endYear < 100 ? 2000 + endYear : endYear;
// + + 00:00:00
// 31228
const startMonthLastDay = new Date(fullStartYear, startMonth, 0).getDate();
const actualStartDay = Math.min(currentDay, startMonthLastDay);
const startDate = `${fullStartYear}-${String(startMonth).padStart(2, '0')}-${String(actualStartDay).padStart(2, '0')} 00:00:00`;
// + + 23:59:59
//
const endMonthLastDay = new Date(fullEndYear, endMonth, 0).getDate();
const actualEndDay = Math.min(currentDay, endMonthLastDay);
const endDate = `${fullEndYear}-${String(endMonth).padStart(2, '0')}-${String(actualEndDay).padStart(2, '0')} 23:59:59`;
return {
startDate,
endDate
};
};
const handlePanelChange1 = async (data) => {
console.log('=== 用户交互触发 ===');
console.log('当前所有控件状态:', data);
// scopeDate: ['26-05', '26-06']
// select: "0836000011"
if (data.scopeDate && data.scopeDate.length > 0 && data.select) {
console.log('参数验证通过,开始加载数据');
//
const dateRange = convertDateRange(data.scopeDate);
if (dateRange) {
console.log('日期转换结果:', dateRange);
await getChartData(dateRange.startDate, dateRange.endDate, data.select);
} else {
console.error('日期转换失败');
}
} else {
loading.value = false;
hasData.value = false;
//
echartsData.value = [];
//
await nextTick();
updateChart();
console.warn('参数不完整:', {
scopeDate: data.scopeDate,
select: data.select
});
}
}
//
const getselsectData = async () => {
let params = {
"filter": {
"logic": "and",
"filters": [
baseid.value != 'all' ? {
"field": "baseId",
"operator": "eq",
"dataType": "string",
"value": baseid.value
} : null,
{
"field": "fhstcd",
"operator": "isnotnull"
},
{
"logic": "or",
"filters": [
{
"field": "sttpCode",
"operator": "eq",
"dataType": "string",
"value": "WTRV"
}
]
}
].filter(Boolean)
},
"select": [
"stcd",
"stnm",
"fhstcd",
"fhstnm",
"sttpCode"
]
}
let res = await msstbprptGetKendoList(params)
console.log(res)
if (!res?.data?.data) {
select.value.value = ''
select.value.options = []
return
}
select.value.value = res.data.data[0].stcd
select.value.options = res.data.data.map((item: any) => {
return {
label: item.stnm,
value: item.stcd
}
})
// debugger
};
////
watch(
() => JidiSelectEventStore.selectedItem,
(newVal) => {
baseid.value = newVal.wbsCode;
getselsectData()
},
{ deep: true, immediate: true }
);
// ==================== ==================== // ==================== ====================
// //
@ -271,7 +611,9 @@ onMounted(() => {
const rect = chartRef.value.getBoundingClientRect(); const rect = chartRef.value.getBoundingClientRect();
if (rect.width > 0 && rect.height > 0) { if (rect.width > 0 && rect.height > 0) {
initChart(); initChart();
loadData(); //
// loadData();
console.log('✅ 图表初始化完成,等待用户交互加载数据');
} else { } else {
console.warn('图表容器尺寸为0启动重试机制'); console.warn('图表容器尺寸为0启动重试机制');
// 退 // 退
@ -284,7 +626,8 @@ onMounted(() => {
const newRect = chartRef.value?.getBoundingClientRect(); const newRect = chartRef.value?.getBoundingClientRect();
if (newRect && newRect.width > 0 && newRect.height > 0) { if (newRect && newRect.width > 0 && newRect.height > 0) {
initChart(); initChart();
loadData(); // loadData();
console.log('✅ 图表初始化完成(重试成功)');
} else { } else {
retryInit(); retryInit();
} }
@ -307,6 +650,8 @@ onUnmounted(() => {
// //
if (chartInstance) { if (chartInstance) {
//
chartInstance.off('click', handleDataPointClick);
chartInstance.dispose(); chartInstance.dispose();
chartInstance = null; chartInstance = null;
} }
@ -314,9 +659,31 @@ onUnmounted(() => {
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.water-temp-chart { .chart-wrapper {
width: 100%; width: 100%;
height: 231px; height: 231px;
min-height: 231px; min-height: 231px;
position: relative;
/* 为empty-overlay提供定位上下文 */
.water-temp-chart {
width: 100% !important;
height: 100% !important;
min-width: 100%;
min-height: 231px;
}
.empty-overlay {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
background: rgba(255, 255, 255);
z-index: 10;
}
} }
</style> </style>

View File

@ -13,10 +13,12 @@ export const useModelStore = defineStore('model', () => {
sttp: string; sttp: string;
stcd: any; stcd: any;
date: any; date: any;
show: any;
}>({ }>({
sttp: 'eng', sttp: 'eng',
stcd: '', stcd: '',
date: '', date: '',
show: true
}); });
return { return {
params, params,

View File

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

View File

@ -148,7 +148,7 @@ const showMapModal17 = () => {
<a-button @click="showMapModal1">打开水温弹窗</a-button> <a-button @click="showMapModal1">打开水温弹窗</a-button>
<!-- <a-button @click="showMapModal2">打开水质弹窗</a-button> --> <!-- <a-button @click="showMapModal2">打开水质弹窗</a-button> -->
<!-- <a-button @click="showMapModal3">打开栖息地弹窗</a-button> --> <!-- <a-button @click="showMapModal3">打开栖息地弹窗</a-button> -->
<!-- <a-button @click="showMapModal4">打开栖息地流量弹窗</a-button> --> <a-button @click="showMapModal4">打开栖息地流量弹窗</a-button>
<!-- <a-button @click="showMapModal5">打开过鱼设施弹窗</a-button> --> <!-- <a-button @click="showMapModal5">打开过鱼设施弹窗</a-button> -->
<!-- <a-button @click="showMapModal6">打开鱼类增殖站弹窗</a-button> --> <!-- <a-button @click="showMapModal6">打开鱼类增殖站弹窗</a-button> -->
<!-- <a-button @click="showMapModal7">打开动物救助站弹窗</a-button> --> <!-- <a-button @click="showMapModal7">打开动物救助站弹窗</a-button> -->

View File

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