Files
Docker/WebVue/src/views/master/Preview.vue
T
2026-09-23 07:25:47 +08:00

189 lines
6.8 KiB
Vue

<template>
<div class="pv-page">
<PhotoSlidePlayer v-model:controls="controlsVisible" :items="slides" @close="onClose" />
</div>
</template>
<script lang="ts" setup>
import { watch, computed, inject, reactive, ref, VNode, h, unref, onMounted, onBeforeMount, onUnmounted } from "vue";
import { useRoute, useRouter } from 'vue-router'
import PhotoSlidePlayer from "@/components/basic/PhotoSlidePlayer.vue";
import type { PhotoSlideItem } from "@/components/basic/PhotoSlidePlayer.vue";
import { PublishPhotoDetailDisplayData } from "@/data/auto/publishPhotoDetailDisplayData";
import publishPhotoService from "@/service/master/publishPhotoService";
import { bsInfo } from "@/utils/browser"
import { DmsFileSearchCriteria } from "@/data/auto/dmsFileSearchCriteria";
import { SearchFilter } from "@/data/auto/searchFilter";
import { DmsFileUploadViewDisplayData } from "@/data/auto/dmsFileUploadViewDisplayData";
import { FileTypePolicy } from "@/policy/custom/fileTypePolicy";
import { DmsFileResultData } from "@/data/auto/dmsFileResultData";
import dmsFileService from "@/service/master/dmsFileService";
import { LeftCircleOutlined, RightCircleOutlined } from '@ant-design/icons-vue';
import { SearchCriteriaResultData } from "@/data/auto/searchCriteriaResultData";
/**
* 后端 PublishPhotoService 会把同一张图给出两个地址(见 PublishPhotoService.auto.cs):
* FileName —— 原图
* FormattedPic —— 缩放后的缩略图
* 幻灯片轮播与图片列表用缩略图(加载快),精看和下载用原图。
*/
const images = ref<PublishPhotoDetailDisplayData[]>([]);
const route = useRoute();
const router = useRouter();
const publishName = ref<string>(route.params.id as string)
const mode = ref<string>(route.params.mode as string)
/**
* 控制区显隐:与影视厅 PreviewVideo.vue 同一套机制——
* 收起控制区时给 body 挂 .pv-fullscreen,藏掉 layout 顶部的两条栏
* (标题栏 + 页面头)并把内容区撑到整屏,图片真正占满全屏。
* class 挂在 body 上,离开本页立即移除,不会影响其它页面(样式见 styles/main.css)。
*/
const controlsVisible = ref(true);
/** 退出全屏后让顶栏短暂不响应指针的定时器 */
let settleTimer = 0;
watch(controlsVisible, (visible) => {
document.body.classList.toggle("pv-fullscreen", !visible);
// 退出全屏时鼠标往往正好停在右上角(恢复按钮的位置,也是顶栏菜单的位置),
// 顶栏一恢复显示就会被 hover 展开下拉。让它先"冷静" 400ms,避开这次误触。
if (visible) {
window.clearTimeout(settleTimer);
document.body.classList.add("pv-settling");
settleTimer = window.setTimeout(() => {
document.body.classList.remove("pv-settling");
}, 400);
}
});
onUnmounted(() => {
document.body.classList.remove("pv-fullscreen");
document.body.classList.remove("pv-settling");
window.clearTimeout(settleTimer);
});
const toIndex = ref<number>(0)
const beforeChange = (from: number, to: number) => {
if (to > 0) {
toIndex.value = to;
}
}
// 交给 PhotoSlidePlayer 播放:舞台与列表用 FormattedPic(缩略图),精看/下载用 FileName(原图)
const slides = computed<PhotoSlideItem[]>(() =>
images.value.map((item) => ({
url: item.FormattedPic || item.FileName,
thumb: item.FormattedPic || item.FileName,
full: item.FileName || item.FormattedPic,
name: (item.FileName || "").replace(/\.[^./\\]+$/, "") || undefined,
}))
);
// 收起播放器:返回来源页;没有历史时回首页
const onClose = (() => {
if (window.history.length > 1) {
router.back();
}
else {
router.push('/');
}
});
const pagingParams = ref<PagingParams>({ pageSize: bsInfo.size.sm ? 30 : 100, page: 1 });
const result = ref<SearchCriteriaResultData>(new SearchCriteriaResultData());
const pageSizeOptions = ref<string[]>(['30', '60', '90', '100', '120']);
result.value.Page = pagingParams.value.page ?? 1;
result.value.Limit = pagingParams.value.pageSize;
onMounted(async () => {
if (mode.value == "list") {
findData(1);
}
else if (publishName.value && publishName.value.length > 0) {
publishPhotoService.FindPublishPhotoDetailByPublishName(publishName.value).then(res => {
images.value = res;
});
}
else {
publishPhotoService.FindPublishPhotoDetail().then(res => {
images.value = res;
});
}
})
const findData = async (params: number) => {
try {
let c: DmsFileSearchCriteria = new DmsFileSearchCriteria();
c.page = params;
c.limit =999999;// pagingParams.value.pageSize;
let filter: SearchFilter;
filter = new SearchFilter();
filter.Column = DmsFileUploadViewDisplayData.DB_FILE_PATH;
filter.Type = "SingleSelection";
filter.Operator = "single";
filter.Value = publishName.value;
c.Filter.push(filter);
filter = new SearchFilter();
filter.Column = DmsFileUploadViewDisplayData.DB_FILE_TYPE;
filter.Type = "SingleSelection";
filter.Operator = "single";
filter.Value = FileTypePolicy.PHOTO;
c.Filter.push(filter);
let dr: DmsFileResultData = await dmsFileService.FindBySearchCriteria(c);
result.value.Total = dr.Total;
result.value.Page = dr.Page;
if (dr && dr.Datas && dr.Datas.length > 0) {
images.value = [];
let detail: PublishPhotoDetailDisplayData;
for (let i = 0; i < dr.Datas.length; i++) {
detail = new PublishPhotoDetailDisplayData()
detail.FileName = dr.Datas[i].Url; // 原图:精看 / 下载
detail.FormattedPic = dr.Datas[i].FormattedPic; // 缩略图:舞台轮播 / 列表
detail.Notice = dr.Datas[i].LandscapeIndc;
images.value.push(detail);
}
}
} catch (error) {
console.log(error);
}
}
const onPagination = (async (params: number) => {
findData(params);
});
const showSizeChange = (async (current: number, size: number) => {
pagingParams.value.page = 1;
pagingParams.value.pageSize = size;
result.value.Page = 1;
result.value.Limit = size;
});
</script>
<style lang="less" scoped>
/* 外层 .layout-content-view 自带 padding-top / padding-left(各 8px),
这里用等量负外边距抵消、同时把宽高补回满格,让播放器铺满整个内容区,
最外层不留任何间距 */
.pv-page {
width: calc(100% + 8px);
height: calc(100% + 8px);
margin: -8px 0 0 -8px;
overflow: hidden;
}
/* 铺满后不再需要卡片圆角,否则四个角会露出留白 */
.pv-page :deep(.psp) {
border-radius: 0;
}
</style>