| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <FormDialog |
| | | v-model="dialogVisible" |
| | | title="ä¸ä¼ å·¡æ£è®°å½" |
| | | width="980px" |
| | | @close="handleClose" |
| | | @cancel="handleClose" |
| | | > |
| | | <main class="upload-content"> |
| | | <el-card v-if="taskInfo" class="section-card"> |
| | | <el-descriptions :column="1" border> |
| | | <el-descriptions-item label="å·¡æ£ä»»å¡åç§°"> |
| | | {{ taskInfo.taskName || "-" }} |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="å·¡æ£é¡¹ç®"> |
| | | {{ taskInfo.inspectionProject || "-" }} |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="夿³¨"> |
| | | {{ taskInfo.remarks || "-" }} |
| | | </el-descriptions-item> |
| | | </el-descriptions> |
| | | </el-card> |
| | | |
| | | <el-card class="section-card"> |
| | | <h3>å·¡æ£ç¶æ</h3> |
| | | <el-radio-group v-model="hasException"> |
| | | <el-radio-button :value="false">æ£å¸¸</el-radio-button> |
| | | <el-radio-button :value="true">åå¨å¼å¸¸</el-radio-button> |
| | | </el-radio-group> |
| | | </el-card> |
| | | |
| | | <el-card v-if="hasException === true" class="section-card"> |
| | | <h3>å¼å¸¸æè¿°</h3> |
| | | <el-input |
| | | v-model="abnormalDescription" |
| | | type="textarea" |
| | | maxlength="500" |
| | | show-word-limit |
| | | :rows="4" |
| | | placeholder="请æè¿°å¼å¸¸æ
åµ..." |
| | | /> |
| | | </el-card> |
| | | |
| | | <el-card v-if="hasException === true" class="section-card"> |
| | | <el-tabs v-model="currentUploadType"> |
| | | <el-tab-pane label="ç产å" name="before" /> |
| | | <el-tab-pane label="ç产ä¸" name="after" /> |
| | | <el-tab-pane label="ç产å" name="issue" /> |
| | | </el-tabs> |
| | | |
| | | <div class="upload-buttons"> |
| | | <el-upload |
| | | :show-file-list="false" |
| | | :http-request="uploadFile" |
| | | :disabled="getCurrentFiles().length >= uploadConfig.limit || uploading" |
| | | accept="image/*" |
| | | > |
| | | <el-button type="primary" :loading="uploading"> |
| | | <el-icon><Camera /></el-icon> |
| | | éæ©å¾ç |
| | | </el-button> |
| | | </el-upload> |
| | | |
| | | <el-upload |
| | | :show-file-list="false" |
| | | :http-request="uploadFile" |
| | | :disabled="getCurrentFiles().length >= uploadConfig.limit || uploading" |
| | | accept="video/*" |
| | | > |
| | | <el-button type="success" :loading="uploading"> |
| | | <el-icon><VideoCamera /></el-icon> |
| | | éæ©è§é¢ |
| | | </el-button> |
| | | </el-upload> |
| | | </div> |
| | | |
| | | <el-progress |
| | | v-if="uploading" |
| | | :percentage="uploadProgress" |
| | | class="upload-progress" |
| | | /> |
| | | |
| | | <div v-if="getCurrentFiles().length" class="file-list"> |
| | | <div |
| | | v-for="(file, index) in getCurrentFiles()" |
| | | :key="file.uid || file.id || index" |
| | | class="file-item" |
| | | > |
| | | <div class="file-preview-container"> |
| | | <el-image |
| | | v-if="file.type === 'image' || !file.type" |
| | | :src="file.url || file.tempFilePath || file.path || file.downloadUrl" |
| | | fit="cover" |
| | | class="file-preview" |
| | | :preview-src-list="[file.url || file.tempFilePath || file.path || file.downloadUrl]" |
| | | preview-teleported |
| | | /> |
| | | |
| | | <div v-else class="video-preview" @click="previewVideo(file)"> |
| | | <el-icon><VideoCamera /></el-icon> |
| | | <span>è§é¢</span> |
| | | </div> |
| | | |
| | | <el-button |
| | | class="delete-btn" |
| | | type="danger" |
| | | circle |
| | | size="small" |
| | | @click="removeFile(index)" |
| | | > |
| | | <el-icon><Close /></el-icon> |
| | | </el-button> |
| | | </div> |
| | | |
| | | <div class="file-info"> |
| | | <div class="file-name"> |
| | | {{ file.bucketFilename || file.name || (file.type === "image" ? "å¾ç" : "è§é¢") }} |
| | | </div> |
| | | <div class="file-size">{{ formatFileSize(file.size) }}</div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | |
| | | <el-empty |
| | | v-else |
| | | :description="`è¯·éæ©è¦ä¸ä¼ ç${getUploadTypeText()}å¾çæè§é¢`" |
| | | /> |
| | | |
| | | <el-alert |
| | | class="upload-summary" |
| | | type="info" |
| | | :closable="false" |
| | | :title="`ç产åï¼${beforeModelValue.length}个æä»¶ | ç产ä¸ï¼${afterModelValue.length}个æä»¶ | ç产åï¼${issueModelValue.length}个æä»¶`" |
| | | /> |
| | | </el-card> |
| | | |
| | | <el-result |
| | | v-if="hasException === false" |
| | | icon="success" |
| | | title="设å¤è¿è¡æ£å¸¸" |
| | | sub-title="æ éä¸ä¼ ç
§ç" |
| | | /> |
| | | </main> |
| | | |
| | | <template #footer> |
| | | <footer class="footer-buttons"> |
| | | <el-button type="primary" @click="submitUpload">æäº¤</el-button> |
| | | <el-button v-if="hasException === true" type="warning" @click="goToRepair"> |
| | | æ°å¢æ¥ä¿® |
| | | </el-button> |
| | | <el-button @click="handleClose">åæ¶</el-button> |
| | | </footer> |
| | | </template> |
| | | </FormDialog> |
| | | |
| | | <el-dialog |
| | | v-model="showVideoDialog" |
| | | :title="currentVideoFile?.originalFilename || currentVideoFile?.name || 'è§é¢é¢è§'" |
| | | width="720px" |
| | | > |
| | | <video |
| | | v-if="currentVideoFile" |
| | | :src="currentVideoFile.url || currentVideoFile.downloadUrl" |
| | | class="video-player" |
| | | controls |
| | | autoplay |
| | | /> |
| | | </el-dialog> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { computed, ref } from "vue"; |
| | | import { useRouter } from "vue-router"; |
| | | import { ElLoading, ElMessage, ElMessageBox } from "element-plus"; |
| | | import { Camera, Close, VideoCamera } from "@element-plus/icons-vue"; |
| | | import axios from "axios"; |
| | | import FormDialog from "@/components/Dialog/FormDialog.vue"; |
| | | import { uploadInspectionTask } from "@/api/inspectionManagement/index.js"; |
| | | import { getToken } from "@/utils/auth"; |
| | | |
| | | const emit = defineEmits(["closeDia", "success"]); |
| | | const router = useRouter(); |
| | | |
| | | const dialogVisible = ref(false); |
| | | const taskInfo = ref(null); |
| | | const uploading = ref(false); |
| | | const uploadProgress = ref(0); |
| | | |
| | | const beforeModelValue = ref([]); |
| | | const afterModelValue = ref([]); |
| | | const issueModelValue = ref([]); |
| | | |
| | | const currentUploadType = ref("before"); |
| | | const hasException = ref(null); |
| | | const abnormalDescription = ref(""); |
| | | |
| | | const showVideoDialog = ref(false); |
| | | const currentVideoFile = ref(null); |
| | | |
| | | const uploadConfig = { |
| | | action: "/common/upload", |
| | | limit: 10, |
| | | fileSize: 50, |
| | | fileType: ["jpg", "jpeg", "png", "mp4", "mov"], |
| | | }; |
| | | |
| | | const uploadFileUrl = computed( |
| | | () => `${import.meta.env.VITE_APP_BASE_API}${uploadConfig.action}` |
| | | ); |
| | | |
| | | const processFileUrl = fileUrl => { |
| | | if (!fileUrl) return ""; |
| | | |
| | | let currentUrl = String(fileUrl); |
| | | if (currentUrl.includes("\\")) { |
| | | const uploadsIndex = currentUrl.toLowerCase().indexOf("uploads"); |
| | | if (uploadsIndex > -1) { |
| | | currentUrl = `/${currentUrl.substring(uploadsIndex).replace(/\\/g, "/")}`; |
| | | } else { |
| | | const fileName = currentUrl.split("\\").pop(); |
| | | currentUrl = `/uploads/${fileName}`; |
| | | } |
| | | } |
| | | |
| | | if (currentUrl && !currentUrl.startsWith("http")) { |
| | | if (!currentUrl.startsWith("/")) { |
| | | currentUrl = `/${currentUrl}`; |
| | | } |
| | | currentUrl = __BASE_API__ + currentUrl; |
| | | } |
| | | |
| | | return currentUrl; |
| | | }; |
| | | |
| | | const normalizeList = (list, fileType) => { |
| | | if (!Array.isArray(list)) return []; |
| | | |
| | | return list.filter(Boolean).map(item => { |
| | | let currentType = item.type; |
| | | if (!currentType && item.contentType) { |
| | | currentType = item.contentType.startsWith("video") ? "video" : "image"; |
| | | } else if (!currentType) { |
| | | currentType = fileType || "image"; |
| | | } |
| | | |
| | | return { |
| | | ...item, |
| | | url: processFileUrl(item.url || item.previewURL || item.downloadUrl || item.path || ""), |
| | | downloadUrl: processFileUrl( |
| | | item.downloadUrl || item.url || item.previewURL || item.path || "" |
| | | ), |
| | | name: item.name || item.originalFilename || item.bucketFilename, |
| | | tempId: item.tempId || item.id || item.tempFileId, |
| | | tempFileId: item.tempFileId || item.tempId || item.id, |
| | | size: item.size || item.byteSize || 0, |
| | | type: currentType, |
| | | status: "success", |
| | | uid: item.uid || `${Date.now()}-${Math.random()}`, |
| | | }; |
| | | }); |
| | | }; |
| | | |
| | | const resetState = () => { |
| | | taskInfo.value = null; |
| | | beforeModelValue.value = []; |
| | | afterModelValue.value = []; |
| | | issueModelValue.value = []; |
| | | currentUploadType.value = "before"; |
| | | hasException.value = null; |
| | | abnormalDescription.value = ""; |
| | | uploading.value = false; |
| | | uploadProgress.value = 0; |
| | | showVideoDialog.value = false; |
| | | currentVideoFile.value = null; |
| | | }; |
| | | |
| | | const openDialog = row => { |
| | | const raw = JSON.parse(JSON.stringify(row?.__raw || row || {})); |
| | | taskInfo.value = raw; |
| | | |
| | | beforeModelValue.value = normalizeList( |
| | | raw.commonFileListBeforeVO || raw.commonFileListBefore || [], |
| | | "image" |
| | | ); |
| | | afterModelValue.value = normalizeList( |
| | | raw.commonFileListVO || raw.commonFileList || [], |
| | | "image" |
| | | ); |
| | | issueModelValue.value = normalizeList( |
| | | raw.commonFileListAfterVO || raw.commonFileListAfter || [], |
| | | "image" |
| | | ); |
| | | |
| | | abnormalDescription.value = raw.abnormalDescription || ""; |
| | | |
| | | if (raw.hasException !== undefined && raw.hasException !== null) { |
| | | hasException.value = raw.hasException; |
| | | } else if (raw.inspectionResult !== undefined && raw.inspectionResult !== null) { |
| | | hasException.value = String(raw.inspectionResult) === "0"; |
| | | } else { |
| | | hasException.value = null; |
| | | } |
| | | |
| | | if ( |
| | | hasException.value !== true && |
| | | (beforeModelValue.value.length || afterModelValue.value.length || issueModelValue.value.length) |
| | | ) { |
| | | hasException.value = true; |
| | | } |
| | | |
| | | dialogVisible.value = true; |
| | | }; |
| | | |
| | | const handleClose = () => { |
| | | dialogVisible.value = false; |
| | | resetState(); |
| | | emit("closeDia"); |
| | | }; |
| | | |
| | | const getCurrentFiles = () => { |
| | | if (currentUploadType.value === "before") return beforeModelValue.value; |
| | | if (currentUploadType.value === "after") return afterModelValue.value; |
| | | if (currentUploadType.value === "issue") return issueModelValue.value; |
| | | return []; |
| | | }; |
| | | |
| | | const getUploadTypeText = () => { |
| | | if (currentUploadType.value === "before") return "ç产å"; |
| | | if (currentUploadType.value === "after") return "ç产ä¸"; |
| | | if (currentUploadType.value === "issue") return "ç产å"; |
| | | return ""; |
| | | }; |
| | | |
| | | const getTabType = () => { |
| | | if (currentUploadType.value === "before") return 10; |
| | | if (currentUploadType.value === "after") return 11; |
| | | if (currentUploadType.value === "issue") return 12; |
| | | return 10; |
| | | }; |
| | | |
| | | const previewVideo = file => { |
| | | currentVideoFile.value = file; |
| | | showVideoDialog.value = true; |
| | | }; |
| | | |
| | | const uploadFile = async uploadRequest => { |
| | | const rawFile = uploadRequest.file; |
| | | |
| | | if (getCurrentFiles().length >= uploadConfig.limit) { |
| | | ElMessage.warning(`æå¤åªè½éæ©${uploadConfig.limit}个æä»¶`); |
| | | return; |
| | | } |
| | | |
| | | const ext = rawFile.name.split(".").pop()?.toLowerCase(); |
| | | if (!uploadConfig.fileType.includes(ext)) { |
| | | ElMessage.warning(`æä»¶æ ¼å¼ä¸æ¯æï¼è¯·ä¸ä¼ ${uploadConfig.fileType.join("/")} æ ¼å¼`); |
| | | return; |
| | | } |
| | | |
| | | if (rawFile.size > uploadConfig.fileSize * 1024 * 1024) { |
| | | ElMessage.warning(`æä»¶å¤§å°ä¸è½è¶
è¿ ${uploadConfig.fileSize}MB`); |
| | | return; |
| | | } |
| | | |
| | | const token = getToken(); |
| | | if (!token) { |
| | | ElMessage.warning("ç¨æ·æªç»å½"); |
| | | return; |
| | | } |
| | | |
| | | const formData = new FormData(); |
| | | formData.append("files", rawFile); |
| | | formData.append("type", getTabType()); |
| | | |
| | | uploading.value = true; |
| | | uploadProgress.value = 0; |
| | | |
| | | try { |
| | | const { data } = await axios.post(uploadFileUrl.value, formData, { |
| | | headers: { |
| | | Authorization: `Bearer ${token}`, |
| | | "Content-Type": "multipart/form-data", |
| | | }, |
| | | onUploadProgress: event => { |
| | | if (event.total) { |
| | | uploadProgress.value = Math.round((event.loaded / event.total) * 100); |
| | | } |
| | | }, |
| | | }); |
| | | |
| | | if (data.code !== 200) { |
| | | ElMessage.error(data.msg || "ä¸ä¼ 失败"); |
| | | return; |
| | | } |
| | | |
| | | const resultData = Array.isArray(data.data) ? data.data[0] : data.data; |
| | | const finalUrl = processFileUrl( |
| | | resultData.url || resultData.previewURL || resultData.downloadUrl || "" |
| | | ); |
| | | const finalName = resultData.name || resultData.originalFilename || resultData.bucketFilename; |
| | | const finalId = resultData.tempId || resultData.id || resultData.tempFileId; |
| | | |
| | | const uploadedFile = { |
| | | ...resultData, |
| | | url: finalUrl, |
| | | downloadUrl: finalUrl, |
| | | name: finalName, |
| | | tempId: finalId, |
| | | tempFileId: resultData.tempFileId || finalId, |
| | | size: rawFile.size || resultData.size || resultData.byteSize || 0, |
| | | type: rawFile.type?.startsWith("video") ? "video" : "image", |
| | | status: "success", |
| | | uid: `${Date.now()}-${Math.random()}`, |
| | | }; |
| | | |
| | | getCurrentFiles().push(uploadedFile); |
| | | ElMessage.success("ä¸ä¼ æå"); |
| | | } catch (error) { |
| | | ElMessage.error(error?.message || "ä¸ä¼ 失败"); |
| | | } finally { |
| | | uploading.value = false; |
| | | } |
| | | }; |
| | | |
| | | const buildFileItem = item => ({ |
| | | id: item?.id, |
| | | tempId: item?.tempId, |
| | | tempFileId: item?.tempFileId, |
| | | url: item?.downloadUrl || item?.url || "", |
| | | downloadUrl: item?.downloadUrl || item?.url || "", |
| | | name: item?.name, |
| | | bucketFilename: item?.bucketFilename || item?.name, |
| | | originalFilename: item?.originalFilename || item?.name, |
| | | size: item?.size || 0, |
| | | byteSize: item?.byteSize || item?.size || 0, |
| | | contentType: item?.contentType || "", |
| | | type: item?.type, |
| | | }); |
| | | |
| | | const submitUpload = async () => { |
| | | if (hasException.value === null) { |
| | | ElMessage.warning("è¯·éæ©å·¡æ£ç¶æ"); |
| | | return; |
| | | } |
| | | |
| | | if (hasException.value === true) { |
| | | const totalFiles = |
| | | beforeModelValue.value.length + |
| | | afterModelValue.value.length + |
| | | issueModelValue.value.length; |
| | | |
| | | if (!totalFiles) { |
| | | ElMessage.warning("请ä¸ä¼ å¼å¸¸ç
§çæè§é¢"); |
| | | return; |
| | | } |
| | | |
| | | if (!abnormalDescription.value.trim()) { |
| | | ElMessage.warning("请填åå¼å¸¸æè¿°"); |
| | | return; |
| | | } |
| | | } |
| | | |
| | | const loading = ElLoading.service({ |
| | | text: "æäº¤ä¸...", |
| | | background: "rgba(0, 0, 0, 0.3)", |
| | | }); |
| | | |
| | | try { |
| | | const allFiles = [ |
| | | ...beforeModelValue.value, |
| | | ...afterModelValue.value, |
| | | ...issueModelValue.value, |
| | | ]; |
| | | |
| | | const tempFileIds = allFiles |
| | | .map(item => item?.tempId ?? item?.tempFileId ?? item?.id) |
| | | .filter(Boolean); |
| | | |
| | | const { |
| | | createTime, |
| | | updateTime, |
| | | storageBlobDTO, |
| | | commonFileListAfterVO, |
| | | commonFileListVO, |
| | | commonFileListBeforeVO, |
| | | commonFileListAfter, |
| | | commonFileList, |
| | | commonFileListBefore, |
| | | __raw, |
| | | ...baseTaskInfo |
| | | } = taskInfo.value || {}; |
| | | |
| | | const submitData = { |
| | | ...baseTaskInfo, |
| | | commonFileListBeforeDTO: beforeModelValue.value.map(buildFileItem), |
| | | commonFileListDTO: afterModelValue.value.map(buildFileItem), |
| | | commonFileListAfterDTO: issueModelValue.value.map(buildFileItem), |
| | | hasException: hasException.value, |
| | | inspectionResult: hasException.value ? 0 : 1, |
| | | abnormalDescription: abnormalDescription.value, |
| | | tempFileIds, |
| | | }; |
| | | |
| | | const result = await uploadInspectionTask(submitData); |
| | | |
| | | if (result && (result.code === 200 || result.success)) { |
| | | ElMessage.success("æäº¤æå"); |
| | | dialogVisible.value = false; |
| | | resetState(); |
| | | emit("success"); |
| | | emit("closeDia"); |
| | | } else { |
| | | ElMessage.error(result?.msg || result?.message || "æäº¤å¤±è´¥"); |
| | | } |
| | | } catch (error) { |
| | | ElMessage.error(error?.message || "æäº¤å¤±è´¥"); |
| | | } finally { |
| | | loading.close(); |
| | | } |
| | | }; |
| | | |
| | | const removeFile = async index => { |
| | | try { |
| | | await ElMessageBox.confirm("ç¡®å®è¦å é¤è¿ä¸ªæä»¶åï¼", "确认å é¤", { |
| | | type: "warning", |
| | | }); |
| | | getCurrentFiles().splice(index, 1); |
| | | } catch {} |
| | | }; |
| | | |
| | | const goToRepair = () => { |
| | | const taskData = { |
| | | taskId: taskInfo.value?.taskId || taskInfo.value?.id, |
| | | taskName: taskInfo.value?.taskName, |
| | | inspectionLocation: taskInfo.value?.inspectionLocation, |
| | | inspector: taskInfo.value?.inspector, |
| | | hasException: hasException.value, |
| | | inspectionResult: hasException.value ? 0 : 1, |
| | | commonFileListBeforeDTO: beforeModelValue.value.map(buildFileItem), |
| | | commonFileListDTO: afterModelValue.value.map(buildFileItem), |
| | | commonFileListAfterDTO: issueModelValue.value.map(buildFileItem), |
| | | uploadedFiles: { |
| | | before: beforeModelValue.value, |
| | | after: afterModelValue.value, |
| | | issue: issueModelValue.value, |
| | | }, |
| | | }; |
| | | |
| | | sessionStorage.setItem("repairTaskInfo", JSON.stringify(taskData)); |
| | | router.push("/equipmentManagement/repair/add"); |
| | | }; |
| | | |
| | | const formatFileSize = size => { |
| | | if (!size) return "0 B"; |
| | | |
| | | const units = ["B", "KB", "MB", "GB"]; |
| | | let index = 0; |
| | | let fileSize = size; |
| | | |
| | | while (fileSize >= 1024 && index < units.length - 1) { |
| | | fileSize /= 1024; |
| | | index += 1; |
| | | } |
| | | |
| | | return `${fileSize.toFixed(2)} ${units[index]}`; |
| | | }; |
| | | |
| | | defineExpose({ |
| | | openDialog, |
| | | }); |
| | | </script> |
| | | |
| | | <style scoped> |
| | | .inspection-upload-page { |
| | | min-height: 70vh; |
| | | background: #f5f7fa; |
| | | padding: 20px 20px 90px; |
| | | box-sizing: border-box; |
| | | } |
| | | |
| | | .upload-content { |
| | | max-width: 960px; |
| | | margin: 20px auto 0; |
| | | } |
| | | |
| | | .section-card { |
| | | margin-bottom: 16px; |
| | | } |
| | | |
| | | .section-card h3 { |
| | | margin: 0 0 16px; |
| | | font-size: 16px; |
| | | } |
| | | |
| | | .upload-buttons { |
| | | display: flex; |
| | | gap: 12px; |
| | | margin: 16px 0; |
| | | } |
| | | |
| | | .upload-progress { |
| | | margin-bottom: 16px; |
| | | } |
| | | |
| | | .file-list { |
| | | display: grid; |
| | | grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); |
| | | gap: 12px; |
| | | } |
| | | |
| | | .file-preview-container { |
| | | position: relative; |
| | | aspect-ratio: 1; |
| | | border-radius: 8px; |
| | | overflow: hidden; |
| | | background: #f2f3f5; |
| | | } |
| | | |
| | | .file-preview { |
| | | width: 100%; |
| | | height: 100%; |
| | | } |
| | | |
| | | .video-preview { |
| | | width: 100%; |
| | | height: 100%; |
| | | background: #303133; |
| | | color: #fff; |
| | | display: flex; |
| | | gap: 6px; |
| | | align-items: center; |
| | | justify-content: center; |
| | | cursor: pointer; |
| | | } |
| | | |
| | | .delete-btn { |
| | | position: absolute; |
| | | top: 6px; |
| | | right: 6px; |
| | | } |
| | | |
| | | .file-info { |
| | | margin-top: 6px; |
| | | font-size: 12px; |
| | | } |
| | | |
| | | .file-name { |
| | | color: #606266; |
| | | overflow: hidden; |
| | | text-overflow: ellipsis; |
| | | white-space: nowrap; |
| | | } |
| | | |
| | | .file-size { |
| | | color: #909399; |
| | | margin-top: 2px; |
| | | } |
| | | |
| | | .upload-summary { |
| | | margin-top: 16px; |
| | | } |
| | | |
| | | .footer-buttons { |
| | | position: sticky; |
| | | left: 0; |
| | | right: 0; |
| | | bottom: 0; |
| | | padding: 14px 20px 0; |
| | | background: #f5f7fa; |
| | | display: flex; |
| | | justify-content: center; |
| | | gap: 12px; |
| | | } |
| | | |
| | | .video-player { |
| | | width: 100%; |
| | | max-height: 70vh; |
| | | background: #000; |
| | | } |
| | | </style> |