Browse Source

水印只用时间、云笔录改成录视频

dev-V1.10.2-temp
wangmingyong@163.com 2 months ago
parent
commit
0b73422703
  1. 30
      zdxt-web-client/zdxt-efcode-enterprise-app/src/util/w6sMedia.js
  2. 152
      zdxt-web-client/zdxt-efcode-enterprise-app/src/views/enterprise/nos/nosCloudRecord.vue

30
zdxt-web-client/zdxt-efcode-enterprise-app/src/util/w6sMedia.js

@ -102,29 +102,29 @@ const getTimeStr = () => {
// 自己拼接时间+定位到 content,设 timeEnable/locationEnable 为 false 跳过 SDK 内部阻塞式 getLocation 调用 // 自己拼接时间+定位到 content,设 timeEnable/locationEnable 为 false 跳过 SDK 内部阻塞式 getLocation 调用
export const takePhotoWithWaterMark = async () => { export const takePhotoWithWaterMark = async () => {
await ensureW6sAuth() await ensureW6sAuth()
console.log('[w6sMedia] 鉴权完成, 准备获取定位') // console.log('[w6sMedia] 鉴权完成, 准备获取定位')
// 预获取定位(带缓存),第二次拍照起直接用缓存不搜星 // // 预获取定位(带缓存),第二次拍照起直接用缓存不搜星
let locationStr = '' // let locationStr = ''
try { // try {
const loc = await getCachedLocation() // const loc = await getCachedLocation()
locationStr = (loc.city || '') + (loc.district || '') + (loc.street || '') // locationStr = (loc.city || '') + (loc.district || '') + (loc.street || '')
console.log('[w6sMedia] 定位地址:', locationStr) // console.log('[w6sMedia] 定位地址:', locationStr)
} catch (e) { // } catch (e) {
console.warn('[w6sMedia] 获取定位失败, 水印不含地址:', e) // console.warn('[w6sMedia] 获取定位失败, 水印不含地址:', e)
} // }
const content = `${getTimeStr()} ${locationStr}`.trim() // const content = `${getTimeStr()} ${locationStr}`.trim()
console.log('[w6sMedia] 水印 content:', content, '准备调起拍照') // console.log('[w6sMedia] 水印 content:', content, '准备调起拍照')
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
w6s.image.takePhotoAndAddWaterMark({ w6s.image.takePhotoAndAddWaterMark({
content, content: '',
fontSize: 14, fontSize: 14,
color: '#FF5858', color: '#FF5858',
markDisable: false, markDisable: false,
timeEnable: false, // 时间已自己拼入 content timeEnable: true,
locationEnable: false, // 定位已自己拼入 content,跳过 SDK 内部 getLocation 阻塞 locationEnable: false,
success: (res) => { success: (res) => {
console.log('[w6sMedia] takePhotoAndAddWaterMark success 原始返回:', res) console.log('[w6sMedia] takePhotoAndAddWaterMark success 原始返回:', res)
const result = res?.result || res || {} const result = res?.result || res || {}

152
zdxt-web-client/zdxt-efcode-enterprise-app/src/views/enterprise/nos/nosCloudRecord.vue

@ -55,22 +55,19 @@
<span class="required-mark">*</span>云视频 <span class="required-mark">*</span>云视频
</div> </div>
<div class="form-value"> <div class="form-value">
<van-uploader <div v-if="videoUploading" class="video-upload-state">
v-model="videoFileList" <van-loading type="spinner" color="#2b7de9" />
:max-count="1" <span class="upload-text">上传中...</span>
:max-size="VIDEO_MAX_SIZE" </div>
accept="video/*" <div v-else-if="cloudVideo" class="video-preview-item">
:after-read="onAfterReadVideo" <video :src="cloudVideo" muted preload="metadata" class="video-thumb" @click="showVideoPreview = true" />
@oversize="onVideoOversize" <van-icon name="play-circle-o" class="play-icon" @click="showVideoPreview = true" />
@delete="onVideoDelete" <van-icon name="cross" class="delete-icon" @click="onVideoDelete" />
> </div>
<template #default> <div v-else class="uploader-btn" @click="onRecordVideo">
<div class="uploader-btn">
<van-icon name="plus" size="24" color="#dcdee0" /> <van-icon name="plus" size="24" color="#dcdee0" />
</div> </div>
</template> <div class="form-tip">(仅支持现场录制视频,200MB以下)</div>
</van-uploader>
<div class="form-tip">(仅支持视频,200MB以下)</div>
</div> </div>
</div> </div>
@ -127,11 +124,9 @@
import { showConfirmDialog, showSuccessToast, showToast } from 'vant'; import { showConfirmDialog, showSuccessToast, showToast } from 'vant';
import noDataImg from '@/assets/images/nos/no-data.png'; import noDataImg from '@/assets/images/nos/no-data.png';
import { submitCloudRecordApi, cloudRecordDetailApi } from '@/api/nos/index'; import { submitCloudRecordApi, cloudRecordDetailApi } from '@/api/nos/index';
import { uploadFile } from '@/util/fileUpload'; import { startVideoRecorder, uploadLocalFileToBackend } from '@/util/w6sMedia';
import { toFullFileUrl, toRelativeFilePath } from '@/util/fileUrl'; import { toFullFileUrl, toRelativeFilePath } from '@/util/fileUrl';
const VIDEO_MAX_SIZE = 200 * 1024 * 1024;
const router = useRouter(); const router = useRouter();
const route = useRoute(); const route = useRoute();
@ -147,8 +142,8 @@ const cloudVideo = ref('');
const cloudText = ref(''); const cloudText = ref('');
const suggestion = ref(''); const suggestion = ref('');
const cloudVideoPath = ref(''); const cloudVideoPath = ref('');
const videoFileList = ref([]);
const showVideoPreview = ref(false); const showVideoPreview = ref(false);
const videoUploading = ref(false);
const applyDetailData = (data) => { const applyDetailData = (data) => {
cloudVideo.value = data.cloudVideo ? toFullFileUrl(data.cloudVideo) : ''; cloudVideo.value = data.cloudVideo ? toFullFileUrl(data.cloudVideo) : '';
@ -191,56 +186,40 @@ const onRefresh = () => {
fetchDetail(); fetchDetail();
}; };
const isVideo = (url) => {
if (!url) return false;
return /\.(mp4|webm|ogg|mov|avi|mkv)$/i.test(url);
};
const onVideoOversize = () => {
showToast('视频大小不能超过 200MB');
};
const onVideoDelete = () => { const onVideoDelete = () => {
cloudVideoPath.value = ''; cloudVideoPath.value = '';
cloudVideo.value = '';
}; };
const onAfterReadVideo = (file) => { const onRecordVideo = async () => {
const files = Array.isArray(file) ? file : [file]; if (videoUploading.value) return;
files.forEach((f) => { try {
if (f.file && !f.file.type.startsWith('video/')) { videoUploading.value = true;
showToast('请上传视频文件'); const res = await startVideoRecorder();
f.status = 'failed'; if (!res?.video_path) {
f.message = '格式错误'; showToast('录制失败');
videoFileList.value = [];
cloudVideoPath.value = '';
return; return;
} }
const uploadRes = await uploadLocalFileToBackend({
uploadFile(f, (res) => { fileURL: res.video_path,
const body = res?.data?.data ?? res?.data ?? res; fileName: res.name || `video-${Date.now()}.mp4`,
const filePath = mimeType: res.type || 'video/mp4',
typeof body === 'string' });
? body const filePath = uploadRes?.data || '';
: (body && (body.url || body.fileUrl || body.path)) || '';
if (filePath) { if (filePath) {
if (!isVideo(filePath) && f.file && !f.file.type.startsWith('video/')) {
showToast('请上传视频文件');
videoFileList.value = [];
cloudVideoPath.value = '';
return;
}
cloudVideoPath.value = filePath; cloudVideoPath.value = filePath;
f.url = toFullFileUrl(filePath); cloudVideo.value = toFullFileUrl(filePath);
f.isImage = false;
f.status = 'done';
} else { } else {
f.status = 'failed'; showToast('上传失败');
f.message = '上传失败'; }
cloudVideoPath.value = ''; } catch (err) {
console.error('[nosCloudRecord] 视频录制/上传失败:', err);
if (err?.message !== '已取消录制') {
showToast(err?.message || '录制失败或已取消');
}
} finally {
videoUploading.value = false;
} }
});
});
}; };
const validateForm = () => { const validateForm = () => {
@ -495,6 +474,63 @@ onMounted(() => {
justify-content: center; justify-content: center;
border-radius: 4px; border-radius: 4px;
border: 1px dashed #ebedf0; border: 1px dashed #ebedf0;
cursor: pointer;
}
.video-upload-state {
width: 80px;
height: 80px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 4px;
background-color: #f7f8fa;
border-radius: 4px;
border: 1px dashed #ebedf0;
.upload-text {
font-size: 12px;
color: #969799;
}
}
.video-preview-item {
position: relative;
width: 80px;
height: 80px;
border-radius: 4px;
overflow: hidden;
background: #000;
.video-thumb {
width: 100%;
height: 100%;
object-fit: cover;
cursor: pointer;
}
.play-icon {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
font-size: 28px;
color: rgba(255, 255, 255, 0.9);
cursor: pointer;
}
.delete-icon {
position: absolute;
top: 2px;
right: 2px;
font-size: 16px;
color: #fff;
background: rgba(0, 0, 0, 0.5);
border-radius: 50%;
padding: 2px;
cursor: pointer;
}
} }
.form-tip { .form-tip {

Loading…
Cancel
Save