更新记录

0.5.25(2026-08-24)

  • 修复本地照片烧录与相机里所见不一致:此前烧录只回退到内置第一套版式与接口入参值,相机里选中的版式、编辑过的时间/字段内容、拖过的水印位置全部丢失。现在相机让位给宿主相册前会把会话水印状态(选中版式、字段值、显隐、位置比例)写成一次性快照,烧录方读后即删并按快照还原,Android/iOS 行为一致;无会话快照时(外部直接打开编辑器)维持原行为,公开 API 不变,宿主零适配
  • 修复相机关闭后再次打开报「已有相机任务正在执行」(9013002)只能杀进程:busy 标记原本只在 Activity 结果回调送达时清除,相机/编辑器异常销毁(系统回收等)会让它永久卡住。现在打开前会查原生侧相机/编辑器是否真实存活,不存在则按「用户取消」给上一次调用收尾后继续打开;同时相机 Activity/控制器没交付结果就被销毁时会主动兜底收尾,两道防线双端同构
  • 修复 iOS 端 weather 声明为 let 导致「天气随定位回传覆盖」云打包编译失败(cannot assign to property)

0.5.24(2026-08-14)

  • 新增与地图厂商无关的运行时定位 Provider:宿主按 reason/requestId/sessionId 接收请求,并用统一定位 JSON 回传成功或失败;新增当前会话地点主动更新与相机状态查询 API
  • 新增结构化省、市、区、详细位置展示与持久化开关,兼容旧 gpsWgs84/addressFull/addressArea 和字符串地址;坐标系以 coordinateSystem 为准
  • 外部定位支持超时、过期结果隔离和系统定位兜底;照片、录像、连拍共用定位 loading 与快门门禁,关闭水印或切到无地点模板会立即解除阻塞
  • 补齐 Mock Provider Demo、包内自包含接入文档与 Android/iOS 真机验收矩阵;本版本仅完成自动契约与本地编译验证,设备权限和弱网场景仍需按 TEST.md 真机验收

0.5.21(2026-08-12)

  • 修复 Android 真机接入宿主后「拿到 tempFilePath 却传不了、预览不了」:拍摄/录像/编辑/相册烧录的产出目录从应用内部 cacheDir/data/user/0/...)改为外部应用私有目录 getExternalFilesDir。uni/plus 文件 API(uni.getFileInfouni.uploadFile)与 WebView 在 Android 上读不到内部目录,此前宿主上传会报「文件不存在」、<image> 无法渲染;getExternalFilesDir 无需任何权限、卸载自动清理,异常时回退内部 cache。ZooWatermarkPreviewProvider 预览基准目录同步迁移,content:// 预览行为不变
  • 升级后旧版 cache 目录里的连拍暂存记录不再可见(一次性影响,新拍摄自动重建);公开 API 结构与字段不变,宿主零适配
查看更多

平台兼容性

uni-app(3.99)

Vue2 Vue3 Vue3插件版本 Chrome Safari app-vue app-vue插件版本 app-nvue app-nvue插件版本 Android Android插件版本 iOS iOS插件版本 鸿蒙
0.4.49 - - 0.4.49 0.4.49 0.4.49 12 0.4.49 ×
微信小程序 微信小程序插件版本 支付宝小程序 支付宝小程序插件版本 抖音小程序 抖音小程序插件版本 百度小程序 百度小程序插件版本 快手小程序 快手小程序插件版本 京东小程序 京东小程序插件版本 鸿蒙元服务 鸿蒙元服务插件版本 QQ小程序 QQ小程序插件版本 飞书小程序 飞书小程序插件版本 小红书小程序 小红书小程序插件版本 快应用-华为 快应用-联盟
0.4.49 0.4.49 0.4.49 0.4.49 0.4.49 0.4.49 0.4.49 0.4.49 0.4.49 0.4.49 × ×

uni-app x(3.99)

Chrome Safari Android iOS 鸿蒙 微信小程序
- - - - - -

其他

多语言 暗黑模式 宽屏模式
×

zoo-watermark-camera

自绘相机预览,水印卡片实时叠加、所见即所得;按快门把水印烧进照片像素,落盘即可直接 uni.uploadFile。对标施工现场水印相机。

系统相机拍出来的是一张干净照片,时间、地点、工程名、拍摄人这些「存证信息」都要后期 P 上去——这个插件解决的就是这件事:拍照/录像的当下,水印直接烧进成片。

特性

  • 实时水印预览:水印卡片叠加在取景区上,拖动即所见,烧录位置与预览一致;拖动落点本地记住,下次打开自动恢复
  • 9 套内置版式:工程(安装服务记录 / 工程记录 / 质量复核 / GPS 存证)、时间(大字时刻 / 简洁时刻)、打卡(考勤打卡 / 打卡时刻)、巡检(维修前后),配色跟随主题色
  • 单份 JSON 驱动:宿主传入一份 watermarkDatawatermarkId 精确匹配内置版式,字段给值即显示,不给不渲染
  • 拍照 + 录像双烧录:照片烧录为 JPG,录像输出已烧录水印的 MP4
  • 本地照片烧录:从系统相册或 zoo-media-picker 选已有照片,按当前水印配置自动生成成片
  • 连拍模式:一次进相机连续拍摄、当场剔除废片,确认后一次性输出全部成片;暂存跨模式、跨会话保留
  • 双端原生:Android CameraX / iOS AVFoundation,零第三方图像库依赖,不和宿主已有库打架
  • 可保留原图:设置内开启后回调同时返回 [原始文件, 水印成片]resultType 区分
  • 完整相机能力:闪光灯(关/开/自动/常亮)、前后摄、点按对焦、捏合变焦、0.5x/1x/2x、九宫格、拍摄音效与振动反馈

快速开始

import { openWatermarkCamera } from '@/uni_modules/zoo-watermark-camera'

openWatermarkCamera({
  mode: 'photo',                                        // photo | video
  watermark: { scale: 0.9, opacity: 0.72, position: 'lb' },
  watermarkData: {
    watermarkId: 1,                                     // 匹配「安装服务记录」版式
    watermarkName: '空调深度清洗记录',                   // 覆盖该版式的默认名称
    title: '3 楼外机清洗',
    content: '分体空调深度清洗',
    weather: '晴 26℃',
    location: {
      gpsWgs84: '120.123456,30.123456',
      addressFull: '示例省示例市示例区示例路 88 号示例大厦',
      addressArea: '示例市示例区'
    },
    unitName: '示例服务单位',
    photographer: '张三',
    remark: '外机深度清洗完成',
    themeColor: '#1677FF',
    custom: { '工单号': 'WO-2026-001' },
    allowEditTime: false,
    allowEditLocation: false
  },
  theme: { primaryColor: '#3B82F6' },
  success: (res) => {
    // res.file 始终指向最终成片,可直接上传
    uni.uploadFile({ url: 'https://your.host/upload', filePath: res.file.tempFilePath, name: 'file' })
  },
  fail: (err) => {
    console.log(err.errCode, err.errMsg)
  }
})

仅支持 App(Android / iOS),H5 与小程序不支持。

兼容版本与基座边界

  • 最低 HBuilderX:3.99。Android 最低版本:API 21(Android 5.0);iOS 最低版本:iOS 12。
  • 本插件是 UTS 原生插件,不支持标准基座,必须用自定义基座真机运行或云打包。
  • Android 的 CameraX 是插件声明的 Maven 三方依赖,必须随自定义基座一起构建;新增/升级依赖后必须重新制作基座,并把宿主 manifest.jsonversionName/versionCode 递增,避免复用不含新依赖的旧基座。
  • 若运行日志出现「手机端自定义基座已是最新版本,跳过更新」但插件刚升级过依赖,请重新制作基座;仍被跳过时,卸载手机上的旧 App 后再安装新基座。

水印数据(watermarkData)

watermarkData 是一份固定结构的 JSON。样式只由 watermarkId 精确匹配;watermarkName 只覆盖命中版式的默认名称,不参与样式匹配。已发布的 ID 不会重排或复用,后续新版式只追加新 ID。

内置版式 ID:

ID 版式 分类
1 安装服务记录 工程
2 工程记录 工程
3 质量复核 工程
4 GPS 存证(原始经纬度 + 每秒走动的实时时钟) 工程
5 大字时刻 时间
6 简洁时刻 时间
7 考勤打卡 打卡
8 打卡时刻 打卡
9 维修前后 巡检

字段说明(每套版式按版式合理取值,字段不要求全部渲染,未传或为空不显示):

字段 类型 含义与控制逻辑
watermarkId number 必填。精确匹配内置版式;未命中时不渲染该份动态水印
watermarkName string 覆盖命中版式的默认名称;为空时保留默认名
title string 水印标题;版式预留标题位时才显示,同时喂给工程类版式的工程名称
content string 施工 / 巡检内容;工程与巡检类版式使用
weather string 天气;版式有天气位时显示,并作为相机内当前天气值
location string / object 初始拍摄地点。新接入推荐传下文的结构化对象并显式标注 coordinateSystemgpsWgs84addressFulladdressArea 和普通字符串继续作为旧格式兼容输入。能否在相机内修改由 allowEditLocation 决定
logoUrl string Logo 图片,支持 http(s) 链接与本地路径(file:// / 绝对路径);仅带 Logo 位的版式使用,加载失败回退默认图形
unitName string 单位名称;仅带单位名称位的版式使用
photographer string 拍摄人名称;仅带拍摄人位的版式使用
photographerAvatar string 拍摄人头像,支持 http(s) 链接与本地路径;仅带头像位的版式使用,加载失败回退默认图形
remark string 备注;仅带备注位的版式使用
themeColor string #RRGGBB 主题色;覆盖命中版式的整套配色。非法或为空时保留版式默认色
custom object 自定义扩展字段,键和值均为字符串;作为额外文本行按需取用,空值不渲染
allowEditTime boolean true 时相机内可编辑拍摄时间;false 时显示设备当前时间且锁定
allowEditLocation boolean true 时地点可编辑、定位图标可打开地点面板;false 时锁定

参数(openWatermarkCamera)

参数 类型 说明
mode string 'photo'(默认)或 'video';连拍只能在相机内切入,不作为入参
watermark.scale number 水印卡片缩放,默认 1.0,夹在 [0.5, 2]
watermark.opacity number 整个水印的最终不透明度,夹在 [0.25, 1]
watermark.position string 'lt'/'tc'/'rt'/'lb'/'bc'/'rb',默认 'lb'
watermarkData object 水印数据 JSON,见上一节
location.address string 拍摄地点(宿主预先传入),未传显示「未授权位置」
location.weather string 天气(宿主预先传入)
theme.primaryColor string 水印卡片标题/底条主题色,#RRGGBB
theme.surfaceColor / theme.onSurfaceColor string 相机上下功能区背景色/前景色
defaultTemplateId string 默认选中的模板 id;缺省为内置工程记录
success / fail / complete function 回调

返回

success 回调收到 res.file(最终成片)与可选的 res.files(多文件场景):

res = {
  captureMode: string,        // 确认返回时相机所处模式:'photo' | 'video' | 'burst'
                              // 连拍确认(哪怕只拍 1 张)报 'burst';本地照片烧录报 'photo'
  keepOriginalPhoto: boolean, // 输出时「保留原图」设置是否开启
  keepOriginalVideo: boolean, // 输出时「保留原视频」设置是否开启
  file: { ... },              // 最终成片,结构同下
  files: [ { ... } ]          // 完整结果
}

res.file = {
  tempFilePath: string,      // 沙盒临时文件绝对路径,可直接 uni.uploadFile
  previewUrl?: string,       // 仅预览使用;Android 为受限 content:// 地址
  thumbTempFilePath: string, // 视频为首帧缩略图路径(抽帧失败为空串);图片为空串,图片本身即封面
  fileName: string,
  fileExtension: string,     // 'jpg' | 'mp4'
  size: number,              // 字节
  mediaType: string,         // 'image' | 'video'
  width: number,
  height: number,
  duration: number,          // 毫秒,图片恒为 0
  resultType: string,        // 'watermarked' | 'original'
  usedTemplateId: string,    // 实际生效的模板
  usedFields: UTSJSONObject, // 最终烧录进照片的字段快照
  // 以下 4 项是「拍这张那一刻」的状态快照,连拍途中改过设置也按张记录
  showWatermark: boolean,    // 拍这张时水印开关是否开;成对返回时原图与成片相同
  cameraFacing: string,      // 'back' | 'front';本地照片烧录链路为 ''
  zoomRatio: number,         // 拍摄时的实际变焦倍率;本地照片为 0
  flashMode: string          // 拍摄时设置的闪光模式 'off'|'on'|'auto'|'torch';本地照片为 ''
}
  • 默认只返回最终成片;关闭「展示水印」拍摄时 resultType'original'
  • 相机设置里启用「保留原图 / 保留原视频」后,res.files[原始文件, 水印成片] 返回两项。
  • 连拍确认输出时,res.files 按拍摄先后逐张展开为 [原图1?, 成片1, 原图2?, 成片2, ...]res.file 指向第一张成片。
  • 最终实际烧录的字段以 usedFields 为准。
  • 本地照片烧录链路没有相机:结果项的 cameraFacing / flashMode 为空串、zoomRatio 为 0,showWatermark 按是否实际烧录水印给出,captureMode 恒为 'photo'

运行时定位 Provider

插件提供与地图厂商无关的数据型定位协议。相机先打开,只有“水印已开启、当前模板存在可见地点字段、地点尚未就绪”时才按配置请求定位;插件不认识业务接口、地图 SDK、Key 或 Token。Provider 只接收 reason/requestId/sessionId 三个字符串,不能接收由 UTS 传出的回调函数;宿主完成异步任务后必须按 requestId 调成功或失败 API 回传。

五个公开 API:

API 返回 用途
setWatermarkCameraLocationProvider(provider) void 注册或以 null 清除外部 Provider;Provider 参数为 (reason, requestId, sessionId)
resolveWatermarkCameraLocation(requestId, location) boolean 回传统一定位对象;只有命中当前有效请求且数据有效时返回 true
rejectWatermarkCameraLocation(requestId, message) boolean 回传可读失败原因;过期、已关闭或不存在的请求返回 false
updateWatermarkCameraLocation(sessionId, location) boolean 宿主主动更新当前会话地点;会话不匹配返回 false
getWatermarkCameraState() ZooWatermarkCameraState 查询当前会话、水印、模板、定位状态和是否可拍摄

可直接复制的外部 Provider 接入:

import {
  setWatermarkCameraLocationProvider,
  resolveWatermarkCameraLocation,
  rejectWatermarkCameraLocation
} from '@/uni_modules/zoo-watermark-camera'

setWatermarkCameraLocationProvider((reason, requestId, sessionId) => {
  console.log('[watermark-location-provider] start', { reason, requestId, sessionId })
  yourLocationService().then(response => {
    // code/message/data 是宿主业务协议,必须由宿主先校验、解包并转换;插件只接收下节的统一对象。
    if (response.code !== 0) throw new Error(response.message || '定位失败')
    const location = normalizeYourLocation(response.data)
    resolveWatermarkCameraLocation(requestId, location)
  }).catch(error => {
    rejectWatermarkCameraLocation(requestId, error?.message || '定位失败')
  })
})

reason 可能为 openretrywatermark-enabledtemplate-changed。同一会话同一时刻最多一个外部请求;新请求、关闭水印、切换到无地点模板或关闭相机都会让旧请求过期,迟到结果返回 false 且不会污染下一次会话。

主动推送前先查询会话:

import { getWatermarkCameraState, updateWatermarkCameraLocation } from '@/uni_modules/zoo-watermark-camera'

const state = getWatermarkCameraState()
if (state.isOpen) updateWatermarkCameraLocation(state.sessionId, cachedLocation)

关闭态固定返回空 sessionIdisOpen: falsecanCapture: false。打开时状态结构为:

type ZooWatermarkCameraState = {
  isOpen: boolean
  sessionId: string
  captureMode: string             // photo | video | burst
  showWatermark: boolean
  selectedTemplateId: string
  requiresLocation: boolean       // 当前模板是否含可见地点字段,不因已定位成功而改变
  locationStatus: string          // idle | loading | success | error
  canCapture: boolean
}

统一定位 JSON

type ZooWatermarkCameraLocation = {
  longitude?: number
  latitude?: number
  coordinateSystem?: 'gcj02' | 'wgs84'
  level?: string
  province?: string
  city?: string
  district?: string
  detail?: string
  addressFull?: string
  addressArea?: string
  township?: string
  street?: string
  streetNumber?: string
  placeName?: string
  placeType?: string
  placeDistance?: number
  source?: string
  gpsWgs84?: string
  weather?: string
}

结构化示例(全部是占位数据):

{
  "longitude": 120.123456,
  "latitude": 30.123456,
  "coordinateSystem": "gcj02",
  "level": "building",
  "province": "示例省",
  "city": "示例市",
  "district": "示例区",
  "detail": "示例街道示例路88号示例大厦",
  "addressFull": "示例省示例市示例区示例街道示例路88号示例大厦",
  "addressArea": "示例省示例市示例区",
  "township": "示例街道",
  "street": "示例路",
  "streetNumber": "88号",
  "placeName": "示例大厦",
  "placeType": "aoi",
  "placeDistance": 0,
  "source": "host-provider",
  "gpsWgs84": "120.123456,30.123456",
  "weather": "晴 28℃"
}

weather 是可选的天气文案(如「晴 28℃」),宿主逆地理时顺手带回即可,插件会用它刷新 valueType=weather 的水印字段。不传或传空串表示「这次没拿到天气」,插件保留原有天气值,不会把水印上已有的天气抹空,所以天气查询失败时直接省略该字段就行,不要传空串以外的占位文案。

gpsWgs84 是旧模板的兼容字段,名称不能作为坐标系事实;新接入必须显式提供 coordinateSystemaddressFulladdressArea 也是旧对象兼容字段,普通字符串地址仍等价于 detail/addressFull。新结构优先,缺失项才用旧字段兜底。坐标小数位不等于精度:不要用 toFixed(13/14) 补零伪造精度;调用第三方逆地理服务时按其约束截取参数,回传对象仍应保留设备提供的原始有效数值。

地点面板的「展示省 / 展示市 / 展示区 / 展示详细位置」分别控制 province/city/district/detail,双端会持久化选择,并统一影响预览、编辑与最终成片。手动地址无法可靠拆分时整串放入 detail/addressFull,插件不会猜行政区划。

location 配置与降级

{
  "location": {
    "provider": "external",
    "autoRequest": true,
    "allowSystemFallback": true,
    "blockCaptureWhileLoading": true,
    "requireLocationBeforeCapture": true,
    "requestTimeoutMs": 15000,
    "loadingText": "定位中..."
  }
}
配置 默认值 说明
provider system external 调宿主 Provider;system 用设备系统定位;none 只保留手动输入
autoRequest false 相机就绪、打开水印或切换地点模板且地点未就绪时自动请求
allowSystemFallback true 外部 Provider 场景在地点面板显示“使用系统定位”兜底;不自动偷偷切换
blockCaptureWhileLoading true 当前模板需要地点且定位中时禁用快门
requireLocationBeforeCapture false 需要地点但仍无成功地址时是否持续禁用快门;旧接入默认不强制
requestTimeoutMs 15000 外部请求超时,配置会夹在 3000..60000 毫秒
loadingText 定位中... 地点字段和快门附近的等待文案,空字符串回落默认值

photo / video / burst(照片 / 录像 / 连拍)共用同一定位快门门禁:定位阻塞时快门不响应,中央显示 loading;关闭按钮、模式、水印、模板和地点入口仍可操作。外部失败或超时会停止 loading;若 requireLocationBeforeCapture=true 且地点仍为空,快门继续禁用,用户可重试、点击“使用系统定位”兜底或手动输入。关闭水印或切到无地点模板会立即解除门禁。

系统定位不会依赖外部 Provider。provider=external && allowSystemFallback=true 时地点面板标题右侧显示“使用系统定位”;权限缺失、拒绝、系统服务关闭或逆地理失败时不关闭面板,仍允许手动输入。provider=system 时“重新定位当前地址”本身就是系统定位,不重复显示兜底按钮;provider=none 时两个定位入口都隐藏。

错误码

errCode 含义
9013001 当前页面无法打开水印相机
9013002 当前已有相机任务正在执行
9013003 用户取消了拍摄
9013004 未获取到有效的拍摄结果
9013005 相机权限被拒绝(含 Android 宿主未声明 CAMERA)
9013006 宿主未声明相机用途描述(iOS 缺 NSCameraUsageDescription)
9013007 拍摄结果落盘失败
9013008 相机初始化失败
9013009 图片处理失败

JSON 配置中心

插件根目录提供 zoo-watermark-camera.config.example.json,是带说明字段的宿主配置模板。复制到宿主 src/config/zoo-watermark-camera.config.json 后,在 App 启动时调用:

import cameraConfig from '@/config/zoo-watermark-camera.config.json'
import { configureWatermarkCamera } from '@/uni_modules/zoo-watermark-camera'

configureWatermarkCamera(cameraConfig)

主要配置项:

配置 默认值 说明
debugLogEnabled true 输出配置解析、相册通道、录像等关键节点日志,真机报错时复制 [zoo-watermark-camera] 日志
useZooMediaPicker 本地照片通道开关:true 使用已注册的 zoo-media-picker,false 直接用系统相册
localPhoto.zooMediaPickerMode 'system' zoo-media-picker 的调用模式,推荐 'custom'
localPhoto.nativeFallback true 自定义相册失败时是否回落系统相册
defaults.themeColor '#1677FF' 主题色初始值
defaults.showWatermark true 是否默认展示水印
defaults.watermarkScale 0.7 水印大小初始值,范围 0.5-2
defaults.watermarkOpacity 0.65 水印透明度初始值,范围 0.25-1
defaults.watermarkPosition 'lb' 水印初始位置
defaults.cameraFacing 'back' 默认前后摄
defaults.zoomRatio 1 默认倍率
defaults.flashMode 'off' 默认闪光灯:off/on/auto/torch
defaults.burstMaxCount 10 连拍暂存上限,范围 1-30

defaults.*首次初始值:用户在相机内手动调整后会保存到本地并优先使用;要让新的配置初始值重新生效,需清除应用数据或在相机设置中改回。

本地照片与 zoo-media-picker 适配器

本地照片默认走 Android/iOS 系统相册;宿主安装了 zoo-media-picker 时可换成它的自绘相册。UTS 插件相互调用必须静态 import 插件根目录@/uni_modules/zoo-media-picker),不支持相对路径,也不能按配置在运行时动态加载,所以配置里只有开关、没有「插件路径」字段。

接入步骤:

  1. 在宿主项目 uni_modules/ 中导入 zoo-media-picker。
  2. 配置顶层 useZooMediaPicker 设为 true
  3. App 启动时注册适配器。注意适配器只收「模式 + 请求号」两个数据参数:UTS 调 JS 时携带的函数实参经桥接后在 JS 侧不可调用(Android 实测报 success is not a function),所以选图结果必须调插件导出的 resolveLocalPhotoResult / rejectLocalPhotoResult 按请求号回传:
import cameraConfig from '@/config/zoo-watermark-camera.config.json'
import { configureWatermarkCamera, resolveLocalPhotoResult, rejectLocalPhotoResult } from '@/uni_modules/zoo-watermark-camera'
import { chooseMediaPro } from '@/uni_modules/zoo-media-picker'

configureWatermarkCamera(cameraConfig, (mode, requestId) => {
  chooseMediaPro({ count: 1, mediaType: ['image'], openType: mode,
    success: res => resolveLocalPhotoResult(requestId, { tempFilePath: res.tempFiles[0]?.tempFilePath || '' }),
    fail: err => rejectLocalPhotoResult(requestId, err?.errMsg || '选择图片失败')
  })
})
  • resolveLocalPhotoResult(requestId, file):回传选图成功结果;requestId 必须原样使用,file 只需提供非空 tempFilePath
  • rejectLocalPhotoResult(requestId, message):回传选图失败或取消原因;未知、过期或已完成的请求号会被忽略。

开关开启但未注册适配器、或调用失败时,双端会先弹出配置警告,再自动回落系统相册,本次选择流程不中断。

注意:本地照片链仅支持单张图片(adapter 必须保持 count: 1mediaType: ['image'])。选取本地视频后烧录输出 MP4 当前不支持;相机内直接录制的视频支持水印烧录。本地照片遵循相机页当时的「展示水印」开关:开启时生成水印 JPG,关闭时输出原图副本。

连拍模式

底部模式条第三项「连拍」进入,点回「照片」或「视频」退出。进入后:

  • 左下角的「本地照片」换成连拍暂存入口(错位堆叠缩略卡 + n/上限 计数),点开可逐张删除。
  • 快门上方出现「清空连拍」和「确认(n)」。清空需二次确认,临时文件一并删除;确认把全部照片按拍摄先后一次性回传并关闭相机。
  • 暂存上限由 defaults.burstMaxCount 控制(默认 10,范围 1-30),达到上限后快门停用。
  • 暂存是本地文件:切模式或直接关闭相机都不丢弃。再次进入连拍时若有未处理暂存,会弹窗询问「继续连拍」或「清空照片」,同一次相机会话只问一次。

每张连拍照片按拍摄那一刻的水印状态独立处理:

拍这张时的水印状态 设置里「保留原图」 该张产出
已开启水印 2 个文件:原图 + 水印成片
已开启水印 1 个文件:水印成片
已关闭水印 开或关 1 个文件:成片本身就是原图,不再重复留一份

所以连拍途中可随时开关水印,res.files 按拍摄先后逐张展开,每项的 resultType 区分 original / watermarkedusedTemplateIdusedFields 记录该张实际生效的模板与字段,showWatermark / cameraFacing / zoomRatio / flashMode 记录拍这张那一刻的相机状态。

宿主权限声明

插件不内置任何敏感权限,需宿主 App 自行声明,插件运行时申请;缺失时优雅报错或降级,不会崩溃:

  • Android 拍照manifest.json → App 权限配置加入 android.permission.CAMERA。未声明时调用直接返回 9013005
  • Android 带声音录像:再声明 android.permission.RECORD_AUDIO;拒绝时自动录制静音视频。
  • Android 定位:相机内「重新定位」需 ACCESS_FINE_LOCATION / ACCESS_COARSE_LOCATION;未声明或拒绝后仍可手动输入地址。
  • Android Logo/头像:插件自身声明普通 INTERNET 权限用于加载 logoUrl / photographerAvatar,不触发运行时授权弹窗。
  • iOSNSCameraUsageDescription(必需);带声音录像加 NSMicrophoneUsageDescription;相机内重新定位加 NSLocationWhenInUseUsageDescription;iOS 13 及以下使用原生相册兜底还需 NSPhotoLibraryUsageDescription

FAQ

Q:标准基座能运行吗? 不能。本插件是 UTS 原生插件,必须制作自定义基座或云打包。Android 的 CameraX 依赖随基座构建,升级插件后如果行为没变,先重打基座并递增 versionCode,必要时卸载手机上的旧 App 再装。

Q:改了 watermarkName 为什么版式没变? 版式只由 watermarkId 匹配。watermarkName 只是覆盖显示名称,传了 ID=1 就永远是「安装服务记录」的版式,改名不换样式。

Q:location 传字符串还是对象? 都行。字符串等价于只提供完整地址;新接入推荐传结构化对象(longitude / latitude / coordinateSystem 与分段地址),旧对象(gpsWgs84 / addressFull / addressArea)继续兼容。GPS 存证等版式需要原始经纬度时只有对象形式能满足。

Q:不传 location 会怎样? 地点位显示「未授权位置」。allowEditLocation: true 时用户可在相机内手动输入或用系统定位反向解析地址。

Q:「保留原图」开启后回调怎么解析?res.files[原始文件, 水印成片] 两项,每项 resultType 分别为 original / watermarkedres.file 始终指向最终成片,兼容单张老调用。

Q:本地照片能选视频来烧录吗? 不能。本地照片链只支持单张图片;要水印视频请在相机内直接录制,输出就是已烧录的 MP4。

Q:配置里改了 watermarkScale / watermarkOpacity 为什么不生效? 这两个是首次初始值。用户在相机内手动调过大小/透明度后,本地值优先。清除应用数据或在相机设置中改回所需数值即可恢复配置生效。

Q:Logo / 头像图片加载失败会怎样? 自动回退为该版式的默认图形,不影响拍摄,也不走 fail。

Q:连拍关了相机,暂存的照片还在吗? 在。暂存是本地文件,跨模式、跨会话保留;下次进连拍会询问「继续连拍」或「清空照片」。

授权说明

本插件按 uni appid + 包名 粒度授权,一次购买绑定一个应用。更换 appid 或包名需重新购买。

隐私、权限声明

1. 本插件需要申请的系统权限列表:

相机权限由宿主 App 自行声明,插件运行时申请:Android 需 CAMERA,带声音录像需 RECORD_AUDIO,设备定位需 ACCESS_FINE_LOCATION/ACCESS_COARSE_LOCATION;iOS 需 NSCameraUsageDescription,带声音录像需 NSMicrophoneUsageDescription,设备定位需 NSLocationWhenInUseUsageDescription,iOS 13 及以下原生相册兜底需 NSPhotoLibraryUsageDescription。定位权限缺失时可手动输入地址,其他能力缺失时插件优雅提示或降级。

2. 本插件采集的数据、发送的服务器地址、以及数据用途说明:

插件在设备本地处理相机画面、照片视频与位置信息:位置来自宿主注册的定位 Provider 或用户授权后的系统定位,用户确认后可作为水印内容写入输出媒体。插件自身不连接任何业务服务器,不上传、不留存上述数据,结果仅写入宿主 App 自身临时目录;是否上传以及宿主 Provider 自身的网络行为由宿主应用负责。

3. 本插件是否包含广告,如包含需详细说明广告表达方式、展示频率: