555 lines
12 KiB
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>
|