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>
|