From ff8cf208380860f559b055e7f93a5142b8e7c32a Mon Sep 17 00:00:00 2001 From: "wangmingyong@163.com" Date: Wed, 26 Aug 2026 17:42:03 +0800 Subject: [PATCH] =?UTF-8?q?=E5=9B=BE=E7=89=87=E5=92=8C=E8=A7=86=E9=A2=91?= =?UTF-8?q?=E6=B0=B4=E5=8D=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/util/w6sMedia.js | 143 +++++++++++------- .../nos/components/NosDetailMaterialsTab.vue | 5 +- 2 files changed, 90 insertions(+), 58 deletions(-) diff --git a/zdxt-web-client/zdxt-efcode-enterprise-app/src/util/w6sMedia.js b/zdxt-web-client/zdxt-efcode-enterprise-app/src/util/w6sMedia.js index cc5f2bda..5e13e525 100644 --- a/zdxt-web-client/zdxt-efcode-enterprise-app/src/util/w6sMedia.js +++ b/zdxt-web-client/zdxt-efcode-enterprise-app/src/util/w6sMedia.js @@ -65,45 +65,32 @@ export const ensureW6sAuth = () => { return w6sAuthPromise } +// 格式化当前时间为 yyyy-MM-dd HH:mm:ss +const formatDateTime = () => { + const d = new Date() + const pad = (n) => String(n).padStart(2, '0') + return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}` +} + // 拍照并加水印(时间戳 + 经纬度自动叠加到照片) -export const takePhotoWithWaterMark = async ({ - content = '', - fontSize = 14, - color = '#FF5858', - timeEnable = true, - locationEnable = true, -} = {}) => { - console.log('[w6sMedia] takePhotoWithWaterMark 开始, 入参:', { content, fontSize, color, timeEnable, locationEnable }) - await ensureW6sAuth() +export const takePhotoWithWaterMark = async () => { + // 如果调用方未传 content,则默认显示当前时间作为水印文字 + const watermarkContent = formatDateTime(); + console.log('[w6sMedia] takePhotoWithWaterMark 开始, 入参:', { content: watermarkContent, fontSize, color, timeEnable, locationEnable }) + await ensureW6sAuth(); console.log('[w6sMedia] 鉴权完成, 准备调起拍照') return new Promise((resolve, reject) => { - // w6s.image.takePicture({ - // editable: false, - // success: function (res) { - // console.log('[w6sMedia] takePhotoAndAddWaterMark success 原始返回:', res) - // console.log('[w6sMedia] 返回字段 imageURL:', res?.imageURL, 'key:', res?.key, 'imageInfo:', res?.imageInfo) - // resolve(res) - // }, - // fail: function (err) { - // console.error('[w6sMedia] takePhotoAndAddWaterMark fail:', err) - // reject(err) - // }, - // }); w6s.image.takePhotoAndAddWaterMark({ - content, - fontSize, - color, + content: watermarkContent, + fontSize: 14, + color: '#FF5858', markDisable: false, timeEnable: true, locationEnable: true, success: (res) => { console.log('[w6sMedia] takePhotoAndAddWaterMark success 原始返回:', res) console.log('[w6sMedia] 返回字段 imageURL:', res?.result.imageURL, 'key:', res?.result.key, 'imageInfo:', res?.result.imageInfo) - // getLocation().then((location) => { - // console.log('[w6sMedia] 获取定位成功:', location) - // res?.result.location = location - resolve(res) - // }) + resolve(res) }, fail: (err) => { console.error('[w6sMedia] takePhotoAndAddWaterMark fail:', err) @@ -113,30 +100,75 @@ export const takePhotoWithWaterMark = async ({ }) } -// 将文件从公共目录复制到应用私有目录(解决 Android 11+ 分区存储导致的 EACCES 权限问题) -const copyFileToAppDir = async (sourcePath) => { - if (!window.cordova?.file?.dataDirectory) { - throw new Error('cordova.file.dataDirectory 不可用') - } - console.log('[w6sMedia] copyFileToAppDir sourcePath:', sourcePath) - - const sourceEntry = await new Promise((resolve, reject) => { - window.resolveLocalFileSystemURL(sourcePath, resolve, reject) - }) - console.log('[w6sMedia] 源文件 entry:', sourceEntry.fullPath, 'name:', sourceEntry.name) - - const dataDirEntry = await new Promise((resolve, reject) => { - window.resolveLocalFileSystemURL(window.cordova.file.dataDirectory, resolve, reject) - }) - console.log('[w6sMedia] 应用私有目录:', dataDirEntry.fullPath) +// 将 MediaFile 从公共目录复制到应用私有目录(解决 Android 11+ 分区存储导致的 EACCES 权限问题) +// 直接使用 MediaFile.file() 获取 FileEntry,避免 resolveLocalFileSystemURL 在公共目录路径上的限制 +const copyMediaFileToAppDir = (mediaFile) => { + return new Promise((resolve, reject) => { + if (!window.cordova?.file?.dataDirectory) { + reject(new Error('cordova.file.dataDirectory 不可用')) + return + } + console.log('[w6sMedia] copyMediaFileToAppDir 开始, localURL:', mediaFile.localURL, 'fullPath:', mediaFile.fullPath) - const newFileEntry = await new Promise((resolve, reject) => { - sourceEntry.copyTo(dataDirEntry, sourceEntry.name, resolve, reject) + // 通过 MediaFile.file() 获取 FileEntry(比 resolveLocalFileSystemURL 更可靠) + mediaFile.file( + (sourceFileEntry) => { + console.log('[w6sMedia] 获取源 FileEntry 成功:', sourceFileEntry.fullPath) + window.resolveLocalFileSystemURL( + window.cordova.file.dataDirectory, + (dataDirEntry) => { + console.log('[w6sMedia] 应用私有目录:', dataDirEntry.fullPath) + sourceFileEntry.copyTo( + dataDirEntry, + mediaFile.name, + (newFileEntry) => { + const fileUrl = newFileEntry.toURL() + console.log('[w6sMedia] 文件已复制到应用私有目录, fileUrl:', fileUrl) + resolve(fileUrl) + }, + (copyErr) => { + // 目标文件已存在时,先删除再复制 + console.warn('[w6sMedia] copyTo 可能目标已存在,尝试移除后重试:', copyErr) + window.resolveLocalFileSystemURL( + window.cordova.file.dataDirectory + mediaFile.name, + (existingEntry) => { + existingEntry.remove( + () => { + sourceFileEntry.copyTo( + dataDirEntry, + mediaFile.name, + (newFileEntry) => { + const fileUrl = newFileEntry.toURL() + console.log('[w6sMedia] 重试复制成功, fileUrl:', fileUrl) + resolve(fileUrl) + }, + reject + ) + }, + () => { + // 无法删除旧文件,直接 resolve 原始 localURL + console.warn('[w6sMedia] 无法删除旧文件,返回原始 localURL') + resolve(mediaFile.localURL) + } + ) + }, + () => { + // 旧文件不存在,直接报错 + reject(copyErr) + } + ) + } + ) + }, + reject + ) + }, + (fileErr) => { + console.error('[w6sMedia] MediaFile.file() 失败:', fileErr) + reject(fileErr) + } + ) }) - // toURL() 返回 file:// 格式的 URL,FileTransfer 可直接使用 - const fileUrl = newFileEntry.toURL() - console.log('[w6sMedia] 文件已复制到应用私有目录, fileUrl:', fileUrl, 'fullPath:', newFileEntry.fullPath) - return fileUrl } // 录制视频(使用 Cordova 原生 Media Capture API) @@ -160,9 +192,10 @@ export const startVideoRecorder = async ({ console.log('[w6sMedia] captureVideo success, mediaFiles:', mediaFiles) if (mediaFiles && mediaFiles.length > 0) { const file = mediaFiles[0] - console.log('[w6sMedia] 视频文件: fullPath=', file.fullPath, 'name=', file.name, 'size=', file.size, 'type=', file.type) + console.log('[w6sMedia] 视频文件: fullPath=', file.fullPath, 'localURL=', file.localURL, 'name=', file.name, 'size=', file.size) try { - const appDirPath = await copyFileToAppDir(file.fullPath) + // 尝试将 MediaFile 复制到应用私有目录,获得可访问的 file:// URL + const appDirPath = await copyMediaFileToAppDir(file) console.log('[w6sMedia] 视频已复制到应用私有目录:', appDirPath) resolve({ video_path: appDirPath, @@ -171,9 +204,9 @@ export const startVideoRecorder = async ({ type: file.type || 'video/mp4', }) } catch (copyErr) { - console.warn('[w6sMedia] 复制视频失败,尝试使用原路径:', copyErr) + console.warn('[w6sMedia] 复制视频失败,尝试使用 localURL:', copyErr) resolve({ - video_path: file.fullPath, + video_path: file.localURL, name: file.name, size: file.size, type: file.type || 'video/mp4', diff --git a/zdxt-web-client/zdxt-efcode-enterprise-app/src/views/enterprise/nos/components/NosDetailMaterialsTab.vue b/zdxt-web-client/zdxt-efcode-enterprise-app/src/views/enterprise/nos/components/NosDetailMaterialsTab.vue index 0a796001..17b520b8 100644 --- a/zdxt-web-client/zdxt-efcode-enterprise-app/src/views/enterprise/nos/components/NosDetailMaterialsTab.vue +++ b/zdxt-web-client/zdxt-efcode-enterprise-app/src/views/enterprise/nos/components/NosDetailMaterialsTab.vue @@ -216,7 +216,7 @@