更新记录
1.0.0(2026-10-28)
面向 uni-app x Android 的双摄合拍插件,支持 VDOM 与 Vapor(蒸汽模式)。插件 ID 为 lf-dual-camera,提供标准组件、拍摄 API 和完整 Demo。
双摄取景与画面布局
- 支持 Android 内置前置与后置摄像头并发取景,将两个视角合成为一张预览、照片或视频。
- 提供画中画、上下分屏、左右分屏三种布局,以及主次摄像头互换。
- 支持小窗位置与大小设置、手势拖动、小窗圆角和前摄镜像。
- 支持 0° / 90° / 180° / 270° 额外画面旋转;前后摄方向、镜像与裁剪统一参与合成。
- 预览在组件区域内等比完整显示,合成画面各区域采用等比裁剪;屏幕比例与输出比例不同时可以留边,保持物体比例。
- 支持文字水印和时间水印,随合成画面写入照片与视频。
合成照片与视频
- 提供当前合成画面的 JPEG 照片拍摄,稳定录制期间也可拍照。
- 提供 H.264 MP4 录像;有声模式包含 AAC 麦克风音轨,无声模式不采集麦克风。
- 支持 720p / 1080p、横屏 / 竖屏输出,默认竖屏 720×1280,目标帧率为 30fps。
- 1080p 按相机输入、并发会话与编码器能力开放,具体可用画质以设备检测和打开结果为准。
- 预览、照片和视频共用同一套合成布局,保持主次画面、镜像、水印与布局设置一致。
- 视频停止时等待 MP4 封装完成后返回文件结果,重复停止请求共享完成结果。
uni-app x 接入与组件能力
- 提供采用
<script setup lang="uts">的标准组件,支持 VDOM 与蒸汽模式接入。 - 通过
style/class或父容器宽高控制预览区域,通过pipRect控制小窗,通过profile/orientation控制导出尺寸。 - 提供
ready、相机状态、录制状态、录像完成、小窗拖动与错误事件,业务页面可据此管理交互状态。 - 提供设备能力检测、打开与关闭相机、拍照、开始与停止录像、缩放、对焦、补光和相册保存 API。
- API 使用一次性的
success、fail、complete回调,支持按错误码与消息处理操作结果。 - 提供显式前摄 / 后摄单摄模式;双摄不可用时,由业务或使用者选择拍摄方式。
全屏取景 Demo 与照片保存查看
- 提供全屏取景容器,以及悬浮的顶部信息、设置入口和底部拍摄操作栏。
- 提供画中画 / 分屏切换、照片 / 录像切换、主次互换、缩放、补光、声音开关和拍摄设置面板。
- 提供图形快门、最近作品入口和操作状态提示;页面控件满足蒸汽模式的组件结构要求。
- 拍照或录像结束后自动保存到系统相册,显示保存中、保存成功或保存失败状态。
- 通过快门左侧“最近作品”查看本次页面内最近拍摄的照片或视频。
- 作品页展示输出尺寸、文件大小、录像时长、声音状态和相册保存状态,支持预览重试、复制文件路径及相册保存重试。
- 操作栏、快门、录制计时和状态标签不写入照片或视频,文字与时间水印按设置写入。
相机控制与页面生命周期
- 提供点按对焦 / 测光、指定摄像头缩放和后摄持续补光,具体能力及范围以设备返回结果为准。
- 录制期间可调整布局、小窗、主次画面、镜像、水印、缩放、对焦和补光。
- 拍摄模式、画质、输出方向、额外旋转和声音设置在停止录制后调整;启动与收尾阶段通过状态控制拍摄操作。
- 页面隐藏、返回及原生视图离开窗口时进行录像收尾与相机关闭,组件卸载时释放原生会话。
- 支持初始化完成前退出页面的资源清理,返回拍摄页后由使用者显式开启预览。
文件保存与权限
- 拍摄 API 生成应用私有文件,业务可通过
saveToAlbum独立保存到系统相册;Demo 自动完成该步骤。 - 照片相册目录为
Pictures/LingfuCamera,视频相册目录为Movies/LingfuCamera,相册应用通常显示为LingfuCamera相簿。 - 相册保存成功返回媒体
uri,path保持为应用私有文件路径;相册写入失败时保留私有文件供查看或重试。 - Demo 按文件路径处理录像完成回调,避免同一文件被重复保存到相册。
- 打开相机时申请
CAMERA,开始有声录像时申请RECORD_AUDIO;麦克风未授权时可继续预览,由使用者明确选择是否使用无声录像。 - Android 6–9 保存相册时申请
WRITE_EXTERNAL_STORAGE;Android 10 及以上通过 MediaStore 保存应用创建的媒体。 - 摄像头画面、麦克风音频、GPU 合成与文件处理在设备本地完成,不上传拍摄内容、不采集设备标识、不内置广告。
平台兼容性
uni-app x(5.26)
| Chrome | Safari | Android | Android插件版本 | iOS | 鸿蒙 | 微信小程序 |
|---|---|---|---|---|---|---|
| × | × | 6.0 | 1.0.0 | × | × | - |
其他
| 多语言 | 暗黑模式 | 宽屏模式 | 蒸汽模式 |
|---|---|---|---|
| × | × | × | √ |
双摄合拍 UTS · uni-app x 使用说明
本插件用于 Android 手机内置前摄与后摄的同屏取景,提供画中画、上下分屏、左右分屏、合成照片和有声/无声录像。预览、照片与视频共用同一套合成布局,可用于双视角记录、讲解、采访等场景。
本插件面向 uni-app x 的 VDOM 与蒸汽模式(Vapor),安装 lf-dual-camera 即可接入组件和拍摄 API。
一、功能与支持范围
已提供的功能
- 内置前后摄并发预览;设备不支持并发时,可由使用者明确选择前置或后置单摄。
- 画中画、上下分屏、左右分屏,以及主次摄像头互换。
- 小窗位置与大小设置、手势拖动、小窗圆角、前摄镜像和画面旋转。
- 拍摄当前合成画面的 JPEG 照片,稳定录像期间也可拍照。
- H.264 MP4 录像;有声模式包含 AAC 麦克风音频,无声模式不采集麦克风。
- 根据设备能力开放缩放、点按对焦/测光、后摄持续补光。
- 文字水印和时间水印,随合成画面写入照片与视频。
- 保存到系统相册,以及完整的全屏取景 Demo、最近作品预览与失败重试入口。
运行环境
| 项目 | 要求或说明 |
|---|---|
| 工程 | uni-app x |
| 渲染模式 | VDOM / 蒸汽模式(Vapor) |
| 开发工具 | HBuilderX 5.26 或更新版本 |
| 插件最低 Android 版本 | Android 6.0 / API 23 |
| 前后摄并发双摄 | Android 11 / API 30 及以上,且系统与硬件实际开放前后摄并发能力 |
| 单摄 | Android 6.0 及以上;仍需满足相机输入、权限与编码器能力要求 |
| iOS、鸿蒙、Web、小程序 | 不支持,相关 API 返回 UNSUPPORTED_PLATFORM |
| 外接摄像头 | 不支持 USB/UVC 等外接摄像头 |
Android 11 以上不等于一定支持双摄,同品牌的不同机型也可能不同。 插件通过系统声明的并发组合检测能力,并在打开相机时继续检查实际配置;最终以 getCapabilities、openCamera 的结果为准。系统 Camera2 并发接口见 Android CameraManager。
双摄不可用时,插件不会在后台自动降为单摄。业务页面应明确提示原因,再由使用者选择 mode: 'back' 或 mode: 'front'。
二、Android已验证版本
| Android 版本 | 当前状态 |
|---|---|
| Android 16 | 已验证 |
| Android 12 | 已验证 |
三、安装与运行 Demo
在业务工程中接入
- 将插件导入 uni-app x 工程的
uni_modules/lf-dual-camera。 - 在需要预览的
.uvue页面中引入标准组件,给组件或其父容器设置明确宽高。 - 等待组件触发
ready,取得sessionId。 - 调用
getCapabilities检测能力,选择双摄或明确的单摄模式。 - 调用
openCamera,等待成功后再启用拍照、录像与相机控制。 - 页面隐藏时关闭相机;组件卸载会释放其原生绑定。
组件导入路径:
import LfDualCameraX from '@/uni_modules/lf-dual-camera/components/lf-dual-camera-x/lf-dual-camera-x.uvue'
API 和类型统一从插件根入口导入:
import { getCapabilities, openCamera, closeCamera, takePhoto,
startRecording, stopRecording, saveToAlbum } from '@/uni_modules/lf-dual-camera'
import type { LfCameraOptions, LfCameraCapabilitiesOptions,
LfCameraResult, LfCameraError } from '@/uni_modules/lf-dual-camera'
不要在业务页面中直接导入插件内部 utssdk 文件。VDOM 页面应显式导入所用参数类型,避免编译器将同名类型自动导入到错误的命名空间。
使用内置 Demo
Demo 路径为 uni_modules/lf-dual-camera/pages/demo/demo.uvue,最近作品页为 uni_modules/lf-dual-camera/pages/result/result.uvue。需要体验完整 Demo 时,在业务工程的 pages.json 中登记这两页,并使用自定义导航栏:
{
"pages": [
{
"path": "uni_modules/lf-dual-camera/pages/demo/demo",
"style": { "navigationStyle": "custom", "disableScroll": true, "backgroundColor": "#0B0F14" }
},
{
"path": "uni_modules/lf-dual-camera/pages/result/result",
"style": { "navigationStyle": "custom", "disableScroll": true, "backgroundColor": "#0B0F14" }
}
]
}
原生基座注意事项
插件的 Android Manifest 和原生配置需要在打包时合并。标准调试基座仅能使用其已包含的原生配置;若缺少相机、麦克风或 Android 6–9 相册保存权限,应制作包含本插件的自定义调试基座。
四、最小预览示例
先取得会话、手动打开双摄预览及关闭相机。应先确认设备支持双摄;不支持时,可将打开参数明确改为 front 或 back。
<template>
<view>
<lf-dual-camera-x style="width:100%;height:480px"
@ready="onCameraReady" @error="onCameraError" />
<button :disabled="sessionId.length == 0 || opened" @click="openPreview">开启相机</button>
<button :disabled="!opened" @click="closePreview">关闭相机</button>
</view>
</template>
<script setup lang="uts">
import LfDualCameraX from '@/uni_modules/lf-dual-camera/components/lf-dual-camera-x/lf-dual-camera-x.uvue'
import { openCamera, closeCamera } from '@/uni_modules/lf-dual-camera'
import type { LfCameraOptions, LfCameraError } from '@/uni_modules/lf-dual-camera'
const sessionId = ref('')
const opened = ref(false)
let disposed = false
function showError(error: LfCameraError): void {
if (!disposed) uni.showToast({ title: error.message, icon: 'none' })
}
function onCameraReady(event: UniNativeViewEvent): void {
const detail = event.detail as UTSJSONObject
sessionId.value = (detail['sessionId'] as string | null) ?? ''
}
function onCameraError(event: UniNativeViewEvent): void {
const detail = event.detail as UTSJSONObject
showError({ code: (detail['code'] as string | null) ?? 'UNKNOWN',
message: (detail['message'] as string | null) ?? '相机暂不可用' })
}
function openPreview(): void {
if (sessionId.value.length == 0 || opened.value) return
const options: LfCameraOptions = {
sessionId: sessionId.value, mode: 'dual', profile: '720p', orientation: 'portrait',
success: (_) => { if (!disposed) opened.value = true },
fail: (error) => showError(error)
}
openCamera(options)
}
function closePreview(): void {
if (sessionId.value.length == 0) return
const options: LfCameraOptions = {
sessionId: sessionId.value,
success: (_) => { if (!disposed) opened.value = false },
fail: (error) => showError(error)
}
closeCamera(options)
}
onHide(() => closePreview())
onUnload(() => { disposed = true; sessionId.value = '' })
</script>
ready 仅代表预览载体已创建,不代表摄像头已打开。需等待 openCamera.success 后再拍摄。离开页面关闭相机后,返回页面需要显式重新打开。
五、设备能力检测
const options: LfCameraCapabilitiesOptions = {
success: (capabilities) => {
console.log('支持双摄:', capabilities.dualSupported)
console.log('不可用原因:', capabilities.reason)
console.log('可用画质:', capabilities.profiles)
console.log('摄像头能力:', capabilities.cameras)
},
fail: (error) => console.error(error.code, error.message)
}
getCapabilities(options)
| 返回字段 | 类型 | 说明 |
|---|---|---|
dualSupported |
boolean | 系统是否声明前后摄并发组合,实际打开仍可能失败 |
reason |
string | 不支持双摄时的原因说明 |
profiles |
string[] | 当前检测可用的 720p、1080p 画质 |
cameras |
LfCameraInfo[] | 每颗摄像头的 ID、朝向、缩放和控制能力 |
LfCameraInfo 包含 id、facing、zoomMin、zoomMax、autofocus、torch。业务界面应按检测结果开放缩放、对焦与补光按钮。
未获得相机权限时,双摄画质检测采用保守结果,可能只显示 720p;授权后可重新检测。dualSupported: true 不保证所有画质都能启动,openCamera 还会检查会话配置和编码器能力。
六、预览尺寸、组件属性与事件
三种尺寸分别控制什么
| 设置方式 | 控制对象 | 是否改变导出尺寸 |
|---|---|---|
组件 style / class 或父容器宽高 |
页面中的预览显示区域 | 否 |
pipRect |
合成画面中的小窗位置和大小 | 改变画面布局,不改变文件宽高 |
openCamera 的 profile / orientation |
合成照片与视频的输出宽高 | 是 |
已有样式尺寸控制,无需额外增加 width / height 属性。例如动态尺寸:
<lf-dual-camera-x :style="'width:' + previewWidth + 'px;height:' + previewHeight + 'px'" />
使用百分比高度时,父容器也必须有明确高度。预览在区域内等比完整显示;容器与输出宽高比不同时可能出现黑边,不会为了铺满而拉伸画面。
组件属性
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
layout |
string | pip |
pip 画中画;split-vertical 上下分屏;split-horizontal 左右分屏 |
mainCamera |
string | back |
主画面/第一分屏的摄像头,back 或 front |
pipRect |
UTSJSONObject | {x:0.67,y:0.03,width:0.3,height:0.225} |
以完整合成画布为基准的归一化小窗区域 |
frontMirror |
boolean | true |
前摄水平镜像,预览与导出一致 |
radius |
number | 0.025 |
小窗圆角占合成画面短边的比例,范围 0–0.5 |
rotation |
number | 0 |
额外画面旋转,允许 0/90/180/270,录像期间不能修改 |
watermarkText |
string | 空字符串 | 文字水印;留空不显示文字水印 |
watermarkTime |
boolean | false |
是否叠加时间水印 |
gestures |
boolean | true |
小窗拖动、点按对焦/测光 |
pipRect 的原点在合成画面左上角,宽高至少为 0.05,并满足 x >= 0、y >= 0、x + width <= 1、y + height <= 1。分屏布局下不使用小窗区域,切回画中画后可继续使用保存的位置。
组件默认小窗位置与 Demo 的初始位置不同:Demo 为配合悬浮操作栏,初始使用 x:0.60, y:0.18。业务接入以自己传入的 pipRect 为准。
组件事件
事件数据从 event.detail 读取:
| 事件 | 关键字段 | 用途 |
|---|---|---|
ready |
sessionId |
取得会话标识,之后可调用打开相机 API |
statechange |
sessionId, state, mode, reason? |
相机状态:opening/previewing/closing/closed/error |
recordingchange |
sessionId, state, recording, reason? |
录像状态:starting/recording/stopping/idle |
recordcomplete |
sessionId, result, reason |
获取完成封装的视频;result 包含文件信息 |
pipchange |
sessionId, pipRect |
小窗被手势拖动后回写业务状态 |
error |
sessionId, code, message |
原生相机或媒体处理错误 |
小窗被拖动后,应把事件中的 pipRect 更新到页面绑定的同名状态,避免后续属性更新将其恢复为旧位置。蒸汽模式中应使用索引读取:
const detail = event.detail as UTSJSONObject
const id = (detail['sessionId'] as string | null) ?? ''
recordingchange.recording 仅在状态为 recording 时为 true。启动和收尾阶段还需结合 state 禁用操作,不宜仅以该布尔值判断是否可以重新开录。
七、API 参数与结果
全部公开函数返回 void,通过一次性的 success、fail、complete 回调获取结果。fail 返回 {code, message}。完整类型见代码包中得 utssdk/interface.uts。
| API | 主要参数 | 行为说明 |
|---|---|---|
getCapabilities |
回调 | 返回设备能力,不需要 sessionId |
openCamera |
sessionId;mode 默认 dual;profile 默认 720p;orientation 默认 portrait |
mode 为 dual/front/back;profile 为 720p/1080p;方向为 portrait/landscape |
closeCamera |
sessionId |
关闭相机,必要时完成录像收尾;保留预览载体 |
takePhoto |
sessionId |
保存当前合成帧为 JPEG;稳定录像期间允许拍照 |
startRecording |
sessionId;audio 默认 true |
开始有声或无声录像;麦克风拒权时不自动改为无声 |
stopRecording |
sessionId |
完成 MP4 封装后返回文件结果;重复停止请求共享完成结果 |
setZoom |
sessionId, camera, zoom |
camera 为 front/back;zoom 范围以能力检测为准 |
focusAt |
sessionId, x, y |
合成画面上的 0–1 坐标,自动识别对应摄像头并映射到传感器 |
setTorch |
sessionId, enabled |
控制后摄持续补光;仅在相应摄像头与设备能力支持时可用 |
saveToAlbum |
path |
将插件生成的应用私有 JPEG/MP4 保存到相册,不需要相机会话 |
setZoom、focusAt、setTorch 等控制 API 可通过 success 确认操作成功,不应读取未在公开类型中声明的 zoom、closed、recording 等原生内部字段。
拍摄结果字段
拍照与停止录像的 success 使用 LfCameraResult。不同操作只填充相关字段,其余字段可能为空或为 0:
| 字段 | 含义 |
|---|---|
sessionId, mode, profile |
当前操作关联的会话、相机模式和画质;按操作实际返回 |
width, height, fps |
输出宽高与帧率;按操作实际返回 |
path |
应用私有目录中的原始拍摄文件绝对路径 |
uri |
保存到相册成功时返回的媒体 URI;普通拍摄结果通常为空 |
mime |
照片 image/jpeg,视频 video/mp4 |
size |
文件字节数 |
duration |
视频时长,单位毫秒 |
hasAudio |
视频是否包含麦克风音轨 |
输出画质
| 画质 | 竖屏 portrait |
横屏 landscape |
|---|---|---|
720p |
720 × 1280 | 1280 × 720 |
1080p |
1080 × 1920 | 1920 × 1080 |
目标帧率为 30fps,实际表现受设备相机、并发配置、编码器、温度和负载影响。照片是当前合成帧的快照,不是两颗传感器的全像素静态照片;前后两路也不保证硬件同步曝光。
八、拍照、录像与相册保存
拍摄 API 与 Demo 的区别
takePhoto / stopRecording 负责生成应用私有文件,不会自动保存系统相册。 本插件的 Demo 会在拍摄完成后继续调用 saveToAlbum,所以 Demo 中拍照和录像结束后会自动入相册。业务自行接入时,需要自行调用该保存 API。
以下代码假设已从 ready 取得 sessionId,并已成功打开预览:
function saveCapturedFile(result: LfCameraResult): void {
const options: LfCameraOptions = {
path: result.path,
success: (saved) => console.log('相册媒体 URI:', saved.uri),
fail: (error) => {
console.error('相册保存失败:', error.message)
console.log('可重试保存的原文件:', result.path)
}
}
saveToAlbum(options)
}
function capturePhoto(): void {
const options: LfCameraOptions = {
sessionId: sessionId.value,
success: (result) => saveCapturedFile(result)
}
takePhoto(options)
}
function beginRecording(): void {
const options: LfCameraOptions = { sessionId: sessionId.value, audio: true }
startRecording(options)
}
function finishRecording(): void {
const options: LfCameraOptions = {
sessionId: sessionId.value,
success: (result) => saveCapturedFile(result)
}
stopRecording(options)
}
展示 API 调用关系。业务按钮还应根据启动、录制、收尾和保存状态禁用重复操作;完整处理可参考内置 Demo。
recordcomplete 与 stopRecording.success 可能指向同一个视频。若两处都触发相册保存,应按文件 path 去重,避免重复生成相册副本。Demo 已处理该情况。
文件保存到哪里
| 文件类型 | 应用内原文件 | 系统相册中的分类目录 |
|---|---|---|
| JPEG 照片 | 应用私有 lf-dual-camera 目录 |
Pictures/LingfuCamera |
| MP4 视频 | 应用私有 lf-dual-camera 目录 |
Movies/LingfuCamera |
系统相册通常显示为 LingfuCamera 相簿,具体展示位置取决于相册应用;也可通过相册的图片、视频或最近项目查看。saveToAlbum.success 的 uri 指向相册媒体,path 仍是应用内原文件路径,不会被替换成相册 URI。
相册保存失败会保留应用内原文件,可以重试。Demo 中可点击快门左侧的“最近作品”预览,并在作品页查看文件详情、复制原文件路径或重试保存。当前“最近作品”只保存本次页面内的最近拍摄结果,不提供跨重启的历史作品列表。应用私有文件也不等同于系统相册,卸载应用可能移除这些原文件。
九、权限与页面生命周期
| 权限 | 申请时机 | 用途 |
|---|---|---|
CAMERA |
打开相机时 | 前摄、后摄预览和拍摄 |
RECORD_AUDIO |
开始有声录像时 | 录制麦克风音轨;无声录像不采集麦克风 |
WRITE_EXTERNAL_STORAGE |
Android 6–9 保存到相册时 | 写入公共图片/视频目录;Manifest 限制 maxSdkVersion=28 |
Android 10 及以上通过 MediaStore 写入应用自己创建的媒体,不为相册保存申请广泛的媒体读取权限。用户拒绝麦克风权限后仍可继续预览;Demo 会让用户明确选择是否改为无声录像。
录像过程中可以调整布局、小窗、主次摄像头、镜像、水印、缩放、对焦与补光。切换单双摄、画质、输出方向、额外画面旋转或声音模式,应先停录并等待视频封装完成;录像启动/停止阶段不能再发起拍照。
切后台、页面隐藏或原生视图脱离窗口时会触发相机关闭与录像收尾。业务页面应处理 recordcomplete,组件卸载后原生绑定会被销毁;返回页面需重新创建或打开会话,不复用已经销毁的 sessionId。
业务自行处理“停止录像并退出”时,应先等待 stopRecording.success 取得完整文件,再处理相册保存和页面跳转。原生关闭逻辑会兜底收尾,但不要依赖已经卸载的页面继续接收事件或更新界面。
十、常见问题与错误处理
| 现象 / 错误码 | 处理方式 |
|---|---|
| 页面没有画面 | 检查组件/父容器宽高、是否收到 ready、是否调用并成功完成 openCamera、是否使用 Android App 真机 |
UNSUPPORTED_DUAL |
当前设备没有可用的前后摄并发组合;提示原因并明确选择单摄 |
UNSUPPORTED_PROFILE |
画质、并发组合或编码器不支持;尝试设备检测允许的 720p 配置 |
CAMERA_PERMISSION_DENIED |
检查系统相机权限;被永久拒绝时引导用户到应用权限设置 |
AUDIO_PERMISSION_DENIED |
保留预览;用户明确同意后改为 audio: false,或重新授权麦克风 |
STORAGE_PERMISSION_DENIED |
Android 6–9 保存相册时检查存储写权限 |
BUSY |
等待当前打开、拍照、录像启动或收尾操作完成,避免连续重复调用 |
NOT_OPEN / SESSION_NOT_FOUND |
等待相机打开,或使用新 ready 返回的有效 sessionId |
CAMERA_TIMEOUT / CAMERA_UNAVAILABLE |
检查其他应用是否占用相机、设备资源与系统状态,再重新打开 |
IO_ERROR |
检查可用空间与相册写入结果;保留原文件,处理重试 |
| 拍摄成功但相册没有文件 | 确认业务代码调用了 saveToAlbum 并收到成功回调;拍摄 API 本身只生成私有文件 |
| 画面显示区域偏小或有黑边 | 调整预览容器样式;黑边来自容器与输出比例不同,不要用非等比缩放强行拉伸 |
| 蒸汽模式提示不支持选项式脚本 | 检查全部注册页面及依赖组件,使用 <script setup> |
button 不能包含子元素 |
图形按钮使用 view,文字按钮使用纯文字 button,并在点击处理内检查禁用状态 |
fail 和组件 error 都应按实际返回的 code / message 提示。不要仅凭 Android 版本或品牌名判断相机能力。
十一、数据处理
摄像头画面、麦克风音频、GPU 合成和文件处理均在设备本地进行。插件不上传拍摄内容、不采集设备标识、不内置广告。
邮箱 lingfugroup@gmail.com。

收藏人数:
购买源码授权版(
试用
赞赏(0)
下载 163
赞赏 1
下载 12723218
赞赏 1958
赞赏
京公网安备:11010802035340号