WholeProcessPlatform/frontend/src/views/DataQueryMenuModule/components/FishResource/index.vue

574 lines
12 KiB
Vue
Raw Normal View History

2026-06-17 11:22:03 +08:00
<template>
<div class="w-full h-full">
<!-- 搜索组件 -->
<FishResourceSearch
:fish_ptype="fish_ptype_list"
:familyList="familyList"
:ptypeLoading="ptypeLoading"
:familyLoading="familyLoading"
:exportBtn="exportBtn"
@search-finish="onSearchFinish"
ref="searchRef"
/>
<a-row
type="flex"
class="mt-[20px] w-full"
style="height: calc(100% - 80px)"
>
<a-col flex="75%" class="pr-4 flex overflow-auto">
<!-- 表格组件 -->
<BasicTable
ref="tableRef"
:scrollX="tableScrollX"
:scrollY="tableScrollY"
:columns="columns"
:list-url="getFishResourceZYList"
:transformData="transformData"
@row-click="handleRowClick"
>
</BasicTable>
</a-col>
<a-col flex="25%" class="overflow-hidden">
<div
ref="rightPanelRef"
class="bg-[#fafafa] h-full pt-[10px] pr-[15px] pl-[15px]"
>
<a-spin :spinning="rightPanelLoading">
<div style="display: flex; align-items: center">
<div
style="
width: 4px;
height: 18px;
background-color: rgb(47, 107, 152);
margin-right: 10px;
"
></div>
<div style="font-size: 18px; color: rgba(0, 0, 0, 0.85)">
{{ fishResource.name }}
</div>
</div>
<div
:style="{ width: carouselWidth + 'px' }"
class="mt-[10px] px-[10px]"
>
<a-carousel
arrows
v-if="fishResource.logoList.length > 0"
class="w-full h-[306px] slick-arrow"
>
<template #prevArrow>
<div
class="custom-slick-arrow"
style="left: 10px; z-index: 1"
>
<left-circle-outlined />
</div>
</template>
<template #nextArrow>
<div class="custom-slick-arrow" style="right: 10px">
<right-circle-outlined />
</div>
</template>
<div v-for="(img, index) in fishResource.logoList" :key="index">
<img
:src="img"
alt=""
class="w-full h-[306px] object-cover"
/>
</div>
</a-carousel>
<a-empty v-else description="暂无图片" class="h-[306px]" />
</div>
<div
class="w-full mt-[10px] px-[10px]"
style="
margin-top: 24px;
font-size: 18px;
color: rgba(0, 0, 0, 0.85);
max-height: 440px;
overflow-y: scroll;
"
>
{{ fishResource.shapedesc }}
</div>
</a-spin>
</div>
</a-col>
</a-row>
</div>
</template>
<script setup lang="ts">
import {
ref,
computed,
watch,
onMounted,
onBeforeUnmount,
nextTick,
h
} from 'vue';
import FishResourceSearch from './FishResourceSearch.vue';
import BasicTable from '@/components/BasicTable/index.vue';
import { LeftCircleOutlined, RightCircleOutlined } from '@ant-design/icons-vue';
import { getDictItemsByCode } from '@/api/dict';
import {
getFishResourceList,
getFishResourceZYList
} from '@/api/DataQueryMenuModule';
import { calcTableScrollY } from '@/utils/index';
const fishResource = ref<any>({
name: '',
logoList: [],
shapedesc: ''
});
const baseUrl = import.meta.env.VITE_APP_ATTACHMENT_URL;
const rightPanelRef = ref<HTMLElement | null>(null);
const carouselWidth = ref(0);
const tableScrollY = ref<string | number>(0);
const updateCarouselWidth = () => {
if (rightPanelRef.value) {
const rect = rightPanelRef.value.getBoundingClientRect();
carouselWidth.value = rect.width - 20; // 减去 px-[10px] 的 padding
}
};
const updateTableScrollY = () => {
nextTick(() => {
tableScrollY.value = calcTableScrollY(rightPanelRef.value);
});
};
const resizeObserver = new ResizeObserver(() => {
updateCarouselWidth();
updateTableScrollY();
});
const tableScrollX = computed(() =>
Math.max(
columns.reduce((sum: number, col: any) => sum + (col.width || 180), 0),
600
)
);
const columns: any = [
{
key: 'index',
title: '序号',
dataIndex: 'index',
visible: true,
width: 80,
fixed: 'left'
},
{
key: 'name',
title: '中文名称',
dataIndex: 'name',
visible: true,
width: 150,
ellipsis: true,
fixed: 'left',
customRender: ({ text }: any) => {
return text
? h('span', { style: { color: '#2f6b98', cursor: 'pointer' } }, text)
: '-';
}
},
{
key: 'nameEn',
title: '拉丁学名',
dataIndex: 'nameEn',
visible: true,
width: 200,
ellipsis: true,
fixed: 'left'
},
{
key: 'rvcdName',
title: '所属流域',
dataIndex: 'rvcdName',
visible: true,
width: 100,
ellipsis: true,
fixed: 'left'
},
{
key: 'typeName',
title: '分类',
dataIndex: 'typeName',
visible: true,
width: 100,
ellipsis: true
},
{
key: 'species',
title: '种',
dataIndex: 'species',
visible: true,
width: 150,
ellipsis: true
},
{
key: 'genus',
title: '属',
dataIndex: 'genus',
visible: true,
width: 100,
ellipsis: true
},
{
key: 'family',
title: '科',
dataIndex: 'family',
visible: true,
width: 100,
ellipsis: true
},
{
key: 'orders',
title: '目',
dataIndex: 'orders',
visible: true,
width: 150,
ellipsis: true
},
// {
// "key": "nameEn",
// "title": "英文名称",
// "dataIndex": "nameEn",
// "visible": true,
// width: 200,
// ellipsis: true,
// },
{
key: 'alias',
title: '俗名',
dataIndex: 'alias',
visible: true,
width: 200,
ellipsis: true
},
{
key: 'fsz',
title: '成鱼大小(cm)',
dataIndex: 'fsz',
visible: true,
width: 100,
ellipsis: true
},
{
key: 'shapedesc',
title: '形态描述',
dataIndex: 'shapedesc',
visible: true,
width: 300,
ellipsis: true
},
{
key: 'habitation',
title: '栖息习性',
dataIndex: 'habitation',
visible: true,
width: 200,
ellipsis: true
},
{
key: 'habitMigrat',
title: '洄游习性',
dataIndex: 'habitMigrat',
visible: true,
width: 100,
ellipsis: true
},
{
key: 'feedingHabit',
title: '摄食习性',
dataIndex: 'feedingHabit',
visible: true,
width: 100,
ellipsis: true
},
{
key: 'food',
title: '主要食物',
dataIndex: 'food',
visible: true,
width: 200,
ellipsis: true
},
{
key: 'timeFeed',
title: '觅食时段',
dataIndex: 'timeFeed',
visible: true,
width: 200,
ellipsis: true
},
{
key: 'spawnMonth',
title: '产卵期(月)',
dataIndex: 'spawnMonth',
visible: true,
width: 200,
ellipsis: true
},
{
key: 'orignDate',
title: '产地及产期',
dataIndex: 'orignDate',
visible: true,
width: 300,
ellipsis: true
},
{
key: 'description',
title: '内容',
dataIndex: 'description',
visible: true,
width: 200,
ellipsis: true
},
{
key: 'pretemp',
title: '适宜温度',
dataIndex: 'pretemp',
visible: true,
width: 100,
ellipsis: true
},
{
key: 'flowRate',
title: '适宜流速',
dataIndex: 'flowRate',
visible: true,
width: 100,
ellipsis: true
},
{
key: 'depth',
title: '适宜水深(m)',
dataIndex: 'depth',
visible: true,
width: 100,
ellipsis: true
},
{
key: 'wqtq',
title: '水质要求',
dataIndex: 'wqtq',
visible: true,
width: 100,
ellipsis: true
},
{
key: 'spawnCharact',
title: '型',
dataIndex: 'spawnCharact',
visible: true,
width: 100,
ellipsis: true
},
{
key: 'botmMater',
title: '地质',
dataIndex: 'botmMater',
visible: true,
width: 100,
ellipsis: true
},
{
key: 'ptypeName',
title: '保护类型',
dataIndex: 'ptypeName',
visible: true,
width: 100,
ellipsis: true
},
{
key: 'specOriginName',
title: '物种来源',
dataIndex: 'specOriginName',
visible: true,
width: 100,
ellipsis: true
},
{
key: 'vlsr',
title: '数据来源',
dataIndex: 'vlsr',
visible: true,
width: 150,
ellipsis: true
}
// {
// "key": "inffile",
// "title": "附件预览",
// "dataIndex": "inffile",
// "visible": true,
// width: 100,
// ellipsis: true,
// "fixed": "right",
// },
];
const rightNumber = ref(0);
const tableRef = ref();
const fish_ptype_list = ref<any>([]);
const familyList = ref<any>([]);
const ptypeLoading = ref(false);
const familyLoading = ref(false);
const rightPanelLoading = ref(false);
const exportBtn = () => {
console.log(fishResource.value);
};
const handleRowClick = (record: any) => {
rightPanelLoading.value = true;
// 处理 inffile支持多个图片逗号分隔、单个图片、无图片
let logoList: string[] = [];
if (record.inffile && record.inffile !== '-') {
// 按逗号分割,过滤空值和占位符,拼接完整 URL
logoList = record.inffile
.split(',')
.map((item: string) => item.trim())
.filter((item: string) => item && item !== '-' && item.length > 0)
.map((item: string) => `${baseUrl}/?${item}&view=jpg`);
}
fishResource.value = {
name: record.name,
logoList,
shapedesc: record.shapedesc
};
// 右侧面板内容更新后关闭 loading
setTimeout(() => {
rightPanelLoading.value = false;
}, 200);
};
const onSearchFinish = (values: any) => {
fishResource.value = {
name: '',
logoList: [],
shapedesc: ''
};
ininTable(values);
};
const transformData = (res: any) => {
if (res?.data?.data) {
if (rightNumber.value === 0) {
handleRowClick(res?.data?.data[0]);
}
rightNumber.value++;
}
return res?.data || [];
};
const ininTable = (values: any) => {
const params = {
logic: 'and',
filters: [
values.ptype && values.ptype !== 'all'
? {
field: 'ptype',
operator: 'eq',
dataType: 'string',
value: values.ptype
}
: null,
values.family && values.family !== 'all' && values.family !== '全部'
? {
field: 'family',
operator: 'eq',
dataType: 'string',
value: values.family
}
: null,
values.name
? {
field: 'name',
operator: 'contains',
dataType: 'string',
value: values.name
}
: null
].filter(Boolean)
};
tableRef.value.getList(params);
};
const init = () => {
ptypeLoading.value = true;
familyLoading.value = true;
getDictItemsByCode({ dictCode: 'FISH_PTYPE' }).then(res => {
res.data.unshift({
itemCode: 'all',
dictName: '全部'
});
fish_ptype_list.value = res.data;
ptypeLoading.value = false;
});
const params = {
group: [
{
field: 'family',
dir: 'asc'
}
]
};
getFishResourceList(params).then(res => {
res?.data?.data.unshift({
count: '全部',
key: '全部'
});
familyList.value = res?.data?.data || [];
familyLoading.value = false;
});
};
onMounted(() => {
init();
nextTick(() => {
updateCarouselWidth();
updateTableScrollY();
if (rightPanelRef.value) {
resizeObserver.observe(rightPanelRef.value);
}
});
});
onBeforeUnmount(() => {
resizeObserver.disconnect();
});
</script>
<style scoped lang="scss">
:deep(.slick-slide) {
text-align: center;
background: #364d79;
overflow: hidden;
}
:deep(.slick-arrow.custom-slick-arrow) {
width: 25px;
height: 25px;
font-size: 25px;
color: #fff;
background-color: rgba(31, 45, 61, 0.11);
transition: ease all 0.3s;
opacity: 0.3;
z-index: 1;
}
:deep(.slick-arrow.custom-slick-arrow:before) {
display: none;
}
:deep(.slick-arrow.custom-slick-arrow:hover) {
color: #fff;
opacity: 0.5;
}
:deep(.slick-slide h3) {
color: #fff;
}
</style>