| | |
| | | <template> |
| | | <view class="attachment-page"> |
| | | <!-- 页面头部 --> |
| | | <PageHeader :title="`查看附件 - ${taskInfo?.taskName || ''}`" @back="goBack" /> |
| | | |
| | | <PageHeader :title="`查看附件 - ${taskInfo?.taskName || ''}`" |
| | | @back="goBack" /> |
| | | <!-- 页面内容 --> |
| | | <view class="attachment-content"> |
| | | <!-- 分类标签页 --> |
| | | <view class="attachment-tabs"> |
| | | <view |
| | | class="tab-item" |
| | | :class="{ active: currentViewType === 'before' }" |
| | | @click="switchViewType('before')" |
| | | > |
| | | 生产前 ({{ getAttachmentsByType(0).length }}) |
| | | </view> |
| | | <view |
| | | class="tab-item" |
| | | :class="{ active: currentViewType === 'after' }" |
| | | @click="switchViewType('after')" |
| | | > |
| | | 生产中 ({{ getAttachmentsByType(1).length }}) |
| | | </view> |
| | | <view |
| | | class="tab-item" |
| | | :class="{ active: currentViewType === 'issue' }" |
| | | @click="switchViewType('issue')" |
| | | > |
| | | 生产后 ({{ getAttachmentsByType(2).length }}) |
| | | </view> |
| | | </view> |
| | | |
| | | <!-- 当前分类的附件列表 --> |
| | | <view class="attachment-list-container"> |
| | | <view v-if="getCurrentViewAttachments().length > 0" class="attachment-list"> |
| | | <view |
| | | v-for="(file, index) in getCurrentViewAttachments()" |
| | | :key="index" |
| | | class="attachment-item" |
| | | @click="previewAttachment(file)" |
| | | > |
| | | <view v-if="attachmentList.length > 0" |
| | | class="attachment-list"> |
| | | <view v-for="(file, index) in attachmentList" |
| | | :key="index" |
| | | class="attachment-item" |
| | | @click="previewAttachment(file)"> |
| | | <view class="attachment-preview-container"> |
| | | <image |
| | | v-if="isImageFile(file)" |
| | | :src="file.url || file.downloadUrl" |
| | | class="attachment-preview" |
| | | mode="aspectFill" |
| | | /> |
| | | <view v-else class="attachment-video-preview"> |
| | | <u-icon name="video" size="40" color="#409eff"></u-icon> |
| | | <image v-if="isImageFile(file)" |
| | | :src="file.url || file.downloadUrl" |
| | | class="attachment-preview" |
| | | mode="aspectFill" /> |
| | | <view v-else |
| | | class="attachment-video-preview"> |
| | | <u-icon name="video" |
| | | size="40" |
| | | color="#409eff"></u-icon> |
| | | <text class="video-text">视频</text> |
| | | </view> |
| | | </view> |
| | |
| | | </view> |
| | | </view> |
| | | </view> |
| | | <view v-else class="attachment-empty"> |
| | | <u-icon name="folder-open" size="60" color="#ccc"></u-icon> |
| | | <view v-else |
| | | class="attachment-empty"> |
| | | <u-icon name="folder-open" |
| | | size="60" |
| | | color="#ccc"></u-icon> |
| | | <text class="empty-text">该分类暂无附件</text> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | |
| | | <!-- 视频预览弹窗 --> |
| | | <view v-if="showVideoDialog" class="video-modal-overlay" @click="closeVideoPreview"> |
| | | <view class="video-modal-container" @click.stop> |
| | | <view v-if="showVideoDialog" |
| | | class="video-modal-overlay" |
| | | @click="closeVideoPreview"> |
| | | <view class="video-modal-container" |
| | | @click.stop> |
| | | <view class="video-modal-header"> |
| | | <text class="video-modal-title">{{ currentVideoFile?.originalFilename || '视频预览' }}</text> |
| | | <view class="close-btn-video" @click="closeVideoPreview"> |
| | | <u-icon name="close" size="20" color="#fff"></u-icon> |
| | | <view class="close-btn-video" |
| | | @click="closeVideoPreview"> |
| | | <u-icon name="close" |
| | | size="20" |
| | | color="#fff"></u-icon> |
| | | </view> |
| | | </view> |
| | | <view class="video-modal-body"> |
| | | <video |
| | | v-if="currentVideoFile" |
| | | :src="currentVideoFile.url || currentVideoFile.downloadUrl" |
| | | class="video-player" |
| | | controls |
| | | autoplay |
| | | @error="handleVideoError" |
| | | ></video> |
| | | <video v-if="currentVideoFile" |
| | | :src="currentVideoFile.url || currentVideoFile.downloadUrl" |
| | | class="video-player" |
| | | controls |
| | | autoplay |
| | | @error="handleVideoError"></video> |
| | | </view> |
| | | </view> |
| | | </view> |
| | |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref } from 'vue'; |
| | | import { onLoad } from '@dcloudio/uni-app'; |
| | | import PageHeader from '@/components/PageHeader.vue'; |
| | | import config from '@/config'; |
| | | import { ref } from "vue"; |
| | | import { onLoad } from "@dcloudio/uni-app"; |
| | | import PageHeader from "@/components/PageHeader.vue"; |
| | | import config from "@/config"; |
| | | |
| | | // 任务信息 |
| | | const taskInfo = ref(null); |
| | | // 任务信息 |
| | | const taskInfo = ref(null); |
| | | |
| | | // 附件列表 |
| | | const attachmentList = ref([]); |
| | | // 附件列表 |
| | | const attachmentList = ref([]); |
| | | |
| | | // 当前查看类型 |
| | | const currentViewType = ref('before'); // 'before', 'after', 'issue' |
| | | // 视频预览相关状态 |
| | | const showVideoDialog = ref(false); |
| | | const currentVideoFile = ref(null); |
| | | |
| | | // 视频预览相关状态 |
| | | const showVideoDialog = ref(false); |
| | | const currentVideoFile = ref(null); |
| | | // 文件访问基础域 |
| | | const filePreviewBase = config.fileUrl; |
| | | |
| | | // 文件访问基础域 |
| | | const filePreviewBase = config.fileUrl; |
| | | |
| | | // 页面加载 |
| | | onLoad((options) => { |
| | | if (options.taskInfo) { |
| | | try { |
| | | taskInfo.value = JSON.parse(decodeURIComponent(options.taskInfo)); |
| | | loadAttachments(); |
| | | } catch (e) { |
| | | console.error('解析任务信息失败:', e); |
| | | uni.showToast({ |
| | | title: '加载失败', |
| | | icon: 'error' |
| | | }); |
| | | // 页面加载 |
| | | onLoad(options => { |
| | | if (options.taskInfo) { |
| | | try { |
| | | taskInfo.value = JSON.parse(decodeURIComponent(options.taskInfo)); |
| | | loadAttachments(); |
| | | } catch (e) { |
| | | console.error("解析任务信息失败:", e); |
| | | uni.showToast({ |
| | | title: "加载失败", |
| | | icon: "error", |
| | | }); |
| | | } |
| | | } |
| | | } |
| | | }); |
| | | }); |
| | | |
| | | // 加载附件数据 |
| | | const loadAttachments = () => { |
| | | const task = taskInfo.value; |
| | | if (!task) return; |
| | | // 加载附件数据 |
| | | const loadAttachments = () => { |
| | | const task = taskInfo.value; |
| | | if (!task) return; |
| | | |
| | | attachmentList.value = []; |
| | | attachmentList.value = []; |
| | | |
| | | // 后端反显字段 |
| | | const allList = Array.isArray(task?.commonFileList) ? task.commonFileList : []; |
| | | const beforeList = Array.isArray(task?.commonFileListBefore) |
| | | ? task.commonFileListBefore |
| | | : allList.filter((f) => f?.type === 10); |
| | | const afterList = Array.isArray(task?.commonFileListAfter) |
| | | ? task.commonFileListAfter |
| | | : allList.filter((f) => f?.type === 11); |
| | | const issueList = Array.isArray(task?.commonFileListIssue) |
| | | ? task.commonFileListIssue |
| | | : allList.filter((f) => f?.type === 12); |
| | | // 获取附件列表,优先从 commonFileListBeforeVO 获取 |
| | | let rawList = []; |
| | | if (Array.isArray(task.commonFileListBeforeVO)) { |
| | | rawList = task.commonFileListBeforeVO; |
| | | } else if (Array.isArray(task.commonFileListBefore)) { |
| | | rawList = task.commonFileListBefore; |
| | | } else if (Array.isArray(task.commonFileList)) { |
| | | // 降级:从通用列表过滤 type 为 10 的 |
| | | rawList = task.commonFileList.filter(f => f?.type === 10); |
| | | } |
| | | |
| | | const mapToViewFile = (file, viewType) => { |
| | | const u = normalizeFileUrl(file?.url || file?.downloadUrl || ''); |
| | | return { |
| | | ...file, |
| | | type: viewType, |
| | | name: file?.name || file?.originalFilename || file?.bucketFilename, |
| | | bucketFilename: file?.bucketFilename || file?.name, |
| | | originalFilename: file?.originalFilename || file?.name, |
| | | url: u, |
| | | downloadUrl: u, |
| | | size: file?.size || file?.byteSize, |
| | | const mapToViewFile = file => { |
| | | // 优先使用 previewURL 或 url |
| | | const rawUrl = |
| | | file?.previewURL || |
| | | file?.url || |
| | | file?.downloadUrl || |
| | | file?.downloadURL || |
| | | ""; |
| | | const u = normalizeFileUrl(rawUrl); |
| | | |
| | | return { |
| | | ...file, |
| | | name: |
| | | file?.name || file?.originalFilename || file?.bucketFilename || "附件", |
| | | bucketFilename: |
| | | file?.bucketFilename || file?.name || file?.originalFilename, |
| | | originalFilename: file?.originalFilename || file?.name, |
| | | url: u, |
| | | downloadUrl: u, |
| | | size: file?.size || file?.byteSize || 0, |
| | | }; |
| | | }; |
| | | |
| | | attachmentList.value = rawList.map(f => mapToViewFile(f)); |
| | | }; |
| | | |
| | | attachmentList.value.push(...beforeList.map((f) => mapToViewFile(f, 0))); |
| | | attachmentList.value.push(...afterList.map((f) => mapToViewFile(f, 1))); |
| | | attachmentList.value.push(...issueList.map((f) => mapToViewFile(f, 2))); |
| | | }; |
| | | // 将后端返回的文件地址规范成可访问URL |
| | | const normalizeFileUrl = rawUrl => { |
| | | try { |
| | | if (!rawUrl || typeof rawUrl !== "string") return ""; |
| | | const url = rawUrl.trim(); |
| | | if (!url) return ""; |
| | | if (/^https?:\/\//i.test(url)) return url; |
| | | if (url.startsWith("/")) return `${filePreviewBase}${url}`; |
| | | |
| | | // 将后端返回的文件地址规范成可访问URL |
| | | const normalizeFileUrl = (rawUrl) => { |
| | | try { |
| | | if (!rawUrl || typeof rawUrl !== 'string') return ''; |
| | | const url = rawUrl.trim(); |
| | | if (!url) return ''; |
| | | if (/^https?:\/\//i.test(url)) return url; |
| | | if (url.startsWith('/')) return `${filePreviewBase}${url}`; |
| | | |
| | | // Windows path -> web path |
| | | if (/^[a-zA-Z]:\\/.test(url)) { |
| | | const normalized = url.replace(/\\/g, '/'); |
| | | const idx = normalized.indexOf('/prod/'); |
| | | if (idx >= 0) { |
| | | const relative = normalized.slice(idx + '/prod/'.length); |
| | | return `${filePreviewBase}/${relative}`; |
| | | // Windows path -> web path |
| | | if (/^[a-zA-Z]:\\/.test(url)) { |
| | | const normalized = url.replace(/\\/g, "/"); |
| | | const idx = normalized.indexOf("/prod/"); |
| | | if (idx >= 0) { |
| | | const relative = normalized.slice(idx + "/prod/".length); |
| | | return `${filePreviewBase}/${relative}`; |
| | | } |
| | | return normalized; |
| | | } |
| | | return normalized; |
| | | |
| | | return `${filePreviewBase}/${url.replace(/^\//, "")}`; |
| | | } catch (e) { |
| | | return rawUrl || ""; |
| | | } |
| | | }; |
| | | |
| | | return `${filePreviewBase}/${url.replace(/^\//, '')}`; |
| | | } catch (e) { |
| | | return rawUrl || ''; |
| | | } |
| | | }; |
| | | // 返回上一页 |
| | | const goBack = () => { |
| | | uni.navigateBack(); |
| | | }; |
| | | |
| | | // 返回上一页 |
| | | const goBack = () => { |
| | | uni.navigateBack(); |
| | | }; |
| | | // 判断是否为图片文件 |
| | | const isImageFile = file => { |
| | | if (file.contentType && file.contentType.startsWith("image/")) { |
| | | return true; |
| | | } |
| | | if (file.type === "image") return true; |
| | | |
| | | // 切换查看类型 |
| | | const switchViewType = (type) => { |
| | | currentViewType.value = type; |
| | | }; |
| | | const name = file.bucketFilename || file.originalFilename || file.name || ""; |
| | | const ext = name.split(".").pop()?.toLowerCase(); |
| | | return ["jpg", "jpeg", "png", "gif", "webp"].includes(ext); |
| | | }; |
| | | |
| | | // 根据type获取对应分类的附件 |
| | | const getAttachmentsByType = (typeValue) => { |
| | | return attachmentList.value.filter((file) => file.type === typeValue) || []; |
| | | }; |
| | | // 预览附件 |
| | | const previewAttachment = file => { |
| | | if (isImageFile(file)) { |
| | | const imageUrls = attachmentList.value |
| | | .filter(f => isImageFile(f)) |
| | | .map(f => f.url || f.downloadUrl); |
| | | |
| | | // 获取当前查看类型的附件 |
| | | const getCurrentViewAttachments = () => { |
| | | switch (currentViewType.value) { |
| | | case 'before': |
| | | return getAttachmentsByType(0); |
| | | case 'after': |
| | | return getAttachmentsByType(1); |
| | | case 'issue': |
| | | return getAttachmentsByType(2); |
| | | default: |
| | | return []; |
| | | } |
| | | }; |
| | | uni.previewImage({ |
| | | urls: imageUrls, |
| | | current: file.url || file.downloadUrl, |
| | | }); |
| | | } else { |
| | | showVideoPreview(file); |
| | | } |
| | | }; |
| | | |
| | | // 判断是否为图片文件 |
| | | const isImageFile = (file) => { |
| | | if (file.contentType && file.contentType.startsWith('image/')) { |
| | | return true; |
| | | } |
| | | if (file.type === 'image') return true; |
| | | // 显示视频预览 |
| | | const showVideoPreview = file => { |
| | | currentVideoFile.value = file; |
| | | showVideoDialog.value = true; |
| | | }; |
| | | |
| | | const name = file.bucketFilename || file.originalFilename || file.name || ''; |
| | | const ext = name.split('.').pop()?.toLowerCase(); |
| | | return ['jpg', 'jpeg', 'png', 'gif', 'webp'].includes(ext); |
| | | }; |
| | | // 关闭视频预览 |
| | | const closeVideoPreview = () => { |
| | | showVideoDialog.value = false; |
| | | currentVideoFile.value = null; |
| | | }; |
| | | |
| | | // 预览附件 |
| | | const previewAttachment = (file) => { |
| | | if (isImageFile(file)) { |
| | | const imageUrls = getCurrentViewAttachments() |
| | | .filter((f) => isImageFile(f)) |
| | | .map((f) => f.url || f.downloadUrl); |
| | | |
| | | uni.previewImage({ |
| | | urls: imageUrls, |
| | | current: file.url || file.downloadUrl, |
| | | // 视频播放错误处理 |
| | | const handleVideoError = () => { |
| | | uni.showToast({ |
| | | title: "视频播放失败", |
| | | icon: "error", |
| | | }); |
| | | } else { |
| | | showVideoPreview(file); |
| | | } |
| | | }; |
| | | }; |
| | | |
| | | // 显示视频预览 |
| | | const showVideoPreview = (file) => { |
| | | currentVideoFile.value = file; |
| | | showVideoDialog.value = true; |
| | | }; |
| | | |
| | | // 关闭视频预览 |
| | | const closeVideoPreview = () => { |
| | | showVideoDialog.value = false; |
| | | currentVideoFile.value = null; |
| | | }; |
| | | |
| | | // 视频播放错误处理 |
| | | const handleVideoError = () => { |
| | | uni.showToast({ |
| | | title: '视频播放失败', |
| | | icon: 'error', |
| | | }); |
| | | }; |
| | | |
| | | // 格式化文件大小 |
| | | const formatFileSize = (size) => { |
| | | if (!size) return ''; |
| | | if (size < 1024) return size + 'B'; |
| | | if (size < 1024 * 1024) return (size / 1024).toFixed(1) + 'KB'; |
| | | return (size / (1024 * 1024)).toFixed(1) + 'MB'; |
| | | }; |
| | | // 格式化文件大小 |
| | | const formatFileSize = size => { |
| | | if (!size) return ""; |
| | | if (size < 1024) return size + "B"; |
| | | if (size < 1024 * 1024) return (size / 1024).toFixed(1) + "KB"; |
| | | return (size / (1024 * 1024)).toFixed(1) + "MB"; |
| | | }; |
| | | </script> |
| | | |
| | | <style scoped> |
| | | .attachment-page { |
| | | min-height: 100vh; |
| | | background-color: #f5f5f5; |
| | | } |
| | | .attachment-page { |
| | | min-height: 100vh; |
| | | background-color: #f5f5f5; |
| | | } |
| | | |
| | | .attachment-content { |
| | | padding: 15px; |
| | | } |
| | | .attachment-content { |
| | | padding: 15px; |
| | | } |
| | | |
| | | /* 标签页样式 */ |
| | | .attachment-tabs { |
| | | display: flex; |
| | | background: #fff; |
| | | border-radius: 12px; |
| | | margin-bottom: 15px; |
| | | padding: 4px; |
| | | box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05); |
| | | } |
| | | /* 标签页样式 */ |
| | | .attachment-tabs { |
| | | display: flex; |
| | | background: #fff; |
| | | border-radius: 12px; |
| | | margin-bottom: 15px; |
| | | padding: 4px; |
| | | box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05); |
| | | } |
| | | |
| | | .tab-item { |
| | | flex: 1; |
| | | text-align: center; |
| | | padding: 12px 8px; |
| | | font-size: 14px; |
| | | color: #666; |
| | | border-radius: 8px; |
| | | transition: all 0.3s ease; |
| | | } |
| | | .tab-item { |
| | | flex: 1; |
| | | text-align: center; |
| | | padding: 12px 8px; |
| | | font-size: 14px; |
| | | color: #666; |
| | | border-radius: 8px; |
| | | transition: all 0.3s ease; |
| | | } |
| | | |
| | | .tab-item.active { |
| | | background: #409eff; |
| | | color: #fff; |
| | | font-weight: 500; |
| | | } |
| | | .tab-item.active { |
| | | background: #409eff; |
| | | color: #fff; |
| | | font-weight: 500; |
| | | } |
| | | |
| | | /* 附件列表样式 */ |
| | | .attachment-list-container { |
| | | background: #fff; |
| | | border-radius: 12px; |
| | | padding: 15px; |
| | | min-height: 400px; |
| | | } |
| | | /* 附件列表样式 */ |
| | | .attachment-list-container { |
| | | background: #fff; |
| | | border-radius: 12px; |
| | | padding: 15px; |
| | | min-height: 400px; |
| | | } |
| | | |
| | | .attachment-list { |
| | | display: flex; |
| | | flex-wrap: wrap; |
| | | gap: 15px; |
| | | } |
| | | .attachment-list { |
| | | display: flex; |
| | | flex-wrap: wrap; |
| | | gap: 15px; |
| | | } |
| | | |
| | | .attachment-item { |
| | | width: calc(33.33% - 10px); |
| | | background: #f8f9fa; |
| | | border-radius: 12px; |
| | | overflow: hidden; |
| | | box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08); |
| | | transition: all 0.3s ease; |
| | | } |
| | | .attachment-item { |
| | | width: calc(33.33% - 10px); |
| | | background: #f8f9fa; |
| | | border-radius: 12px; |
| | | overflow: hidden; |
| | | box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08); |
| | | transition: all 0.3s ease; |
| | | } |
| | | |
| | | .attachment-item:active { |
| | | transform: scale(0.98); |
| | | } |
| | | .attachment-item:active { |
| | | transform: scale(0.98); |
| | | } |
| | | |
| | | .attachment-preview-container { |
| | | width: 100%; |
| | | height: 120px; |
| | | background: #e9ecef; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | } |
| | | .attachment-preview-container { |
| | | width: 100%; |
| | | height: 120px; |
| | | background: #e9ecef; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | } |
| | | |
| | | .attachment-preview { |
| | | width: 100%; |
| | | height: 100%; |
| | | object-fit: cover; |
| | | } |
| | | .attachment-preview { |
| | | width: 100%; |
| | | height: 100%; |
| | | object-fit: cover; |
| | | } |
| | | |
| | | .attachment-video-preview { |
| | | display: flex; |
| | | flex-direction: column; |
| | | align-items: center; |
| | | justify-content: center; |
| | | gap: 8px; |
| | | } |
| | | .attachment-video-preview { |
| | | display: flex; |
| | | flex-direction: column; |
| | | align-items: center; |
| | | justify-content: center; |
| | | gap: 8px; |
| | | } |
| | | |
| | | .video-text { |
| | | font-size: 12px; |
| | | color: #666; |
| | | } |
| | | .video-text { |
| | | font-size: 12px; |
| | | color: #666; |
| | | } |
| | | |
| | | .attachment-info { |
| | | padding: 10px; |
| | | } |
| | | .attachment-info { |
| | | padding: 10px; |
| | | } |
| | | |
| | | .attachment-name { |
| | | font-size: 12px; |
| | | color: #333; |
| | | display: block; |
| | | white-space: nowrap; |
| | | overflow: hidden; |
| | | text-overflow: ellipsis; |
| | | margin-bottom: 4px; |
| | | } |
| | | .attachment-name { |
| | | font-size: 12px; |
| | | color: #333; |
| | | display: block; |
| | | white-space: nowrap; |
| | | overflow: hidden; |
| | | text-overflow: ellipsis; |
| | | margin-bottom: 4px; |
| | | } |
| | | |
| | | .attachment-size { |
| | | font-size: 10px; |
| | | color: #999; |
| | | } |
| | | .attachment-size { |
| | | font-size: 10px; |
| | | color: #999; |
| | | } |
| | | |
| | | /* 空状态样式 */ |
| | | .attachment-empty { |
| | | display: flex; |
| | | flex-direction: column; |
| | | align-items: center; |
| | | justify-content: center; |
| | | padding: 80px 20px; |
| | | color: #999; |
| | | } |
| | | /* 空状态样式 */ |
| | | .attachment-empty { |
| | | display: flex; |
| | | flex-direction: column; |
| | | align-items: center; |
| | | justify-content: center; |
| | | padding: 80px 20px; |
| | | color: #999; |
| | | } |
| | | |
| | | .empty-text { |
| | | margin-top: 15px; |
| | | font-size: 14px; |
| | | } |
| | | .empty-text { |
| | | margin-top: 15px; |
| | | font-size: 14px; |
| | | } |
| | | |
| | | /* 视频弹窗样式 */ |
| | | .video-modal-overlay { |
| | | position: fixed; |
| | | top: 0; |
| | | left: 0; |
| | | right: 0; |
| | | bottom: 0; |
| | | background: rgba(0, 0, 0, 0.9); |
| | | z-index: 10000; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | padding: 20px; |
| | | } |
| | | /* 视频弹窗样式 */ |
| | | .video-modal-overlay { |
| | | position: fixed; |
| | | top: 0; |
| | | left: 0; |
| | | right: 0; |
| | | bottom: 0; |
| | | background: rgba(0, 0, 0, 0.9); |
| | | z-index: 10000; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | padding: 20px; |
| | | } |
| | | |
| | | .video-modal-container { |
| | | width: 100%; |
| | | max-width: 800px; |
| | | background: #000; |
| | | border-radius: 12px; |
| | | overflow: hidden; |
| | | } |
| | | .video-modal-container { |
| | | width: 100%; |
| | | max-width: 800px; |
| | | background: #000; |
| | | border-radius: 12px; |
| | | overflow: hidden; |
| | | } |
| | | |
| | | .video-modal-header { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | padding: 15px 20px; |
| | | background: #1a1a1a; |
| | | } |
| | | .video-modal-header { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | padding: 15px 20px; |
| | | background: #1a1a1a; |
| | | } |
| | | |
| | | .video-modal-title { |
| | | font-size: 16px; |
| | | color: #fff; |
| | | font-weight: 500; |
| | | } |
| | | .video-modal-title { |
| | | font-size: 16px; |
| | | color: #fff; |
| | | font-weight: 500; |
| | | } |
| | | |
| | | .close-btn-video { |
| | | width: 32px; |
| | | height: 32px; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | background: rgba(255, 255, 255, 0.1); |
| | | border-radius: 50%; |
| | | } |
| | | .close-btn-video { |
| | | width: 32px; |
| | | height: 32px; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | background: rgba(255, 255, 255, 0.1); |
| | | border-radius: 50%; |
| | | } |
| | | |
| | | .video-modal-body { |
| | | padding: 20px; |
| | | } |
| | | .video-modal-body { |
| | | padding: 20px; |
| | | } |
| | | |
| | | .video-player { |
| | | width: 100%; |
| | | height: 400px; |
| | | border-radius: 8px; |
| | | } |
| | | .video-player { |
| | | width: 100%; |
| | | height: 400px; |
| | | border-radius: 8px; |
| | | } |
| | | </style> |