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

555 lines
12 KiB
Vue

<template>
<div class="w-full h-full flex flex-col" style="height:calc(60vh)">
<!-- 搜索组件 -->
<FishDataSearch
@export-btn="exportBtn"
@search-finish="onSearchFinish"
@reset="onReset"
:exportLoading="exportLoading"
:modalData = "props.modalData"
ref="searchRef"
/>
<!-- 表格组件 -->
<BasicTable
ref="tableRef"
:scrollX="tableScrollX"
:scrollY="tableScrollY"
:columns="tableColumns"
:list-url="getFishReleaseMonitorList"
:searchParams="{
sort: sort
}"
>
</BasicTable>
<!-- 图片预览弹框 -->
<a-modal
v-model:open="imagePreviewVisible"
title="图片预览"
:footer="null"
width="800px"
>
<div style="text-align: center">
<img :src="previewImageUrl" style="width: 100%; height: 600px" />
</div>
</a-modal>
<!-- 视频预览弹框 -->
<a-modal
v-model:open="videoPreviewVisible"
title="视频预览"
:footer="null"
width="800px"
>
<div style="text-align: center">
<video
:src="previewVideoUrl"
controls
style="width: 100%; height: 600px"
></video>
</div>
</a-modal>
</div>
</template>
<script setup lang="ts">
import { ref, computed, onMounted, nextTick, h } from 'vue';
import dayjs from 'dayjs';
import { Button } from 'ant-design-vue';
import FishDataSearch from './FishResourceSearch.vue';
import BasicTable from '@/components/BasicTable/index.vue';
import { getFishReleaseMonitorList } from '@/api/DataQueryMenuModule';
import { calcTableScrollY } from '@/utils/index';
import { useDraggable } from '@/utils/drag';
const props = defineProps<{
modalData?: any;
}>();
const sort = ref<any>([
{
field: 'baseStepSort',
dir: 'asc'
},
{
field: 'rvcdStepSort',
dir: 'asc'
},
{
field: 'rstcdStepSort',
dir: 'asc'
},
{
field: 'ttpwr',
dir: 'asc'
},
{
field: 'tm',
dir: 'desc'
}
]);
const tableRef = ref();
const searchRef = ref();
const tableScrollY = ref<string | number>(0);
const currentSearchParams = ref<any>({});
const exportLoading = ref(false);
// 图片预览
const imagePreviewVisible = ref(false);
const previewImageUrl = ref('');
// 视频预览
const videoPreviewVisible = ref(false);
const previewVideoUrl = ref('');
useDraggable(imagePreviewVisible, { boundary: true, resetOnOpen: true });
useDraggable(videoPreviewVisible, { boundary: true, resetOnOpen: true });
const openImagePreview = (url: string) => {
previewImageUrl.value = import.meta.env.VITE_APP_ATTACHMENT_URL + '/?' + url;
imagePreviewVisible.value = true;
};
const openVideoPreview = (url: string) => {
previewVideoUrl.value = import.meta.env.VITE_APP_ATTACHMENT_URL + '/?' + url;
videoPreviewVisible.value = true;
};
const tableScrollX = computed(() =>
Math.max(
tableColumns.value.reduce(
(sum: number, col: any) => sum + (col.width || 180),
0
),
600
)
);
// 人工列
const columnsManual: any[] = [
{
key: 'stnm',
title: '电站名称',
dataIndex: 'stnm',
visible: true,
width: 200,
sort: true,
merge: true
},
{
key: 'baseName',
title: '所属基地',
dataIndex: 'baseName',
sort: true,
width: 120
},
{
key: 'stnm',
title: '过鱼设施名称',
dataIndex: 'stnm',
sort: true,
width: 200
},
{
key: 'strdt',
title: '开始时间',
dataIndex: 'strdt',
sort: true,
width: 100,
customRender: ({ text }) => dayjs(text).format('YYYY-MM-DD') || '-'
},
{
key: 'enddt',
title: '结束时间',
dataIndex: 'enddt',
sort: true,
width: 100,
customRender: ({ text }) => dayjs(text).format('YYYY-MM-DD') || '-'
},
{
key: 'tm',
title: '填报时间',
dataIndex: 'tm',
sort: true,
width: 160
},
{
key: 'ftp',
title: '鱼种类',
dataIndex: 'ftp',
sort: true,
width: 120
},
{
key: 'fcnt',
title: '过鱼数量(尾)',
dataIndex: 'fcnt',
sort: true,
width: 120
},
{
key: 'directionName',
title: '鱼游向',
dataIndex: 'directionName',
sort: true,
width: 120
},
{
key: 'fsz',
title: '鱼类规格',
dataIndex: 'fsz',
sort: true,
width: 160
},
{
key: 'firstimgurl',
title: '图片',
dataIndex: 'firstimgurl',
align: 'center',
width: 110,
customRender: ({ text }: any) => {
const hasValidUrl =
text &&
text !== '-' &&
text.trim() !== '' &&
text !== 'null' &&
text !== 'undefined';
return h(
Button,
{
type: 'link',
disabled: !hasValidUrl,
style: {
color: !hasValidUrl ? '#ccc' : '#1890ff',
cursor: !hasValidUrl ? 'not-allowed' : 'pointer'
},
onClick: () => {
if (hasValidUrl) {
openImagePreview(text);
}
}
},
() => '查看图片'
);
}
},
{
key: 'videourl',
title: '视频',
dataIndex: 'videourl',
align: 'center',
width: 110,
customRender: ({ text }: any) => {
const hasValidUrl =
text &&
text !== '-' &&
text.trim() !== '' &&
text !== 'null' &&
text !== 'undefined';
return h(
Button,
{
type: 'link',
disabled: !hasValidUrl,
style: {
color: !hasValidUrl ? '#ccc' : '#1890ff',
cursor: !hasValidUrl ? 'not-allowed' : 'pointer'
},
onClick: () => {
if (hasValidUrl) {
openVideoPreview(text);
}
}
},
() => '查看视频'
);
}
}
];
// 自动列
const columnsAuto: any[] = [
{
key: 'stnm',
title: '电站名称',
dataIndex: 'stnm',
visible: true,
width: 200,
sort: true,
merge: true
},
{
key: 'baseName',
title: '所属基地',
dataIndex: 'baseName',
sort: true,
width: 120
},
{
key: 'stnm',
title: '过鱼设施名称',
dataIndex: 'stnm',
sort: true,
width: 200
},
{
key: 'tm',
title: '时间',
dataIndex: 'tm',
sort: true,
width: 160
},
{
key: 'ftp',
title: '鱼种类',
dataIndex: 'ftp',
sort: true,
width: 120
},
{
key: 'length',
title: '鱼长度(cm)',
dataIndex: 'length',
sort: true,
width: 120
},
{
key: 'width',
title: '鱼宽度(cm)',
dataIndex: 'width',
sort: true,
width: 120,
customRender: ({ text }: any) => {
const num = parseFloat(text);
return isNaN(num) ? '-' : num.toFixed(2);
}
},
{
key: 'fishspeed',
title: '鱼速度(m/s)',
dataIndex: 'fishspeed',
sort: true,
width: 80,
customRender: ({ text }: any) => {
const num = parseFloat(text);
return isNaN(num) ? '-' : num.toFixed(2);
}
},
{
key: 'directionName',
title: '鱼游向',
dataIndex: 'directionName',
sort: true,
width: 160
},
{
key: 'temperature',
title: '水温(℃)',
dataIndex: 'temperature',
sort: true,
width: 160,
customRender: ({ text }: any) => {
const num = parseFloat(text);
return isNaN(num) ? '-' : num.toFixed(1);
}
},
{
key: 'speed',
title: '流速(m/s)',
dataIndex: 'speed',
sort: true,
width: 160,
customRender: ({ text }: any) => {
const num = parseFloat(text);
return isNaN(num) ? '-' : num.toFixed(2);
}
},
{
key: 'fcnt',
title: '过鱼数量(尾)',
dataIndex: 'fcnt',
sort: true,
width: 120
},
{
key: 'channelno',
title: '过鱼通道',
dataIndex: 'channelno',
sort: true,
width: 140
},
{
key: 'firstimgurl',
title: '轮廓图',
dataIndex: 'firstimgurl',
align: 'center',
width: 110,
customRender: ({ text }: any) => {
const hasValidUrl =
text &&
text !== '-' &&
text.trim() !== '' &&
text !== 'null' &&
text !== 'undefined';
return h(
Button,
{
type: 'link',
disabled: !hasValidUrl,
style: {
color: !hasValidUrl ? '#ccc' : '#1890ff',
cursor: !hasValidUrl ? 'not-allowed' : 'pointer'
},
onClick: () => {
if (hasValidUrl) {
openImagePreview(text);
}
}
},
() => '查看图片'
);
}
},
{
key: 'videourl',
title: '短视频',
dataIndex: 'videourl',
align: 'center',
width: 110,
customRender: ({ text }: any) => {
const hasValidUrl =
text &&
text !== '-' &&
text.trim() !== '' &&
text !== 'null' &&
text !== 'undefined';
return h(
Button,
{
type: 'link',
disabled: !hasValidUrl,
style: {
color: !hasValidUrl ? '#ccc' : '#1890ff',
cursor: !hasValidUrl ? 'not-allowed' : 'pointer'
},
onClick: () => {
if (hasValidUrl) {
openVideoPreview(text);
}
}
},
() => '查看视频'
);
}
}
];
// 根据数据类型动态切换列
const tableColumns = computed(() => {
const mway = currentSearchParams.value.mway;
return mway === '2' ? columnsAuto : columnsManual;
});
const onSearchFinish = async (values: any) => {
currentSearchParams.value = values;
// 等待 prop 更新后再调用 getList
await nextTick();
initTable(values);
};
const onReset = async (values: any) => {
currentSearchParams.value = values;
await nextTick();
initTable(values);
};
const exportBtn = () => {
if (exportLoading.value) return;
exportLoading.value = true;
searchRef.value.btnLoading = true;
tableRef.value
.exportTable({
fileName: `过鱼监测数据_${dayjs().format('YYYY-MM-DD HH-mm-ss')}`
})
.finally(() => {
exportLoading.value = false;
searchRef.value.btnLoading = false;
});
};
const initTable = (values: any) => {
console.log(values);
const filters = [
values.baseId && values.baseId !== 'all'
? {
field: 'baseId',
operator: 'contains',
dataType: 'string',
value: values.baseId
}
: null,
values.rstcd
? {
field: 'rstcd',
operator: 'eq',
dataType: 'string',
value: values.rstcd
}
: null,
values.mway
? {
field: 'mway',
operator: 'eq',
dataType: 'string',
value: values.mway
}
: null
].filter(Boolean); // 移除空项
if (values.stcd) {
filters.push({
logic: 'or',
filters: [
{
field: 'stcd',
operator: 'eq',
dataType: 'string',
value: values.stcd
},
{
field: 'stcode',
operator: 'eq',
dataType: 'string',
value: values.stcd
}
]
});
}
if (values.jcdt) {
filters.push(
{
field: 'tm',
operator: 'gte',
dataType: 'date',
value: dayjs(values.jcdt.min).format('YYYY-MM-DD HH:mm:ss')
},
{
field: 'tm',
operator: 'lte',
dataType: 'date',
value: dayjs(values.jcdt.max).format('YYYY-MM-DD HH:mm:ss')
}
);
}
const params = {
logic: 'and',
filters
};
tableRef.value.getList(params);
};
onMounted(() => {
nextTick(() => {
tableScrollY.value = calcTableScrollY();
});
});
</script>
<style scoped lang="scss"></style>