Browse Source

图片和视频水印

dev-V1.10.2-temp
wangmingyong@163.com 2 months ago
parent
commit
ff8cf20838
  1. 143
      zdxt-web-client/zdxt-efcode-enterprise-app/src/util/w6sMedia.js
  2. 5
      zdxt-web-client/zdxt-efcode-enterprise-app/src/views/enterprise/nos/components/NosDetailMaterialsTab.vue

143
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',

5
zdxt-web-client/zdxt-efcode-enterprise-app/src/views/enterprise/nos/components/NosDetailMaterialsTab.vue

@ -216,7 +216,7 @@
</template>
<script setup name="NosDetailMaterialsTab">
import { showImagePreview, showToast, showSuccessToast } from 'vant'
import { showImagePreview, showToast, showSuccessToast, showConfirmDialog } from 'vant'
import { useRoute } from 'vue-router'
import noDataImg from '@/assets/images/nos/no-data.png'
import { uploadFile } from '@/util/fileUpload'
@ -437,8 +437,7 @@ const onSelectUploadAction = ({ name }) => {
const onTakePhoto = async (item) => {
console.log('[NosDetail] onTakePhoto 开始, item:', item, 'fileList:', item?.fileList)
try {
const watermarkContent = $globalStore.useCommon?.enterpriseName || '深圳市行政执法监督码'
const res = await takePhotoWithWaterMark({ content: watermarkContent })
const res = await takePhotoWithWaterMark()
console.log('[NosDetail] 拍照返回 res:', res, 'imageURL:', res?.result.imageURL, 'key:', res?.result.key)
const localPath = res.result.imageURL || res.result.key
console.log('[NosDetail] 解析出的 localPath:', localPath)

Loading…
Cancel
Save