# 上传功能按环境拆分:本地用 van-uploader,生产强制现场拍摄/录制 ## Context(背景) [NosDetailMaterialsTab.vue](file:///d:/Enforcementcode/EnforcementCode-2.0/zdxt-web-client/zdxt-efcode-enterprise-app/src/views/enterprise/nos/components/NosDetailMaterialsTab.vue) 第 48-63 行使用单个 `van-uploader` 同时处理图片/视频/PDF。生产环境(beeworks 容器/深 i 企)内合规要求:**线上不能用相册的图片和视频,必须是现场拍摄和录制**,且**拍照必须带水印与经纬度**。 按环境拆分上传入口: - **开发环境(dev)**:保留现有 `van-uploader` 不变(支持图片/视频/PDF)。 - **生产环境(prod)**:只允许两种方式,均来自设备现场采集: 1. **拍照** → [`w6s.image.takePhotoAndAddWaterMark`](https://open.beeworks.cn/js-sdk/image.html#%E5%9B%BE%E7%89%87%E6%B7%BB%E5%8A%A0%E6%B0%B4%E5%8D%B0)(`timeEnable: true, locationEnable: true` 自动叠加时间戳与经纬度水印) 2. **录制视频** → [`w6s.video.startVideoRecoder`](https://open.beeworks.cn/js-sdk/video.html#%E8%A7%86%E9%A2%91%E5%BD%95%E5%88%B6) 3. 上传到后端 → [`w6s.file.upload`](https://open.beeworks.cn/js-sdk/file.html#%E6%96%87%E4%BB%B6%E4%B8%8A%E4%BC%A0)(原生 FileTransfer,直接传本地路径,无需 base64 转换) 4. 经纬度获取(可选) → [`w6s.location.getLocation`](https://open.beeworks.cn/js-sdk/location.html) > **关键约束**:生产环境**不提供**相册选择、文件选择器入口,避免用户上传已有图片/视频。PDF 上传在生产环境是否保留待用户确认(见开放问题 1)。 ## 关键复用点 - **环境判断**:[src/config/env.js](file:///d:/Enforcementcode/EnforcementCode-2.0/zdxt-web-client/zdxt-efcode-enterprise-app/src/config/env.js) 的 `appEnv`,生产值为 `'prod'`。 - **w6s SDK 导入**:`import * as w6s from 'szient-js-sdk'`(参考 [qrCode.vue#L42](file:///d:/Enforcementcode/EnforcementCode-2.0/zdxt-web-client/zdxt-efcode-enterprise-app/src/views/enterprise/qr/qrCode.vue#L42))。鉴权已在 [enterpriseHome.vue#L295-L344](file:///d:/Enforcementcode/EnforcementCode-2.0/zdxt-web-client/zdxt-efcode-enterprise-app/src/views/enterprise/home/enterpriseHome.vue#L295-L344) 完成。 - **后端上传地址**:[src/util/fileUpload.js](file:///d:/Enforcementcode/EnforcementCode-2.0/zdxt-web-client/zdxt-efcode-enterprise-app/src/util/fileUpload.js) 中 `/app-enf/common/file/upload`,完整 URL 为 `${baseUrl}/app-enf/common/file/upload`。 - **认证头**:[src/http/http.js#L26-L31](file:///d:/Enforcementcode/EnforcementCode-2.0/zdxt-web-client/zdxt-efcode-enterprise-app/src/http/http.js#L26-L31) 拦截器加 `Authorization: Bearer + token` 与 `clientid: 9e6eb24cd4e169a98b0d2cd8034d877b`,`w6s.file.upload` 需手动传入。 - **URL 拼接**:复用 [src/util/fileUrl.js](file:///d:/Enforcementcode/EnforcementCode-2.0/zdxt-web-client/zdxt-efcode-enterprise-app/src/util/fileUrl.js) 的 `toFullFileUrl`。 - **全局状态**:`$globalStore.useCommon`(取 `token`、`enterpriseName`)。 ## 实施步骤 ### 步骤 1:新建 `src/util/w6sMedia.js` Promise 化封装 4 个方法: ```js import * as w6s from 'szient-js-sdk' import { baseUrl } from '@/config/env' import { $globalStore } from '@/common/globalStore' // 1. 拍照并加水印(时间戳 + 经纬度自动叠加) export const takePhotoWithWaterMark = ({ content = '', fontSize = 14, color = '#FF5858', timeEnable = true, locationEnable = true, } = {}) => new Promise((resolve, reject) => { w6s.image.takePhotoAndAddWaterMark({ content, fontSize, color, markDisable: false, timeEnable, locationEnable, success: resolve, fail: reject, }) }) // 2. 录制视频 export const startVideoRecorder = ({ duration = 60, quality = 1, // 0 高清 / 1 一般 / 2 流畅 front = false, syncSystemAlbum = false, } = {}) => new Promise((resolve, reject) => { w6s.video.startVideoRecoder({ duration, quality, front, sync_system_album: syncSystemAlbum, success: resolve, fail: reject, }) }) // 3. 获取定位(可选,用于单独保存经纬度元数据) export const getLocation = () => new Promise((resolve, reject) => { w6s.location.getLocation({ success: resolve, fail: reject }) }) // 4. 上传本地文件到后端 /app-enf/common/file/upload export const uploadLocalFileToBackend = ({ fileURL, // SDK 返回的本地路径 fileName = 'upload', mimeType = 'image/jpeg', }) => new Promise((resolve, reject) => { const token = $globalStore.useCommon.token || '' // w6s.file.upload 需要完整 URL const api = `${baseUrl}/app-enf/common/file/upload` const server = /^https?:/.test(api) ? api : `${window.location.origin}${api}` const opts = new window.FileUploadOptions() opts.fileKey = 'file' opts.fileName = fileName opts.mimeType = mimeType opts.headers = { 'Authorization': token ? `Bearer ${token}` : '', 'clientid': '9e6eb24cd4e169a98b0d2cd8034d877b', } const fileIns = new w6s.file.upload(opts) fileIns.upload({ fileURL, server, trustAllHosts: false, success: (res) => { // Cordova FileTransfer 返回 res.response 为字符串 try { const parsed = typeof res.response === 'string' ? JSON.parse(res.response) : res.response resolve(parsed) } catch (e) { resolve(res.response) } }, fail: reject, }) }) ``` ### 步骤 2:修改 `NosDetailMaterialsTab.vue` #### 2.1 新增引入 ```js import { appEnv } from '@/config/env' import { takePhotoWithWaterMark, startVideoRecorder, uploadLocalFileToBackend, } from '@/util/w6sMedia' const isProd = computed(() => appEnv === 'prod') const showUploadAction = ref(false) const activeUploadItem = ref(null) const uploadActions = [ { name: '拍照(带水印与经纬度)' }, { name: '录制视频' }, ] ``` #### 2.2 模板改造(替换第 47-64 行) ```html