WholeProcessPlatform/frontend/src/modules/alongFishMod/TwoLayers/Yanchengyulei.vue

711 lines
17 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!-- 沿程鱼类变化详情组件 -->
<template>
<div class="yancheng-yulei">
<!-- 搜索表单 -->
<a-form layout="inline" class="search-form">
<!-- 基地名称 -->
<a-form-item label="基地名称">
<a-select
v-model:value="formState.baseId"
placeholder="请选择"
style="width: 150px"
@change="handleBaseChange"
>
<a-select-option
v-for="opt in baseOptions"
:key="opt.value"
:value="opt.value"
>
{{ opt.label }}
</a-select-option>
</a-select>
</a-form-item>
<!-- 所在流域 -->
<a-form-item label="所在流域">
<a-select
v-model:value="formState.rvcd"
placeholder="请选择"
style="width: 150px"
@change="handleRvcdChange"
>
<a-select-option
v-for="opt in lyOptions"
:key="opt.value"
:value="opt.value"
>
{{ opt.label }}
</a-select-option>
</a-select>
</a-form-item>
<!-- 断面名称(沿程模式显示) -->
<!-- <a-form-item v-if="props.ly" label="断面名称">
<a-select
v-model:value="formState.stcd"
placeholder="请选择"
style="width: 150px"
@change="handleStcdChange"
>
<a-select-option
v-for="opt in dmOptions"
:key="opt.value"
:value="opt.value"
>
{{ opt.label }}
</a-select-option>
</a-select>
</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="props.ly && 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 #rsctdName="{ record }">
{{ record.rsctdName || '-' }}
</template>
<template #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 #fishList="{ record }">
<a-list size="small" :data-source="record.fishList">
<template #renderItem="{ item: fish }">
<a-list-item>{{ fish }}</a-list-item>
</template>
</a-list>
</template>
</BasicTable> -->
<!-- 基地模式表格 -->
<BasicTable
:ref="el => setTableRef(el, item.key)"
:columns="tableColumns"
:scroll-y="500"
:scroll-x="800"
:list-url="listUrl"
:search-params="searchParams"
:transform-data="customTransform"
>
<template #stnm="{ record }">
<a
@click="handleViewDetail(record.stcd, record.stnm)"
class="text-link"
>
{{ record.stnm }}
</a>
</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 ? baseColumns : lyColumns));
/** 根据 ly 模式切换表格 API */
const listUrl = computed(() => (props.ly ?fishQgcDetail : fishDetail));
/** 表格搜索参数 */
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: [
formState.value.baseId != 'all'
? {
field: 'fullPath',
operator: 'startswith',
dataType: 'string',
value: " formState.value.baseId+','"
}
: null,
{
field: 'wbsType',
operator: 'eq',
dataType: 'string',
value: 'PSB_RVCD'
}
].filter(Boolean)
},
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 () => {
let params = {
filter: {
logic: 'and',
filters: [
formState.value.baseId != 'all'
? {
field: 'baseId',
operator: 'eq',
dataType: 'string',
value: formState.value.baseId
}
: null,
{
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: 'isnotnull',
dataType: 'string',
value: tabIndex.value
}
].filter(Boolean)
}
};
try {
const res = await getTabNum(params);
// getTabNum 返回格式res.data 直接是数组 [{ty, num}, ...]
const data = res?.data?.data || [];
// debugger
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[] = [
formState.value.baseId != 'all'
? {
field: 'baseId',
operator: 'eq',
dataType: 'string',
value: formState.value.baseId
}
: null,
{
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 = async () => {
// 等待新 Tab 的 BasicTable 渲染完成
await nextTick();
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>