WholeProcessPlatform/frontend/src/modules/liuyu/ai/spdzwaijc/components/VideoDetails.vue

318 lines
8.1 KiB
Vue
Raw Normal View History

2026-07-06 08:54:58 +08:00
<template>
<div class="video-details">
<a-tabs v-model:activeKey="activeKey" @change="handleTabChange">
<a-tab-pane key="2" tab="运行">
<a-spin :spinning="loading">
<div class="video-container">
<VideoPlayer
:list="videoList"
:activeFid="activeFid"
:activeMedia="activeMedia"
:loading="loading"
:page="page"
:pageSize="pageSize"
:pageSizeOptions="pageSizeOptions"
:total="total"
@select="handleSelectMedia"
@pageChange="handlePageChange"
/>
</div>
</a-spin>
</a-tab-pane>
<a-tab-pane key="1" tab="未运行">
<a-spin :spinning="loading">
<div class="video-container">
<VideoPlayer
:list="videoList"
:activeFid="activeFid"
:activeMedia="activeMedia"
:loading="loading"
:page="page"
:pageSize="pageSize"
:pageSizeOptions="pageSizeOptions"
:total="total"
@select="handleSelectMedia"
@pageChange="handlePageChange"
/>
</div>
</a-spin>
</a-tab-pane>
</a-tabs>
</div>
</template>
<script lang="ts" setup>
import { ref, watch, onMounted } from 'vue';
import VideoPlayer from '@/modules/yunXingGaoJIng/Aisbdbyx/components/VideoPlayer.vue';
import { getAiFile } from '@/api/zngj';
import { postIdUrl } from '@/api/mapModal';
import dayjs from 'dayjs';
const baseUrl = import.meta.env.VITE_APP_ATTACHMENT_URL;
// ==================== Props ====================
const props = defineProps<{
baseid?: string;
month?: any;
type?: string;
vdDate?: string;
activeKey?: string;
stnm?: string;
rstcd?: string;
pieCode?: string[];
}>();
// ==================== 状态 ====================
const activeKey = ref(props.activeKey || '2');
const loading = ref(false);
const videoList = ref<any[]>([]);
const activeFid = ref('');
const activeMedia = ref<{ type: string; src: string }>({ type: '', src: '' });
const page = ref(1);
const pageSize = ref(20);
const pageSizeOptions = ['20', '50', '100'];
const total = ref(0);
// ==================== 获取视频列表 ====================
const fetchVideoList = async () => {
loading.value = true;
try {
const filters: any[] = [
// baseId过滤
props.pieCode && props.pieCode.length > 0 && props.baseid === 'all'
? {
field: 'baseId',
operator: 'in',
dataType: 'string',
value: props.pieCode
}
: props.baseid && props.baseid !== 'all'
? { field: 'baseId', operator: 'eq', value: props.baseid }
: null,
// rstcd过滤
props.pieCode && props.pieCode.length > 0 && props.baseid !== 'all'
? {
field: 'rstcd',
operator: 'in',
dataType: 'string',
value: props.pieCode
}
: null,
// type过滤
{ field: 'type', operator: 'eq', value: props.type },
// dataType过滤运行/未运行)
{ field: 'dataType', operator: 'eq', value: activeKey.value },
// 时间范围(按天)
{
field: 'tm',
operator: 'gte',
dataType: 'date',
value: dayjs(props.vdDate)
.startOf('day')
.format('YYYY-MM-DD 00:00:00')
},
{
field: 'tm',
operator: 'lte',
dataType: 'date',
value: dayjs(props.vdDate)
.endOf('day')
.format('YYYY-MM-DD 23:59:59')
},
// stnm文本搜索
props.stnm
? { field: 'stnm', operator: 'contains', value: props.stnm }
: null,
// rstcd电站选择
props.rstcd
? {
field: 'rstcd',
operator: 'in',
dataType: 'string',
value: [props.rstcd]
}
: null
].filter(Boolean);
const params = {
filter: { logic: 'and', filters },
sort: [{ field: 'tm', dir: 'desc' }],
skip: (page.value - 1) * pageSize.value,
take: pageSize.value
};
const res: any = await getAiFile(params);
const rData = res?.data ?? {};
const rawData = rData?.data || [];
total.value = rData?.total || rawData.length;
// 按 fid 展开
const expandedData = rawData
.map((item: any) => {
const fids = item.fid ? item.fid.split(',') : [];
return fids.map((fid: string) => ({ ...item, fid: fid.trim() }));
})
.flat();
// 收集所有 fid
const allFids = expandedData
.filter((item: any) => item.fid)
.map((item: any) => item.fid)
.join(',');
// 调用 postIdUrl 获取媒体详情
let mediaMap: Record<string, any> = {};
if (allFids) {
const formData = new FormData();
formData.append('id', allFids);
const mediaRes: any = await postIdUrl(formData);
mediaMap = mediaRes?.data || {};
}
// 构建列表
const list: any[] = [];
expandedData.forEach((vd: any) => {
const fileInfo = mediaMap[vd.fid];
const hasValidExtension =
fileInfo && ['.jpg', '.png'].includes(fileInfo.ext);
let flpth = '';
let imgPath = '';
let type = '';
if (hasValidExtension !== undefined && !hasValidExtension) {
flpth = fileInfo.fullpath;
type = 'vdsp';
} else if (hasValidExtension !== undefined) {
imgPath = fileInfo.fullpath;
type = 'img';
}
const tm = vd.tm ? dayjs(vd.tm).format('YYYY-MM-DD HH:mm:ss') : '-';
list.push({
...vd,
flpth,
imgPath,
flnm: `${vd.ennm || ''}${tm}`,
type,
tm
});
});
// 过滤掉无效项
const listMap = list.filter(
item => item.flpth !== '' || item.imgPath !== ''
);
videoList.value = listMap;
// 默认选中第一条
if (listMap.length > 0) {
const first = listMap[0];
activeFid.value = first.fid;
if (first.type === 'img') {
activeMedia.value = {
type: 'image',
src: baseUrl + '?' + first.fid + '&view=jpg'
};
} else if (first.type === 'vdsp') {
activeMedia.value = {
type: 'video',
src: baseUrl + '?' + first.fid + '&view=jpg'
};
} else {
activeMedia.value = { type: '', src: '' };
}
} else {
activeFid.value = '';
activeMedia.value = { type: '', src: '' };
}
} catch (error) {
console.error('获取视频详情列表失败:', error);
videoList.value = [];
activeFid.value = '';
activeMedia.value = { type: '', src: '' };
} finally {
loading.value = false;
}
};
// ==================== 事件处理 ====================
const handleTabChange = () => {
page.value = 1;
activeFid.value = '';
activeMedia.value = { type: '', src: '' };
fetchVideoList();
};
const handleSelectMedia = (item: any) => {
activeFid.value = item.fid;
if (item.type === 'img') {
activeMedia.value = {
type: 'image',
src: baseUrl + '?' + item.fid + '&view=jpg'
};
} else if (item.type === 'vdsp') {
activeMedia.value = {
type: 'video',
src: baseUrl + '?' + item.fid + '&view=jpg'
};
} else {
activeMedia.value = { type: '', src: '' };
}
};
const handlePageChange = (newPage: number, newPageSize: number) => {
page.value = newPage;
pageSize.value = newPageSize;
activeFid.value = '';
activeMedia.value = { type: '', src: '' };
fetchVideoList();
};
// ==================== 监听 ====================
watch(
() => [props.vdDate, props.type, activeKey.value],
() => {
if (props.vdDate) {
page.value = 1;
fetchVideoList();
}
}
);
// ==================== 生命周期 ====================
onMounted(() => {
if (props.vdDate) {
fetchVideoList();
}
});
</script>
<style lang="scss" scoped>
.video-details {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
:deep(.ant-tabs) {
flex: 1;
display: flex;
flex-direction: column;
}
:deep(.ant-tabs-content) {
flex: 1;
}
:deep(.ant-tabs-tabpane) {
height: 100%;
}
.video-container {
height: 100%;
}
}
</style>