From 744455243dada491a3b4bfa042da31f4dd78f606 Mon Sep 17 00:00:00 2001 From: dzc <2388224167@qq.com> Date: Tue, 27 Oct 2026 15:07:04 +0800 Subject: [PATCH] 11 --- .../preview-video/preview-video-dialog.vue | 366 ++++------- .../replay-video/replay-video-dialog.vue | 607 ++++++------------ src/views/device/components/useVideoDialog.ts | 385 ++++++----- .../device/components/video-channel-panel.vue | 511 +++++++++++---- src/views/device/components/video-dialog.scss | 388 ++++++++--- 5 files changed, 1239 insertions(+), 1018 deletions(-) diff --git a/src/views/device/components/preview-video/preview-video-dialog.vue b/src/views/device/components/preview-video/preview-video-dialog.vue index 1e9f1e1..d7c4050 100644 --- a/src/views/device/components/preview-video/preview-video-dialog.vue +++ b/src/views/device/components/preview-video/preview-video-dialog.vue @@ -1,260 +1,126 @@ - diff --git a/src/views/device/components/replay-video/replay-video-dialog.vue b/src/views/device/components/replay-video/replay-video-dialog.vue index 1a67776..0536f91 100644 --- a/src/views/device/components/replay-video/replay-video-dialog.vue +++ b/src/views/device/components/replay-video/replay-video-dialog.vue @@ -1,410 +1,231 @@ - diff --git a/src/views/device/components/useVideoDialog.ts b/src/views/device/components/useVideoDialog.ts index e5cecac..046f1ad 100644 --- a/src/views/device/components/useVideoDialog.ts +++ b/src/views/device/components/useVideoDialog.ts @@ -1,179 +1,248 @@ -import { computed, nextTick, onBeforeUnmount, ref } from 'vue'; -import * as DeviceManageApi from '/@/api/device/deviceManage'; -import * as DeviceChannelApi from '/@/api/device/deviceChannel'; -import { useMessage } from '/@/hooks/message'; +import { computed, nextTick, onBeforeUnmount, ref } from "vue"; +import * as DeviceManageApi from "/@/api/device/deviceManage"; +import * as DeviceChannelApi from "/@/api/device/deviceChannel"; +import { useMessage } from "/@/hooks/message"; export interface VideoChannel { - channelCode: string; - channelName?: string; - channelSeq?: string | number; - deviceName?: string; + channelCode: string; + channelName?: string; + channelSeq?: string | number; + deviceName?: string; } -export type ChannelSelectionMode = 'single' | 'multiple'; +export type ChannelSelectionMode = "single" | "multiple"; interface VideoDialogOptions { - mode: 'preview' | 'replay'; - getPlayOptions: () => Record | null; + mode: "preview" | "replay"; + getPlayOptions: () => Record | null; } interface DialogPlayer { - startReal: (channels: Record[]) => Promise; - startPlayback: (channels: Record[]) => Promise; - closeVideo: () => Promise; - destroy?: () => void; - updateVisible: (visible: boolean) => void; + startReal: (channels: Record[]) => Promise; + startPlayback: (channels: Record[]) => Promise; + closeVideo: () => Promise; + destroy?: () => void; + updateVisible: (visible: boolean) => void; } export function useVideoDialog(options: VideoDialogOptions) { - const dialogVisible = ref(false); - const loading = ref(false); - const loadError = ref(''); - const channels = ref([]); - const selectedCodes = ref([]); - const playingCodes = ref([]); - const selectionMode = ref('single'); - const playerRef = ref(); - const deviceName = ref(''); - const currentPage = ref(1); - const pageSize = 16; - const divisionOptions = [1, 4, 9, 16]; - let sourceType = ''; - let sourceCode = ''; - let session = 0; - let playRequest = 0; - let appliedOptions: Record = {}; + const dialogVisible = ref(false); + const loading = ref(false); + const loadError = ref(""); + const channels = ref([]); + const selectedCodes = ref([]); + const playingCodes = ref([]); + const selectionMode = ref("single"); + const playerRef = ref(); + const deviceName = ref(""); + const currentPage = ref(1); + const pageSize = 16; + const divisionOptions = [1, 4, 9, 16]; + let sourceType = ""; + let sourceCode = ""; + let session = 0; + let playRequest = 0; + let appliedOptions: Record = {}; - const playingChannels = computed(() => { - const codes = new Set(playingCodes.value); - return channels.value.filter((channel) => codes.has(channel.channelCode)); - }); - const pageChannels = computed(() => playingChannels.value.slice((currentPage.value - 1) * pageSize, currentPage.value * pageSize)); - const division = computed(() => divisionOptions.find((count) => count >= pageChannels.value.length) || pageSize); - const playingSummary = computed(() => { - if (!playingChannels.value.length) return '尚未选择播放通道'; - if (playingChannels.value.length === 1) return playingChannels.value[0].channelName || '未命名通道'; - const summary = `共 ${playingChannels.value.length} 个通道`; - return playingChannels.value.length > pageSize ? `${summary} · 当前第 ${(currentPage.value - 1) * pageSize + 1}–${Math.min(currentPage.value * pageSize, playingChannels.value.length)} 路` : summary; - }); + const playingChannels = computed(() => { + const codes = new Set(playingCodes.value); + return channels.value.filter((channel) => codes.has(channel.channelCode)); + }); + const pageChannels = computed(() => + playingChannels.value.slice( + (currentPage.value - 1) * pageSize, + currentPage.value * pageSize + ) + ); + const division = computed( + () => + divisionOptions.find((count) => count >= pageChannels.value.length) || + pageSize + ); + const playingSummary = computed(() => { + if (!playingChannels.value.length) return "尚未选择播放通道"; + if (playingChannels.value.length === 1) + return playingChannels.value[0].channelName || "未命名通道"; + const summary = `共 ${playingChannels.value.length} 个通道`; + return playingChannels.value.length > pageSize + ? `${summary} · 当前第 ${ + (currentPage.value - 1) * pageSize + 1 + }–${Math.min( + currentPage.value * pageSize, + playingChannels.value.length + )} 路` + : summary; + }); - async function playPage() { - const request = ++playRequest; - const activeSession = session; - const list = pageChannels.value.map((channel, snum) => ({ - ...appliedOptions, - channelId: channel.channelCode, - channelName: channel.channelName, - snum, - })); - await nextTick(); - const player = playerRef.value; - if (!player || !dialogVisible.value || request !== playRequest) return; - try { - // 切换前清空旧窗口,避免取消选择的通道继续播放。 - await player.closeVideo(); - if (!dialogVisible.value || activeSession !== session || request !== playRequest || player !== playerRef.value) return; - if (options.mode === 'preview') await player.startReal(list); - else await player.startPlayback(list); - } catch { - if (dialogVisible.value && request === playRequest) useMessage().error('视频播放失败,请重试'); - } - } + async function playPage() { + const request = ++playRequest; + const activeSession = session; + const list = pageChannels.value.map((channel, snum) => ({ + ...appliedOptions, + channelId: channel.channelCode, + channelName: channel.channelName, + snum, + })); + await nextTick(); + const player = playerRef.value; + if (!player || !dialogVisible.value || request !== playRequest) return; + try { + // 切换前清空旧窗口,避免取消选择的通道继续播放。 + await player.closeVideo(); + if ( + !dialogVisible.value || + activeSession !== session || + request !== playRequest || + player !== playerRef.value + ) + return; + if (options.mode === "preview") await player.startReal(list); + else await player.startPlayback(list); + } catch { + if (dialogVisible.value && request === playRequest) + useMessage().error("视频播放失败,请重试"); + } + } - async function playSelected() { - if (loading.value || !selectedCodes.value.length) return; - const nextOptions = options.getPlayOptions(); - if (!nextOptions) return; - // 翻页沿用本次筛选快照,不受尚未提交的表单修改影响。 - appliedOptions = { ...nextOptions }; - playingCodes.value = [...selectedCodes.value]; - currentPage.value = 1; - await playPage(); - } + async function playSelected() { + if (loading.value || !selectedCodes.value.length) return; + const nextOptions = options.getPlayOptions(); + if (!nextOptions) return; + // 翻页沿用本次筛选快照,不受尚未提交的表单修改影响。 + appliedOptions = { ...nextOptions }; + playingCodes.value = [...selectedCodes.value]; + currentPage.value = 1; + await playPage(); + } - function changeSelection(codes: string[]) { - const available = new Set(channels.value.map((channel) => channel.channelCode)); - selectedCodes.value = [...new Set(codes)].filter((code) => available.has(code)); - if (selectionMode.value === 'single') { - selectedCodes.value = selectedCodes.value.slice(0, 1); - void playSelected(); - } - } + function changeSelection(codes: string[]) { + const available = new Set( + channels.value.map((channel) => channel.channelCode) + ); + selectedCodes.value = [...new Set(codes)].filter((code) => + available.has(code) + ); + if (selectionMode.value === "single") { + selectedCodes.value = selectedCodes.value.slice(0, 1); + void playSelected(); + } + } - function changeSelectionMode(mode: ChannelSelectionMode) { - selectionMode.value = mode; - if (mode === 'single') { - selectedCodes.value = [selectedCodes.value[0] || playingCodes.value[0] || channels.value[0]?.channelCode].filter(Boolean); - void playSelected(); - } - } + function changeSelectionMode(mode: ChannelSelectionMode) { + selectionMode.value = mode; + if (mode === "single") { + selectedCodes.value = [ + selectedCodes.value[0] || + playingCodes.value[0] || + channels.value[0]?.channelCode, + ].filter(Boolean); + void playSelected(); + } + } - async function changePage(page: number) { - currentPage.value = Math.max(1, Math.min(page, Math.ceil(playingChannels.value.length / pageSize) || 1)); - await playPage(); - } + async function changePage(page: number) { + currentPage.value = Math.max( + 1, + Math.min(page, Math.ceil(playingChannels.value.length / pageSize) || 1) + ); + await playPage(); + } - function closeDialog() { - ++session; - ++playRequest; - dialogVisible.value = false; - loading.value = false; - playerRef.value?.updateVisible(false); - if (playerRef.value?.destroy) playerRef.value.destroy(); - else void playerRef.value?.closeVideo(); - } + function closeDialog() { + ++session; + ++playRequest; + dialogVisible.value = false; + loading.value = false; + playerRef.value?.updateVisible(false); + if (playerRef.value?.destroy) playerRef.value.destroy(); + else void playerRef.value?.closeVideo(); + } - async function openDialog(type: string, code: string) { - if (!type || !code) return; - closeDialog(); - const activeSession = ++session; - sourceType = type; - sourceCode = code; - channels.value = []; - selectedCodes.value = []; - playingCodes.value = []; - deviceName.value = ''; - loadError.value = ''; - selectionMode.value = 'single'; - currentPage.value = 1; - dialogVisible.value = true; - loading.value = true; - try { - const query: Record = {}; - if (type === 'device') { - // 预览入口传设备 ID,回放入口传设备编码。 - const result = options.mode === 'preview' ? await DeviceManageApi.getObj(code) : await DeviceManageApi.getByCode({ code }); - if (activeSession !== session) return; - if (result.code != 0 || !result.data?.deviceCode) throw new Error('获取设备信息失败'); - query.deviceCode = result.data.deviceCode; - deviceName.value = result.data.deviceName || ''; - } else if (type === 'channel') query.channelCode = code; - else if (type === 'playbackGroup') query.playbackGroupChannelCode = code; - else throw new Error('不支持的视频来源'); - const result = await DeviceChannelApi.list(query); - if (activeSession !== session) return; - if (result.code != 0) throw new Error('加载通道失败,请重试'); - const seen = new Set(); - channels.value = (Array.isArray(result.data) ? result.data : []).filter((channel: VideoChannel) => { - const channelCode = channel.channelCode == null ? '' : String(channel.channelCode); - if (!channelCode || seen.has(channelCode)) return false; - seen.add(channelCode); - return true; - }).map((channel: VideoChannel) => ({ ...channel, channelCode: String(channel.channelCode) })); - deviceName.value ||= channels.value[0]?.deviceName || (type === 'playbackGroup' ? '回放分组' : '设备视频'); - selectedCodes.value = channels.value.slice(0, 1).map((channel) => channel.channelCode); - } catch (error) { - if (activeSession === session) loadError.value = error instanceof Error ? error.message : '加载通道失败,请重试'; - } finally { - if (activeSession === session) loading.value = false; - } - if (activeSession === session && dialogVisible.value) void playSelected(); - } + async function openDialog(type: string, code: string) { + if (!type || !code) return; + closeDialog(); + const activeSession = ++session; + sourceType = type; + sourceCode = code; + channels.value = []; + selectedCodes.value = []; + playingCodes.value = []; + deviceName.value = ""; + loadError.value = ""; + selectionMode.value = "single"; + currentPage.value = 1; + dialogVisible.value = true; + loading.value = true; + try { + const query: Record = {}; + if (type === "device") { + // 预览入口传设备 ID,回放入口传设备编码。 + const result = + options.mode === "preview" + ? await DeviceManageApi.getObj(code) + : await DeviceManageApi.getByCode({ code }); + if (activeSession !== session) return; + if (result.code != 0 || !result.data?.deviceCode) + throw new Error("获取设备信息失败"); + query.deviceCode = result.data.deviceCode; + deviceName.value = result.data.deviceName || ""; + } else if (type === "channel") query.channelCode = code; + else if (type === "playbackGroup") query.playbackGroupChannelCode = code; + else throw new Error("不支持的视频来源"); + const result = await DeviceChannelApi.list(query); + if (activeSession !== session) return; + if (result.code != 0) throw new Error("加载通道失败,请重试"); + const seen = new Set(); + channels.value = (Array.isArray(result.data) ? result.data : []) + .filter((channel: VideoChannel) => { + const channelCode = + channel.channelCode == null ? "" : String(channel.channelCode); + if (!channelCode || seen.has(channelCode)) return false; + seen.add(channelCode); + return true; + }) + .map((channel: VideoChannel) => ({ + ...channel, + channelCode: String(channel.channelCode), + })); + deviceName.value ||= + channels.value[0]?.deviceName || + (type === "playbackGroup" ? "回放分组" : "设备视频"); + selectedCodes.value = channels.value + .slice(0, 1) + .map((channel) => channel.channelCode); + } catch (error) { + if (activeSession === session) + loadError.value = + error instanceof Error ? error.message : "加载通道失败,请重试"; + } finally { + if (activeSession === session) loading.value = false; + } + if (activeSession === session && dialogVisible.value) void playSelected(); + } - onBeforeUnmount(closeDialog); + onBeforeUnmount(closeDialog); - return { - dialogVisible, channels, selectedCodes, selectionMode, loading, loadError, playingCodes, playingChannels, - deviceName, playerRef, currentPage, pageSize, division, divisionOptions, playingSummary, - openDialog, closeDialog, changeSelection, changeSelectionMode, playSelected, changePage, - reloadChannels: () => openDialog(sourceType, sourceCode), - }; + return { + dialogVisible, + channels, + selectedCodes, + selectionMode, + loading, + loadError, + playingCodes, + playingChannels, + deviceName, + playerRef, + currentPage, + pageSize, + division, + divisionOptions, + playingSummary, + openDialog, + closeDialog, + changeSelection, + changeSelectionMode, + playSelected, + changePage, + reloadChannels: () => openDialog(sourceType, sourceCode), + }; } diff --git a/src/views/device/components/video-channel-panel.vue b/src/views/device/components/video-channel-panel.vue index ff286aa..5c58eb8 100644 --- a/src/views/device/components/video-channel-panel.vue +++ b/src/views/device/components/video-channel-panel.vue @@ -1,169 +1,404 @@ diff --git a/src/views/device/components/video-dialog.scss b/src/views/device/components/video-dialog.scss index 4b9174e..e00b62c 100644 --- a/src/views/device/components/video-dialog.scss +++ b/src/views/device/components/video-dialog.scss @@ -1,92 +1,322 @@ // 限定到视频弹窗,覆盖项目弹窗的全局滚动与高度规则。 .el-overlay .el-overlay-dialog .el-dialog.device-video-dialog { - --device-video-stage-bg: #111820; - --device-video-stage-text: #aebaca; - display: flex; - flex-direction: column; - max-width: 1560px; - height: min(860px, calc(100vh - 48px)); - height: min(860px, calc(100dvh - 48px)); - border-radius: 12px; + --device-video-stage-bg: #111820; + --device-video-stage-text: #aebaca; + display: flex; + flex-direction: column; + max-width: 1560px; + height: min(860px, calc(100vh - 48px)); + height: min(860px, calc(100dvh - 48px)); + border-radius: 12px; - .el-dialog__header { flex: 0 0 auto; padding: 18px 24px; margin: 0; border-bottom: 1px solid var(--el-border-color-lighter); } - .el-dialog__headerbtn { top: 12px; right: 16px; width: 40px; height: 40px; &:focus-visible { outline: 2px solid var(--el-color-primary) !important; outline-offset: -4px; } } - .el-dialog__body { display: flex; flex: 1; min-height: 0; max-height: none !important; padding: 16px !important; overflow: hidden; background: var(--el-fill-color-lighter); } - .el-dialog__footer { flex: 0 0 auto; padding: 12px 20px; border-top: 1px solid var(--el-border-color-lighter); } - .el-button:focus-visible, .el-radio-button:has(input:focus-visible) .el-radio-button__inner { outline: 2px solid var(--el-color-primary) !important; outline-offset: 2px; } - .el-input__wrapper:focus-within, .el-select .el-input.is-focus .el-input__wrapper { box-shadow: 0 0 0 2px var(--el-color-primary) inset; } + .el-dialog__header { + flex: 0 0 auto; + padding: 18px 24px; + margin: 0; + border-bottom: 1px solid var(--el-border-color-lighter); + } + .el-dialog__headerbtn { + top: 12px; + right: 16px; + width: 40px; + height: 40px; + &:focus-visible { + outline: 2px solid var(--el-color-primary) !important; + outline-offset: -4px; + } + } + .el-dialog__body { + display: flex; + flex: 1; + min-height: 0; + max-height: none !important; + padding: 16px !important; + overflow: hidden; + background: var(--el-fill-color-lighter); + } + .el-dialog__footer { + flex: 0 0 auto; + padding: 12px 20px; + border-top: 1px solid var(--el-border-color-lighter); + } + .el-button:focus-visible, + .el-radio-button:has(input:focus-visible) .el-radio-button__inner { + outline: 2px solid var(--el-color-primary) !important; + outline-offset: 2px; + } + .el-input__wrapper:focus-within, + .el-select .el-input.is-focus .el-input__wrapper { + box-shadow: 0 0 0 2px var(--el-color-primary) inset; + } - .device-video-heading { display: flex; align-items: center; gap: 12px; min-width: 0; padding-right: 40px; h2 { margin: 0; white-space: nowrap; line-height: 28px; } } - .device-video-heading__icon { display: grid; place-items: center; width: 32px; height: 32px; flex: 0 0 32px; border-radius: 8px; color: var(--el-color-primary); background: var(--el-color-primary-light-9); font-size: 18px; } - .device-video-heading__device { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 13px; color: var(--el-text-color-regular); border-left: 1px solid var(--el-border-color); padding-left: 12px; } - .device-video-layout { display: flex; width: 100%; min-height: 0; min-width: 0; gap: 16px; } - .device-video-workspace { display: flex; flex: 1; flex-direction: column; min-width: 0; min-height: 0; overflow: hidden; background: var(--el-bg-color); border: 1px solid var(--el-border-color-light); border-radius: 8px; } - .device-video-toolbar { display: flex; align-items: center; justify-content: space-between; gap: 12px; flex: 0 0 auto; min-height: 48px; padding: 10px 16px; border-bottom: 1px solid var(--el-border-color-lighter); } - .device-video-toolbar__title { display: flex; align-items: center; gap: 8px; color: var(--el-text-color-primary); font-weight: 500; .el-icon { color: var(--el-text-color-regular); font-size: 16px; } } - .device-video-toolbar__meta { color: var(--el-text-color-regular); font-size: 12px; white-space: nowrap; } - .device-video-stage { - display: flex; - flex: 1; - align-items: center; - justify-content: center; - min-width: 0; - min-height: 0; - overflow: hidden; - background: var(--device-video-stage-bg); - container-type: size; - .video-container { - flex: 0 0 auto; - width: 100%; - max-height: 100%; - // 原生插件取实际 DOM 尺寸,保持 16:9 且不超出可用高度。 - @supports (width: 1cqh) { width: min(100cqw, calc(100cqh * 16 / 9)); } - background: var(--device-video-stage-bg); - } - .loading-mask { display: grid; place-items: center; background: var(--device-video-stage-bg); color: var(--device-video-stage-text); font-size: 13px; } - } - .device-video-empty { display: flex; flex-direction: column; align-items: center; gap: 12px; padding: 24px; text-align: center; color: var(--device-video-stage-text); > .el-icon { margin-bottom: 8px; font-size: 40px; } strong { color: #e4ebf3; font-size: 15px; font-weight: 500; } span { font-size: 13px; } } - .device-video-status { display: flex; align-items: center; justify-content: space-between; gap: 12px; flex: 0 0 auto; min-height: 42px; padding: 7px 16px; color: var(--el-text-color-regular); font-size: 12px; > span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } } - .device-video-query { - display: grid; - grid-template-columns: minmax(330px, 1fr) minmax(100px, 132px) minmax(100px, 132px) auto; - gap: 12px; - align-items: end; - flex: 0 0 auto; - padding: 16px; - border-bottom: 1px solid var(--el-border-color-lighter); - .el-form-item { min-width: 0; margin: 0; } - .el-form-item__label { padding: 0; margin-bottom: 6px; font-size: 12px; line-height: 18px; } - .el-form-item__content { min-width: 0; } - .el-date-editor, .el-select { width: 100%; min-width: 0; } - .el-date-editor .el-range-input { font-size: 12px; } - .el-date-editor .el-range-separator { flex: 0 0 20px; font-size: 12px; } - > .el-button { margin: 0; } - } - .device-video-footer { display: flex; align-items: center; justify-content: space-between; gap: 16px; > span { color: var(--el-text-color-regular); font-size: 12px; text-align: left; } } + .device-video-heading { + display: flex; + align-items: center; + gap: 12px; + min-width: 0; + padding-right: 40px; + h2 { + margin: 0; + white-space: nowrap; + line-height: 28px; + } + } + .device-video-heading__icon { + display: grid; + place-items: center; + width: 32px; + height: 32px; + flex: 0 0 32px; + border-radius: 8px; + color: var(--el-color-primary); + background: var(--el-color-primary-light-9); + font-size: 18px; + } + .device-video-heading__device { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + font-size: 13px; + color: var(--el-text-color-regular); + border-left: 1px solid var(--el-border-color); + padding-left: 12px; + } + .device-video-layout { + display: flex; + width: 100%; + min-height: 0; + min-width: 0; + gap: 16px; + } + .device-video-workspace { + display: flex; + flex: 1; + flex-direction: column; + min-width: 0; + min-height: 0; + overflow: hidden; + background: var(--el-bg-color); + border: 1px solid var(--el-border-color-light); + border-radius: 8px; + } + .device-video-toolbar { + display: flex; + align-items: center; + justify-content: space-between; + gap: 12px; + flex: 0 0 auto; + min-height: 48px; + padding: 10px 16px; + border-bottom: 1px solid var(--el-border-color-lighter); + } + .device-video-toolbar__title { + display: flex; + align-items: center; + gap: 8px; + color: var(--el-text-color-primary); + font-weight: 500; + .el-icon { + color: var(--el-text-color-regular); + font-size: 16px; + } + } + .device-video-toolbar__meta { + color: var(--el-text-color-regular); + font-size: 12px; + white-space: nowrap; + } + .device-video-stage { + display: flex; + flex: 1; + align-items: center; + justify-content: center; + min-width: 0; + min-height: 0; + overflow: hidden; + background: var(--device-video-stage-bg); + container-type: size; + .video-container { + flex: 0 0 auto; + width: 100%; + max-height: 100%; + // 原生插件取实际 DOM 尺寸,保持 16:9 且不超出可用高度。 + @supports (width: 1cqh) { + width: min(100cqw, calc(100cqh * 16 / 9)); + } + background: var(--device-video-stage-bg); + } + .loading-mask { + display: grid; + place-items: center; + background: var(--device-video-stage-bg); + color: var(--device-video-stage-text); + font-size: 13px; + } + } + .device-video-empty { + display: flex; + flex-direction: column; + align-items: center; + gap: 12px; + padding: 24px; + text-align: center; + color: var(--device-video-stage-text); + > .el-icon { + margin-bottom: 8px; + font-size: 40px; + } + strong { + color: #e4ebf3; + font-size: 15px; + font-weight: 500; + } + span { + font-size: 13px; + } + } + .device-video-status { + display: flex; + align-items: center; + justify-content: space-between; + gap: 12px; + flex: 0 0 auto; + min-height: 42px; + padding: 7px 16px; + color: var(--el-text-color-regular); + font-size: 12px; + > span { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + } + .device-video-query { + display: grid; + grid-template-columns: minmax(330px, 1fr) minmax(100px, 132px) minmax( + 100px, + 132px + ) auto; + gap: 12px; + align-items: end; + flex: 0 0 auto; + padding: 16px; + border-bottom: 1px solid var(--el-border-color-lighter); + .el-form-item { + min-width: 0; + margin: 0; + } + .el-form-item__label { + padding: 0; + margin-bottom: 6px; + font-size: 12px; + line-height: 18px; + } + .el-form-item__content { + min-width: 0; + } + .el-date-editor, + .el-select { + width: 100%; + min-width: 0; + } + .el-date-editor .el-range-input { + font-size: 12px; + } + .el-date-editor .el-range-separator { + flex: 0 0 20px; + font-size: 12px; + } + > .el-button { + margin: 0; + } + } + .device-video-footer { + display: flex; + align-items: center; + justify-content: space-between; + gap: 16px; + > span { + color: var(--el-text-color-regular); + font-size: 12px; + text-align: left; + } + } } @media (max-width: 1250px) { - .el-overlay .el-overlay-dialog .el-dialog.device-video-dialog .device-video-query { grid-template-columns: minmax(110px, 1fr) minmax(110px, 1fr) auto; .device-video-query__date { grid-column: 1 / -1; } } + .el-overlay + .el-overlay-dialog + .el-dialog.device-video-dialog + .device-video-query { + grid-template-columns: minmax(110px, 1fr) minmax(110px, 1fr) auto; + .device-video-query__date { + grid-column: 1 / -1; + } + } } @media (max-width: 700px) { - .el-overlay .el-overlay-dialog .el-dialog.device-video-dialog { - width: calc(100% - 16px) !important; - height: calc(100vh - 16px); - height: calc(100dvh - 16px); - .el-dialog__header { padding: 12px 16px; } - .el-dialog__body { padding: 8px !important; overflow-y: auto; } - .el-dialog__footer { padding: 10px 16px; } - .device-video-heading { gap: 8px; } - .device-video-heading__device { padding-left: 8px; font-size: 12px; } - .device-video-layout { flex-direction: column; gap: 8px; min-height: min-content; } - .device-video-workspace { flex: 0 0 auto; min-height: 330px; overflow: visible; } - .device-video-stage { flex: 0 0 auto; height: auto; min-height: 180px; aspect-ratio: 16 / 9; } - .device-video-query { padding: 12px; gap: 8px; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); > .el-button { grid-column: 1 / -1; } } - .device-video-toolbar, .device-video-status { padding-left: 12px; padding-right: 12px; } - .device-video-footer > span { max-width: 75%; line-height: 18px; } - } + .el-overlay .el-overlay-dialog .el-dialog.device-video-dialog { + width: calc(100% - 16px) !important; + height: calc(100vh - 16px); + height: calc(100dvh - 16px); + .el-dialog__header { + padding: 12px 16px; + } + .el-dialog__body { + padding: 8px !important; + overflow-y: auto; + } + .el-dialog__footer { + padding: 10px 16px; + } + .device-video-heading { + gap: 8px; + } + .device-video-heading__device { + padding-left: 8px; + font-size: 12px; + } + .device-video-layout { + flex-direction: column; + gap: 8px; + min-height: min-content; + } + .device-video-workspace { + flex: 0 0 auto; + min-height: 330px; + overflow: visible; + } + .device-video-stage { + flex: 0 0 auto; + height: auto; + min-height: 180px; + aspect-ratio: 16 / 9; + } + .device-video-query { + padding: 12px; + gap: 8px; + grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); + > .el-button { + grid-column: 1 / -1; + } + } + .device-video-toolbar, + .device-video-status { + padding-left: 12px; + padding-right: 12px; + } + .device-video-footer > span { + max-width: 75%; + line-height: 18px; + } + } } @media (prefers-reduced-motion: reduce) { - .el-dialog.device-video-dialog { --el-transition-duration: 0s; .el-dialog__close { transition: none !important; } } + .el-dialog.device-video-dialog { + --el-transition-duration: 0s; + .el-dialog__close { + transition: none !important; + } + } }