水印相机插件,支持拍照和录制视频,自动添加水印信息(位置、公司、部门、姓名、时间等),支持前后摄像头切换,可用于打卡、考勤、外勤等场景。
- ✅ 支持拍照和录制视频
- ✅ 自动添加水印信息(位置、公司、部门、姓名、时间等)
- ✅ 支持前后摄像头切换
- ✅ 支持设置视频最大录制时长
- ✅ 支持预览和删除已拍摄的照片/视频
- ✅ 支持清除缓存
- ✅ Android(最低版本:23)
- ✅ iOS(最低版本:13.0)
- 在 uni-app 项目中,通过 HBuilderX 导入插件
- 或手动将
uni_modules/watermark_camera目录复制到项目的uni_modules目录下
使用前请确保在 manifest.json 中配置了以下权限:
{
"app-plus": {
"distribute": {
"android": {
"permissions": [
"<uses-permission android:name=\"android.permission.CAMERA\"/>",
"<uses-permission android:name=\"android.permission.RECORD_AUDIO\"/>",
"<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>",
"<uses-feature android:name=\"android.hardware.camera\"/>",
"<uses-feature android:name=\"android.hardware.camera.autofocus\"/>"
]
}
}
}
}在 manifest.json 的 app-plus -> distribute -> ios 中配置:
{
"NSCameraUsageDescription": "需要使用相机进行拍照和录制视频",
"NSMicrophoneUsageDescription": "需要录制视频时使用麦克风"
}重要: 在使用其他功能之前,必须先初始化SDK并设置许可证密钥。
在 App.vue 的 onLaunch 生命周期中自动初始化SDK:
<script>
import { init } from "@/uni_modules/watermark_camera"
export default {
onLaunch: function() {
// #ifdef APP-PLUS
// 仅在App平台初始化
// #ifdef APP-PLUS-ANDROID
// Android 平台的 license(注意:Android 和 iOS 需要使用不同的 license)
const androidLicenseKey = `-----BEGIN RLICENSE-----
version: 1
issuer: R
platforms: android
identifiers: your.android.bundle.id
description: 水印相机许可证(仅限Android平台)
tps_limit: -1
not_before: 1766897395
not_after: 1769489395
not_before_utc: 2025-12-28T04:49:55Z
not_after_utc: 2026-01-27T04:49:55Z
features: watermark_camera
device_ids:
aes_keys: 0,9,68
xor_key: your-xor-key
blob:
your-license-blob-here
-----END RLICENSE-----
`;
init(androidLicenseKey).then((result) => {
if (result.code === 0) {
console.log('Android SDK初始化成功');
} else {
console.error('Android SDK初始化失败:', result.msg);
}
}).catch((error) => {
console.error('Android SDK初始化异常:', error);
});
// #endif
// #ifdef APP-PLUS-IOS
// iOS 平台的 license(注意:Android 和 iOS 需要使用不同的 license)
const iosLicenseKey = `-----BEGIN RLICENSE-----
version: 1
issuer: R
platforms: ios
identifiers: com.your.bundle.id
description: 水印相机许可证(仅限iOS平台)
tps_limit: -1
not_before: 1766927103
not_after: 1769519103
not_before_utc: 2025-12-28T13:05:03Z
not_after_utc: 2026-01-27T13:05:03Z
features: watermark_camera
device_ids:
blob:
your-license-blob-here
-----END RLICENSE-----
`;
init(iosLicenseKey).then((result) => {
if (result.code === 0) {
console.log('iOS SDK初始化成功');
} else {
console.error('iOS SDK初始化失败:', result.msg);
}
}).catch((error) => {
console.error('iOS SDK初始化异常:', error);
});
// #endif
// #endif
}
}
</script>注意事项:
- 必须在应用启动时(
App.vue的onLaunch)初始化SDK - 重要:Android 和 iOS 必须使用不同的 license 密钥,因为 license 中包含平台标识(
platforms: android或platforms: ios) - 许可证密钥必须包含
watermark_camera功能授权 - Android license 的
identifiers字段应填写 Android 应用的包名(bundle ID) - iOS license 的
identifiers字段应填写 iOS 应用的 Bundle Identifier - 如果许可证验证失败,会返回错误码
-1和错误信息 - 初始化失败时,后续调用
openMediaCamera也会失败(iOS 会返回LICENSE_NOT_ACTIVATED或LICENSE_INVALID错误)
import { init, openMediaCamera, clearCache } from "@/uni_modules/watermark_camera"注意: init 方法已在 App.vue 中调用,通常不需要在其他地方再次调用。
打开水印相机界面,支持拍照和录制视频。
函数签名:
openMediaCamera(optionsString: string): Promise<MediaResult>参数说明:
optionsString (string): JSON字符串格式的配置对象,包含以下字段:
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| location | string | 否 | 位置信息,将显示在水印中 |
| company | string | 否 | 公司名称,将显示在水印中 |
| department | string | 否 | 部门名称,将显示在水印中 |
| name | string | 否 | 姓名,将显示在水印中 |
| videoMaxDuration | number | 否 | 视频最大录制时长(秒),默认30秒 |
返回值:
返回 Promise<MediaResult>,包含以下字段:
| 字段名 | 类型 | 说明 |
|---|---|---|
| code | string | 状态码,SUCCESS 表示成功,其他表示失败 |
| msg | string | 结果描述 |
| photoPaths | string[] | 照片路径数组 |
| videoPaths | string[] | 视频路径数组 |
状态码说明:
| 状态码 | 说明 |
|---|---|
| SUCCESS | 操作成功 |
| CANCEL | 用户取消操作 |
| EXCEPTION | 执行异常 |
| CAMERA_PERMISSION_EXCEPTION | 相机权限未授予 |
| EXPIRED | SDK已过期 |
| LICENSE_NOT_ACTIVATED | License未激活(iOS) |
| LICENSE_INVALID | License无效(iOS) |
使用示例:
// 注意:SDK已在App.vue中自动初始化,这里直接使用即可
const options = {
location: "北京市朝阳区",
company: "示例公司",
department: "技术部",
name: "张三",
videoMaxDuration: 30
};
const result = await openMediaCamera(JSON.stringify(options));
if (result.code === "SUCCESS") {
console.log("照片路径:", result.photoPaths);
console.log("视频路径:", result.videoPaths);
} else {
console.error("操作失败:", result.msg);
}清除插件生成的照片、视频、日志等缓存文件。
函数签名:
clearCache(): Promise<ClearCacheResult>返回值:
返回 Promise<ClearCacheResult>,包含以下字段:
| 字段名 | 类型 | 说明 |
|---|---|---|
| code | number | 状态码,0 表示成功,-1 表示失败 |
| msg | string | 结果描述 |
使用示例:
const result = await clearCache();
if (result.code === 0) {
console.log("缓存清除成功");
} else {
console.error("缓存清除失败:", result.msg);
}<template>
<view class="container">
<view class="form">
<input v-model="location" placeholder="位置信息" />
<input v-model="company" placeholder="公司名称" />
<input v-model="department" placeholder="部门名称" />
<input v-model="name" placeholder="姓名" />
<input v-model.number="videoMaxDuration" type="number" placeholder="视频最大时长(秒)" />
</view>
<button @click="openCamera">打开水印相机</button>
<button @click="clearCacheData">清除缓存</button>
<view v-if="result" class="result">
<text>状态:{{ result.code }}</text>
<text>消息:{{ result.msg }}</text>
<text v-if="result.photoPaths && result.photoPaths.length > 0">
照片数量:{{ result.photoPaths.length }}
</text>
<text v-if="result.videoPaths && result.videoPaths.length > 0">
视频数量:{{ result.videoPaths.length }}
</text>
</view>
</view>
</template>
<script lang="ts">
import { openMediaCamera, clearCache } from "@/uni_modules/watermark_camera"
export default {
data() {
return {
location: "北京市朝阳区",
company: "示例公司",
department: "技术部",
name: "张三",
videoMaxDuration: 30,
result: null as any
}
},
methods: {
async openCamera() {
try {
const options = {
location: this.location,
company: this.company,
department: this.department,
name: this.name,
videoMaxDuration: this.videoMaxDuration
};
const result = await openMediaCamera(JSON.stringify(options));
this.result = result;
if (result.code === "SUCCESS") {
uni.showToast({
title: "操作成功",
icon: "success"
});
// 处理照片和视频
if (result.photoPaths && result.photoPaths.length > 0) {
console.log("照片路径:", result.photoPaths);
// 可以在这里上传照片到服务器
}
if (result.videoPaths && result.videoPaths.length > 0) {
console.log("视频路径:", result.videoPaths);
// 可以在这里上传视频到服务器
}
} else if (result.code === "CANCEL") {
uni.showToast({
title: "用户取消",
icon: "none"
});
} else {
uni.showToast({
title: result.msg || "操作失败",
icon: "none"
});
}
} catch (error) {
console.error("打开相机失败:", error);
uni.showToast({
title: "打开相机失败",
icon: "none"
});
}
},
async clearCacheData() {
try {
const result = await clearCache();
if (result.code === 0) {
uni.showToast({
title: "缓存清除成功",
icon: "success"
});
} else {
uni.showToast({
title: "缓存清除失败",
icon: "none"
});
}
} catch (error) {
console.error("清除缓存失败:", error);
uni.showToast({
title: "清除缓存失败",
icon: "none"
});
}
}
}
}
</script>
<style>
.container {
padding: 20px;
}
.form {
margin-bottom: 20px;
}
.form input {
width: 100%;
padding: 10px;
margin-bottom: 10px;
border: 1px solid #ddd;
border-radius: 4px;
}
button {
width: 100%;
padding: 12px;
margin-bottom: 10px;
background-color: #007aff;
color: white;
border: none;
border-radius: 4px;
}
.result {
margin-top: 20px;
padding: 10px;
background-color: #f5f5f5;
border-radius: 4px;
}
.result text {
display: block;
margin-bottom: 5px;
}
</style>- 拍照:点击快门按钮进行拍照
- 录制视频:长按快门按钮开始录制,松开结束录制
- 切换摄像头:点击切换按钮切换前后摄像头
- 预览:点击缩略图可以预览照片或视频
- 删除:点击缩略图右上角的删除按钮可以删除已拍摄的照片或视频
- 完成:点击完成按钮返回结果
水印会自动添加到拍摄的照片上,包含以下信息:
- 标题:"外出打卡"
- 时间:当前时间(HH:mm:ss)
- 日期:当前日期(yyyy-MM-dd)
- 位置:传入的位置信息
- 公司-部门-姓名:传入的公司、部门、姓名信息
- 声明:"本人办公已验证身份信息真实性"
- 照片和视频存储在应用的私有目录中
- 照片格式:JPG
- 视频格式:MP4
- 文件命名:
esand_时间戳.扩展名
- 权限申请:首次使用需要授予相机和麦克风权限
- 文件路径:返回的文件路径是本地路径,需要上传到服务器时请使用
uni.uploadFile等方法 - 视频时长:视频最大录制时长受
videoMaxDuration参数限制,达到最大时长会自动停止录制 - 缓存清理:定期清理缓存可以释放存储空间
A: 返回的文件路径是应用的私有目录路径,如果需要上传到服务器或分享,请使用 uni-app 的文件上传 API。
A: 可以使用 uni.uploadFile API:
// 上传照片
for (const photoPath of result.photoPaths) {
uni.uploadFile({
url: 'https://your-server.com/upload',
filePath: photoPath,
name: 'file',
success: (res) => {
console.log('上传成功', res);
}
});
}A: 当前版本的水印样式是固定的,但可以通过传入不同的参数来改变水印内容(位置、公司、部门、姓名)。
A: 当前版本支持 Android(最低版本 23)和 iOS(最低版本 13.0)。
A: iOS 平台的初始化方式与 Android 完全一致,在 App.vue 的 onLaunch 中使用条件编译分别调用 init(licenseKey) 即可。重要:Android 和 iOS 必须使用不同的 license 密钥,因为 license 中包含平台标识。请确保:
- Android license 的
platforms字段为android - iOS license 的
platforms字段为ios - 两个 license 的
identifiers字段分别对应各自的包名/Bundle ID
详见 changelog.md
MIT License
如有问题或建议,请通过以下方式联系:
- 微信:esand_info
- QQ:3626921591
- 电话:+8613691664797
- 邮箱:reid.li@foxmail.com
- Telegram:@R90000000
- 微信二维码:
扫描上方二维码添加微信,或直接搜索微信号:esand_info
