更新记录
0.1.9(2026-06-27)
0.1.9
平台兼容性
uni-app(4.87)
| Vue2 | Vue3 | Chrome | Safari | app-vue | app-nvue | Android | iOS | 鸿蒙 |
|---|---|---|---|---|---|---|---|---|
| √ | √ | × | × | √ | √ | √ | √ | × |
| 微信小程序 | 支付宝小程序 | 抖音小程序 | 百度小程序 | 快手小程序 | 京东小程序 | 鸿蒙元服务 | QQ小程序 | 飞书小程序 | 小红书小程序 | 快应用-华为 | 快应用-联盟 |
|---|---|---|---|---|---|---|---|---|---|---|---|
| × | × | × | × | × | × | × | × | × | × | × | × |
uni-app x(4.87)
| Chrome | Safari | Android | iOS | 鸿蒙 | 微信小程序 |
|---|---|---|---|---|---|
| × | × | √ | √ | × | × |
ainuo-face-sdk
Ainuo Face SDK uni-app 插件,提供人脸初始化、相机录入、相机识别、动作活体、图片特征提取、图片 1:1 对比、图片 1:N 搜索、人脸库管理,以及可嵌入页面的 <ainuo-face-search-view /> 相机识别组件。
本文面向 uni-app 业务开发者,只说明 JS/UTS 调用方式、参数、事件和使用注意事项。
🚀 核心优势
- 纯离线运行:SDK 所有的活体检测、特征提取、1:1 / 1:N 对比均在本地端侧完成,完全支持离线运行,无需依赖外网,保证数据的绝对安全与极高的识别响应速度。
- Web 端生态支持:除了提供 App 端的插件,我们还提供配套的 Web-SDK。支持在纯 Web 浏览器环境下提取人脸特征,方便业务侧实现多端统一的人脸信息采集与注册。
- 支持双引擎切换:默认内置极速版轻量模型(体积小、推理极快);同时,为了满足更苛刻的场景,SDK 可免费平替/兼容更高精度的工业级模型(如海量数据训练的经典通用大库模型),业务线可按需灵活选型!
🎯 适用场景与说明
- ✅ 推荐场景:非常适合用于企业考勤打卡、门禁通行、内部人员签到、展会/活动入场核验等日常业务级的身份核实,可快速帮助您搭建高效、低成本的人脸识别功能。
- ⚠️ 不适用场景(非金融级方案):本 SDK 属于业务级通用人脸识别方案,并非“金融级”防伪方案。不支持且强烈不建议用于支付、金融转账、高等级政务安防等对防伪级别有极其严苛要求的场景。请各位开发者合理评估业务需求后使用。
📱 体验 App (Demo)
欢迎扫码下载体验 App,直观感受人脸识别、动作活体等功能的效果:
| 原生体验版 (Android/iOS) | uni-app 体验版 (Android) |
|---|---|
💡 购买权益与授权
购买本插件(普通版或源码版)后,您将获得以下权益,请联系作者(微信 z448401921 / 邮箱 448401921@qq.com)获取相关资料与授权:
- 终身离线授权:获取无时间限制、完全纯离线运行的
licenseText授权文本,绝无定期联网验证,保证绝对离线安全。 - Web-SDK 授权:免费赠送配套的纯前端 Web-SDK,方便您在浏览器或网页管理后台实现统一的人脸采集与录入。
- 插件源码开放:提供当前 uni-app 插件层面的全部源代码,方便您在业务侧进行深度二次定制与开发。
- 底层 SDK 源码(仅限源码买断版):如果您购买的是“源码买断版”,除上述内容外,您还将获得完整的 Android / iOS 底层原生 SDK 源代码,实现底层技术的完全自主可控。
- 高阶技术支持:提供 VIP 级别的远程技术支持,包含免费指导替换高精度工业级模型以及深度的远程模型定制训练技术支持。
支持范围
- App Android
- App iOS
- uni-app 普通
.vue页面可调用 API 方法 <ainuo-face-search-view />需要放在app-nvue页面或 uni-app xapp-uvue页面中- H5、小程序端不支持本插件能力
插件包含相机、识别和人脸库能力,首次集成、升级插件版本、变更权限或变更平台依赖后,需要重新制作自定义基座或重新云打包。普通热更新适合调试页面 JS 逻辑,不适合验证新增的插件能力。
快速开始
在页面中导入插件方法:
import {
initialize,
isInitialized,
getSdkInfo,
release,
startRegister,
startSearch,
startLiveness,
extractFeatureFromImage,
compareImages,
searchImage,
importPersonFeature,
countPersons,
listPersons,
deletePerson,
clearPersons,
} from '@/uni_modules/ainuo-face-sdk'
应用启动后先初始化一次:
initialize({
licenseText: '你的授权文本',
success: res => {
console.log('初始化成功', res)
},
fail: err => {
console.log('初始化失败', err)
},
})
推荐流程:
- 调用
initialize。 - 调用
countPersons/listPersons刷新人脸库。 - 使用
startRegister录入人员,或用extractFeatureFromImage+importPersonFeature从图片导入人员。 - 使用
startSearch打开识别页面,或在app-nvue页面中使用<ainuo-face-search-view />。
通用返回格式
所有异步 API 都通过 success、fail、complete 回调返回对象。基础字段如下:
| 字段 | 类型 | 说明 |
|---|---|---|
success |
boolean | 本次调用是否成功 |
code |
string | 状态码,例如 OK、SDK_NOT_INITIALIZED |
nativeCode |
number | 平台状态码,业务通常不需要处理 |
message |
string | 状态说明 |
errMsg |
string | uni-app 风格消息,例如 ainuo-face-sdk:ok |
常见状态码:
| code | 说明 |
|---|---|
OK |
成功 |
SDK_NOT_INITIALIZED |
尚未初始化,请先调用 initialize |
CONFIG_INVALID |
参数缺失或格式不正确 |
CAMERA_PERMISSION_DENIED |
用户未授权相机权限 |
FACE_LIBRARY_EMPTY |
人脸库为空,无法识别或搜索 |
INPUT_IMAGE_ERROR |
图片路径无效、图片无法读取或未检测到有效人脸 |
PERSON_ALREADY_EXISTS |
人员 ID 已存在 |
PERSON_NOT_FOUND |
人员不存在 |
LICENSE_NOT_FOUND / LICENSE_INVALID / LICENSE_PACKAGE_MISMATCH |
授权缺失、无效或与当前应用不匹配 |
USER_CANCELLED |
用户主动关闭页面 |
INTERNAL_ERROR |
内部错误,可结合 message 查看原因 |
常见业务字段:
| 字段 | 类型 | 说明 |
|---|---|---|
initialized |
boolean | 是否已初始化 |
version |
string | 插件当前识别能力版本 |
modelInfo |
object | 当前模型信息 |
licenseInfo |
object | 授权信息 |
personId |
string | 人员 ID |
name |
string | 人员名称 |
person |
object | 人员详情 |
persons |
array | 人员列表 |
count |
number | 人员数量 |
matched |
boolean | 是否达到命中条件 |
similarity |
number | 相似度,通常为 0 到 1 |
threshold |
number | 本次判断阈值 |
matches |
array | TopK 候选结果 |
featureBase64 |
string | 人脸特征 base64,不是图片 base64 |
featureFormat |
string | 特征格式,当前为 float32-le-base64 |
faceBox / faces |
object / array | 人脸框信息 |
quality |
object | 图片或人脸质量信息 |
API 方法
initialize(options)
初始化插件。建议在 App 启动或首页进入时调用一次。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
licenseText |
string | 是 | 授权文本 |
success |
function | 否 | 初始化成功回调 |
fail |
function | 否 | 初始化失败回调 |
complete |
function | 否 | 完成回调 |
说明:
- 已初始化时再次调用会直接返回当前状态,不会重复初始化。
- 授权文本通常与应用包名绑定,换包名后需要使用匹配的授权。
isInitialized()
同步返回是否已初始化。
const ready = isInitialized()
getSdkInfo()
同步返回当前初始化状态、版本、模型信息和授权信息。
const info = getSdkInfo()
console.log(info.initialized, info.version)
release()
释放当前插件资源。一般只在退出登录、切换授权、应用不再需要人脸能力时调用。普通页面返回不需要调用全局 release()。
const res = release()
startRegister(options)
打开内置相机录入页面。录入成功后自动返回当前 uni-app 页面。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
personId |
string | 是 | 人员唯一 ID,建议业务侧生成 |
name |
string | 是 | 人员名称 |
extraJson |
string | 否 | 业务扩展信息,建议传 JSON 字符串 |
groupId |
string | 否 | 分组 ID,用于分组管理和分组搜索 |
title |
string | 否 | 相机页面标题 |
success / fail / complete |
function | 否 | 回调 |
示例:
startRegister({
personId: 'person-' + Date.now(),
name: '张三',
extraJson: JSON.stringify({ source: 'uniapp' }),
groupId: 'default',
title: '录入人脸',
success: res => {
console.log('录入成功', res.personId, res.name)
},
})
startSearch(options)
打开内置相机识别页面。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
title |
string | 否 | 页面标题 |
searchConfig |
object | 否 | 识别配置,字段见“识别配置” |
finishOnMatched |
boolean | 否 | 命中后是否自动返回,默认推荐 true |
success / fail / complete |
function | 否 | 回调 |
也可以把 threshold、topK、livenessRequired 等识别配置字段直接写在 options 顶层。顶层字段会覆盖 searchConfig 中同名字段。
startSearch({
title: '人脸识别',
finishOnMatched: true,
searchConfig: {
threshold: 0.55,
topK: 3,
livenessRequired: false,
groupId: 'default',
},
success: res => {
if (res.matched) {
console.log('命中人员', res.personId, res.name, res.similarity)
}
},
})
startLiveness(options)
打开内置动作活体页面。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
title |
string | 否 | 页面标题 |
enableSound |
boolean | 否 | 是否开启提示音 |
success / fail / complete |
function | 否 | 回调 |
startLiveness({
title: '活体检测',
enableSound: true,
success: res => {
console.log('活体结果', res.code)
},
})
extractFeatureFromImage(options)
从图片路径中提取人脸特征。图片路径可使用 uni.chooseImage 返回的 tempFilePaths[0]。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
imagePath |
string | 是 | 图片路径 |
success / fail / complete |
function | 否 | 回调 |
成功返回的主要字段:
| 字段 | 类型 | 说明 |
|---|---|---|
featureBase64 |
string | 提取出的人脸特征(base64 编码) |
featureFormat |
string | 特征格式,当前通常为 float32-le-base64 |
dimension |
number | 特征维度 |
modelVersion |
string | 提取该特征使用的模型版本标识 |
modelSha256 |
string | 模型文件的 Hash |
normalized |
boolean | 特征是否已归一化 |
metric |
string | 推荐的相似度计算方式,如 cosine |
faceBox |
object | 提取特征对应的人脸框位置信息 |
quality |
object | 图片或人脸的质量评估信息 |
uni.chooseImage({
count: 1,
success: ({ tempFilePaths }) => {
extractFeatureFromImage({
imagePath: tempFilePaths[0],
success: res => {
console.log('特征', res.featureBase64)
},
})
},
})
compareImages(options)
图片 1:1 对比,返回两张图片是否为同一人。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
firstImagePath |
string | 是 | 图片 A |
secondImagePath |
string | 是 | 图片 B |
success / fail / complete |
function | 否 | 回调 |
compareImages({
firstImagePath: imageA,
secondImagePath: imageB,
success: res => {
console.log(res.matched, res.similarity, res.threshold)
},
})
searchImage(options)
图片 1:N 搜索,在本地人脸库中查找最相似的人员。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
imagePath |
string | 是 | 图片路径 |
topK |
number | 否 | 返回候选数量,默认建议 3 或 5 |
groupId |
string | 否 | 只搜索指定分组 |
success / fail / complete |
function | 否 | 回调 |
成功返回最佳结果字段,并在 matches 中返回 TopK 候选列表。
searchImage({
imagePath,
topK: 5,
groupId: 'default',
success: res => {
console.log('最佳结果', res.personId, res.similarity)
console.log('TopK', res.matches)
},
})
importPersonFeature(options)
把已提取的人脸特征导入人脸库。适合做 JSON 导入、后台下发人员数据、跨端迁移等场景。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
personId |
string | 是 | 人员唯一 ID |
name |
string | 是 | 人员名称 |
featureBase64 |
string | 是 | 人脸特征 base64 |
featureFormat |
string | 否 | 默认 float32-le-base64 |
modelVersion |
string | 否 | 特征对应的模型版本,省略时使用当前模型 |
modelSha256 |
string | 否 | 特征对应的模型标识 |
normalized |
boolean | 否 | 特征是否已归一化 |
metric |
string | 否 | 相似度算法标识 |
groupId |
string | 否 | 分组 ID |
extraJson |
string | 否 | 业务扩展信息 |
success / fail / complete |
function | 否 | 回调 |
importPersonFeature({
personId: 'person-001',
name: '张三',
featureBase64: record.featureBase64,
featureFormat: record.featureFormat || 'float32-le-base64',
modelVersion: record.modelVersion,
groupId: 'default',
extraJson: JSON.stringify({ source: 'manual' }),
success: res => {
console.log('导入成功', res.person)
},
})
推荐手工导入 JSON 结构:
{
"persons": [
{
"personId": "person-001",
"name": "张三",
"featureBase64": "完整特征base64",
"featureFormat": "float32-le-base64",
"modelVersion": "face-model",
"groupId": "default",
"extraJson": "{\"source\":\"manual\"}"
}
]
}
countPersons(options)
查询人脸库人数。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
groupId |
string | 否 | 只统计指定分组 |
success / fail / complete |
function | 否 | 回调 |
listPersons(options)
分页查询人脸库。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
groupId |
string | 否 | 只查询指定分组 |
offset |
number | 否 | 起始位置,默认 0 |
limit |
number | 否 | 返回数量,默认 50 |
success / fail / complete |
function | 否 | 回调 |
deletePerson(options)
删除指定人员。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
personId |
string | 是 | 人员 ID |
success / fail / complete |
function | 否 | 回调 |
clearPersons(options)
清空人脸库。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
groupId |
string | 否 | 只清空指定分组;不传则清空全部 |
success / fail / complete |
function | 否 | 回调 |
识别配置 searchConfig
searchConfig 可用于 startSearch,也对应 <ainuo-face-search-view /> 的主要 props。
| 字段 | 默认值 | 说明 |
|---|---|---|
threshold |
0.55 |
命中阈值,相似度大于等于该值才算命中 |
minThreshold |
0 |
阈值下限 |
maxThreshold |
1 |
阈值上限 |
searchIntervalMs |
1200 |
识别间隔,单位毫秒 |
minSearchIntervalMs |
0 |
识别间隔下限 |
maxSearchIntervalMs |
9000 |
识别间隔上限 |
topK |
3 |
候选结果数量 |
singleFaceOnly |
false |
是否要求画面中只有一张脸 |
returnFaceBitmap |
false |
是否返回人脸图片数据 |
returnFeature |
false |
是否返回识别到的人脸特征 |
livenessRequired |
false |
是否要求活体通过后才确认 |
minTopKConfidenceGap |
0.04 |
候选结果分差要求 |
confirmWindowMs |
3000 |
连续确认窗口,单位毫秒 |
confirmHitCount |
2 |
窗口内需要命中的次数 |
confirmMinBestScore |
0.70 |
确认时最佳分数要求 |
confirmMinAverageScore |
0.55 |
确认时平均分数要求 |
groupId |
null / "" |
指定分组搜索 |
调参建议:
- 想更容易命中:适当降低
threshold或confirmHitCount。 - 想更严格:提高
threshold,开启livenessRequired,或提高confirmMinAverageScore。 - 页面需要实时候选展示:关注
mostSimilar事件和matches。 - 业务最终确认:优先以
matched事件或success回调中的matched === true为准。
人脸搜索组件
<ainuo-face-search-view /> 是可嵌入页面的相机识别组件。它必须运行在 app-nvue 或 app-uvue 页面中;普通 .vue 页面请使用 API 方法,不要直接承载该组件。
组件不会自动初始化插件。请先在应用入口调用 initialize,初始化成功后再进入组件页面。
基础用法
<template>
<view class="page">
<ainuo-face-search-view
ref="faceSearchView"
class="face-search-view"
:autoStart="true"
:threshold="0.55"
:topK="3"
:livenessRequired="false"
:enableRealtimeFaceBox="true"
lensFacing="front"
previewScaleType="fitCenter"
@ready="onViewEvent('ready', $event)"
@faceDetected="onViewEvent('faceDetected', $event)"
@mostSimilar="onViewEvent('mostSimilar', $event)"
@matched="onViewEvent('matched', $event)"
@failed="onViewEvent('failed', $event)"
@tips="onViewEvent('tips', $event)"
@log="onViewEvent('log', $event)"
/>
</view>
</template>
.page {
flex: 1;
background-color: #000000;
}
.face-search-view {
width: 750rpx;
height: 1334rpx;
}
全屏页面建议用 uni.getSystemInfoSync() 读取 windowWidth/windowHeight,把组件 style 设置为真实窗口宽高,避免相机画面被错误拉伸。
Props
| prop | 类型 | 默认值 | 说明 |
|---|---|---|---|
autoStart |
boolean | true |
组件加载完成后自动启动 |
livenessRequired |
boolean | true |
是否要求活体 |
threshold |
number | 0.55 |
命中阈值 |
minThreshold |
number | 0 |
阈值下限 |
maxThreshold |
number | 1 |
阈值上限 |
searchIntervalMs |
number | 1200 |
识别间隔,毫秒 |
minSearchIntervalMs |
number | 0 |
识别间隔下限 |
maxSearchIntervalMs |
number | 9000 |
识别间隔上限 |
topK |
number | 3 |
候选数量 |
singleFaceOnly |
boolean | false |
是否只允许单人脸 |
returnFaceBitmap |
boolean | false |
是否返回人脸图片数据 |
returnFeature |
boolean | false |
是否返回特征数据 |
enableRealtimeFaceBox |
boolean | true |
是否由组件内部绘制实时人脸框 |
minTopKConfidenceGap |
number | 0.04 |
TopK 分差要求 |
confirmWindowMs |
number | 3000 |
连续确认窗口,毫秒 |
confirmHitCount |
number | 2 |
连续确认命中次数 |
confirmMinBestScore |
number | 0.70 |
最佳分数要求 |
confirmMinAverageScore |
number | 0.55 |
平均分数要求 |
groupId |
string | "" |
搜索分组 |
previewScaleType |
string | fitCenter |
预览缩放方式 |
lensFacing |
string | front |
front 前置,back 后置 |
debug |
boolean | false |
是否输出组件调试日志 |
previewScaleType 可选:
fitCenterfitStartfitEndfillCenterfillStartfillEnd
enableRealtimeFaceBox=false 时,组件不绘制内置跟随框。业务可以监听 faceDetected.faces,根据 left/top/right/bottom 自行画框或自绘 UI。
Events
| 事件 | 触发时机 | 主要字段 |
|---|---|---|
ready |
相机识别组件准备完成 | success、code、message |
faceDetected |
检测到人脸位置变化 | count、faces |
mostSimilar |
实时得到最相似候选 | personId、name、similarity、matches |
matched |
达到确认条件 | personId、name、similarity、threshold、matches |
failed |
组件启动或识别失败 | code、message |
tips |
识别过程提示 | code、message |
log |
调试日志 | message |
faces 单项字段:
| 字段 | 说明 |
|---|---|
left / top / right / bottom |
人脸框坐标 |
confidence |
置信度 |
trackingId |
跟踪 ID |
yaw / pitch / roll |
姿态角 |
matches 单项字段:
| 字段 | 说明 |
|---|---|
person |
人员详情 |
personId |
人员 ID |
name |
人员名称 |
similarity |
相似度 |
rank |
排名 |
threshold |
阈值 |
matched |
当前候选是否达到阈值 |
mostSimilar.matches 适合展示实时 TopK 候选;matched 事件适合作为业务确认依据。
Android 和 iOS 组件都声明相同事件。由于不同平台的事件对象包装方式不同,建议在页面中统一解析:
function parseAinuoEvent(event) {
const detail = event && event.detail ? event.detail : event
const payloadJson =
detail && detail.payloadJson
? detail.payloadJson
: detail && detail.dynamicJSONFields
? detail.dynamicJSONFields.payloadJson
: ''
if (payloadJson) {
try {
return JSON.parse(payloadJson)
} catch (error) {
return {}
}
}
if (detail && detail.dynamicJSONFields) {
return detail.dynamicJSONFields
}
return detail || {}
}
日志里看到 dynamicJSONFields 是 uni-app 对事件对象的包装,不代表字段丢失。
Methods
通过 ref 调用组件方法:
this.$refs.faceSearchView.start()
this.$refs.faceSearchView.stop()
| 方法 | 说明 |
|---|---|
start() |
启动识别 |
stop() |
停止识别并关闭相机采集 |
pauseSearch() |
暂停搜索 |
resumeSearch() |
恢复搜索 |
switchCamera() |
前后摄像头切换 |
getLastEventPayloadJson() |
返回最后一次事件 JSON 字符串 |
getLastEventPayloadJsonAsync(name, callback) |
按事件名取回最后一次事件 JSON 字符串 |
页面生命周期建议:
export default {
onHide() {
this.$refs.faceSearchView && this.$refs.faceSearchView.stop()
},
onUnload() {
this.$refs.faceSearchView && this.$refs.faceSearchView.stop()
},
}
人脸库与分组
groupId 用于把人员划分到不同业务分组。以下 API 支持 groupId:
startRegisterstartSearchsearchImageimportPersonFeaturecountPersonslistPersonsclearPersons<ainuo-face-search-view />
注意:
deletePerson按personId删除,不需要groupId。- 同一个
personId不建议跨分组重复使用。 - 识别时传入
groupId后,只会在该分组内搜索。
图片特征与 JSON 导入
featureBase64 是人脸特征,不是图片。它适合保存到你的业务数据库中,用于后续导入:
- 通过
extractFeatureFromImage提取图片特征。 - 保存
personId、name、featureBase64、featureFormat、modelVersion、groupId等字段。 - 在新设备或新安装包中调用
importPersonFeature导入。
手工导入建议支持两种结构:
[
{
"personId": "person-001",
"name": "张三",
"featureBase64": "完整特征base64",
"featureFormat": "float32-le-base64"
}
]
{
"persons": [
{
"personId": "person-001",
"name": "张三",
"featureBase64": "完整特征base64",
"featureFormat": "float32-le-base64"
}
]
}
Demo 页面
您可以在插件市场点击“导入示例项目”,或者在下载的 ZIP 包中找到完整的 Demo 工程。Demo 包含两个核心页面:
pages/index/index.vue:普通.vue页面,演示初始化、内置相机录入、内置相机识别、动作活体、图片特征、图片 1:1、图片 1:N、手工导入、人脸库管理。pages/face-search-view/face-search-view.nvue:app-nvue页面,演示<ainuo-face-search-view />全屏识别组件。
普通 .vue 页面适合验证 API 方法;app-nvue / app-uvue 页面适合验证相机识别组件。
使用注意事项
- 必须先调用
initialize,再使用录入、识别、图片和人脸库 API。 - 授权文本要与当前 App 匹配;更换包名或应用标识后,请使用新的授权。
- 第一次安装、升级插件、变更相机权限或变更平台能力后,需要重新制作自定义基座或云打包。
- 使用相机能力前,用户需要同意相机权限。
- 内置相机识别和图片 1:N 搜索前,请先确认人脸库不为空,否则会返回
FACE_LIBRARY_EMPTY。 - 图片路径建议使用
uni.chooseImage返回的本地路径;网络图片请先下载到本地再传入。 <ainuo-face-search-view />必须设置明确宽高;看不到画面时先检查页面是否为app-nvue/app-uvue,再检查组件尺寸。- 页面离开时建议调用组件
stop(),避免相机继续工作。 release()是全局释放,不是普通页面返回的必需步骤;调用后如需继续使用,需要重新initialize。

收藏人数:
https://github.com/ainuoface/face-sdk
购买源码授权版(
试用
使用 HBuilderX 导入示例项目
赞赏(0)
下载 647
赞赏 1
下载 12346999
赞赏 1925
赞赏
京公网安备:11010802035340号