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 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 ({ export const takePhotoWithWaterMark = async () => {
content = '', // 如果调用方未传 content,则默认显示当前时间作为水印文字
fontSize = 14, const watermarkContent = formatDateTime();
color = '#FF5858', console.log('[w6sMedia] takePhotoWithWaterMark 开始, 入参:', { content: watermarkContent, fontSize, color, timeEnable, locationEnable })
timeEnable = true, await ensureW6sAuth();
locationEnable = true,
} = {}) => {
console.log('[w6sMedia] takePhotoWithWaterMark 开始, 入参:', { content, fontSize, color, timeEnable, locationEnable })
await ensureW6sAuth()
console.log('[w6sMedia] 鉴权完成, 准备调起拍照') console.log('[w6sMedia] 鉴权完成, 准备调起拍照')
return new Promise((resolve, reject) => { 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({ w6s.image.takePhotoAndAddWaterMark({
content, content: watermarkContent,
fontSize, fontSize: 14,
color, color: '#FF5858',
markDisable: false, markDisable: false,
timeEnable: true, timeEnable: true,
locationEnable: true, locationEnable: true,
success: (res) => { success: (res) => {
console.log('[w6sMedia] takePhotoAndAddWaterMark success 原始返回:', res) console.log('[w6sMedia] takePhotoAndAddWaterMark success 原始返回:', res)
console.log('[w6sMedia] 返回字段 imageURL:', res?.result.imageURL, 'key:', res?.result.key, 'imageInfo:', res?.result.imageInfo) console.log('[w6sMedia] 返回字段 imageURL:', res?.result.imageURL, 'key:', res?.result.key, 'imageInfo:', res?.result.imageInfo)
// getLocation().then((location) => { resolve(res)
// console.log('[w6sMedia] 获取定位成功:', location)
// res?.result.location = location
resolve(res)
// })
}, },
fail: (err) => { fail: (err) => {
console.error('[w6sMedia] takePhotoAndAddWaterMark fail:', err) console.error('[w6sMedia] takePhotoAndAddWaterMark fail:', err)
@ -113,30 +100,75 @@ export const takePhotoWithWaterMark = async ({
}) })
} }
// 将文件从公共目录复制到应用私有目录(解决 Android 11+ 分区存储导致的 EACCES 权限问题) // 将 MediaFile 从公共目录复制到应用私有目录(解决 Android 11+ 分区存储导致的 EACCES 权限问题)
const copyFileToAppDir = async (sourcePath) => { // 直接使用 MediaFile.file() 获取 FileEntry,避免 resolveLocalFileSystemURL 在公共目录路径上的限制
if (!window.cordova?.file?.dataDirectory) { const copyMediaFileToAppDir = (mediaFile) => {
throw new Error('cordova.file.dataDirectory 不可用') return new Promise((resolve, reject) => {
} if (!window.cordova?.file?.dataDirectory) {
console.log('[w6sMedia] copyFileToAppDir sourcePath:', sourcePath) reject(new Error('cordova.file.dataDirectory 不可用'))
return
const sourceEntry = await new Promise((resolve, reject) => { }
window.resolveLocalFileSystemURL(sourcePath, resolve, reject) console.log('[w6sMedia] copyMediaFileToAppDir 开始, localURL:', mediaFile.localURL, 'fullPath:', mediaFile.fullPath)
})
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)
const newFileEntry = await new Promise((resolve, reject) => { // 通过 MediaFile.file() 获取 FileEntry(比 resolveLocalFileSystemURL 更可靠)
sourceEntry.copyTo(dataDirEntry, sourceEntry.name, resolve, reject) 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) // 录制视频(使用 Cordova 原生 Media Capture API)
@ -160,9 +192,10 @@ export const startVideoRecorder = async ({
console.log('[w6sMedia] captureVideo success, mediaFiles:', mediaFiles) console.log('[w6sMedia] captureVideo success, mediaFiles:', mediaFiles)
if (mediaFiles && mediaFiles.length > 0) { if (mediaFiles && mediaFiles.length > 0) {
const file = mediaFiles[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 { try {
const appDirPath = await copyFileToAppDir(file.fullPath) // 尝试将 MediaFile 复制到应用私有目录,获得可访问的 file:// URL
const appDirPath = await copyMediaFileToAppDir(file)
console.log('[w6sMedia] 视频已复制到应用私有目录:', appDirPath) console.log('[w6sMedia] 视频已复制到应用私有目录:', appDirPath)
resolve({ resolve({
video_path: appDirPath, video_path: appDirPath,
@ -171,9 +204,9 @@ export const startVideoRecorder = async ({
type: file.type || 'video/mp4', type: file.type || 'video/mp4',
}) })
} catch (copyErr) { } catch (copyErr) {
console.warn('[w6sMedia] 复制视频失败,尝试使用原路径:', copyErr) console.warn('[w6sMedia] 复制视频失败,尝试使用 localURL:', copyErr)
resolve({ resolve({
video_path: file.fullPath, video_path: file.localURL,
name: file.name, name: file.name,
size: file.size, size: file.size,
type: file.type || 'video/mp4', 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> </template>
<script setup name="NosDetailMaterialsTab"> <script setup name="NosDetailMaterialsTab">
import { showImagePreview, showToast, showSuccessToast } from 'vant' import { showImagePreview, showToast, showSuccessToast, showConfirmDialog } from 'vant'
import { useRoute } from 'vue-router' import { useRoute } from 'vue-router'
import noDataImg from '@/assets/images/nos/no-data.png' import noDataImg from '@/assets/images/nos/no-data.png'
import { uploadFile } from '@/util/fileUpload' import { uploadFile } from '@/util/fileUpload'
@ -437,8 +437,7 @@ const onSelectUploadAction = ({ name }) => {
const onTakePhoto = async (item) => { const onTakePhoto = async (item) => {
console.log('[NosDetail] onTakePhoto 开始, item:', item, 'fileList:', item?.fileList) console.log('[NosDetail] onTakePhoto 开始, item:', item, 'fileList:', item?.fileList)
try { try {
const watermarkContent = $globalStore.useCommon?.enterpriseName || '深圳市行政执法监督码' const res = await takePhotoWithWaterMark()
const res = await takePhotoWithWaterMark({ content: watermarkContent })
console.log('[NosDetail] 拍照返回 res:', res, 'imageURL:', res?.result.imageURL, 'key:', res?.result.key) console.log('[NosDetail] 拍照返回 res:', res, 'imageURL:', res?.result.imageURL, 'key:', res?.result.key)
const localPath = res.result.imageURL || res.result.key const localPath = res.result.imageURL || res.result.key
console.log('[NosDetail] 解析出的 localPath:', localPath) console.log('[NosDetail] 解析出的 localPath:', localPath)

Loading…
Cancel
Save