|
|
@@ -124,14 +124,19 @@
|
|
|
<div class="detail-label">背景:</div>
|
|
|
<div class="detail-value">
|
|
|
<div v-if="personnelDetail.backgroundUrl" class="background-images">
|
|
|
- <img
|
|
|
- v-for="(url, index) in getBackgroundImageList(personnelDetail.backgroundUrl)"
|
|
|
- :key="index"
|
|
|
- :src="url"
|
|
|
- alt="背景图片"
|
|
|
- class="background-image"
|
|
|
- @click="previewBackgroundImage(url)"
|
|
|
- />
|
|
|
+ <template v-for="(url, index) in getBackgroundImageList(personnelDetail.backgroundUrl)" :key="index">
|
|
|
+ <div
|
|
|
+ v-if="isVideoBackgroundUrl(url)"
|
|
|
+ class="background-media-item background-media-item--video"
|
|
|
+ @click="previewBackgroundImage(url)"
|
|
|
+ >
|
|
|
+ <video :src="url" class="background-image" muted preload="metadata" playsinline />
|
|
|
+ <span class="background-media-video-badge">
|
|
|
+ <el-icon :size="20"><VideoPlay /></el-icon>
|
|
|
+ </span>
|
|
|
+ </div>
|
|
|
+ <img v-else :src="url" alt="背景图片" class="background-image" @click="previewBackgroundImage(url)" />
|
|
|
+ </template>
|
|
|
</div>
|
|
|
<span v-else>—</span>
|
|
|
</div>
|
|
|
@@ -178,6 +183,21 @@
|
|
|
<!-- 背景图片预览弹窗 -->
|
|
|
<el-image-viewer v-if="previewImageVisible" :url-list="[previewImageUrl]" @close="previewImageVisible = false" />
|
|
|
|
|
|
+ <!-- 背景视频放大预览弹窗 -->
|
|
|
+ <el-dialog
|
|
|
+ v-model="previewVideoVisible"
|
|
|
+ title="视频预览"
|
|
|
+ width="70%"
|
|
|
+ top="5vh"
|
|
|
+ destroy-on-close
|
|
|
+ align-center
|
|
|
+ @close="previewVideoUrl = ''"
|
|
|
+ >
|
|
|
+ <div class="video-preview-wrap">
|
|
|
+ <video v-if="previewVideoUrl" :src="previewVideoUrl" controls autoplay class="video-preview-player" />
|
|
|
+ </div>
|
|
|
+ </el-dialog>
|
|
|
+
|
|
|
<!-- 新建/编辑人员弹窗 -->
|
|
|
<el-dialog v-model="dialogVisible" :title="dialogTitle" width="600px" @close="resetForm">
|
|
|
<el-form ref="formRef" :model="formData" :rules="rules" label-width="100px">
|
|
|
@@ -204,17 +224,18 @@
|
|
|
ref="backgroundImagesUploadRef"
|
|
|
v-model:file-list="formData.backgroundImages"
|
|
|
:limit="9"
|
|
|
- :file-size="20"
|
|
|
- :file-type="['image/jpeg', 'image/png', 'image/gif', 'image/webp']"
|
|
|
+ :file-size="100"
|
|
|
+ :file-type="['image/jpeg', 'image/png', 'image/gif', 'image/webp', 'video/mp4'] as any"
|
|
|
:width="'150px'"
|
|
|
:height="'150px'"
|
|
|
:border-radius="'8px'"
|
|
|
:api="handleCustomUpload"
|
|
|
:show-success-notification="false"
|
|
|
:on-success="handleBackgroundImageSuccess"
|
|
|
+ :on-video-preview="handleVideoPreviewInForm"
|
|
|
>
|
|
|
<template #tip>
|
|
|
- <div class="upload-tip">上传图片 ({{ formData.backgroundImages.length }}/9)</div>
|
|
|
+ <div class="upload-tip">上传图片或视频 ({{ formData.backgroundImages.length }}/9),单个文件不超过 100M</div>
|
|
|
</template>
|
|
|
</UploadImgs>
|
|
|
</el-form-item>
|
|
|
@@ -268,7 +289,7 @@
|
|
|
import { ref, reactive, computed, onMounted, watch, nextTick } from "vue";
|
|
|
import { ElMessage, ElMessageBox } from "element-plus";
|
|
|
import type { FormInstance, FormRules } from "element-plus";
|
|
|
-import { Picture, Plus, Delete, ArrowDown } from "@element-plus/icons-vue";
|
|
|
+import { Picture, Plus, Delete, ArrowDown, VideoPlay } from "@element-plus/icons-vue";
|
|
|
import ProTable from "@/components/ProTable/index.vue";
|
|
|
import { ColumnProps, ProTableInstance } from "@/components/ProTable/interface";
|
|
|
import UploadImg from "@/components/Upload/Img.vue";
|
|
|
@@ -321,6 +342,8 @@ const detailLoading = ref(false);
|
|
|
const personnelDetail = ref<any>(null);
|
|
|
const previewImageVisible = ref(false);
|
|
|
const previewImageUrl = ref("");
|
|
|
+const previewVideoVisible = ref(false);
|
|
|
+const previewVideoUrl = ref("");
|
|
|
|
|
|
// 标题相关
|
|
|
const hasTitle = ref(false); // 是否已有标题
|
|
|
@@ -554,7 +577,7 @@ const getTableList = (params: any) => {
|
|
|
};
|
|
|
|
|
|
// 格式化时间
|
|
|
-// 获取背景图片列表
|
|
|
+// 获取背景图片/视频列表
|
|
|
const getBackgroundImageList = (backgroundUrl: string | null | undefined): string[] => {
|
|
|
if (!backgroundUrl) return [];
|
|
|
if (typeof backgroundUrl === "string") {
|
|
|
@@ -563,12 +586,26 @@ const getBackgroundImageList = (backgroundUrl: string | null | undefined): strin
|
|
|
return Array.isArray(backgroundUrl) ? backgroundUrl : [];
|
|
|
};
|
|
|
|
|
|
-// 预览背景图片
|
|
|
+/** 判断背景 URL 是否为视频(用于详情展示与预览) */
|
|
|
+const isVideoBackgroundUrl = (url: string) => /\.(mp4|webm|ogg|mov)(\?|$)/i.test(url);
|
|
|
+
|
|
|
+// 预览背景图片/视频(图片用查看器,视频用放大弹窗)
|
|
|
const previewBackgroundImage = (url: string) => {
|
|
|
+ if (isVideoBackgroundUrl(url)) {
|
|
|
+ previewVideoUrl.value = url;
|
|
|
+ previewVideoVisible.value = true;
|
|
|
+ return;
|
|
|
+ }
|
|
|
previewImageUrl.value = url;
|
|
|
previewImageVisible.value = true;
|
|
|
};
|
|
|
|
|
|
+// 编辑/新增表单内点击视频「查看」时,复用同一套视频放大弹窗
|
|
|
+const handleVideoPreviewInForm = (url: string) => {
|
|
|
+ previewVideoUrl.value = url;
|
|
|
+ previewVideoVisible.value = true;
|
|
|
+};
|
|
|
+
|
|
|
const formatTime = (time: string | null | undefined) => {
|
|
|
if (!time) return "";
|
|
|
try {
|
|
|
@@ -2386,9 +2423,55 @@ onMounted(async () => {
|
|
|
transform: scale(1.05);
|
|
|
}
|
|
|
}
|
|
|
+ .background-media-item {
|
|
|
+ position: relative;
|
|
|
+ width: 120px;
|
|
|
+ height: 120px;
|
|
|
+ overflow: hidden;
|
|
|
+ cursor: pointer;
|
|
|
+ border-radius: 8px;
|
|
|
+ transition: transform 0.2s;
|
|
|
+ &:hover {
|
|
|
+ box-shadow: 0 2px 8px rgb(0 0 0 / 15%);
|
|
|
+ transform: scale(1.05);
|
|
|
+ .background-media-video-badge {
|
|
|
+ opacity: 1;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ .background-image {
|
|
|
+ width: 100%;
|
|
|
+ height: 100%;
|
|
|
+ }
|
|
|
+ .background-media-video-badge {
|
|
|
+ position: absolute;
|
|
|
+ bottom: 6px;
|
|
|
+ left: 6px;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ width: 28px;
|
|
|
+ height: 28px;
|
|
|
+ color: #ffffff;
|
|
|
+ background: rgb(0 0 0 / 55%);
|
|
|
+ border-radius: 6px;
|
|
|
+ opacity: 0.9;
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
+.video-preview-wrap {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ min-height: 200px;
|
|
|
+ background: #000000;
|
|
|
+ .video-preview-player {
|
|
|
+ width: 100%;
|
|
|
+ max-width: 100%;
|
|
|
+ max-height: 75vh;
|
|
|
+ }
|
|
|
+}
|
|
|
</style>
|