生态调查-线上接口
This commit is contained in:
commit
c7a9ff346f
@ -1,4 +1,12 @@
|
|||||||
import request from '@/utils/request';
|
import request from '@/utils/request';
|
||||||
|
//水电基地数据获取
|
||||||
|
export function wbsbGetKendoList(data: any) {
|
||||||
|
return request({
|
||||||
|
url: '/env/hydrobase/wbsb/GetKendoList',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// 基地下拉列表
|
// 基地下拉列表
|
||||||
export function getBaseDropdown(data: any) {
|
export function getBaseDropdown(data: any) {
|
||||||
|
|||||||
139
frontend/src/api/stdc/index.ts
Normal file
139
frontend/src/api/stdc/index.ts
Normal file
@ -0,0 +1,139 @@
|
|||||||
|
import request from '@/utils/request';
|
||||||
|
|
||||||
|
//水生生态调查情况-下拉选择框
|
||||||
|
export function wbsbGetKendoList(data: any) {
|
||||||
|
return request({
|
||||||
|
url: '/api/dec-lygk-base-server/base/wbsb/GetKendoList',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 水生生态调查情况-获取默认年份
|
||||||
|
export function getDftYear(data: any) {
|
||||||
|
return request({
|
||||||
|
url: '/api/wmp-env-server/env/we/fisht/year/qgc/GetKendoListCust',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 水生生态调查情况-获取统计数据
|
||||||
|
export function getWeFisht(data: any) {
|
||||||
|
return request({
|
||||||
|
url: '/api/wmp-env-server/env/we/fisht/statics/GetKendoListCust',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 水生生态调查概况-表格数据接口
|
||||||
|
export function getWeStaticsSecond(data: any) {
|
||||||
|
return request({
|
||||||
|
url: '/api/wmp-env-server/env/we/fisht/statics/getWeStaticsSecond',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 沿程鱼类变化-图表数据接口
|
||||||
|
export function fishChanges2(data: any) {
|
||||||
|
return request({
|
||||||
|
url: '/api/wmp-env-server/env/we/fisht/stat/GetKendoListCust',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 沿程鱼类变化-详情表格接口
|
||||||
|
export function fishDetail(data: any) {
|
||||||
|
return request({
|
||||||
|
url: '/api/wmp-env-server/env/we/fisht/detail/GetKendoListCust',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 沿程鱼类变化-Tab数量统计接口
|
||||||
|
export function getTabNum(data: any) {
|
||||||
|
return request({
|
||||||
|
url: '/api/wmp-env-server/env/we/fisht/statTab/GetKendoListCust',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 沿程鱼类变化-获取断面列表
|
||||||
|
export function getStcdList(data: any) {
|
||||||
|
return request({
|
||||||
|
url: '/api/dec-lygk-base-server/base/vmsstbprpt/noWqDtinType0Auth/GetKendoList',
|
||||||
|
method: 'post',
|
||||||
|
data: {
|
||||||
|
filter: data,
|
||||||
|
orders: JSON.stringify({ siteStepSort: 'asc' })
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 沿程鱼类变化-基地模式详情表格接口
|
||||||
|
export function fishQgcDetail(data: any) {
|
||||||
|
return request({
|
||||||
|
url: '/api/wmp-env-server/env/we/fisht/qgcDetail/GetKendoListCust',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 陆生生态监测情况-获取统计数据
|
||||||
|
export function getEvnmAutoMonitor(data: any) {
|
||||||
|
return request({
|
||||||
|
url: '/api/dec-lygk-base-server/base/evnmAutoMonitor/GetKendoListCust',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 陆生生态监测-红外相机信息表格
|
||||||
|
export function getVmsstbprptList(data: any) {
|
||||||
|
return request({
|
||||||
|
url: '/api/dec-lygk-base-server/base/vmsstbprpt/GetKendoList',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 陆生生态监测-重点陆生动物表格
|
||||||
|
export function getTetSpecList(data: any) {
|
||||||
|
return request({
|
||||||
|
url: '/api/wmp-env-server/te/tet/spec/GetKendoListCust',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 野生动物监测-获取运行数据
|
||||||
|
export function getWvaData(data: any) {
|
||||||
|
return request({
|
||||||
|
url: '/api/wmp-env-server/env/wva/GetKendoListCust',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 野生动物监测-获取默认数据
|
||||||
|
export function getWvaDftData(data: any) {
|
||||||
|
return request({
|
||||||
|
url: '/api/wmp-env-server/env/wva/default/GetKendoListCust',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 野生动物监测-获取默认年份列表
|
||||||
|
export function getWvaDftTm(data: any) {
|
||||||
|
return request({
|
||||||
|
url: '/api/wmp-env-server/env/wva/year/GetKendoListCust',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
});
|
||||||
|
}
|
||||||
@ -596,12 +596,18 @@ const enhancedColumns = computed(() => {
|
|||||||
const { slots, ...restCol } = enhancedCol;
|
const { slots, ...restCol } = enhancedCol;
|
||||||
return {
|
return {
|
||||||
...restCol,
|
...restCol,
|
||||||
|
customCell: createMergeCellHandler(col),
|
||||||
_slotName: slots.customRender,
|
_slotName: slots.customRender,
|
||||||
...(props.enableEllipsis ? { ellipsis: true } : {})
|
...(props.enableEllipsis ? { ellipsis: true } : {})
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
return enhancedCol;
|
// 没有 slots 时,返回包含 customCell 的列配置
|
||||||
|
return {
|
||||||
|
...enhancedCol,
|
||||||
|
customCell: createMergeCellHandler(col),
|
||||||
|
...(props.enableEllipsis ? { ellipsis: true } : {})
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// 如果有 slots.customRender,移除 slots
|
// 如果有 slots.customRender,移除 slots
|
||||||
|
|||||||
@ -47,26 +47,7 @@
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="title_right">
|
<div class="title_right">
|
||||||
<div v-if="select.show">
|
|
||||||
<a-select
|
|
||||||
v-model:value="selectValue"
|
|
||||||
show-search
|
|
||||||
placeholder="请选择"
|
|
||||||
:size="'small'"
|
|
||||||
:style="{ width: select.width || '80px' }"
|
|
||||||
:dropdown-style="{
|
|
||||||
maxHeight: '400px',
|
|
||||||
overflow: 'auto',
|
|
||||||
minWidth: '200px'
|
|
||||||
}"
|
|
||||||
:options="select.options"
|
|
||||||
:filter-option="filterOption"
|
|
||||||
@focus="handleFocus"
|
|
||||||
@blur="handleBlur"
|
|
||||||
@change="handleChange"
|
|
||||||
@select="handleTreeSelect1"
|
|
||||||
></a-select>
|
|
||||||
</div>
|
|
||||||
<div v-if="shrink" class="title_shrink" @click="isExpand = !isExpand">
|
<div v-if="shrink" class="title_shrink" @click="isExpand = !isExpand">
|
||||||
<img v-if="isExpand" src="@/assets/components/arrow-up.png" alt="" />
|
<img v-if="isExpand" src="@/assets/components/arrow-up.png" alt="" />
|
||||||
<img v-else src="@/assets/components/arrow-down.png" alt="" />
|
<img v-else src="@/assets/components/arrow-down.png" alt="" />
|
||||||
@ -96,6 +77,26 @@
|
|||||||
@expand="handleTreeExpand"
|
@expand="handleTreeExpand"
|
||||||
>
|
>
|
||||||
</a-tree-select>
|
</a-tree-select>
|
||||||
|
</div>
|
||||||
|
<div v-if="select.show">
|
||||||
|
<a-select
|
||||||
|
v-model:value="selectValue"
|
||||||
|
show-search
|
||||||
|
placeholder="请选择"
|
||||||
|
:size="'small'"
|
||||||
|
:style="{ width: select.width || '80px' }"
|
||||||
|
:dropdown-style="{
|
||||||
|
maxHeight: '400px',
|
||||||
|
overflow: 'auto',
|
||||||
|
minWidth: '200px'
|
||||||
|
}"
|
||||||
|
:options="select.options"
|
||||||
|
:filter-option="filterOption"
|
||||||
|
@focus="handleFocus"
|
||||||
|
@blur="handleBlur"
|
||||||
|
@change="handleChange"
|
||||||
|
@select="handleTreeSelect1"
|
||||||
|
></a-select>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="datetimePicker.show">
|
<div v-if="datetimePicker.show">
|
||||||
<!-- 添加 locale 属性来设置语言 -->
|
<!-- 添加 locale 属性来设置语言 -->
|
||||||
|
|||||||
@ -48,7 +48,6 @@ app
|
|||||||
.use(router)
|
.use(router)
|
||||||
.use(ElementPlus)
|
.use(ElementPlus)
|
||||||
.use(Antd)
|
.use(Antd)
|
||||||
.use(Antd)
|
|
||||||
.use(WujieVue)
|
.use(WujieVue)
|
||||||
.use(i18n)
|
.use(i18n)
|
||||||
.mount('#app');
|
.mount('#app');
|
||||||
|
|||||||
@ -1,164 +1,184 @@
|
|||||||
<!-- SidePanelItem.vue -->
|
<!-- 野生动物监测 -->
|
||||||
<template>
|
<template>
|
||||||
<SidePanelItem title="野生动物监测" :select="select" :datetimePicker="datetimePicker" :tabs="tabs"
|
<SidePanelItem
|
||||||
@tab-change="handleTabChange">
|
title="野生动物监测"
|
||||||
<div v-if="tabsa == 'one'" class="container" @mouseenter="handleMouseEnter" @mouseleave="handleMouseLeave">
|
:select="select"
|
||||||
<!-- 跑马灯轨道容器 -->
|
:datetimePicker="datetimePicker"
|
||||||
<div class="carousel-track" :class="{ 'no-transition': isTransitioning }"
|
:tabs="tabs"
|
||||||
:style="{ transform: `translateX(-${currentIndex * 100}%)` }">
|
@tab-change="handleTabChange"
|
||||||
<!-- 遍历所有媒体项(包含克隆项) -->
|
@update-values="handlePanelChange"
|
||||||
<div v-for="(item, index) in renderMediaData" :key="index" class="carousel-item">
|
>
|
||||||
<!-- 图片 -->
|
<a-spin :spinning="loading" tip="加载中...">
|
||||||
<a-image :src="item.url" />
|
<div v-if="originalMediaData.length === 0" class="empty-wrapper">
|
||||||
<!-- 说明文字 -->
|
<a-empty description="暂无数据" />
|
||||||
|
</div>
|
||||||
|
<template v-else>
|
||||||
|
<div
|
||||||
|
v-if="tabsa == 'one'"
|
||||||
|
class="container"
|
||||||
|
@mouseenter="handleMouseEnter"
|
||||||
|
@mouseleave="handleMouseLeave"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="carousel-track"
|
||||||
|
:class="{ 'no-transition': isTransitioning }"
|
||||||
|
:style="{ transform: `translateX(-${currentIndex * 100}%)` }"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
v-for="(item, index) in renderMediaData"
|
||||||
|
:key="index"
|
||||||
|
class="carousel-item"
|
||||||
|
@click="handleItemClick(item)"
|
||||||
|
>
|
||||||
|
<img :src="item.url" />
|
||||||
<div class="text">{{ item.text }}</div>
|
<div class="text">{{ item.text }}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-else class="container video-carousel-container" @mouseenter="handleMouseEnter"
|
<div
|
||||||
@mouseleave="handleMouseLeave">
|
v-else
|
||||||
<!-- 跑马灯轨道容器 -->
|
class="container video-carousel-container"
|
||||||
<div class="carousel-track" :class="{ 'no-transition': isTransitioning }"
|
@mouseenter="handleMouseEnter"
|
||||||
:style="{ transform: `translateX(-${currentIndex * 30}%)` }">
|
@mouseleave="handleMouseLeave"
|
||||||
<!-- 遍历所有媒体项(包含克隆项) -->
|
>
|
||||||
<div v-for="(item, index) in renderMediaData" :key="index" class="carousel-item1">
|
<div
|
||||||
<!-- 图片 -->
|
class="carousel-track"
|
||||||
<a-image :src="item.url" />
|
:class="{ 'no-transition': isTransitioning }"
|
||||||
|
:style="{ transform: `translateX(-${currentIndex * 30}%)` }"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
v-for="(item, index) in renderMediaData"
|
||||||
|
:key="index"
|
||||||
|
class="carousel-item1"
|
||||||
|
@click="handleItemClick(item)"
|
||||||
|
>
|
||||||
|
<img :src="item.url" />
|
||||||
<div class="biaozji">
|
<div class="biaozji">
|
||||||
<span class="icon iconfont icon-play"></span>
|
<span class="icon iconfont icon-play"></span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
</template>
|
||||||
|
</a-spin>
|
||||||
</SidePanelItem>
|
</SidePanelItem>
|
||||||
</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 { getVmsstbprptList, getWvaData, getWvaDftTm } from '@/api/stdc';
|
||||||
|
import { useModelStore } from '@/store/modules/model';
|
||||||
|
|
||||||
// 定义组件名(便于调试和递归)
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: 'ZhiWuYuanJianSheJiJieRuQingKuangBar'
|
name: 'ZhiWuYuanJianSheJiJieRuQingKuangBar'
|
||||||
});
|
});
|
||||||
// ==================== 响应式变量定义 ====================// 选择器配置
|
|
||||||
|
const modelStore = useModelStore();
|
||||||
|
|
||||||
|
// ==================== 选择器配置 ====================
|
||||||
const select = ref({
|
const select = ref({
|
||||||
show: true,
|
show: true,
|
||||||
value: undefined,
|
value: undefined as string | undefined,
|
||||||
options: [],
|
options: [] as Array<{ label: string; value: string }>,
|
||||||
picker: undefined,
|
picker: undefined,
|
||||||
format: undefined
|
format: undefined
|
||||||
});
|
});
|
||||||
|
|
||||||
// 日期选择器配置
|
// ==================== 日期选择器配置 ====================
|
||||||
const datetimePicker = ref({
|
const datetimePicker = ref({
|
||||||
show: true,
|
show: true,
|
||||||
value: `2024`,
|
value: '',
|
||||||
format: 'YYYY',
|
format: 'YYYY',
|
||||||
picker: 'year' as const,
|
picker: 'year' as const
|
||||||
options: []
|
// options: [] as string[]
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// ==================== Tab 配置 ====================
|
||||||
const tabs = ref({
|
const tabs = ref({
|
||||||
show: true,
|
show: true,
|
||||||
value: 'one',
|
value: 'one'
|
||||||
|
|
||||||
});
|
});
|
||||||
|
const tabsa = ref('one');
|
||||||
|
|
||||||
|
// ==================== 媒体数据 ====================
|
||||||
// 媒体类型定义
|
|
||||||
interface MediaItem {
|
interface MediaItem {
|
||||||
type: 'image' | 'video';
|
type: 'image' | 'video';
|
||||||
url: string;
|
url: string;
|
||||||
text: string;
|
text: string;
|
||||||
|
stcd: string;
|
||||||
|
stnm: string;
|
||||||
|
fid?: string;
|
||||||
|
flpth?: string;
|
||||||
|
flnm?: string;
|
||||||
|
tm?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 原始媒体数据(5条测试数据,包含图片和视频混合)
|
const originalMediaData = ref<MediaItem[]>([]);
|
||||||
const originalMediaData = ref<MediaItem[]>([
|
|
||||||
{
|
|
||||||
type: 'image',
|
|
||||||
url: 'https://211.99.26.225:12125/?20250709001636453909987812803850&view=jpg&token=bearer 0a2549fe-9bc6-4bef-b098-1344e569c395',
|
|
||||||
text: '糯扎渡动物救护站 2025-04-11 15'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'image',
|
|
||||||
url: 'https://211.99.26.225:12125/?20250709001636453909987812803850&view=jpg&token=bearer 0a2549fe-9bc6-4bef-b098-1344e569c395',
|
|
||||||
text: '糯扎渡动物救护站 2025-04-11 15'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'image',
|
|
||||||
url: 'https://211.99.26.225:12125/?20250709001636453909987812803850&view=jpg&token=bearer 0a2549fe-9bc6-4bef-b098-1344e569c395',
|
|
||||||
text: '糯扎渡动物救护站 2025-04-11 15'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'image',
|
|
||||||
url: 'https://211.99.26.225:12125/?20250709001636453909987812803850&view=jpg&token=bearer 0a2549fe-9bc6-4bef-b098-1344e569c395',
|
|
||||||
text: '糯扎渡动物救护站 2025-04-11 15'
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
const tabsa = ref('one')
|
|
||||||
|
|
||||||
// 克隆首尾项后的渲染数组(用于无缝循环)
|
|
||||||
// 结构:[最后一项克隆, 原始数据..., 第一项克隆]
|
|
||||||
const renderMediaData = ref<MediaItem[]>([]);
|
const renderMediaData = ref<MediaItem[]>([]);
|
||||||
|
const currentIndex = ref(1);
|
||||||
// 当前显示索引(指向renderMediaData)
|
|
||||||
const currentIndex = ref(1); // 从1开始,跳过克隆的首项
|
|
||||||
|
|
||||||
// 定时器引用
|
|
||||||
let timer: any = null;
|
let timer: any = null;
|
||||||
|
|
||||||
// 鼠标悬停状态
|
|
||||||
const isHovering = ref(false);
|
const isHovering = ref(false);
|
||||||
|
|
||||||
// 是否正在切换动画中(用于禁用transition)
|
|
||||||
const isTransitioning = ref(false);
|
const isTransitioning = ref(false);
|
||||||
|
const loading = ref(false);
|
||||||
|
|
||||||
// 初始化渲染数组(克隆首尾项)
|
// ==================== 初始化渲染数组 ====================
|
||||||
const initRenderData = () => {
|
const initRenderData = () => {
|
||||||
const length = originalMediaData.value.length;
|
const length = originalMediaData.value.length;
|
||||||
if (length === 0) return;
|
if (length === 0) {
|
||||||
|
renderMediaData.value = [];
|
||||||
|
currentIndex.value = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 只有数据 > 1 条时才克隆首尾用于无缝轮播
|
||||||
|
if (length === 1) {
|
||||||
|
renderMediaData.value = [...originalMediaData.value];
|
||||||
|
currentIndex.value = 0;
|
||||||
|
} else {
|
||||||
renderMediaData.value = [
|
renderMediaData.value = [
|
||||||
originalMediaData.value[length - 1], // 克隆最后一项
|
originalMediaData.value[length - 1],
|
||||||
...originalMediaData.value, // 原始数据
|
...originalMediaData.value,
|
||||||
originalMediaData.value[0] // 克隆第一项
|
originalMediaData.value[0]
|
||||||
];
|
];
|
||||||
|
currentIndex.value = 1;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 启动自动轮播
|
// ==================== 自动轮播 ====================
|
||||||
const startAutoPlay = () => {
|
const startAutoPlay = () => {
|
||||||
if (timer) clearInterval(timer);
|
if (timer) clearInterval(timer);
|
||||||
|
// 只有数据大于 1 条时才启动轮播
|
||||||
|
if (originalMediaData.value.length <= 1) return;
|
||||||
timer = setInterval(() => {
|
timer = setInterval(() => {
|
||||||
if (!isHovering.value && !isTransitioning.value) {
|
if (
|
||||||
|
!isHovering.value &&
|
||||||
|
!isTransitioning.value &&
|
||||||
|
originalMediaData.value.length > 1
|
||||||
|
) {
|
||||||
nextSlide();
|
nextSlide();
|
||||||
}
|
}
|
||||||
}, 2000);
|
}, 3000);
|
||||||
|
};
|
||||||
|
|
||||||
|
const stopAutoPlay = () => {
|
||||||
|
if (timer) {
|
||||||
|
clearInterval(timer);
|
||||||
|
timer = null;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 切换到下一张
|
|
||||||
const nextSlide = () => {
|
const nextSlide = () => {
|
||||||
currentIndex.value++;
|
currentIndex.value++;
|
||||||
|
|
||||||
// 延迟检查是否需要无缝跳转
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
checkSeamlessJump();
|
checkSeamlessJump();
|
||||||
}, 500); // 与transition时间一致
|
}, 500);
|
||||||
};
|
};
|
||||||
|
|
||||||
// 检查是否需要无缝跳转(克隆项处理)
|
|
||||||
const checkSeamlessJump = () => {
|
const checkSeamlessJump = () => {
|
||||||
const realLength = originalMediaData.value.length;
|
const realLength = originalMediaData.value.length;
|
||||||
|
|
||||||
// 如果到达克隆的最后一项(索引 = realLength + 1)
|
|
||||||
if (currentIndex.value >= realLength + 1) {
|
if (currentIndex.value >= realLength + 1) {
|
||||||
// 1. 禁用过渡动画,实现瞬间跳转
|
|
||||||
isTransitioning.value = true;
|
isTransitioning.value = true;
|
||||||
|
|
||||||
// 2. 瞬间跳转到真实的第二项(索引1),用户看不到跳变
|
|
||||||
currentIndex.value = 1;
|
currentIndex.value = 1;
|
||||||
|
|
||||||
// 3. 等待两帧后恢复过渡动画(确保DOM已更新)
|
|
||||||
requestAnimationFrame(() => {
|
requestAnimationFrame(() => {
|
||||||
requestAnimationFrame(() => {
|
requestAnimationFrame(() => {
|
||||||
isTransitioning.value = false;
|
isTransitioning.value = false;
|
||||||
@ -167,70 +187,270 @@ const checkSeamlessJump = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 处理鼠标进入
|
|
||||||
const handleMouseEnter = () => {
|
const handleMouseEnter = () => {
|
||||||
isHovering.value = true;
|
isHovering.value = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
// 处理鼠标离开
|
|
||||||
const handleMouseLeave = () => {
|
const handleMouseLeave = () => {
|
||||||
isHovering.value = false;
|
isHovering.value = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleTabChange = (data: { tabValue: string; tabLabel: string }) => {
|
const handleTabChange = (data: { tabValue: string; tabLabel: string }) => {
|
||||||
console.log('当前选中标签:', data.tabValue);
|
|
||||||
console.log('标签名称:', data.tabLabel);
|
|
||||||
// 执行相应业务逻辑
|
|
||||||
tabsa.value = data.tabValue;
|
tabsa.value = data.tabValue;
|
||||||
|
// 切换 tab 时重新请求数据
|
||||||
|
getData();
|
||||||
};
|
};
|
||||||
|
|
||||||
// 页面加载时执行
|
// ==================== 统一处理面板控件变化 ====================
|
||||||
onMounted(() => {
|
const handlePanelChange = (data: any) => {
|
||||||
|
if (data.select !== undefined && data.select !== null) {
|
||||||
|
select.value.value = data.select;
|
||||||
|
}
|
||||||
|
if (data.datetime !== undefined && data.datetime !== null) {
|
||||||
|
datetimePicker.value.value = data.datetime;
|
||||||
|
}
|
||||||
|
if (data.tabs !== undefined && data.tabs !== null) {
|
||||||
|
tabsa.value = data.tabs;
|
||||||
|
}
|
||||||
|
// 选择器或日期变化时,重新获取数据
|
||||||
|
getData();
|
||||||
|
};
|
||||||
|
|
||||||
|
// ==================== 点击打开详情弹窗 ====================
|
||||||
|
const handleItemClick = (item: MediaItem) => {
|
||||||
|
modelStore.modalVisible = true;
|
||||||
|
modelStore.params.sttp = 'WVA';
|
||||||
|
modelStore.params.stcd = item.stcd;
|
||||||
|
modelStore.title = `${item.stnm} 野生动物监测`;
|
||||||
|
modelStore.isBasicEdit = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
// ==================== 获取站点列表 ====================
|
||||||
|
const getStationList = async () => {
|
||||||
|
try {
|
||||||
|
const res = await getVmsstbprptList({
|
||||||
|
filter: {
|
||||||
|
logic: 'and',
|
||||||
|
filters: [
|
||||||
|
{
|
||||||
|
field: 'sttpCode',
|
||||||
|
operator: 'eq',
|
||||||
|
dataType: 'string',
|
||||||
|
value: 'WVA'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
select: [
|
||||||
|
'id',
|
||||||
|
'stcd',
|
||||||
|
'rstcd',
|
||||||
|
'ennm',
|
||||||
|
'stnm',
|
||||||
|
'sttpCode',
|
||||||
|
'orderIndex',
|
||||||
|
'lgtd',
|
||||||
|
'lttd'
|
||||||
|
]
|
||||||
|
});
|
||||||
|
const list = res?.data?.data ?? [];
|
||||||
|
// debugger
|
||||||
|
if (list.length) {
|
||||||
|
select.value.options = list.map((item: any) => ({
|
||||||
|
label: item.stnm,
|
||||||
|
value: item.stcd
|
||||||
|
}));
|
||||||
|
select.value.value = list[0].stcd;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取站点列表失败:', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// ==================== 获取默认年份 ====================
|
||||||
|
const getDefaultYear = async () => {
|
||||||
|
try {
|
||||||
|
let params = {
|
||||||
|
filter: {
|
||||||
|
logic: 'and',
|
||||||
|
filters: [
|
||||||
|
tabsa.value === 'one'
|
||||||
|
? {
|
||||||
|
field: 'fid',
|
||||||
|
operator: 'isnotnull',
|
||||||
|
dataType: 'string'
|
||||||
|
}
|
||||||
|
: { field: 'flpth', operator: 'isnotnull', dataType: 'string' }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const res = await getWvaDftTm(params);
|
||||||
|
const data = res?.data?.data ?? [];
|
||||||
|
if (data.length) {
|
||||||
|
const yrList = data
|
||||||
|
.map((el: any) => el.ym)
|
||||||
|
.sort((a: any, b: any) => b - a);
|
||||||
|
|
||||||
|
// datetimePicker.value.options = yrList;
|
||||||
|
datetimePicker.value.value = String(yrList[0]);
|
||||||
|
} else {
|
||||||
|
datetimePicker.value.value = String(new Date().getFullYear());
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取默认年份失败:', error);
|
||||||
|
datetimePicker.value.value = String(new Date().getFullYear());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// ==================== 获取监测数据 ====================
|
||||||
|
const getFileUrl = (path: string, view = 'jpg') => {
|
||||||
|
const fsAddress =import.meta.env.VITE_APP_ATTACHMENT_URL ||'';
|
||||||
|
return `${fsAddress}?${path}&view=${view}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getData = async () => {
|
||||||
|
if (!datetimePicker.value.value) return;
|
||||||
|
loading.value = true;
|
||||||
|
try {
|
||||||
|
// datetimePicker.value.value 格式为 YYYY-MM,提取年份
|
||||||
|
const year = datetimePicker.value.value?.toString().slice(0, 4);
|
||||||
|
let params = {
|
||||||
|
filter: {
|
||||||
|
logic: 'and',
|
||||||
|
filters: [
|
||||||
|
{
|
||||||
|
field: 'tm',
|
||||||
|
operator: 'gte',
|
||||||
|
dataType: 'date',
|
||||||
|
value: `${year}-01-01 00:00:00`
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'tm',
|
||||||
|
operator: 'lte',
|
||||||
|
dataType: 'date',
|
||||||
|
value: `${year}-12-31 23:59:59`
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'stcd',
|
||||||
|
operator: 'eq',
|
||||||
|
dataType: 'string',
|
||||||
|
value: select.value.value
|
||||||
|
},
|
||||||
|
tabsa.value === 'one'
|
||||||
|
? {
|
||||||
|
field: 'fid',
|
||||||
|
operator: 'isnotnull',
|
||||||
|
dataType: 'string'
|
||||||
|
}
|
||||||
|
: { field: 'flpth', operator: 'isnotnull' }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const res = await getWvaData(params);
|
||||||
|
const datas = res?.data?.data ?? [];
|
||||||
|
if (tabsa.value === 'one') {
|
||||||
|
const pics = datas.reduce((result: MediaItem[], el: any) => {
|
||||||
|
const ids = el?.fid?.split(',') ?? [];
|
||||||
|
ids.forEach((id: string) => {
|
||||||
|
result.push({
|
||||||
|
type: 'image',
|
||||||
|
url: getFileUrl(id),
|
||||||
|
text: el?.flnm || '',
|
||||||
|
stcd: el?.stcd,
|
||||||
|
stnm: el?.stnm,
|
||||||
|
fid: id,
|
||||||
|
flnm: el?.flnm,
|
||||||
|
tm: el?.tm
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
}, []);
|
||||||
|
originalMediaData.value = pics;
|
||||||
|
} else {
|
||||||
|
const videos = datas.map((el: any) => ({
|
||||||
|
type: 'video' as const,
|
||||||
|
url: el.imgPath ? getFileUrl(el.imgPath) : '',
|
||||||
|
text: el?.flnm || '',
|
||||||
|
stcd: el?.stcd,
|
||||||
|
stnm: el?.stnm,
|
||||||
|
flpth: el?.flpth,
|
||||||
|
flnm: el?.flnm,
|
||||||
|
tm: el?.tm
|
||||||
|
}));
|
||||||
|
originalMediaData.value = videos;
|
||||||
|
}
|
||||||
|
|
||||||
initRenderData();
|
initRenderData();
|
||||||
startAutoPlay();
|
startAutoPlay();
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取数据失败:', error);
|
||||||
|
} finally {
|
||||||
|
loading.value = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 监听数据变化,数据 <= 1 条时停止轮播
|
||||||
|
watch(
|
||||||
|
() => originalMediaData.value.length,
|
||||||
|
(newLen) => {
|
||||||
|
if (newLen <= 1) {
|
||||||
|
stopAutoPlay();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// ==================== 生命周期 ====================
|
||||||
|
onMounted(async () => {
|
||||||
|
await getStationList();
|
||||||
|
await getDefaultYear();
|
||||||
|
// 初始加载数据
|
||||||
|
getData();
|
||||||
});
|
});
|
||||||
|
|
||||||
// 组件卸载时清理
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
if (timer) clearInterval(timer);
|
if (timer) clearInterval(timer);
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
.empty-wrapper {
|
||||||
|
width: 100%;
|
||||||
|
height: 290px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
.container {
|
.container {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 290px;
|
height: 290px;
|
||||||
border: 1px solid #7fd6ff;
|
border: 1px solid #7fd6ff;
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
position: relative;
|
position: relative;
|
||||||
overflow: hidden; // 隐藏超出容器的内容
|
overflow: hidden;
|
||||||
|
|
||||||
// 跑马灯轨道
|
|
||||||
.carousel-track {
|
.carousel-track {
|
||||||
display: flex; // 横向排列所有媒体项
|
display: flex;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
transition: transform 0.5s ease-in-out; // 0.5秒平滑过渡
|
transition: transform 0.5s ease-in-out;
|
||||||
|
|
||||||
// 禁用过渡动画(用于无缝跳转)
|
|
||||||
&.no-transition {
|
&.no-transition {
|
||||||
transition: none;
|
transition: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 单个媒体项
|
|
||||||
.carousel-item {
|
.carousel-item {
|
||||||
width: 100% !important; // 每个项目占满容器宽度
|
width: 100% !important;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
position: relative;
|
position: relative;
|
||||||
flex-shrink: 0; // 防止被压缩
|
flex-shrink: 0;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
img,
|
img,
|
||||||
video {
|
video {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
object-fit: cover; // 保持比例填充
|
object-fit: cover;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ant Design Vue 的 a-image 组件样式
|
|
||||||
:deep(.ant-image) {
|
:deep(.ant-image) {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
@ -261,26 +481,25 @@ onUnmounted(() => {
|
|||||||
.video-carousel-container {
|
.video-carousel-container {
|
||||||
border: 0px solid #7fd6ff !important;
|
border: 0px solid #7fd6ff !important;
|
||||||
border-radius: 0px !important;
|
border-radius: 0px !important;
|
||||||
|
|
||||||
.carousel-item1 {
|
.carousel-item1 {
|
||||||
position: relative;
|
position: relative;
|
||||||
width: 40% !important; // 每个项目占满容器宽度
|
width: 40% !important;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
position: relative;
|
flex-shrink: 0;
|
||||||
flex-shrink: 0; // 防止被压缩
|
|
||||||
// margin-right: 10px;
|
|
||||||
padding: 6px;
|
padding: 6px;
|
||||||
border-top: #f0f0f0 solid 1px;
|
border-top: #f0f0f0 solid 1px;
|
||||||
border-bottom: #f0f0f0 solid 1px;
|
border-bottom: #f0f0f0 solid 1px;
|
||||||
border-right: #f0f0f0 solid 1px;
|
border-right: #f0f0f0 solid 1px;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
img,
|
img,
|
||||||
video {
|
video {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
object-fit: cover; // 保持比例填充
|
object-fit: cover;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ant Design Vue 的 a-image 组件样式
|
|
||||||
:deep(.ant-image) {
|
:deep(.ant-image) {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
@ -291,6 +510,7 @@ onUnmounted(() => {
|
|||||||
object-fit: cover;
|
object-fit: cover;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.biaozji {
|
.biaozji {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
width: 40px;
|
width: 40px;
|
||||||
@ -305,6 +525,7 @@ onUnmounted(() => {
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
||||||
span {
|
span {
|
||||||
width: 16px;
|
width: 16px;
|
||||||
height: 16px;
|
height: 16px;
|
||||||
|
|||||||
615
frontend/src/modules/alongFishMod/TwoLayers/Yanchengyulei.vue
Normal file
615
frontend/src/modules/alongFishMod/TwoLayers/Yanchengyulei.vue
Normal file
@ -0,0 +1,615 @@
|
|||||||
|
<!-- 沿程鱼类变化详情组件 -->
|
||||||
|
<template>
|
||||||
|
<div class="yancheng-yulei">
|
||||||
|
<!-- 搜索表单 -->
|
||||||
|
<a-form layout="inline" class="search-form">
|
||||||
|
<!-- 基地名称(非沿程模式显示) -->
|
||||||
|
<a-form-item v-if="!props.ly" label="基地名称">
|
||||||
|
<a-select
|
||||||
|
v-model:value="formState.baseId"
|
||||||
|
placeholder="请选择"
|
||||||
|
style="width: 150px"
|
||||||
|
:options="baseOptions"
|
||||||
|
@change="handleBaseChange"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
|
||||||
|
<!-- 所在流域 -->
|
||||||
|
<a-form-item label="所在流域">
|
||||||
|
<a-select
|
||||||
|
v-model:value="formState.rvcd"
|
||||||
|
placeholder="请选择"
|
||||||
|
style="width: 150px"
|
||||||
|
:options="lyOptions"
|
||||||
|
@change="handleRvcdChange"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
|
||||||
|
<!-- 断面名称(沿程模式显示) -->
|
||||||
|
<a-form-item v-if="props.ly" label="断面名称">
|
||||||
|
<a-select
|
||||||
|
v-model:value="formState.stcd"
|
||||||
|
placeholder="请选择"
|
||||||
|
style="width: 150px"
|
||||||
|
:options="dmOptions"
|
||||||
|
@change="handleStcdChange"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
|
||||||
|
<!-- 年份 -->
|
||||||
|
<a-form-item label="年份">
|
||||||
|
<a-date-picker
|
||||||
|
v-model:value="yearValue"
|
||||||
|
picker="year"
|
||||||
|
format="YYYY"
|
||||||
|
:allow-clear="false"
|
||||||
|
style="width: 120px"
|
||||||
|
@change="handleYearChange"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
|
||||||
|
<a-form-item>
|
||||||
|
<a-space>
|
||||||
|
<a-button type="primary" @click="handleSearch">查询</a-button>
|
||||||
|
<a-button @click="handleReset">重置</a-button>
|
||||||
|
</a-space>
|
||||||
|
</a-form-item>
|
||||||
|
</a-form>
|
||||||
|
|
||||||
|
<!-- Tab 切换 + 表格 -->
|
||||||
|
<a-tabs v-model:activeKey="tabIndex" class="ylycbh-tabs" @change="handleTabChange">
|
||||||
|
<a-tab-pane v-for="item in tabList" :key="item.key" :tab="item.labelWithTooltip">
|
||||||
|
<div class="table-wrapper">
|
||||||
|
<BasicTable
|
||||||
|
v-if="tabIndex === item.key"
|
||||||
|
:ref="el => setTableRef(el, item.key)"
|
||||||
|
:columns="tableColumns"
|
||||||
|
:scroll-y="500"
|
||||||
|
:scroll-x="800"
|
||||||
|
:list-url="listUrl"
|
||||||
|
:search-params="searchParams"
|
||||||
|
:transform-data="customTransform"
|
||||||
|
>
|
||||||
|
<!-- 沿程模式:断面名称列(带电站名称分组) -->
|
||||||
|
<template v-if="props.ly" #rsctdName="{ record }">
|
||||||
|
{{ record.rsctdName || '-' }}
|
||||||
|
</template>
|
||||||
|
<template v-if="props.ly" #stcdList="{ record }">
|
||||||
|
<div v-for="st in record.stcdList" :key="st.stcd">
|
||||||
|
<a @click="handleViewDetail(st.stcd, st.stnm)" class="text-link">
|
||||||
|
{{ st.stnm }}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template v-if="props.ly" #fishList="{ record }">
|
||||||
|
<a-list size="small" :data-source="record.fishList">
|
||||||
|
<template #renderItem="{ item }">
|
||||||
|
<a-list-item>{{ item }}</a-list-item>
|
||||||
|
</template>
|
||||||
|
</a-list>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- 基地模式:调查断面列 -->
|
||||||
|
<template v-else #stnm="{ record }">
|
||||||
|
<a @click="handleViewDetail(record.stcd, record.stnm)" class="text-link">
|
||||||
|
{{ record.stnm }}
|
||||||
|
</a>
|
||||||
|
</template>
|
||||||
|
<template v-else #name="{ record }">
|
||||||
|
{{ record.name || '-' }}
|
||||||
|
</template>
|
||||||
|
</BasicTable>
|
||||||
|
</div>
|
||||||
|
</a-tab-pane>
|
||||||
|
</a-tabs>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { ref, computed, watch, onMounted, nextTick, h } from 'vue';
|
||||||
|
import { Tooltip } from 'ant-design-vue';
|
||||||
|
import BasicTable from '@/components/BasicTable/index.vue';
|
||||||
|
import {
|
||||||
|
wbsbGetKendoList,
|
||||||
|
getStcdList,
|
||||||
|
getTabNum,
|
||||||
|
fishDetail,
|
||||||
|
fishQgcDetail
|
||||||
|
} from '@/api/stdc';
|
||||||
|
import dayjs from 'dayjs';
|
||||||
|
import { useModelStore } from '@/store/modules/model';
|
||||||
|
|
||||||
|
const modelStore = useModelStore();
|
||||||
|
|
||||||
|
// ==================== Props ====================
|
||||||
|
const props = defineProps<{
|
||||||
|
tm: string; // 年份,如 "2024"
|
||||||
|
dataDimensionVal: string; // 基地ID
|
||||||
|
hbrvcd: string; // 流域代码
|
||||||
|
jdList: any[]; // 基地列表
|
||||||
|
stateName: string; // 鱼类类型名称(如"外来鱼类")
|
||||||
|
sectionName: string; // 断面名称
|
||||||
|
stcd: string; // 站点代码
|
||||||
|
ly: boolean; // 沿程模式标识
|
||||||
|
}>();
|
||||||
|
|
||||||
|
// ==================== Tab 配置 ====================
|
||||||
|
const TAB_CONFIG = [
|
||||||
|
{ key: '1', label: '外来鱼类', prompt: '通过人为行为引入的本流域原生态没有的鱼类' },
|
||||||
|
{ key: '2', label: '本土受威胁鱼类', prompt: '流域原生态鱼类中处于近危或者濒危的鱼类' },
|
||||||
|
{ key: '3', label: '本土无危鱼类', prompt: '分类属于本土且保护类型属于无危的鱼类' }
|
||||||
|
];
|
||||||
|
|
||||||
|
// ==================== 搜索表单状态 ====================
|
||||||
|
const formState = ref({
|
||||||
|
baseId: props.dataDimensionVal || 'all',
|
||||||
|
rvcd: props.hbrvcd || '',
|
||||||
|
stcd: props.stcd || '',
|
||||||
|
yr: props.tm || dayjs().format('YYYY')
|
||||||
|
});
|
||||||
|
|
||||||
|
const yearValue = ref(props.tm ? dayjs(props.tm) : dayjs());
|
||||||
|
|
||||||
|
// ==================== 下拉选项 ====================
|
||||||
|
/** 基地下拉选项 */
|
||||||
|
const baseOptions = computed(() =>
|
||||||
|
props.jdList.map((item: any) => ({
|
||||||
|
label: item.wbsName,
|
||||||
|
value: item.wbsCode
|
||||||
|
}))
|
||||||
|
);
|
||||||
|
|
||||||
|
/** 流域下拉选项 */
|
||||||
|
const lyOptions = ref<Array<{ label: string; value: string }>>([]);
|
||||||
|
|
||||||
|
/** 断面下拉选项 */
|
||||||
|
const dmOptions = ref<Array<{ label: string; value: string }>>([
|
||||||
|
{ label: '全部', value: '' }
|
||||||
|
]);
|
||||||
|
|
||||||
|
// ==================== Tab 状态 ====================
|
||||||
|
/** 根据 props.stateName 确定初始 Tab */
|
||||||
|
const initTabIndex = (() => {
|
||||||
|
if (props.stateName === '外来鱼类') return '1';
|
||||||
|
if (props.stateName === '本土受威胁鱼类') return '2';
|
||||||
|
return '3';
|
||||||
|
})();
|
||||||
|
|
||||||
|
const tabIndex = ref(initTabIndex);
|
||||||
|
|
||||||
|
/** Tab 列表(带 Tooltip 渲染) */
|
||||||
|
const tabList = ref<any[]>(
|
||||||
|
TAB_CONFIG.map(item => ({
|
||||||
|
...item,
|
||||||
|
labelWithTooltip: renderTabLabel(item.label, item.prompt)
|
||||||
|
}))
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 渲染 Tab 标签(带 Tooltip 提示)
|
||||||
|
*/
|
||||||
|
function renderTabLabel(label: string, prompt: string) {
|
||||||
|
return () =>
|
||||||
|
h('div', { style: { display: 'flex', alignItems: 'center' } }, [
|
||||||
|
h('span', label),
|
||||||
|
h(
|
||||||
|
Tooltip,
|
||||||
|
{ placement: 'topLeft', title: prompt },
|
||||||
|
{
|
||||||
|
default: () =>
|
||||||
|
h('span', {
|
||||||
|
class: 'icon iconfont icon-iconQuestion',
|
||||||
|
style: { marginLeft: '4px', cursor: 'pointer' }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
)
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================== 表格配置 ====================
|
||||||
|
/** 沿程模式表格列 */
|
||||||
|
const lyColumns = [
|
||||||
|
{
|
||||||
|
key: 'rsctdName',
|
||||||
|
title: '电站名称',
|
||||||
|
dataIndex: 'rsctdName',
|
||||||
|
width: 200,
|
||||||
|
ellipsis: true,
|
||||||
|
slots: { customRender: 'rsctdName' }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'stcdList',
|
||||||
|
title: '调查断面',
|
||||||
|
dataIndex: 'stcdList',
|
||||||
|
width: 200,
|
||||||
|
ellipsis: true,
|
||||||
|
slots: { customRender: 'stcdList' }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'fishList',
|
||||||
|
title: '渔获物种类',
|
||||||
|
dataIndex: 'fishList',
|
||||||
|
width: 300,
|
||||||
|
ellipsis: true,
|
||||||
|
slots: { customRender: 'fishList' }
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
/** 基地模式表格列 */
|
||||||
|
const baseColumns = [
|
||||||
|
{
|
||||||
|
key: 'stnm',
|
||||||
|
title: '调查断面',
|
||||||
|
dataIndex: 'stnm',
|
||||||
|
fixed: 'left' as const,
|
||||||
|
width: 200,
|
||||||
|
ellipsis: true,
|
||||||
|
slots: { customRender: 'stnm' },
|
||||||
|
merge: true,
|
||||||
|
mergeDeps: ['stnm']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'name',
|
||||||
|
title: '渔获物种类',
|
||||||
|
dataIndex: 'name',
|
||||||
|
width: 300,
|
||||||
|
ellipsis: true,
|
||||||
|
slots: { customRender: 'name' }
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
/** 根据 ly 模式切换表格列 */
|
||||||
|
const tableColumns = computed(() => (props.ly ? lyColumns : baseColumns));
|
||||||
|
|
||||||
|
/** 根据 ly 模式切换表格 API */
|
||||||
|
const listUrl = computed(() => (props.ly ? fishDetail : fishQgcDetail));
|
||||||
|
|
||||||
|
/** 表格搜索参数 */
|
||||||
|
const searchParams = computed(() => ({
|
||||||
|
group: [],
|
||||||
|
select: [],
|
||||||
|
groupResultFlat: true
|
||||||
|
}));
|
||||||
|
|
||||||
|
/** 表格 ref 映射(每个 Tab 一个 ref) */
|
||||||
|
const tableRefs = ref<Record<string, any>>({});
|
||||||
|
|
||||||
|
function setTableRef(el: any, key: string) {
|
||||||
|
if (el) tableRefs.value[key] = el;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 数据转换 */
|
||||||
|
const customTransform = (res: any) => ({
|
||||||
|
records: res?.data?.data || [],
|
||||||
|
total: res?.data?.total || 0
|
||||||
|
});
|
||||||
|
|
||||||
|
// ==================== 获取流域列表 ====================
|
||||||
|
const fetchLyList = async () => {
|
||||||
|
try {
|
||||||
|
const params = {
|
||||||
|
filter: {
|
||||||
|
logic: 'and',
|
||||||
|
filters: [
|
||||||
|
{ field: 'wbsType', operator: 'eq', value: 'PSB_RVCD' },
|
||||||
|
{
|
||||||
|
field: 'fullPath',
|
||||||
|
operator: 'contains',
|
||||||
|
dataType: 'string',
|
||||||
|
value: formState.value.baseId + ','
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
group: [
|
||||||
|
{ dir: 'asc', field: 'objId' },
|
||||||
|
{ dir: 'asc', field: 'orderIndex' },
|
||||||
|
{ dir: 'des', field: 'wbsCode' },
|
||||||
|
{ dir: 'des', field: 'wbsName' }
|
||||||
|
],
|
||||||
|
groupResultFlat: true
|
||||||
|
};
|
||||||
|
|
||||||
|
const res = await wbsbGetKendoList(params);
|
||||||
|
const data = res?.data?.data || [];
|
||||||
|
|
||||||
|
if (data.length > 0) {
|
||||||
|
const options = data.map((item: any) => ({
|
||||||
|
label: item.wbsName,
|
||||||
|
value: item.wbsCode
|
||||||
|
}));
|
||||||
|
lyOptions.value = options;
|
||||||
|
|
||||||
|
// 匹配传入的 hbrvcd,否则默认第一个
|
||||||
|
const matched = options.find(o => o.value === props.hbrvcd);
|
||||||
|
formState.value.rvcd = matched?.value || options[0]?.value || '';
|
||||||
|
} else {
|
||||||
|
lyOptions.value = [];
|
||||||
|
formState.value.rvcd = '';
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取流域列表失败:', error);
|
||||||
|
lyOptions.value = [];
|
||||||
|
formState.value.rvcd = '';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// ==================== 获取断面列表 ====================
|
||||||
|
const fetchDmList = async () => {
|
||||||
|
try {
|
||||||
|
const params = {
|
||||||
|
logic: 'and',
|
||||||
|
filters: [
|
||||||
|
{ field: 'hbrvcd', operator: 'eq', value: formState.value.rvcd },
|
||||||
|
{ field: 'sttpCode', operator: 'eq', value: 'ENG' },
|
||||||
|
{
|
||||||
|
field: 'bldsttCcode',
|
||||||
|
operator: 'in',
|
||||||
|
value: ['1', '2']
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
const res = await getStcdList(params);
|
||||||
|
const data = res?.data?.data || [];
|
||||||
|
|
||||||
|
if (data.length > 0) {
|
||||||
|
const options = data.map((item: any) => ({
|
||||||
|
label: item.stnm,
|
||||||
|
value: item.stcd
|
||||||
|
}));
|
||||||
|
dmOptions.value = [{ label: '全部', value: '' }, ...options];
|
||||||
|
|
||||||
|
// 匹配传入的 sectionName,否则默认第一个
|
||||||
|
const matched = options.find(o => o.label === props.sectionName);
|
||||||
|
formState.value.stcd = matched?.value || options[0]?.value || '';
|
||||||
|
} else {
|
||||||
|
dmOptions.value = [{ label: '全部', value: '' }];
|
||||||
|
formState.value.stcd = '';
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取断面列表失败:', error);
|
||||||
|
dmOptions.value = [{ label: '全部', value: '' }];
|
||||||
|
formState.value.stcd = '';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// ==================== 获取 Tab 数量统计 ====================
|
||||||
|
const getTabNumData = async () => {
|
||||||
|
const params: any = {
|
||||||
|
baseId: formState.value.baseId,
|
||||||
|
rvcd: formState.value.rvcd,
|
||||||
|
yr: dayjs(yearValue.value).format('YYYY'),
|
||||||
|
ty: tabIndex.value
|
||||||
|
};
|
||||||
|
|
||||||
|
// 沿程模式传 stcd
|
||||||
|
if (props.ly) {
|
||||||
|
params.stcd = formState.value.stcd;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 空值过滤
|
||||||
|
if (!params.stcd) delete params.stcd;
|
||||||
|
if (params.baseId === 'all') delete params.baseId;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const res = await getTabNum(params);
|
||||||
|
// getTabNum 返回格式:res.data 直接是数组 [{ty, num}, ...]
|
||||||
|
const data = res?.data || [];
|
||||||
|
|
||||||
|
const list = TAB_CONFIG.map(item => ({
|
||||||
|
...item,
|
||||||
|
labelWithTooltip: renderTabLabel(item.label, item.prompt)
|
||||||
|
}));
|
||||||
|
|
||||||
|
if (data.length > 0) {
|
||||||
|
data.forEach((el: any) => {
|
||||||
|
const found = list.find(item => item.key === el?.ty);
|
||||||
|
if (found) {
|
||||||
|
found.labelWithTooltip = renderTabLabel(
|
||||||
|
`${found.label}(${el?.num}种)`,
|
||||||
|
found.prompt
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
tabList.value = list;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取 Tab 数量失败:', error);
|
||||||
|
tabList.value = TAB_CONFIG.map(item => ({
|
||||||
|
...item,
|
||||||
|
labelWithTooltip: renderTabLabel(item.label, item.prompt)
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// ==================== 搜索与重置 ====================
|
||||||
|
/** 构建表格过滤条件 */
|
||||||
|
const getFilter = () => {
|
||||||
|
const filters: any[] = [
|
||||||
|
{
|
||||||
|
field: 'baseId',
|
||||||
|
operator: 'eq',
|
||||||
|
dataType: 'string',
|
||||||
|
value: formState.value.baseId
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'rvcd',
|
||||||
|
operator: 'eq',
|
||||||
|
dataType: 'string',
|
||||||
|
value: formState.value.rvcd
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'yr',
|
||||||
|
operator: 'eq',
|
||||||
|
dataType: 'string',
|
||||||
|
value: dayjs(yearValue.value).format('YYYY')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'ty',
|
||||||
|
operator: 'eq',
|
||||||
|
dataType: 'string',
|
||||||
|
value: tabIndex.value
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
// 沿程模式传 stcd
|
||||||
|
if (props.ly && formState.value.stcd) {
|
||||||
|
filters.push({
|
||||||
|
field: 'stcd',
|
||||||
|
operator: 'eq',
|
||||||
|
dataType: 'string',
|
||||||
|
value: formState.value.stcd
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
logic: 'and',
|
||||||
|
filters: filters.filter(Boolean)
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
/** 查询 */
|
||||||
|
const handleSearch = () => {
|
||||||
|
const filter = getFilter();
|
||||||
|
const currentTable = tableRefs.value[tabIndex.value];
|
||||||
|
currentTable?.getList(filter);
|
||||||
|
getTabNumData();
|
||||||
|
};
|
||||||
|
|
||||||
|
/** 重置 */
|
||||||
|
const handleReset = () => {
|
||||||
|
formState.value = {
|
||||||
|
baseId: props.dataDimensionVal || 'all',
|
||||||
|
rvcd: props.hbrvcd || '',
|
||||||
|
stcd: props.stcd || '',
|
||||||
|
yr: props.tm || dayjs().format('YYYY')
|
||||||
|
};
|
||||||
|
yearValue.value = props.tm ? dayjs(props.tm) : dayjs();
|
||||||
|
handleSearch();
|
||||||
|
};
|
||||||
|
|
||||||
|
// ==================== 表单联动 ====================
|
||||||
|
/** 基地变化 */
|
||||||
|
const handleBaseChange = () => {
|
||||||
|
formState.value.rvcd = '';
|
||||||
|
formState.value.stcd = '';
|
||||||
|
fetchLyList();
|
||||||
|
};
|
||||||
|
|
||||||
|
/** 流域变化 */
|
||||||
|
const handleRvcdChange = () => {
|
||||||
|
formState.value.stcd = '';
|
||||||
|
if (props.ly) {
|
||||||
|
fetchDmList();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/** 断面变化 */
|
||||||
|
const handleStcdChange = () => {
|
||||||
|
// 仅更新表单值,不触发请求
|
||||||
|
};
|
||||||
|
|
||||||
|
/** 年份变化 */
|
||||||
|
const handleYearChange = () => {
|
||||||
|
// 年份变化不自动请求,需点击查询
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Tab 切换 */
|
||||||
|
const handleTabChange = () => {
|
||||||
|
handleSearch();
|
||||||
|
};
|
||||||
|
|
||||||
|
// ==================== 查看详情 ====================
|
||||||
|
/**
|
||||||
|
* 点击断面链接弹出详情
|
||||||
|
* 参考 SsstdcgkTk.vue 的 handleViewDetail
|
||||||
|
*/
|
||||||
|
const handleViewDetail = (stcd: string, stnm: string) => {
|
||||||
|
modelStore.modalVisible = true;
|
||||||
|
modelStore.params.sttp = 'WE';
|
||||||
|
modelStore.params.stcd = stcd;
|
||||||
|
modelStore.params.timeRange = [
|
||||||
|
dayjs(yearValue.value).startOf('year'),
|
||||||
|
dayjs(yearValue.value).endOf('year')
|
||||||
|
];
|
||||||
|
modelStore.params.ty1 = tabIndex.value;
|
||||||
|
modelStore.title = stnm;
|
||||||
|
};
|
||||||
|
|
||||||
|
// ==================== Watch ====================
|
||||||
|
/** 监听 props 变化,同步表单状态 */
|
||||||
|
watch(
|
||||||
|
() => [props.tm, props.hbrvcd, props.stcd, props.sectionName],
|
||||||
|
() => {
|
||||||
|
formState.value = {
|
||||||
|
baseId: props.dataDimensionVal || 'all',
|
||||||
|
rvcd: props.hbrvcd || '',
|
||||||
|
stcd: props.stcd || '',
|
||||||
|
yr: props.tm || dayjs().format('YYYY')
|
||||||
|
};
|
||||||
|
yearValue.value = props.tm ? dayjs(props.tm) : dayjs();
|
||||||
|
},
|
||||||
|
{ immediate: false }
|
||||||
|
);
|
||||||
|
|
||||||
|
// ==================== 生命周期 ====================
|
||||||
|
onMounted(async () => {
|
||||||
|
await nextTick();
|
||||||
|
|
||||||
|
// 获取流域列表
|
||||||
|
await fetchLyList();
|
||||||
|
|
||||||
|
// 沿程模式获取断面列表
|
||||||
|
if (props.ly) {
|
||||||
|
await fetchDmList();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取 Tab 数量
|
||||||
|
await getTabNumData();
|
||||||
|
|
||||||
|
// 执行首次查询
|
||||||
|
handleSearch();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.yancheng-yulei {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
.search-form {
|
||||||
|
padding: 16px 0;
|
||||||
|
border-bottom: 1px solid #f0f0f0;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ylycbh-tabs {
|
||||||
|
flex: 1;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
:deep(.ant-tabs-content) {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.ant-tabs-tabpane) {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-wrapper {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-link {
|
||||||
|
color: #1990ff;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: #40a9ff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -1,67 +1,171 @@
|
|||||||
<!-- SidePanelItem.vue -->
|
<!-- 沿程鱼类变化情况 -->
|
||||||
<template>
|
<template>
|
||||||
<SidePanelItem title="水生生态调查" :select="select" :datetimePicker="datetimePicker">
|
<SidePanelItem
|
||||||
<div ref="chartRef" class="chart-container"></div>
|
title="沿程鱼类变化情况"
|
||||||
|
:moreSelect="moreSelect"
|
||||||
|
:select="select"
|
||||||
|
@update-values="handlePanelChange"
|
||||||
|
>
|
||||||
|
<a-spin :spinning="dataLoading" tip="加载中...">
|
||||||
|
<div
|
||||||
|
v-show="chartData.length > 0 && select.options.length > 0"
|
||||||
|
ref="chartRef"
|
||||||
|
class="chart-container"
|
||||||
|
></div>
|
||||||
|
<a-empty
|
||||||
|
v-show="
|
||||||
|
!dataLoading &&
|
||||||
|
(chartData.length === 0 || select.options.length === 0)
|
||||||
|
"
|
||||||
|
:description="select.options.length === 0 ? '暂无数据' : '暂无数据'"
|
||||||
|
class="empty-placeholder"
|
||||||
|
/>
|
||||||
|
</a-spin>
|
||||||
</SidePanelItem>
|
</SidePanelItem>
|
||||||
|
|
||||||
|
<!-- 沿程鱼类变化详情弹框 -->
|
||||||
|
<a-modal
|
||||||
|
v-model:open="modalVisible"
|
||||||
|
title="沿程鱼类变化"
|
||||||
|
width="1536px"
|
||||||
|
:footer="null"
|
||||||
|
@cancel="handleModalClose"
|
||||||
|
>
|
||||||
|
<Yanchengyulei
|
||||||
|
v-if="modalVisible"
|
||||||
|
:tm="modalYear"
|
||||||
|
:dataDimensionVal="baseid"
|
||||||
|
:hbrvcd="modalHbrvcd"
|
||||||
|
:jdList="JidiSelectEventStore.jidiData"
|
||||||
|
:stateName="modalStateName"
|
||||||
|
:sectionName="modalSectionName"
|
||||||
|
:stcd="modalStcd"
|
||||||
|
:ly="true"
|
||||||
|
/>
|
||||||
|
</a-modal>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { ref, onMounted, onUnmounted, nextTick } from 'vue';
|
import { ref, watch, onMounted, onUnmounted, nextTick } 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 Yanchengyulei from './TwoLayers/Yanchengyulei.vue';
|
||||||
|
import { useJidiSelectEventStore } from '@/store/modules/jidiSelectEvent';
|
||||||
|
import { wbsbGetKendoList, getDftYear, fishChanges2 } from '@/api/stdc';
|
||||||
|
import moment from 'moment';
|
||||||
|
|
||||||
// 定义组件名
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: 'shuiShengShengTaiDiaoCha'
|
name: 'alongFishMod'
|
||||||
});
|
});
|
||||||
|
|
||||||
// ==================== 图表相关 ====================
|
// ==================== Store ====================
|
||||||
const chartRef = ref<HTMLDivElement>();
|
const JidiSelectEventStore = useJidiSelectEventStore();
|
||||||
let chartInstance: any = null;
|
|
||||||
|
|
||||||
// 选择器配置
|
// ==================== 基础状态 ====================
|
||||||
|
/** 当前选中的基地ID,来自 JidiSelectEventStore.selectedItem */
|
||||||
|
const baseid = ref('');
|
||||||
|
/** 图表数据加载状态 */
|
||||||
|
const dataLoading = ref(false);
|
||||||
|
/** 初始化标志位:初始化期间不响应控件变化触发数据请求 */
|
||||||
|
const isInitializing = ref(false);
|
||||||
|
|
||||||
|
// ==================== 选择器状态 ====================
|
||||||
|
/** 流域树形选择器配置 */
|
||||||
|
const moreSelect = ref({
|
||||||
|
show: true,
|
||||||
|
value: undefined as string | undefined,
|
||||||
|
options: [] as any[]
|
||||||
|
});
|
||||||
|
|
||||||
|
/** 年份选择器配置 */
|
||||||
const select = ref({
|
const select = ref({
|
||||||
show: true,
|
show: true,
|
||||||
value: undefined,
|
value: undefined as string | undefined,
|
||||||
options: [],
|
options: [] as any[],
|
||||||
picker: undefined,
|
picker: undefined,
|
||||||
format: undefined
|
format: undefined,
|
||||||
|
width: '120px'
|
||||||
});
|
});
|
||||||
|
|
||||||
// 日期选择器配置
|
/**
|
||||||
const datetimePicker = ref({
|
* 按流域分组的年份数据
|
||||||
show: true,
|
* 结构:{ "wbsCode1": [{label:'2023', value:'2023'}], "wbsCode2": [...] }
|
||||||
value: '2020',
|
* 用于流域切换时快速更新年份下拉选项
|
||||||
format: 'YYYY',
|
*/
|
||||||
picker: 'year' as const,
|
const yearOptions = ref<Record<string, any[]>>({});
|
||||||
options: []
|
|
||||||
});
|
|
||||||
|
|
||||||
// 初始化图表
|
// ==================== 图表状态 ====================
|
||||||
|
const chartRef = ref<HTMLDivElement>();
|
||||||
|
let chartInstance: any = null;
|
||||||
|
/** 图表原始数据,由 API 返回 */
|
||||||
|
const chartData = ref<any[]>([]);
|
||||||
|
|
||||||
|
// ==================== 弹框状态 ====================
|
||||||
|
const modalVisible = ref(false);
|
||||||
|
const modalYear = ref('');
|
||||||
|
const modalHbrvcd = ref('');
|
||||||
|
const modalStateName = ref('');
|
||||||
|
const modalSectionName = ref('');
|
||||||
|
const modalStcd = ref('');
|
||||||
|
|
||||||
|
// ==================== 图表相关 ====================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化 ECharts 实例
|
||||||
|
* 在 onMounted 中调用,绑定点击事件用于打开详情弹框
|
||||||
|
*/
|
||||||
const initChart = () => {
|
const initChart = () => {
|
||||||
if (!chartRef.value) return;
|
if (!chartRef.value) return;
|
||||||
|
|
||||||
// 销毁已存在的实例
|
|
||||||
if (chartInstance) {
|
if (chartInstance) {
|
||||||
chartInstance.dispose();
|
chartInstance.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 创建新实例
|
|
||||||
chartInstance = echarts.init(chartRef.value);
|
chartInstance = echarts.init(chartRef.value);
|
||||||
|
|
||||||
// 图表配置
|
// 点击柱子弹出详情
|
||||||
|
chartInstance.on('click', (params: any) => {
|
||||||
|
if (params.componentType === 'series') {
|
||||||
|
openDetail(params);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
updateChart();
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据 chartData 更新图表配置
|
||||||
|
* 将 API 返回的 detail 对象(key: 1=外来鱼类, 2=本土受威胁, 3=本土无危)
|
||||||
|
* 转换为 ECharts 堆叠柱状图数据
|
||||||
|
*/
|
||||||
|
const updateChart = () => {
|
||||||
|
if (!chartInstance) return;
|
||||||
|
|
||||||
|
// 按 sort 字段排序
|
||||||
|
const sortedData = [...chartData.value].sort(
|
||||||
|
(a, b) => (a.sort || 0) - (b.sort || 0)
|
||||||
|
);
|
||||||
|
|
||||||
|
const waimaiData: number[] = [];
|
||||||
|
const bentuThreatData: number[] = [];
|
||||||
|
const bentuSafeData: number[] = [];
|
||||||
|
const xAxisData: string[] = [];
|
||||||
|
|
||||||
|
sortedData.forEach((item: any) => {
|
||||||
|
xAxisData.push(item.stnm || '');
|
||||||
|
waimaiData.push(item.detail?.['1'] || 0);
|
||||||
|
bentuThreatData.push(item.detail?.['2'] || 0);
|
||||||
|
bentuSafeData.push(item.detail?.['3'] || 0);
|
||||||
|
});
|
||||||
|
|
||||||
const option: EChartsOption = {
|
const option: EChartsOption = {
|
||||||
tooltip: {
|
tooltip: {
|
||||||
trigger: 'axis',
|
trigger: 'axis',
|
||||||
axisPointer: {
|
axisPointer: { type: 'shadow' },
|
||||||
type: 'shadow'
|
|
||||||
},
|
|
||||||
backgroundColor: 'rgba(0, 0, 0, 0.5)',
|
backgroundColor: 'rgba(0, 0, 0, 0.5)',
|
||||||
borderColor: 'transparent',
|
borderColor: 'transparent',
|
||||||
textStyle: {
|
textStyle: { color: '#ffffff' }
|
||||||
color: '#ffffff'
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
grid: {
|
grid: {
|
||||||
top: 30,
|
top: 30,
|
||||||
@ -79,105 +183,412 @@ const initChart = () => {
|
|||||||
type: 'value',
|
type: 'value',
|
||||||
minInterval: 1,
|
minInterval: 1,
|
||||||
name: '种',
|
name: '种',
|
||||||
axisLine: {
|
axisLine: { lineStyle: { color: '#e0e0e0' } },
|
||||||
lineStyle: {
|
axisLabel: { color: '#666' },
|
||||||
color: '#e0e0e0'
|
splitLine: { lineStyle: { color: '#e0e0e0', type: 'solid' } }
|
||||||
}
|
|
||||||
},
|
|
||||||
axisLabel: {
|
|
||||||
color: '#666'
|
|
||||||
},
|
|
||||||
splitLine: {
|
|
||||||
lineStyle: {
|
|
||||||
color: '#e0e0e0',
|
|
||||||
type: 'solid'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
xAxis: {
|
xAxis: {
|
||||||
type: 'category',
|
type: 'category',
|
||||||
axisTick: {
|
axisTick: { show: false },
|
||||||
show: false
|
|
||||||
},
|
|
||||||
axisLabel: {
|
axisLabel: {
|
||||||
interval: 0,
|
interval: 0,
|
||||||
rotate: 0,
|
rotate: 0,
|
||||||
color: '#666',
|
color: '#666',
|
||||||
rich: {
|
rich: {
|
||||||
a: {
|
a: { height: 24, align: 'center' },
|
||||||
height: 24,
|
b: { height: 30, align: 'center' }
|
||||||
align: 'center'
|
|
||||||
},
|
|
||||||
b: {
|
|
||||||
height: 30,
|
|
||||||
align: 'center'
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
name: '数量(个)',
|
name: '数量(个)',
|
||||||
data: [
|
data: xAxisData
|
||||||
'两河口',
|
|
||||||
'杨房沟',
|
|
||||||
'锦屏一级',
|
|
||||||
'锦屏二级',
|
|
||||||
'官地',
|
|
||||||
'二滩',
|
|
||||||
'桐子林'
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
series: [
|
series: [
|
||||||
{
|
{
|
||||||
name: '外来鱼类',
|
name: '外来鱼类',
|
||||||
type: 'bar',
|
type: 'bar',
|
||||||
stack: 'stack',
|
stack: 'stack',
|
||||||
data: [0, 0, 0, 0, 0, 1, 2],
|
data: waimaiData,
|
||||||
label: {
|
label: { show: false }
|
||||||
show: true,
|
|
||||||
position: 'top',
|
|
||||||
color: '#FFF'
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: '本土受威胁鱼类',
|
name: '本土受威胁鱼类',
|
||||||
type: 'bar',
|
type: 'bar',
|
||||||
stack: 'stack',
|
stack: 'stack',
|
||||||
data: [1, 10, 10, 5, 4, 6, 11],
|
data: bentuThreatData,
|
||||||
label: {
|
label: { show: false }
|
||||||
show: true,
|
|
||||||
position: 'top',
|
|
||||||
color: '#FFF'
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: '本土无危鱼类',
|
name: '本土无危鱼类',
|
||||||
type: 'bar',
|
type: 'bar',
|
||||||
stack: 'stack',
|
stack: 'stack',
|
||||||
data: [1, 10, 6, 5, 2, 13, 23],
|
data: bentuSafeData,
|
||||||
label: {
|
label: { show: false }
|
||||||
show: true,
|
|
||||||
position: 'top',
|
|
||||||
color: '#FFF'
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
barWidth: 15,
|
barWidth: 15,
|
||||||
color: ['#4B79AB', '#78C300', '#9556A4']
|
color: ['#4B79AB', '#78C300', '#9556A4']
|
||||||
};
|
};
|
||||||
|
|
||||||
// 设置图表配置
|
|
||||||
chartInstance.setOption(option);
|
chartInstance.setOption(option);
|
||||||
|
nextTick(() => {
|
||||||
|
chartInstance.resize();
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// 处理窗口大小变化
|
/** 窗口大小变化时自适应 */
|
||||||
const handleResize = () => {
|
const handleResize = () => {
|
||||||
if (chartInstance) {
|
if (chartInstance) {
|
||||||
chartInstance.resize();
|
chartInstance.resize();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// ==================== 数据获取 ====================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取年份选项数据
|
||||||
|
* 调用 getDftYear API,按基地/流域分组返回可用年份
|
||||||
|
* 初始化时调用,为每个流域缓存其对应的年份列表
|
||||||
|
*/
|
||||||
|
const getYearData = async () => {
|
||||||
|
try {
|
||||||
|
const filter: any = {};
|
||||||
|
if (baseid.value && baseid.value !== 'all') {
|
||||||
|
filter.baseId = baseid.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
const params = {
|
||||||
|
filter,
|
||||||
|
group: [{ dir: 'des', field: 'yr' }]
|
||||||
|
};
|
||||||
|
|
||||||
|
const res = await getDftYear(params);
|
||||||
|
const data = res?.data?.data?.[0] || {};
|
||||||
|
|
||||||
|
if (Object.keys(data).length > 0) {
|
||||||
|
const objYear: Record<string, any[]> = {};
|
||||||
|
const allYears: any[] = [];
|
||||||
|
|
||||||
|
Object.keys(data).forEach(key => {
|
||||||
|
objYear[key] = data[key].map((item: any) => ({
|
||||||
|
label: String(item),
|
||||||
|
value: String(item)
|
||||||
|
}));
|
||||||
|
allYears.push(...data[key]);
|
||||||
|
});
|
||||||
|
|
||||||
|
yearOptions.value = objYear;
|
||||||
|
|
||||||
|
// 去重排序(降序),取最新年份作为默认值
|
||||||
|
const uniqueYears = [...new Set(allYears)].sort(
|
||||||
|
(a: any, b: any) => Number(b) - Number(a)
|
||||||
|
);
|
||||||
|
const latestYear = uniqueYears[0];
|
||||||
|
|
||||||
|
// baseid 为 all 时取第一个有数据的流域
|
||||||
|
const firstWbsCode = Object.keys(data)[0];
|
||||||
|
select.value.options = objYear[firstWbsCode] || [];
|
||||||
|
select.value.value = latestYear
|
||||||
|
? String(latestYear)
|
||||||
|
: moment().format('YYYY');
|
||||||
|
} else {
|
||||||
|
yearOptions.value = {};
|
||||||
|
select.value.options = [];
|
||||||
|
select.value.value = moment().format('YYYY');
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取年份数据失败:', error);
|
||||||
|
select.value.options = [];
|
||||||
|
select.value.value = moment().format('YYYY');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据选中的流域代码更新年份下拉选项
|
||||||
|
* 从 yearOptions 缓存中查找对应流域的年份列表并更新 select.options
|
||||||
|
* @param wbsCode 流域代码
|
||||||
|
*/
|
||||||
|
const updateYearOptionsByBase = async (wbsCode: string) => {
|
||||||
|
const options = yearOptions.value[wbsCode];
|
||||||
|
if (options) {
|
||||||
|
select.value.options = options;
|
||||||
|
await nextTick();
|
||||||
|
// 默认选中最新年份(数组第一项)
|
||||||
|
select.value.value = options[0]?.value || '';
|
||||||
|
} else {
|
||||||
|
select.value.options = [];
|
||||||
|
await nextTick();
|
||||||
|
select.value.value = '';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取流域树形选择器数据 + 年份数据 + 图表数据
|
||||||
|
* 在基地变化时调用,是整个模块数据加载的入口
|
||||||
|
*/
|
||||||
|
const getMoreSelectData = async () => {
|
||||||
|
if (!baseid.value) return;
|
||||||
|
|
||||||
|
isInitializing.value = true;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const params = {
|
||||||
|
filter: {
|
||||||
|
logic: 'and',
|
||||||
|
filters: [
|
||||||
|
{
|
||||||
|
field: 'wbsType',
|
||||||
|
operator: 'eq',
|
||||||
|
value: 'PSB_RVCD'
|
||||||
|
},
|
||||||
|
baseid.value !== 'all'
|
||||||
|
? {
|
||||||
|
field: 'objid',
|
||||||
|
operator: 'eq',
|
||||||
|
value: baseid.value
|
||||||
|
}
|
||||||
|
: null
|
||||||
|
].filter(Boolean)
|
||||||
|
},
|
||||||
|
group: [
|
||||||
|
{ dir: 'asc', field: 'orderIndex' },
|
||||||
|
{ dir: 'asc', field: 'wbsCode' },
|
||||||
|
{ dir: 'asc', field: 'wbsName' },
|
||||||
|
{ dir: 'asc', field: 'objid' }
|
||||||
|
],
|
||||||
|
groupResultFlat: true
|
||||||
|
};
|
||||||
|
|
||||||
|
const res = await wbsbGetKendoList(params);
|
||||||
|
const data = res?.data?.data || [];
|
||||||
|
|
||||||
|
if (data.length > 0) {
|
||||||
|
const treeData = buildTreeData(data);
|
||||||
|
const filteredData = filterSelectOptions(treeData);
|
||||||
|
|
||||||
|
// 只有一个流域时隐藏树形选择器
|
||||||
|
moreSelect.value.options = filteredData;
|
||||||
|
moreSelect.value.value = treeData?.[0]?.children?.[0]?.value;
|
||||||
|
|
||||||
|
// 先获取年份数据缓存到 yearOptions,再根据实际选中的流域设置年份选项
|
||||||
|
await getYearData();
|
||||||
|
if (moreSelect.value.value) {
|
||||||
|
await updateYearOptionsByBase(moreSelect.value.value);
|
||||||
|
}
|
||||||
|
await getChartData();
|
||||||
|
} else {
|
||||||
|
moreSelect.value.options = [];
|
||||||
|
moreSelect.value.value = undefined;
|
||||||
|
await getYearData();
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取树形选择器数据失败:', error);
|
||||||
|
moreSelect.value.options = [];
|
||||||
|
moreSelect.value.value = undefined;
|
||||||
|
} finally {
|
||||||
|
isInitializing.value = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取图表数据
|
||||||
|
* 根据当前选中的流域(hbrvcd)、年份(yr)、基地(baseId)调用 fishChanges2 API
|
||||||
|
*/
|
||||||
|
const getChartData = async () => {
|
||||||
|
if (!select.value.value) return;
|
||||||
|
|
||||||
|
dataLoading.value = true;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const params = {
|
||||||
|
filter: {
|
||||||
|
logic: 'and',
|
||||||
|
filters: [
|
||||||
|
moreSelect.value.value
|
||||||
|
? {
|
||||||
|
field: 'hbrvcd',
|
||||||
|
operator: 'eq',
|
||||||
|
dataType: 'string',
|
||||||
|
value: moreSelect.value.value
|
||||||
|
}
|
||||||
|
: null,
|
||||||
|
{
|
||||||
|
field: 'yr',
|
||||||
|
operator: 'eq',
|
||||||
|
dataType: 'string',
|
||||||
|
value: moment(select.value.value).format('YYYY')
|
||||||
|
},
|
||||||
|
baseid.value !== 'all'
|
||||||
|
? {
|
||||||
|
field: 'baseId',
|
||||||
|
operator: 'eq',
|
||||||
|
dataType: 'string',
|
||||||
|
value: baseid.value
|
||||||
|
}
|
||||||
|
: null
|
||||||
|
].filter(Boolean)
|
||||||
|
},
|
||||||
|
group: [
|
||||||
|
{ dir: 'des', field: 'stnm' },
|
||||||
|
{ dir: 'des', field: 'stcd' },
|
||||||
|
{ dir: 'des', field: 'ty' }
|
||||||
|
],
|
||||||
|
groupResultFlat: 'true'
|
||||||
|
};
|
||||||
|
|
||||||
|
const res = await fishChanges2(params);
|
||||||
|
chartData.value = res?.data?.data || [];
|
||||||
|
nextTick(() => {
|
||||||
|
updateChart();
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取图表数据失败:', error);
|
||||||
|
chartData.value = [];
|
||||||
|
nextTick(() => {
|
||||||
|
updateChart();
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
dataLoading.value = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// ==================== 树形工具函数 ====================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将 API 返回的扁平数据转换为树形结构
|
||||||
|
* 按 objid(基地)分组,每个基地下包含其流域列表
|
||||||
|
* @param data 扁平的流域数据数组
|
||||||
|
*/
|
||||||
|
const buildTreeData = (data: any[]) => {
|
||||||
|
if (!data || data.length === 0) return [];
|
||||||
|
|
||||||
|
// 构建基地名称映射
|
||||||
|
const jiDiListMap: Record<string, string> = {};
|
||||||
|
JidiSelectEventStore.jidiData.forEach((item: any) => {
|
||||||
|
jiDiListMap[item.wbsCode] = item.wbsName;
|
||||||
|
});
|
||||||
|
|
||||||
|
// 按 objid 分组并去重
|
||||||
|
const dataMapNameMap: Record<string, any[]> = {};
|
||||||
|
data.forEach(item => {
|
||||||
|
const objId = item.objid || item.objId;
|
||||||
|
if (!dataMapNameMap[objId]) {
|
||||||
|
dataMapNameMap[objId] = [];
|
||||||
|
}
|
||||||
|
const exists = dataMapNameMap[objId].some(
|
||||||
|
existing => existing.wbsCode === item.wbsCode
|
||||||
|
);
|
||||||
|
if (!exists) {
|
||||||
|
dataMapNameMap[objId].push(item);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const treeData = Object.keys(dataMapNameMap).map(objId => ({
|
||||||
|
title: jiDiListMap[objId] || '---',
|
||||||
|
value: objId,
|
||||||
|
selectable: false,
|
||||||
|
children: dataMapNameMap[objId].map((item: any) => ({
|
||||||
|
title: item.wbsName,
|
||||||
|
value: item.wbsCode
|
||||||
|
}))
|
||||||
|
}));
|
||||||
|
|
||||||
|
// 排序:数字在前升序,非数字排在最后
|
||||||
|
treeData.sort((a, b) => {
|
||||||
|
const numA = Number(a.value);
|
||||||
|
const numB = Number(b.value);
|
||||||
|
const aIsNum = !isNaN(numA);
|
||||||
|
const bIsNum = !isNaN(numB);
|
||||||
|
if (aIsNum && bIsNum) return numA - numB;
|
||||||
|
if (aIsNum) return -1;
|
||||||
|
if (bIsNum) return 1;
|
||||||
|
return a.value.localeCompare(b.value);
|
||||||
|
});
|
||||||
|
|
||||||
|
return treeData;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 过滤树形选择器数据
|
||||||
|
* 当只有一个基地且只有一个流域时,直接返回流域列表(跳过基地层级)
|
||||||
|
*/
|
||||||
|
const filterSelectOptions = (options: any[]) => {
|
||||||
|
if (options?.length === 1 && options?.[0]?.children?.length === 1) {
|
||||||
|
return options[0].children;
|
||||||
|
}
|
||||||
|
return options;
|
||||||
|
};
|
||||||
|
|
||||||
|
// ==================== 弹框相关 ====================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 打开详情弹框
|
||||||
|
* 点击图表柱子时触发,传递当前选中的年份、流域、站点名称等参数
|
||||||
|
*/
|
||||||
|
const openDetail = (params: any) => {
|
||||||
|
// 从 chartData 中查找对应站点的 stcd
|
||||||
|
const found = chartData.value.find((el: any) => el.stnm === params.name);
|
||||||
|
|
||||||
|
modalYear.value = select.value.value;
|
||||||
|
modalHbrvcd.value = moreSelect.value.value || '';
|
||||||
|
modalStateName.value = params.name || '';
|
||||||
|
modalSectionName.value = params.name || '';
|
||||||
|
modalStcd.value = found?.stcd || '';
|
||||||
|
modalVisible.value = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
/** 关闭详情弹框 */
|
||||||
|
const handleModalClose = () => {
|
||||||
|
modalVisible.value = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
// ==================== 事件处理 ====================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理侧边面板控件变化(流域或年份选择改变时触发)
|
||||||
|
* SidePanelItem 内部通过 watch 监听用户操作后 emit 此事件
|
||||||
|
* @param data 包含 { select, moreSelect, ... } 的变更值
|
||||||
|
*/
|
||||||
|
const handlePanelChange = async (data: any) => {
|
||||||
|
// 初始化期间不响应控件变化
|
||||||
|
if (isInitializing.value) return;
|
||||||
|
|
||||||
|
// 流域选择变化:更新年份选项 + 请求图表数据
|
||||||
|
if (
|
||||||
|
data.moreSelect !== undefined &&
|
||||||
|
data.moreSelect !== moreSelect.value.value
|
||||||
|
) {
|
||||||
|
moreSelect.value.value = data.moreSelect;
|
||||||
|
await updateYearOptionsByBase(data.moreSelect);
|
||||||
|
await getChartData();
|
||||||
|
return; // 已用新年份请求数据,避免年份变化再次触发请求
|
||||||
|
}
|
||||||
|
|
||||||
|
// 年份选择变化:直接请求图表数据
|
||||||
|
if (data.select !== undefined && data.select !== select.value.value) {
|
||||||
|
select.value.value = data.select;
|
||||||
|
await getChartData();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// ==================== Watch 监听 ====================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 监听基地变化
|
||||||
|
* 当用户在顶部切换基地时,重新加载流域列表、年份和图表数据
|
||||||
|
*/
|
||||||
|
watch(
|
||||||
|
() => JidiSelectEventStore.selectedItem,
|
||||||
|
async newVal => {
|
||||||
|
if (newVal && newVal.wbsCode) {
|
||||||
|
baseid.value = newVal.wbsCode;
|
||||||
|
await getMoreSelectData();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ deep: true, immediate: true }
|
||||||
|
);
|
||||||
|
|
||||||
// ==================== 生命周期钩子 ====================
|
// ==================== 生命周期钩子 ====================
|
||||||
|
|
||||||
// 页面加载时执行
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
@ -187,12 +598,9 @@ onMounted(() => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// 组件卸载时清理
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
// 移除事件监听
|
|
||||||
window.removeEventListener('resize', handleResize);
|
window.removeEventListener('resize', handleResize);
|
||||||
|
|
||||||
// 销毁图表实例
|
|
||||||
if (chartInstance) {
|
if (chartInstance) {
|
||||||
chartInstance.dispose();
|
chartInstance.dispose();
|
||||||
chartInstance = null;
|
chartInstance = null;
|
||||||
@ -206,4 +614,18 @@ onUnmounted(() => {
|
|||||||
height: 231px;
|
height: 231px;
|
||||||
min-height: 231px;
|
min-height: 231px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.empty-placeholder {
|
||||||
|
width: 100%;
|
||||||
|
min-height: 231px;
|
||||||
|
display: flex;
|
||||||
|
background: #fff;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.ant-empty-description) {
|
||||||
|
text-indent: 0em !important;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
@ -14,7 +14,7 @@ onMounted(() => {});
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="jidiSelectorMod">
|
<div class="jidiSelectorMod">
|
||||||
<a-spin :spinning="loading">
|
<a-spin :spinning="JidiSelectEventStore.loading">
|
||||||
<div
|
<div
|
||||||
class="qgc-dropdown-select"
|
class="qgc-dropdown-select"
|
||||||
@mouseenter="jidiDataNum = JidiSelectEventStore.jidiData.length"
|
@mouseenter="jidiDataNum = JidiSelectEventStore.jidiData.length"
|
||||||
|
|||||||
234
frontend/src/modules/lushengshengtaijiance/LsstjkTk.vue
Normal file
234
frontend/src/modules/lushengshengtaijiance/LsstjkTk.vue
Normal file
@ -0,0 +1,234 @@
|
|||||||
|
<template>
|
||||||
|
<div class="lsstjk-table">
|
||||||
|
<!-- 表格 -->
|
||||||
|
<div class="table-wrapper">
|
||||||
|
<BasicTable
|
||||||
|
ref="tableRef"
|
||||||
|
:columns="columns"
|
||||||
|
:scroll-y="500"
|
||||||
|
:scroll-x="800"
|
||||||
|
:list-url="listUrl"
|
||||||
|
:search-params="searchParams"
|
||||||
|
:transform-data="customTransform"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { ref, computed, watch, onMounted } from 'vue';
|
||||||
|
import BasicTable from '@/components/BasicTable/index.vue';
|
||||||
|
import { getVmsstbprptList, getTetSpecList } from '@/api/stdc';
|
||||||
|
import dayjs from 'dayjs';
|
||||||
|
|
||||||
|
// ==================== Props ====================
|
||||||
|
const props = defineProps<{
|
||||||
|
selectDateTime?: any;
|
||||||
|
orderIndex?: string;
|
||||||
|
hbrvcd?: string;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
// ==================== 搜索状态 ====================
|
||||||
|
const tableRef = ref();
|
||||||
|
const yearValue = ref(
|
||||||
|
props.selectDateTime ? dayjs(props.selectDateTime) : dayjs()
|
||||||
|
);
|
||||||
|
|
||||||
|
// ==================== 接口URL ====================
|
||||||
|
const listUrl = computed(() => {
|
||||||
|
return props.orderIndex === '20' ? getTetSpecList : getVmsstbprptList;
|
||||||
|
});
|
||||||
|
|
||||||
|
// ==================== 表格列配置 ====================
|
||||||
|
const columns = computed(() => {
|
||||||
|
if (props.orderIndex === '20') {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
key: 'name',
|
||||||
|
title: '名称',
|
||||||
|
dataIndex: 'name',
|
||||||
|
fixed: 'left' as const,
|
||||||
|
width: 200,
|
||||||
|
ellipsis: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'nameEn',
|
||||||
|
title: '英文名称',
|
||||||
|
dataIndex: 'nameEn',
|
||||||
|
width: 200,
|
||||||
|
ellipsis: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'protectlvlName',
|
||||||
|
title: '保护级别',
|
||||||
|
dataIndex: 'protectlvlName',
|
||||||
|
width: 200,
|
||||||
|
ellipsis: true
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}else{
|
||||||
|
// orderIndex === '16' 红外相机
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
key: 'box',
|
||||||
|
title: '监测区域',
|
||||||
|
dataIndex: 'box',
|
||||||
|
fixed: 'left' as const,
|
||||||
|
width: 200,
|
||||||
|
ellipsis: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'stnm',
|
||||||
|
title: '站点名称',
|
||||||
|
dataIndex: 'stnm',
|
||||||
|
width: 200,
|
||||||
|
ellipsis: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'baseName',
|
||||||
|
title: '所属基地',
|
||||||
|
dataIndex: 'baseName',
|
||||||
|
width: 200,
|
||||||
|
ellipsis: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'stlc',
|
||||||
|
title: '所在地址',
|
||||||
|
dataIndex: 'stlc',
|
||||||
|
width: 200,
|
||||||
|
ellipsis: true
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
// ==================== 搜索参数 ====================
|
||||||
|
const searchParams = computed(() => {
|
||||||
|
const filters: any[] = [];
|
||||||
|
|
||||||
|
if (props.hbrvcd) {
|
||||||
|
filters.push({
|
||||||
|
field: 'hbrvcd',
|
||||||
|
operator: 'in',
|
||||||
|
dataType: 'string',
|
||||||
|
value: props.hbrvcd
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (props.orderIndex === '20') {
|
||||||
|
filters.push({
|
||||||
|
field: 'sttpCode',
|
||||||
|
operator: 'eq',
|
||||||
|
dataType: 'string',
|
||||||
|
value: 'TE_ANIMALS'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const startTime = `${dayjs(yearValue.value).format('YYYY')}-01-01 00:00:00`;
|
||||||
|
const endTime = `${dayjs(yearValue.value).format('YYYY')}-12-31 23:59:59`;
|
||||||
|
filters.push({
|
||||||
|
field: 'startTime',
|
||||||
|
operator: 'gte',
|
||||||
|
dataType: 'date',
|
||||||
|
value: startTime
|
||||||
|
});
|
||||||
|
filters.push({
|
||||||
|
field: 'endTime',
|
||||||
|
operator: 'lte',
|
||||||
|
dataType: 'date',
|
||||||
|
value: endTime
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
filter: {
|
||||||
|
logic: 'and',
|
||||||
|
filters
|
||||||
|
},
|
||||||
|
sort: [],
|
||||||
|
group: [],
|
||||||
|
select: [],
|
||||||
|
groupResultFlat: false
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
// ==================== 数据转换 ====================
|
||||||
|
const customTransform = (res: any) => {
|
||||||
|
return {
|
||||||
|
records: res?.data?.data || [],
|
||||||
|
total: res?.data?.total || 0
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
// ==================== 搜索与重置 ====================
|
||||||
|
const handleSearch = () => {
|
||||||
|
const filter = getFilter();
|
||||||
|
tableRef.value?.getList(filter);
|
||||||
|
};
|
||||||
|
|
||||||
|
const getFilter = () => {
|
||||||
|
const filters: any[] = [
|
||||||
|
{
|
||||||
|
field: 'sttpCode',
|
||||||
|
operator: 'eq',
|
||||||
|
dataType: 'string',
|
||||||
|
value: 'WVA'
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
if (props.hbrvcd) {
|
||||||
|
filters.push({
|
||||||
|
field: 'hbrvcd',
|
||||||
|
operator: 'in',
|
||||||
|
dataType: 'string',
|
||||||
|
value: props.hbrvcd
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
logic: 'and',
|
||||||
|
filters
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
// ==================== 监听 selectDateTime 变化 ====================
|
||||||
|
watch(
|
||||||
|
() => props.selectDateTime,
|
||||||
|
newVal => {
|
||||||
|
if (newVal) {
|
||||||
|
yearValue.value = dayjs(newVal);
|
||||||
|
handleSearch();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ immediate: false }
|
||||||
|
);
|
||||||
|
|
||||||
|
// ==================== 监听 orderIndex 变化 ====================
|
||||||
|
watch(
|
||||||
|
() => props.orderIndex,
|
||||||
|
() => {
|
||||||
|
handleSearch();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// ==================== 挂载时执行 ====================
|
||||||
|
onMounted(() => {
|
||||||
|
handleSearch();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.lsstjk-table {
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
.search-form {
|
||||||
|
padding: 16px 0;
|
||||||
|
border-bottom: 1px solid #f0f0f0;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-wrapper {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -1,42 +1,96 @@
|
|||||||
<!-- SidePanelItem.vue -->
|
<!-- SidePanelItem.vue -->
|
||||||
<template>
|
<template>
|
||||||
<SidePanelItem title="水生生态调查情况" :select="select" :datetimePicker="datetimePicker">
|
<SidePanelItem
|
||||||
|
title="陆生生态监测情况"
|
||||||
|
:select="select"
|
||||||
|
:datetimePicker="datetimePicker"
|
||||||
|
@update-values="handlePanelChange"
|
||||||
|
>
|
||||||
|
<a-spin :spinning="dataLoading" tip="加载中...">
|
||||||
<div class="facility-grid">
|
<div class="facility-grid">
|
||||||
<div v-for="facility in facilities" :key="facility.name" class="facility-card">
|
<div
|
||||||
<div style="width: 60px;height: 62px;display: flex;align-items: center;justify-content: center;">
|
v-for="facility in facilities"
|
||||||
|
:key="facility.name"
|
||||||
|
class="facility-card"
|
||||||
|
@click="handleCardClick(facility)"
|
||||||
|
>
|
||||||
|
<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>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="facility-info">
|
<div class="facility-info">
|
||||||
<div class="facility-name">{{ facility.name }}</div>
|
<div class="facility-name">{{ facility.name }}</div>
|
||||||
<div style="font-size: 16px;"> <span class="facility-count">{{ facility.count
|
<div style="font-size: 16px">
|
||||||
}}</span><span>个</span></div>
|
<span class="facility-count">{{ facility.count }}</span>
|
||||||
|
<span>{{ facility.unit }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
</a-spin>
|
||||||
</SidePanelItem>
|
</SidePanelItem>
|
||||||
|
|
||||||
|
<!-- 陆生生态调查概况弹框 -->
|
||||||
|
<a-modal
|
||||||
|
v-model:open="modalVisible"
|
||||||
|
:title="currentOrderIndex === '16' ? '红外相机信息' : '重点陆生动物'"
|
||||||
|
width="1536px"
|
||||||
|
:footer="null"
|
||||||
|
@cancel="handleModalClose"
|
||||||
|
>
|
||||||
|
<LsstjkTk
|
||||||
|
v-if="modalVisible"
|
||||||
|
:selectDateTime="datetimePicker.value"
|
||||||
|
:orderIndex="currentOrderIndex"
|
||||||
|
:hbrvcd="select.value"
|
||||||
|
/>
|
||||||
|
</a-modal>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { ref, onMounted, onUnmounted, nextTick } from 'vue';
|
import { ref, watch } from 'vue';
|
||||||
import * as echarts from 'echarts';
|
import { useJidiSelectEventStore } from '@/store/modules/jidiSelectEvent';
|
||||||
import type { EChartsOption } from 'echarts';
|
|
||||||
import SidePanelItem from '@/components/SidePanelItem/index.vue';
|
import SidePanelItem from '@/components/SidePanelItem/index.vue';
|
||||||
|
import LsstjkTk from './LsstjkTk.vue';
|
||||||
|
import { wbsbGetKendoList, getEvnmAutoMonitor } from '@/api/stdc';
|
||||||
|
import moment from 'moment';
|
||||||
|
|
||||||
// 定义组件名
|
// 定义组件名
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: 'shuishengshengtaijiance'
|
name: 'lushengshengtaijiance'
|
||||||
});
|
});
|
||||||
|
|
||||||
// ==================== 响应式变量定义 ====================// 选择器配置
|
const JidiSelectEventStore = useJidiSelectEventStore();
|
||||||
|
const baseid = ref('');
|
||||||
|
const dataLoading = ref(false);
|
||||||
|
|
||||||
|
// ==================== 弹框状态 ====================
|
||||||
|
const modalVisible = ref(false);
|
||||||
|
const currentOrderIndex = ref('16');
|
||||||
|
|
||||||
|
// ==================== 响应式变量定义 ====================
|
||||||
|
// 选择器配置
|
||||||
const select = ref({
|
const select = ref({
|
||||||
show: true,
|
show: true,
|
||||||
value: undefined,
|
value: undefined,
|
||||||
options: [],
|
options: [],
|
||||||
picker: undefined,
|
picker: undefined,
|
||||||
format: undefined
|
format: undefined,
|
||||||
|
width: '120px'
|
||||||
});
|
});
|
||||||
|
|
||||||
// 日期选择器配置
|
// 日期选择器配置
|
||||||
@ -48,30 +102,202 @@ const datetimePicker = ref({
|
|||||||
options: []
|
options: []
|
||||||
});
|
});
|
||||||
|
|
||||||
const facilities = ref([
|
const facilities:any = ref([
|
||||||
{
|
{
|
||||||
name: '红外相机数量',
|
name: '野生动物监测',
|
||||||
count: 31,
|
count: 0,
|
||||||
icon: 'icon iconfont icon-shipinjiankongshebei'
|
unit: '个',
|
||||||
|
icon: 'icon iconfont icon-dongwujiuzhuzhan',
|
||||||
|
orderInx: '16'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: '重点陆生动物',
|
name: '重点陆生动物',
|
||||||
count: 24,
|
count: 0,
|
||||||
icon: 'icon iconfont icon-dongwujiuzhuzhan'
|
unit: '种',
|
||||||
},
|
icon: 'icon iconfont icon-shipinjiankongshebei',
|
||||||
|
orderInx: '20'
|
||||||
|
}
|
||||||
]);
|
]);
|
||||||
// ==================== 生命周期钩子 ====================
|
|
||||||
|
|
||||||
// 页面加载时执行
|
// ==================== 事件处理 ====================
|
||||||
onMounted(() => {
|
const handlePanelChange = (data: any) => {
|
||||||
|
console.log('当前所有控件状态:', data);
|
||||||
|
if (data.select) {
|
||||||
|
select.value.value = data.select;
|
||||||
|
}
|
||||||
|
if (data.datetime) {
|
||||||
|
datetimePicker.value.value = data.datetime;
|
||||||
|
}
|
||||||
|
// 选择器或日期变化时,重新获取统计数据
|
||||||
|
getCardData();
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleCardClick = (facility: any) => {
|
||||||
|
console.log('卡片被点击,准备打开弹框');
|
||||||
|
// debugger
|
||||||
|
currentOrderIndex.value = facility.orderInx;
|
||||||
|
modalVisible.value = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleModalClose = () => {
|
||||||
|
modalVisible.value = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
// ==================== 获取卡片统计数据 ====================
|
||||||
|
const getCardData = async () => {
|
||||||
|
if (!datetimePicker.value.value) return;
|
||||||
|
|
||||||
|
dataLoading.value = true;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const year = moment(datetimePicker.value.value).format('YYYY');
|
||||||
|
const startTime = `${year}-01-01 00:00:00`;
|
||||||
|
const endTime = `${year}-12-31 23:59:59`;
|
||||||
|
|
||||||
|
const filters: any[] = [
|
||||||
|
{
|
||||||
|
field: 'showIds',
|
||||||
|
operator: 'in',
|
||||||
|
value: ['16', '20']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'startTime',
|
||||||
|
operator: 'gte',
|
||||||
|
dataType: 'date',
|
||||||
|
value: startTime
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'endTime',
|
||||||
|
operator: 'lte',
|
||||||
|
dataType: 'date',
|
||||||
|
value: endTime
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
if (baseid.value && baseid.value !== 'all') {
|
||||||
|
filters.unshift({
|
||||||
|
field: 'hbrvcd',
|
||||||
|
operator: 'in',
|
||||||
|
value: [baseid.value]
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// 组件卸载时清理
|
const params = {
|
||||||
onUnmounted(() => {
|
filter: {
|
||||||
|
logic: 'and',
|
||||||
|
filters
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const res = await getEvnmAutoMonitor(params);
|
||||||
|
const data = res?.data?.data;
|
||||||
|
|
||||||
|
if (data && Array.isArray(data)) {
|
||||||
|
const wildAnimalItem = data.find((item: any) => item.orderInx === 16);
|
||||||
|
const keyAnimalItem = data.find((item: any) => item.orderInx === 20);
|
||||||
|
|
||||||
|
facilities.value = [
|
||||||
|
{
|
||||||
|
name: '野生动物监测',
|
||||||
|
count: wildAnimalItem?.cnt ?? 0,
|
||||||
|
unit: '个',
|
||||||
|
icon: 'icon iconfont icon-dongwujiuzhuzhan',
|
||||||
|
orderInx: '16'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '重点陆生动物',
|
||||||
|
count: keyAnimalItem?.cnt ?? 0,
|
||||||
|
unit: '种',
|
||||||
|
icon: 'icon iconfont icon-shipinjiankongshebei',
|
||||||
|
orderInx: '20'
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取统计数据失败:', error);
|
||||||
|
facilities.value = [
|
||||||
|
{
|
||||||
|
name: '野生动物监测',
|
||||||
|
count: 0,
|
||||||
|
unit: '个',
|
||||||
|
icon: 'icon iconfont icon-dongwujiuzhuzhan',
|
||||||
|
orderInx: '16'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '重点陆生动物',
|
||||||
|
count: 0,
|
||||||
|
unit: '种',
|
||||||
|
icon: 'icon iconfont icon-shipinjiankongshebei',
|
||||||
|
orderInx: '20'
|
||||||
|
}
|
||||||
|
];
|
||||||
|
} finally {
|
||||||
|
dataLoading.value = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// ==================== 获取选择器数据 ====================
|
||||||
|
const getSelectData = async () => {
|
||||||
|
let params = {
|
||||||
|
filter: {
|
||||||
|
logic: 'and',
|
||||||
|
filters: [
|
||||||
|
{
|
||||||
|
field: 'wbsType',
|
||||||
|
operator: 'eq',
|
||||||
|
value: 'PSB_RVCD'
|
||||||
|
},
|
||||||
|
baseid.value != 'all'
|
||||||
|
? {
|
||||||
|
field: 'objId',
|
||||||
|
operator: 'eq',
|
||||||
|
value: baseid.value
|
||||||
|
}
|
||||||
|
: null
|
||||||
|
].filter(Boolean)
|
||||||
|
},
|
||||||
|
group: [
|
||||||
|
{
|
||||||
|
dir: 'asc',
|
||||||
|
field: 'orderIndex'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
dir: 'asc',
|
||||||
|
field: 'wbsCode'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
dir: 'asc',
|
||||||
|
field: 'wbsName'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
dir: 'asc',
|
||||||
|
field: 'objid'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
groupResultFlat: true
|
||||||
|
};
|
||||||
|
const res = await wbsbGetKendoList(params);
|
||||||
|
let data = res?.data?.data;
|
||||||
|
select.value.options = data.map((item: any) => {
|
||||||
|
return {
|
||||||
|
value: item.wbsCode,
|
||||||
|
label: item.wbsName
|
||||||
|
};
|
||||||
});
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// ==================== 监听基地变化 ====================
|
||||||
|
watch(
|
||||||
|
() => JidiSelectEventStore.selectedItem,
|
||||||
|
async newVal => {
|
||||||
|
if (newVal && newVal.wbsCode) {
|
||||||
|
baseid.value = newVal.wbsCode;
|
||||||
|
getSelectData();
|
||||||
|
getCardData();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ deep: true, immediate: true }
|
||||||
|
);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
@ -80,7 +306,9 @@ onUnmounted(() => {
|
|||||||
flex-flow: wrap;
|
flex-flow: wrap;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Helvetica Neue, Arial, Noto Sans, sans-serif, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol, Noto Color Emoji;
|
font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto,
|
||||||
|
Helvetica Neue, Arial, Noto Sans, sans-serif, Apple Color Emoji,
|
||||||
|
Segoe UI Emoji, Segoe UI Symbol, Noto Color Emoji;
|
||||||
}
|
}
|
||||||
|
|
||||||
.facility-card {
|
.facility-card {
|
||||||
@ -91,7 +319,7 @@ onUnmounted(() => {
|
|||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
margin: 4px 0px;
|
margin: 4px 0px;
|
||||||
background: #fff;
|
background: #fff;
|
||||||
border: 1px solid #e8e8e8;
|
border: 1px solid rgb(229, 236, 245);
|
||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
transition: all 0.3s;
|
transition: all 0.3s;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
@ -104,7 +332,6 @@ onUnmounted(() => {
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
width: 40px;
|
width: 40px;
|
||||||
height: 40px;
|
height: 40px;
|
||||||
// margin-right: 8px;
|
|
||||||
background: #2f6b98;
|
background: #2f6b98;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
|
|
||||||
@ -121,13 +348,14 @@ onUnmounted(() => {
|
|||||||
.facility-name {
|
.facility-name {
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
color: #333;
|
color: #333;
|
||||||
// margin-bottom: 4px;
|
|
||||||
// font-weight: 500;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.facility-count {
|
.facility-count {
|
||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
color: #2f6b98;
|
color: #2f6b98;
|
||||||
// font-weight: 600;
|
}
|
||||||
|
|
||||||
|
:deep(.ant-spin-nested-loading) {
|
||||||
|
min-height: 72px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
362
frontend/src/modules/shuishengshengtaijiance/SsstdcgkTk.vue
Normal file
362
frontend/src/modules/shuishengshengtaijiance/SsstdcgkTk.vue
Normal file
@ -0,0 +1,362 @@
|
|||||||
|
<template>
|
||||||
|
<div class="ssst-dcgk-table">
|
||||||
|
<!-- 搜索表单 -->
|
||||||
|
<a-form layout="inline" class="search-form">
|
||||||
|
<a-form-item label="年份">
|
||||||
|
<a-date-picker
|
||||||
|
v-model:value="yearValue"
|
||||||
|
picker="year"
|
||||||
|
format="YYYY"
|
||||||
|
:allow-clear="false"
|
||||||
|
:show-time="false"
|
||||||
|
style="width: 150px"
|
||||||
|
@change="handleYearChange"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label="调查批次">
|
||||||
|
<a-select
|
||||||
|
v-model:value="dcpcValue"
|
||||||
|
placeholder="请选择"
|
||||||
|
style="width: 150px"
|
||||||
|
:options="dcpcOptions"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label="调查鱼种类">
|
||||||
|
<a-input
|
||||||
|
v-model:value="nameValue"
|
||||||
|
placeholder="请输入鱼种类"
|
||||||
|
allow-clear
|
||||||
|
style="width: 200px"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item>
|
||||||
|
<a-space>
|
||||||
|
<a-button type="primary" @click="handleSearch">查询</a-button>
|
||||||
|
<a-button @click="handleReset">重置</a-button>
|
||||||
|
</a-space>
|
||||||
|
</a-form-item>
|
||||||
|
</a-form>
|
||||||
|
|
||||||
|
<!-- 表格 -->
|
||||||
|
<div class="table-wrapper">
|
||||||
|
<BasicTable
|
||||||
|
ref="tableRef"
|
||||||
|
:columns="columns"
|
||||||
|
:scroll-y="500"
|
||||||
|
:scroll-x="800"
|
||||||
|
:list-url="listUrl"
|
||||||
|
:search-params="searchParams"
|
||||||
|
:transform-data="customTransform"
|
||||||
|
>
|
||||||
|
<template #stnm="{ record }">
|
||||||
|
<a @click="handleViewDetail(record)" class="text-link">
|
||||||
|
{{ record.stnm }}
|
||||||
|
</a>
|
||||||
|
</template>
|
||||||
|
<template #tm="{ record }">
|
||||||
|
{{ record.tm ? dayjs(record.tm).format('YYYY-MM-DD') : '-' }}
|
||||||
|
</template>
|
||||||
|
<template #fid="{ record }">
|
||||||
|
<a-button
|
||||||
|
type="link"
|
||||||
|
style="padding: 4px"
|
||||||
|
:disabled="record.fid"
|
||||||
|
@click="handleViewAttachment(record.fid)"
|
||||||
|
>
|
||||||
|
查看附件
|
||||||
|
</a-button>
|
||||||
|
</template>
|
||||||
|
</BasicTable>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<CommonAttachmentModal
|
||||||
|
v-model:open="attachmentModalVisible"
|
||||||
|
:fid="currentAttachmentFid"
|
||||||
|
title="查看附件"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { ref, computed, watch, onMounted } from 'vue';
|
||||||
|
import BasicTable from '@/components/BasicTable/index.vue';
|
||||||
|
import { getWeStaticsSecond } from '@/api/stdc';
|
||||||
|
import dayjs from 'dayjs';
|
||||||
|
import { useModelStore } from "@/store/modules/model";
|
||||||
|
|
||||||
|
const modelStore = useModelStore();
|
||||||
|
// ==================== Props ====================
|
||||||
|
const props = defineProps<{
|
||||||
|
selectDateTime?: any; // 选中的日期
|
||||||
|
stcd?: string; // 断面编码
|
||||||
|
all?: boolean; // 是否不限制断面
|
||||||
|
}>();
|
||||||
|
|
||||||
|
// ==================== 搜索状态 ====================
|
||||||
|
const tableRef = ref();
|
||||||
|
const yearValue = ref(props.selectDateTime ? dayjs(props.selectDateTime) : dayjs());
|
||||||
|
const dcpcValue = ref('');
|
||||||
|
const nameValue = ref('');
|
||||||
|
const dcpcOptions = ref<Array<{ label: string; value: string }>>([
|
||||||
|
{ label: '全部', value: '' }
|
||||||
|
]);
|
||||||
|
|
||||||
|
// ==================== 弹框状态 ====================
|
||||||
|
const attachmentModalVisible = ref(false);
|
||||||
|
const currentAttachmentFid = ref('');
|
||||||
|
// ==================== 接口URL ====================
|
||||||
|
const listUrl = getWeStaticsSecond;
|
||||||
|
|
||||||
|
// ==================== 搜索参数 ====================
|
||||||
|
const searchParams = computed(() => ({
|
||||||
|
sort: [
|
||||||
|
{
|
||||||
|
field: 'tm',
|
||||||
|
dir: 'desc'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
group: [],
|
||||||
|
select: [],
|
||||||
|
groupResultFlat: false
|
||||||
|
}));
|
||||||
|
|
||||||
|
// ==================== 表格列配置 ====================
|
||||||
|
const columns = [
|
||||||
|
{
|
||||||
|
key: 'stnm',
|
||||||
|
title: '断面名称',
|
||||||
|
dataIndex: 'stnm',
|
||||||
|
fixed: 'left' as const,
|
||||||
|
width: 200,
|
||||||
|
ellipsis: true,
|
||||||
|
slots: { customRender: 'stnm' },
|
||||||
|
merge: true,
|
||||||
|
mergeDeps: ['stnm']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'tm',
|
||||||
|
title: '调查日期',
|
||||||
|
dataIndex: 'tm',
|
||||||
|
fixed: 'left' as const,
|
||||||
|
width: 200,
|
||||||
|
ellipsis: true,
|
||||||
|
slots: { customRender: 'tm' },
|
||||||
|
merge: true,
|
||||||
|
mergeDeps: ['stnm']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'dcpc',
|
||||||
|
title: '调查批次',
|
||||||
|
dataIndex: 'dcpc',
|
||||||
|
width: 200,
|
||||||
|
ellipsis: true,
|
||||||
|
merge: true,
|
||||||
|
mergeDeps: ['stnm']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'ftpName',
|
||||||
|
title: '调查鱼种类',
|
||||||
|
dataIndex: 'ftpName',
|
||||||
|
width: 200,
|
||||||
|
ellipsis: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'fcnt',
|
||||||
|
title: '调查鱼总数',
|
||||||
|
dataIndex: 'fcnt',
|
||||||
|
width: 200,
|
||||||
|
ellipsis: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'fid',
|
||||||
|
title: '证明材料',
|
||||||
|
dataIndex: 'fid',
|
||||||
|
width: 200,
|
||||||
|
ellipsis: true,
|
||||||
|
slots: { customRender: 'fid' }
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
// ==================== 过滤条件构建 ====================
|
||||||
|
const getFilter = () => {
|
||||||
|
const filters: any[] = [
|
||||||
|
{
|
||||||
|
field: 'year',
|
||||||
|
operator: 'in',
|
||||||
|
dataType: 'string',
|
||||||
|
value: [dayjs(yearValue.value).format('YYYY')]
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
// 断面筛选
|
||||||
|
if (!props.all && props.stcd) {
|
||||||
|
filters.push({
|
||||||
|
field: 'rstcd',
|
||||||
|
operator: 'in',
|
||||||
|
dataType: 'string',
|
||||||
|
value: props.stcd
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 调查批次筛选
|
||||||
|
if (dcpcValue.value) {
|
||||||
|
filters.push({
|
||||||
|
field: 'dcpc',
|
||||||
|
operator: 'eq',
|
||||||
|
dataType: 'string',
|
||||||
|
value: dcpcValue.value
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 鱼种类搜索
|
||||||
|
if (nameValue.value) {
|
||||||
|
filters.push({
|
||||||
|
field: 'ftpName',
|
||||||
|
operator: 'contains',
|
||||||
|
dataType: 'string',
|
||||||
|
value: nameValue.value
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
logic: 'and',
|
||||||
|
filters: filters.filter((el: any) => el != null)
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
// ==================== 数据转换 ====================
|
||||||
|
const customTransform = (res: any) => {
|
||||||
|
return {
|
||||||
|
records: res?.data?.data || [],
|
||||||
|
total: res?.data?.total || 0
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
// ==================== 获取调查批次选项 ====================
|
||||||
|
const getDcpcOptions = async () => {
|
||||||
|
try {
|
||||||
|
const params = {
|
||||||
|
logic: 'and',
|
||||||
|
filters: [
|
||||||
|
...(props.all
|
||||||
|
? []
|
||||||
|
: [
|
||||||
|
{
|
||||||
|
field: 'rstcd',
|
||||||
|
operator: 'in',
|
||||||
|
dataType: 'string',
|
||||||
|
value: props.stcd
|
||||||
|
}
|
||||||
|
]),
|
||||||
|
{
|
||||||
|
field: 'year',
|
||||||
|
operator: 'in',
|
||||||
|
dataType: 'string',
|
||||||
|
value: [dayjs(yearValue.value).format('YYYY')]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
const group = [
|
||||||
|
{
|
||||||
|
dir: 'des',
|
||||||
|
field: 'dcpc'
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
const res = await getWeStaticsSecond( { filter: params, group });
|
||||||
|
|
||||||
|
if (res?.data?.data?.length) {
|
||||||
|
const options = res.data.data.map((item: any) => ({
|
||||||
|
label: item.dcpc,
|
||||||
|
value: item.dcpc
|
||||||
|
}));
|
||||||
|
dcpcOptions.value = [{ label: '全部', value: '' }, ...options];
|
||||||
|
} else {
|
||||||
|
dcpcOptions.value = [{ label: '全部', value: '' }];
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取调查批次选项失败:', error);
|
||||||
|
dcpcOptions.value = [{ label: '全部', value: '' }];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// ==================== 搜索与重置 ====================
|
||||||
|
const handleSearch = () => {
|
||||||
|
const filter = getFilter();
|
||||||
|
tableRef.value?.getList(filter);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleReset = () => {
|
||||||
|
yearValue.value = props.selectDateTime ? dayjs(props.selectDateTime) : dayjs();
|
||||||
|
dcpcValue.value = '';
|
||||||
|
nameValue.value = '';
|
||||||
|
handleSearch();
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleYearChange = () => {
|
||||||
|
dcpcValue.value = '';
|
||||||
|
getDcpcOptions();
|
||||||
|
};
|
||||||
|
|
||||||
|
// 查看详情(占位)
|
||||||
|
const handleViewDetail = (record: any) => {
|
||||||
|
modelStore.modalVisible = true;
|
||||||
|
modelStore.params = record;
|
||||||
|
modelStore.params.sttp = "WE";
|
||||||
|
modelStore.title = record.stnm;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 查看附件(占位)
|
||||||
|
const handleViewAttachment = (fid: string) => {
|
||||||
|
currentAttachmentFid.value = fid;
|
||||||
|
attachmentModalVisible.value = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
// ==================== 监听 selectDateTime 变化 ====================
|
||||||
|
watch(
|
||||||
|
() => props.selectDateTime,
|
||||||
|
(newVal) => {
|
||||||
|
if (newVal) {
|
||||||
|
yearValue.value = dayjs(newVal);
|
||||||
|
getDcpcOptions();
|
||||||
|
handleSearch();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ immediate: false }
|
||||||
|
);
|
||||||
|
|
||||||
|
// ==================== 挂载时执行 ====================
|
||||||
|
onMounted(() => {
|
||||||
|
getDcpcOptions();
|
||||||
|
handleSearch();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.ssst-dcgk-table {
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
.search-form {
|
||||||
|
padding: 16px 0;
|
||||||
|
border-bottom: 1px solid #f0f0f0;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-wrapper {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-link {
|
||||||
|
color: #2f6b98;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: #40a9ff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-placeholder,
|
||||||
|
.attachment-placeholder {
|
||||||
|
padding: 40px 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -1,48 +1,101 @@
|
|||||||
<!-- SidePanelItem.vue -->
|
<!-- SidePanelItem.vue -->
|
||||||
<template>
|
<template>
|
||||||
<SidePanelItem title="水生生态调查情况" :select="select" :datetimePicker="datetimePicker">
|
<SidePanelItem
|
||||||
|
title="水生生态调查情况"
|
||||||
|
:select="select"
|
||||||
|
:datetimePicker="datetimePicker"
|
||||||
|
@update-values="handlePanelChange"
|
||||||
|
>
|
||||||
|
<a-spin :spinning="dataLoading" tip="加载中...">
|
||||||
<div class="facility-grid">
|
<div class="facility-grid">
|
||||||
<div v-for="facility in facilities" :key="facility.name" class="facility-card">
|
<div
|
||||||
<div style="width: 60px;height: 62px;display: flex;align-items: center;justify-content: center;">
|
v-for="facility in facilities"
|
||||||
|
:key="facility.name"
|
||||||
|
class="facility-card"
|
||||||
|
@click="handleCardClick"
|
||||||
|
>
|
||||||
|
<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>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="facility-info">
|
<div class="facility-info">
|
||||||
<div class="facility-name">{{ facility.name }}</div>
|
<div class="facility-name">{{ facility.name }}</div>
|
||||||
<div style="font-size: 16px;"> <span class="facility-count">{{ facility.count
|
<div style="font-size: 16px">
|
||||||
}}</span><span>个</span></div>
|
<span class="facility-count">{{ facility.count }}</span>
|
||||||
|
<span>{{ facility.unit }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
</a-spin>
|
||||||
</SidePanelItem>
|
</SidePanelItem>
|
||||||
|
|
||||||
|
<!-- 水生生态调查概况弹框 -->
|
||||||
|
<a-modal
|
||||||
|
v-model:open="modalVisible"
|
||||||
|
title="水生生态调查概况"
|
||||||
|
width="1536px"
|
||||||
|
:footer="null"
|
||||||
|
@cancel="handleModalClose"
|
||||||
|
>
|
||||||
|
<SsstdcgkTk
|
||||||
|
v-if="modalVisible"
|
||||||
|
:selectDateTime="datetimePicker.value"
|
||||||
|
:all="true"
|
||||||
|
/>
|
||||||
|
</a-modal>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { ref, onMounted, onUnmounted, nextTick } from 'vue';
|
import { ref, watch } from 'vue';
|
||||||
import * as echarts from 'echarts';
|
import { useJidiSelectEventStore } from '@/store/modules/jidiSelectEvent';
|
||||||
import type { EChartsOption } from 'echarts';
|
|
||||||
import SidePanelItem from '@/components/SidePanelItem/index.vue';
|
import SidePanelItem from '@/components/SidePanelItem/index.vue';
|
||||||
|
import SsstdcgkTk from './SsstdcgkTk.vue';
|
||||||
|
import { wbsbGetKendoList, getDftYear, getWeFisht } from '@/api/stdc';
|
||||||
|
import moment from 'moment';
|
||||||
|
import { Label } from 'cesium';
|
||||||
|
|
||||||
// 定义组件名
|
// 定义组件名
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: 'shuishengshengtaijiance'
|
name: 'shuishengshengtaijiance'
|
||||||
});
|
});
|
||||||
|
|
||||||
// ==================== 响应式变量定义 ====================// 选择器配置
|
const JidiSelectEventStore = useJidiSelectEventStore();
|
||||||
|
const baseid = ref('');
|
||||||
|
const dataLoading = ref(false);
|
||||||
|
|
||||||
|
// ==================== 弹框状态 ====================
|
||||||
|
const modalVisible = ref(false);
|
||||||
|
|
||||||
|
// ==================== 响应式变量定义 ====================
|
||||||
|
// 选择器配置
|
||||||
const select = ref({
|
const select = ref({
|
||||||
show: true,
|
show: true,
|
||||||
value: undefined,
|
value: undefined,
|
||||||
options: [],
|
options: [],
|
||||||
picker: undefined,
|
picker: undefined,
|
||||||
format: undefined
|
format: undefined,
|
||||||
|
width: '120px'
|
||||||
});
|
});
|
||||||
|
|
||||||
// 日期选择器配置
|
// 日期选择器配置
|
||||||
const datetimePicker = ref({
|
const datetimePicker = ref({
|
||||||
show: true,
|
show: true,
|
||||||
value: '2023',
|
value: '',
|
||||||
format: 'YYYY',
|
format: 'YYYY',
|
||||||
picker: 'year' as const,
|
picker: 'year' as const,
|
||||||
options: []
|
options: []
|
||||||
@ -51,27 +104,235 @@ const datetimePicker = ref({
|
|||||||
const facilities = ref([
|
const facilities = ref([
|
||||||
{
|
{
|
||||||
name: '水生态调查断面',
|
name: '水生态调查断面',
|
||||||
count: 145,
|
count: 0,
|
||||||
|
unit: '个',
|
||||||
icon: 'icon iconfont icon-liuliangjiancezhan'
|
icon: 'icon iconfont icon-liuliangjiancezhan'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: '调查鱼类统计',
|
name: '调查鱼类统计',
|
||||||
count: 24,
|
count: 0,
|
||||||
|
unit: '种',
|
||||||
icon: 'icon iconfont icon-yuleizengzhizhan'
|
icon: 'icon iconfont icon-yuleizengzhizhan'
|
||||||
},
|
}
|
||||||
|
|
||||||
]);
|
]);
|
||||||
// ==================== 生命周期钩子 ====================
|
|
||||||
|
|
||||||
// 页面加载时执行
|
// ==================== 事件处理 ====================
|
||||||
onMounted(() => {
|
const handlePanelChange = (data: any) => {
|
||||||
|
console.log('当前所有控件状态:', data);
|
||||||
|
if (data.select) {
|
||||||
|
select.value.value = data.select;
|
||||||
|
}
|
||||||
|
if (data.datetime) {
|
||||||
|
datetimePicker.value.value = data.datetime;
|
||||||
|
}
|
||||||
|
// 选择器或日期变化时,重新获取统计数据
|
||||||
|
getCardData();
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleCardClick = () => {
|
||||||
|
console.log('卡片被点击,准备打开弹框');
|
||||||
|
console.log('modalVisible:', datetimePicker.value.value);
|
||||||
|
modalVisible.value = true;
|
||||||
|
console.log('modalVisible:', modalVisible.value);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleModalClose = () => {
|
||||||
|
modalVisible.value = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
// ==================== 获取默认年份 ====================
|
||||||
|
const getDefaultYear = async () => {
|
||||||
|
if (!baseid.value) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
// const params = {
|
||||||
|
// filter: { baseId: baseid.value },
|
||||||
|
// group: [{ dir: 'des', field: 'yr' }]
|
||||||
|
// };
|
||||||
|
let params = {
|
||||||
|
filter: {
|
||||||
|
logic: 'and',
|
||||||
|
filters: [
|
||||||
|
baseid.value != 'all'
|
||||||
|
? {
|
||||||
|
field: 'baseId',
|
||||||
|
operator: 'eq',
|
||||||
|
dataType: 'string',
|
||||||
|
value: baseid.value
|
||||||
|
}
|
||||||
|
: null
|
||||||
|
].filter(Boolean)
|
||||||
|
},
|
||||||
|
group: [
|
||||||
|
{
|
||||||
|
dir: 'des',
|
||||||
|
field: 'yr'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
const res = await getDftYear(params);
|
||||||
|
const data = res?.data?.data?.[0] || {};
|
||||||
|
const allYears = Object.values(data);
|
||||||
|
|
||||||
|
if (allYears?.length) {
|
||||||
|
const last = (allYears as any[]).flat().sort().pop();
|
||||||
|
datetimePicker.value.value = last;
|
||||||
|
} else {
|
||||||
|
datetimePicker.value.value = moment().format('YYYY');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取统计数据
|
||||||
|
getCardData();
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取默认年份失败:', error);
|
||||||
|
datetimePicker.value.value = moment().format('YYYY');
|
||||||
|
getCardData();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// ==================== 获取卡片统计数据 ====================
|
||||||
|
const getCardData = async () => {
|
||||||
|
if (!datetimePicker.value.value) return;
|
||||||
|
|
||||||
|
dataLoading.value = true;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const filters: any[] = [
|
||||||
|
{
|
||||||
|
field: 'yr',
|
||||||
|
operator: 'eq',
|
||||||
|
dataType: 'string',
|
||||||
|
value: moment(datetimePicker.value.value).format('YYYY')
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
if (baseid.value && baseid.value !== 'all') {
|
||||||
|
filters.push({
|
||||||
|
field: 'baseId',
|
||||||
|
operator: 'eq',
|
||||||
|
dataType: 'string',
|
||||||
|
value: baseid.value
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// 组件卸载时清理
|
if (select.value.value) {
|
||||||
onUnmounted(() => {
|
filters.push({
|
||||||
|
field: 'hbrvcd',
|
||||||
|
operator: 'eq',
|
||||||
|
dataType: 'string',
|
||||||
|
value: select.value.value
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const params = {
|
||||||
|
filter: {
|
||||||
|
logic: 'and',
|
||||||
|
filters
|
||||||
|
},
|
||||||
|
group: [],
|
||||||
|
groupResultFlat: true
|
||||||
|
};
|
||||||
|
|
||||||
|
const res = await getWeFisht(params);
|
||||||
|
const data = res?.data?.data?.[0];
|
||||||
|
|
||||||
|
facilities.value = [
|
||||||
|
{
|
||||||
|
name: '水生态调查断面',
|
||||||
|
count: data?.stcdCount ?? 0,
|
||||||
|
unit: '个',
|
||||||
|
icon: 'icon iconfont icon-liuliangjiancezhan'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '调查鱼类统计',
|
||||||
|
count: data?.ftpCount ?? 0,
|
||||||
|
unit: '种',
|
||||||
|
icon: 'icon iconfont icon-yuleizengzhizhan'
|
||||||
|
}
|
||||||
|
];
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取统计数据失败:', error);
|
||||||
|
facilities.value = [
|
||||||
|
{
|
||||||
|
name: '水生态调查断面',
|
||||||
|
count: 0,
|
||||||
|
unit: '个',
|
||||||
|
icon: 'icon iconfont icon-liuliangjiancezhan'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '调查鱼类统计',
|
||||||
|
count: 0,
|
||||||
|
unit: '种',
|
||||||
|
icon: 'icon iconfont icon-yuleizengzhizhan'
|
||||||
|
}
|
||||||
|
];
|
||||||
|
} finally {
|
||||||
|
dataLoading.value = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// ==================== 获取选择器数据 ====================
|
||||||
|
const getSelectData = async () => {
|
||||||
|
let params = {
|
||||||
|
filter: {
|
||||||
|
logic: 'and',
|
||||||
|
filters: [
|
||||||
|
{
|
||||||
|
field: 'wbsType',
|
||||||
|
operator: 'eq',
|
||||||
|
value: 'PSB_RVCD'
|
||||||
|
},
|
||||||
|
baseid.value != 'all'
|
||||||
|
? {
|
||||||
|
field: 'objId',
|
||||||
|
operator: 'eq',
|
||||||
|
value: '水生生态调查情况'
|
||||||
|
}
|
||||||
|
: null
|
||||||
|
].filter(Boolean)
|
||||||
|
},
|
||||||
|
group: [
|
||||||
|
{
|
||||||
|
dir: 'asc',
|
||||||
|
field: 'orderIndex'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
dir: 'asc',
|
||||||
|
field: 'wbsCode'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
dir: 'asc',
|
||||||
|
field: 'wbsName'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
dir: 'asc',
|
||||||
|
field: 'objid'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
groupResultFlat: true
|
||||||
|
};
|
||||||
|
const res = await wbsbGetKendoList(params);
|
||||||
|
let data = res?.data?.data;
|
||||||
|
select.value.options = data.map((item: any) => {
|
||||||
|
return {
|
||||||
|
value: item.wbsCode,
|
||||||
|
label: item.wbsName
|
||||||
|
};
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// ==================== 监听基地变化 ====================
|
||||||
|
watch(
|
||||||
|
() => JidiSelectEventStore.selectedItem,
|
||||||
|
async newVal => {
|
||||||
|
if (newVal && newVal.wbsCode) {
|
||||||
|
baseid.value = newVal.wbsCode;
|
||||||
|
getSelectData();
|
||||||
|
getDefaultYear();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ deep: true, immediate: true }
|
||||||
|
);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
@ -80,7 +341,9 @@ onUnmounted(() => {
|
|||||||
flex-flow: wrap;
|
flex-flow: wrap;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Helvetica Neue, Arial, Noto Sans, sans-serif, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol, Noto Color Emoji;
|
font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto,
|
||||||
|
Helvetica Neue, Arial, Noto Sans, sans-serif, Apple Color Emoji,
|
||||||
|
Segoe UI Emoji, Segoe UI Symbol, Noto Color Emoji;
|
||||||
}
|
}
|
||||||
|
|
||||||
.facility-card {
|
.facility-card {
|
||||||
@ -91,7 +354,7 @@ onUnmounted(() => {
|
|||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
margin: 4px 0px;
|
margin: 4px 0px;
|
||||||
background: #fff;
|
background: #fff;
|
||||||
border: 1px solid #e8e8e8;
|
border: 1px solid rgb(229, 236, 245);
|
||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
transition: all 0.3s;
|
transition: all 0.3s;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
@ -130,4 +393,8 @@ onUnmounted(() => {
|
|||||||
color: #2f6b98;
|
color: #2f6b98;
|
||||||
// font-weight: 600;
|
// font-weight: 600;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
:deep(.ant-spin-nested-loading) {
|
||||||
|
min-height: 72px;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
@ -1,81 +1,15 @@
|
|||||||
import { defineStore } from 'pinia';
|
import { defineStore } from 'pinia';
|
||||||
import { ref } from 'vue';
|
import { ref, onMounted } from 'vue';
|
||||||
|
import { wbsbGetKendoList } from '@/api/select';
|
||||||
export const useJidiSelectEventStore = defineStore('jidiSelectEvent', () => {
|
export const useJidiSelectEventStore = defineStore('jidiSelectEvent', () => {
|
||||||
const jidiData: any = ref([
|
const jidiData: any = ref([
|
||||||
{
|
{
|
||||||
wbsCode: 'all',
|
wbsCode: 'all',
|
||||||
wbsName: '当前全部',
|
wbsName: '当前全部',
|
||||||
selected: true
|
selected: true
|
||||||
},
|
|
||||||
{
|
|
||||||
wbsCode: '01',
|
|
||||||
wbsName: '金沙江干流',
|
|
||||||
selected: false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
wbsCode: '02',
|
|
||||||
wbsName: '雅砻江干流',
|
|
||||||
selected: false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
wbsCode: '03',
|
|
||||||
wbsName: '大渡河干流',
|
|
||||||
selected: false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
wbsCode: '04',
|
|
||||||
wbsName: '乌江干流',
|
|
||||||
selected: false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
wbsCode: '05',
|
|
||||||
wbsName: '长江上游干流',
|
|
||||||
selected: false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
wbsCode: '10',
|
|
||||||
wbsName: '湘西',
|
|
||||||
selected: false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
wbsCode: '08',
|
|
||||||
wbsName: '黄河上游干流',
|
|
||||||
selected: false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
wbsCode: '09',
|
|
||||||
wbsName: '黄河中游干流',
|
|
||||||
selected: false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
wbsCode: '06',
|
|
||||||
wbsName: '南盘江-红水河',
|
|
||||||
selected: false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
wbsCode: '12',
|
|
||||||
wbsName: '东北',
|
|
||||||
selected: false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
wbsCode: '07',
|
|
||||||
wbsName: '澜沧江干流',
|
|
||||||
selected: false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
wbsCode: '13',
|
|
||||||
wbsName: '怒江干流'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
wbsCode: '11',
|
|
||||||
wbsName: '闽浙赣'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
wbsCode: 'other',
|
|
||||||
wbsName: '其他'
|
|
||||||
}
|
}
|
||||||
]);
|
]);
|
||||||
|
const loading = ref(false);
|
||||||
const selectedItem = ref(jidiData.value[0]);
|
const selectedItem = ref(jidiData.value[0]);
|
||||||
const updataJidiData = (index: any) => {
|
const updataJidiData = (index: any) => {
|
||||||
console.log(index);
|
console.log(index);
|
||||||
@ -85,10 +19,58 @@ export const useJidiSelectEventStore = defineStore('jidiSelectEvent', () => {
|
|||||||
});
|
});
|
||||||
jidiData.value[index].selected = true;
|
jidiData.value[index].selected = true;
|
||||||
};
|
};
|
||||||
|
const getJidiData = async () => {
|
||||||
|
loading.value = true;
|
||||||
|
let params = {
|
||||||
|
filter: {
|
||||||
|
logic: 'and',
|
||||||
|
filters: [
|
||||||
|
{
|
||||||
|
field: 'wbsType',
|
||||||
|
operator: 'eq',
|
||||||
|
dataType: 'string',
|
||||||
|
value: 'PSB'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'treeLevel',
|
||||||
|
operator: 'eq',
|
||||||
|
dataType: 'string',
|
||||||
|
value: '1'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
sort: [
|
||||||
|
{
|
||||||
|
field: 'orderIndex',
|
||||||
|
dir: 'asc'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
select: ['wbsCode', 'wbsName']
|
||||||
|
};
|
||||||
|
const res = await wbsbGetKendoList(params);
|
||||||
|
let data = res?.data?.data || [
|
||||||
|
{
|
||||||
|
wbsCode: 'all',
|
||||||
|
wbsName: '当前全部',
|
||||||
|
selected: true
|
||||||
|
}
|
||||||
|
];
|
||||||
|
jidiData.value = data.map((item: any) => {
|
||||||
|
return {
|
||||||
|
...item,
|
||||||
|
selected: item.wbsCode == 'all' ? true : false
|
||||||
|
};
|
||||||
|
});
|
||||||
|
loading.value = false;
|
||||||
|
};
|
||||||
|
onMounted(() => {
|
||||||
|
getJidiData();
|
||||||
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
jidiData,
|
jidiData,
|
||||||
selectedItem,
|
selectedItem,
|
||||||
updataJidiData
|
updataJidiData,
|
||||||
|
loading
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|||||||
@ -154,7 +154,7 @@ const showMapModal17 = () => {
|
|||||||
<!-- <a-button @click="showMapModal7">打开动物救助站弹窗</a-button> -->
|
<!-- <a-button @click="showMapModal7">打开动物救助站弹窗</a-button> -->
|
||||||
<!-- <a-button @click="showMapModal8">打开珍稀植物园弹窗</a-button> -->
|
<!-- <a-button @click="showMapModal8">打开珍稀植物园弹窗</a-button> -->
|
||||||
<!-- <a-button @click="showMapModal9">打开水生生态调查断面弹窗</a-button> -->
|
<!-- <a-button @click="showMapModal9">打开水生生态调查断面弹窗</a-button> -->
|
||||||
<!-- <a-button @click="showMapModal10">打开野生动物监测弹窗</a-button> -->
|
<a-button @click="showMapModal10">打开野生动物监测弹窗</a-button>
|
||||||
<!-- <a-button @click="showMapModal11">打开AI视频监控站弹窗</a-button> -->
|
<!-- <a-button @click="showMapModal11">打开AI视频监控站弹窗</a-button> -->
|
||||||
<!-- <a-button @click="showMapModal12">打开水电站告警情况弹窗</a-button> -->
|
<!-- <a-button @click="showMapModal12">打开水电站告警情况弹窗</a-button> -->
|
||||||
<!-- <a-button @click="showMapModal13">打开鱼类调查装置弹窗</a-button> -->
|
<!-- <a-button @click="showMapModal13">打开鱼类调查装置弹窗</a-button> -->
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user