更新记录
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.getFileInfo、uni.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 驱动:宿主传入一份
watermarkData,watermarkId精确匹配内置版式,字段给值即显示,不给不渲染 - 拍照 + 录像双烧录:照片烧录为 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.json的versionName/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 | 初始拍摄地点。新接入推荐传下文的结构化对象并显式标注 coordinateSystem;gpsWgs84、addressFull、addressArea 和普通字符串继续作为旧格式兼容输入。能否在相机内修改由 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 可能为 open、retry、watermark-enabled、template-changed。同一会话同一时刻最多一个外部请求;新请求、关闭水印、切换到无地点模板或关闭相机都会让旧请求过期,迟到结果返回 false 且不会污染下一次会话。
主动推送前先查询会话:
import { getWatermarkCameraState, updateWatermarkCameraLocation } from '@/uni_modules/zoo-watermark-camera'
const state = getWatermarkCameraState()
if (state.isOpen) updateWatermarkCameraLocation(state.sessionId, cachedLocation)
关闭态固定返回空 sessionId、isOpen: false、canCapture: 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 是旧模板的兼容字段,名称不能作为坐标系事实;新接入必须显式提供 coordinateSystem。addressFull 与 addressArea 也是旧对象兼容字段,普通字符串地址仍等价于 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),不支持相对路径,也不能按配置在运行时动态加载,所以配置里只有开关、没有「插件路径」字段。
接入步骤:
- 在宿主项目
uni_modules/中导入 zoo-media-picker。 - 配置顶层
useZooMediaPicker设为true。 - 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: 1、mediaType: ['image'])。选取本地视频后烧录输出 MP4 当前不支持;相机内直接录制的视频支持水印烧录。本地照片遵循相机页当时的「展示水印」开关:开启时生成水印 JPG,关闭时输出原图副本。
连拍模式
底部模式条第三项「连拍」进入,点回「照片」或「视频」退出。进入后:
- 左下角的「本地照片」换成连拍暂存入口(错位堆叠缩略卡 +
n/上限计数),点开可逐张删除。 - 快门上方出现「清空连拍」和「确认(n)」。清空需二次确认,临时文件一并删除;确认把全部照片按拍摄先后一次性回传并关闭相机。
- 暂存上限由
defaults.burstMaxCount控制(默认 10,范围 1-30),达到上限后快门停用。 - 暂存是本地文件:切模式或直接关闭相机都不丢弃。再次进入连拍时若有未处理暂存,会弹窗询问「继续连拍」或「清空照片」,同一次相机会话只问一次。
每张连拍照片按拍摄那一刻的水印状态独立处理:
| 拍这张时的水印状态 | 设置里「保留原图」 | 该张产出 |
|---|---|---|
| 已开启水印 | 开 | 2 个文件:原图 + 水印成片 |
| 已开启水印 | 关 | 1 个文件:水印成片 |
| 已关闭水印 | 开或关 | 1 个文件:成片本身就是原图,不再重复留一份 |
所以连拍途中可随时开关水印,res.files 按拍摄先后逐张展开,每项的 resultType 区分 original / watermarked,usedTemplateId 与 usedFields 记录该张实际生效的模板与字段,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,不触发运行时授权弹窗。 - iOS:
NSCameraUsageDescription(必需);带声音录像加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 / watermarked;res.file 始终指向最终成片,兼容单张老调用。
Q:本地照片能选视频来烧录吗? 不能。本地照片链只支持单张图片;要水印视频请在相机内直接录制,输出就是已烧录的 MP4。
Q:配置里改了 watermarkScale / watermarkOpacity 为什么不生效?
这两个是首次初始值。用户在相机内手动调过大小/透明度后,本地值优先。清除应用数据或在相机设置中改回所需数值即可恢复配置生效。
Q:Logo / 头像图片加载失败会怎样? 自动回退为该版式的默认图形,不影响拍摄,也不走 fail。
Q:连拍关了相机,暂存的照片还在吗? 在。暂存是本地文件,跨模式、跨会话保留;下次进连拍会询问「继续连拍」或「清空照片」。
授权说明
本插件按 uni appid + 包名 粒度授权,一次购买绑定一个应用。更换 appid 或包名需重新购买。

收藏人数:
https://gitee.com/harveyzoo/dcloud-uts-zoo-watermark-camera
购买源码授权版(
试用
使用 HBuilderX 导入示例项目
赞赏(0)
下载 26
赞赏 0
下载 12617297
赞赏 1949
赞赏
京公网安备:11010802035340号