import * as w6s from 'szient-js-sdk' import sha1 from 'crypto-js/sha1' import { siqCode } from '@/api/enterprise' import { baseUrl } from '@/config/env' import { $globalStore } from '@/common/globalStore' // ========== w6s 鉴权(参照 enterpriseHome.vue 的写法) ========== const generateRandomNumber = () => { const min = 100000 const max = 999999 const randomNum = (Math.floor(Math.random() * (max - min + 1)) + min) return randomNum.toString().padStart(6, '0') } let w6sAuthPromise = null // 确保 w6s 鉴权完成,缓存 Promise 避免重复鉴权 export const ensureW6sAuth = () => { if (w6sAuthPromise) return w6sAuthPromise w6sAuthPromise = new Promise((resolve, reject) => { siqCode(null, success => { let access_key = '' let access_secret = '' if (success.success) { access_key = success.data.accessKey access_secret = success.data.accessSecret } if (!access_key || !access_secret) { w6sAuthPromise = null reject(new Error('获取深i企认证信息错误: access_key 或 access_secret 为空')) return } const nonce = generateRandomNumber() const timestamp = `${Date.now()}` const str = [access_secret, nonce, timestamp] .sort() .reduce((s, item) => s + item, '') const signature = sha1(str).toString().toLowerCase() w6s.config({ access_key: access_key, nonce: nonce, timestamp: timestamp, signature: signature, }).then((res) => { console.log('w6s 鉴权成功', res) }).catch((err) => { w6sAuthPromise = null reject(err) }) w6s.ready(() => { console.log('w6s ready') resolve() }) }, error => { w6sAuthPromise = null reject(error) }) }) 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,则默认显示当前时间作为水印文字 const watermarkContent = formatDateTime(); await ensureW6sAuth(); console.log('[w6sMedia] 鉴权完成, 准备调起拍照') return new Promise((resolve, reject) => { w6s.image.takePhotoAndAddWaterMark({ 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) resolve(res) }, fail: (err) => { console.error('[w6sMedia] takePhotoAndAddWaterMark fail:', err) reject(err) }, }) }) } // 将 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) // 通过 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) } ) }) } // 录制视频(使用 Cordova 原生 Media Capture API) export const startVideoRecorder = async ({ duration = 0, quality = 1, // 0 高清 / 1 一般 / 2 流畅 front = false, syncSystemAlbum = false, } = {}) => { console.log('[w6sMedia] startVideoRecorder 开始, 入参:', { duration, quality, front, syncSystemAlbum }) await ensureW6sAuth() console.log('[w6sMedia] 录制前鉴权完成, 准备调起录制') return new Promise((resolve, reject) => { if (!navigator?.device?.capture?.captureVideo) { console.error('[w6sMedia] navigator.device.capture.captureVideo 不可用') reject(new Error('当前环境不支持视频录制')) return } navigator.device.capture.captureVideo( async (mediaFiles) => { console.log('[w6sMedia] captureVideo success, mediaFiles:', mediaFiles) if (mediaFiles && mediaFiles.length > 0) { const file = mediaFiles[0] console.log('[w6sMedia] 视频文件: fullPath=', file.fullPath, 'localURL=', file.localURL, 'name=', file.name, 'size=', file.size) try { // 尝试将 MediaFile 复制到应用私有目录,获得可访问的 file:// URL const appDirPath = await copyMediaFileToAppDir(file) console.log('[w6sMedia] 视频已复制到应用私有目录:', appDirPath) resolve({ video_path: appDirPath, name: file.name, size: file.size, type: file.type || 'video/mp4', }) } catch (copyErr) { console.warn('[w6sMedia] 复制视频失败,尝试使用 localURL:', copyErr) resolve({ video_path: file.localURL, name: file.name, size: file.size, type: file.type || 'video/mp4', }) } } else { reject(new Error('录制未生成视频文件')) } }, (error) => { console.error('[w6sMedia] captureVideo fail:', error, 'code:', error?.code, 'message:', error?.message) reject(error) }, duration ? { limit: 1, duration } : { limit: 1 } ) }) } // 获取定位(经纬度) export const getLocation = async () => { await ensureW6sAuth() return new Promise((resolve, reject) => { w6s.location.getLocation({ success: resolve, fail: reject }) }) } // 上传本地文件到后端 /app-enf/common/file/upload // 使用 w6s.file.upload(Cordova FileTransfer),直接传本地路径,无需 base64 转换 export const uploadLocalFileToBackend = async ({ fileURL, fileName = 'upload', mimeType = 'image/jpeg', }) => { console.log('[w6sMedia] uploadLocalFileToBackend 开始, 入参:', { fileURL, fileName, mimeType }) await ensureW6sAuth() console.log('[w6sMedia] 上传前鉴权完成') return new Promise((resolve, reject) => { const token = $globalStore.useCommon?.token || '' const api = `${baseUrl}/app-enf/common/file/upload` const server = /^https?:/.test(api) ? api : `${window.location.origin}${api}` console.log('[w6sMedia] 上传目标 server:', server, 'token 存在:', !!token) const opts = new window.FileUploadOptions() opts.fileKey = 'file' opts.fileName = fileName opts.mimeType = mimeType opts.headers = { Authorization: token ? `Bearer ${token}` : '', clientid: '9e6eb24cd4e169a98b0d2cd8034d877b', } console.log('[w6sMedia] FileUploadOptions 构造完成:', { fileKey: opts.fileKey, fileName: opts.fileName, mimeType: opts.mimeType }) w6s.file.upload( { fileURL, server, trustAllHosts: false, success: (res) => { console.log('[w6sMedia] file.upload success 原始返回:', res) try { const parsed = JSON.parse(res.response) console.log('[w6sMedia] 解析后的上传响应:', parsed) resolve(parsed) } catch (e) { console.warn('[w6sMedia] 响应 JSON 解析失败, 返回原始 response:', res.response) resolve(res.response) } }, fail: (err) => { console.error('[w6sMedia] file.upload fail:', err) reject(err) }, }, opts ).catch((err) => { // w6s.file.upload 在 responseCode !== 200 时会 reject 但不调用 fail 回调 console.error('[w6sMedia] w6s.file.upload Promise reject:', err) reject(err) }) }) }