From 2c42a6e2992290b9c6331dd53a2cc1d4e8df4f00 Mon Sep 17 00:00:00 2001 From: duoaohui <928970622@qq.com> Date: Mon, 25 May 2026 22:03:13 +0800 Subject: [PATCH] =?UTF-8?q?=E8=A7=86=E9=A2=91=E8=BF=9E=E7=BA=BF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/pages/cloud-law/video-call.vue | 142 ++++++++++++++++++-- 1 file changed, 133 insertions(+), 9 deletions(-) diff --git a/frontend/src/pages/cloud-law/video-call.vue b/frontend/src/pages/cloud-law/video-call.vue index 08757e1..e76cf15 100644 --- a/frontend/src/pages/cloud-law/video-call.vue +++ b/frontend/src/pages/cloud-law/video-call.vue @@ -95,7 +95,7 @@ - + @@ -121,6 +121,17 @@ + + + + + 手机端请长按图片保存,电脑端可点击下载图片 + + + + + + @@ -176,6 +187,8 @@ const captureIcon = '/static/capture.png' const flipCameraIcon = '/static/camera.png' const captureIconError = ref(false) const flipCameraIconError = ref(false) +const snapshotPreviewUrl = ref('') +const snapshotBusy = ref(false) const digitsOf = (value) => String(value || '').replace(/\D/g, '').slice(0, 9) const sameUser = (a, b) => String(a || '') === String(b || '') @@ -448,10 +461,62 @@ const toggleSpeaker = () => { setSpeakerMuted(!speakerMuted.value) } +const isMobileSnapshotBrowser = () => { + // #ifdef H5 + const ua = navigator?.userAgent || '' + return /Android|iPhone|iPad|iPod|Mobile|MicroMessenger/i.test(ua) + // #endif + // #ifndef H5 + return false + // #endif +} + +const findSnapshotVideo = () => { + // #ifdef H5 + const selectors = [ + '.cloud-law-call .main-video video', + '.cloud-law-call .remote-stage video', + '.cloud-law-call video' + ] + for (const selector of selectors) { + const list = Array.from(document.querySelectorAll(selector)) + const video = list.find(el => el && el.videoWidth && el.videoHeight && el.readyState >= 2) + if (video) return video + } + // #endif + return null +} + +const downloadSnapshotDataUrl = (dataUrl) => { + // #ifdef H5 + const a = document.createElement('a') + a.href = dataUrl + a.download = `cloud-law-video-${Date.now()}.png` + document.body.appendChild(a) + a.click() + document.body.removeChild(a) + // #endif +} + +const closeSnapshotPreview = () => { + snapshotPreviewUrl.value = '' +} + +const downloadSnapshotPreview = () => { + if (!snapshotPreviewUrl.value) return + downloadSnapshotDataUrl(snapshotPreviewUrl.value) + uni.showToast({ title: isMobileSnapshotBrowser() ? '如未下载,请长按图片保存' : '截屏已下载', icon: 'none' }) +} + const takeSnapshot = () => { + if (snapshotBusy.value) return + snapshotBusy.value = true + setTimeout(() => { + snapshotBusy.value = false + }, 600) // #ifdef H5 try { - const video = document.querySelector('.cloud-law-call .main-video video') || document.querySelector('.cloud-law-call video') + const video = findSnapshotVideo() if (!video || !video.videoWidth || !video.videoHeight) { uni.showToast({ title: '当前无视频画面可截屏', icon: 'none' }) return @@ -461,18 +526,22 @@ const takeSnapshot = () => { canvas.height = video.videoHeight const ctx = canvas.getContext('2d') ctx.drawImage(video, 0, 0, canvas.width, canvas.height) - const a = document.createElement('a') - a.href = canvas.toDataURL('image/png') - a.download = `cloud-law-video-${Date.now()}.png` - document.body.appendChild(a) - a.click() - document.body.removeChild(a) - uni.showToast({ title: '截屏已保存', icon: 'success' }) + const dataUrl = canvas.toDataURL('image/png') + snapshotPreviewUrl.value = dataUrl + if (isMobileSnapshotBrowser()) { + uni.showToast({ title: '截屏成功,长按图片保存', icon: 'none' }) + } else { + downloadSnapshotDataUrl(dataUrl) + uni.showToast({ title: '截屏已下载', icon: 'success' }) + } } catch (err) { console.warn('[CloudLawVideoCall] snapshot failed', err) uni.showToast({ title: '截屏失败', icon: 'none' }) } // #endif + // #ifndef H5 + uni.showToast({ title: '当前环境暂不支持截屏', icon: 'none' }) + // #endif } const flipCameraByCommand = async (value) => { @@ -1100,6 +1169,61 @@ onUnload(() => { .loading { opacity: 0.68; } +.snapshot-preview-mask { + position: fixed; + left: 0; + right: 0; + top: 0; + bottom: 0; + z-index: 99; + background: rgba(0, 0, 0, 0.78); + display: flex; + align-items: center; + justify-content: center; + padding: 36rpx; + box-sizing: border-box; +} +.snapshot-preview-panel { + width: 100%; + max-width: 720rpx; + max-height: 88vh; + border-radius: 24rpx; + background: #111827; + padding: 24rpx; + box-sizing: border-box; + display: flex; + flex-direction: column; + gap: 20rpx; +} +.snapshot-preview-image { + width: 100%; + height: 58vh; + border-radius: 16rpx; + background: #000000; +} +.snapshot-preview-tip { + color: rgba(255, 255, 255, 0.82); + font-size: 24rpx; + line-height: 1.45; + text-align: center; +} +.snapshot-preview-actions { + display: flex; + gap: 18rpx; +} +.snapshot-preview-btn { + flex: 1; + height: 72rpx; + line-height: 72rpx; + border-radius: 999rpx; + color: #ffffff; + background: #2563eb; + font-size: 26rpx; +} +.snapshot-preview-btn.secondary { + color: rgba(255, 255, 255, 0.86); + background: rgba(255, 255, 255, 0.14); +} .clean-video-tile :deep(.footer) { display: none; }