WholeProcessPlatform/frontend/src/views/DataQueryMenuModule/components/FishResource/index.vue
2026-06-17 11:22:03 +08:00

574 lines
12 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="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>