574 lines
12 KiB
Vue
574 lines
12 KiB
Vue
<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>
|