更新记录
1.0.1(2026-07-10)
-优化鸿蒙异步执行不阻塞UI,优化性能
1.0.0(2026-07-10)
平台兼容性
uni-app(4.18)
| Vue2 | Vue3 | Chrome | Safari | app-vue | app-nvue | Android | iOS | 鸿蒙 |
|---|---|---|---|---|---|---|---|---|
| √ | √ | × | × | √ | √ | 5.0 | √ | 4.18 |
| 微信小程序 | 支付宝小程序 | 抖音小程序 | 百度小程序 | 快手小程序 | 京东小程序 | 鸿蒙元服务 | QQ小程序 | 飞书小程序 | 小红书小程序 | 快应用-华为 | 快应用-联盟 |
|---|---|---|---|---|---|---|---|---|---|---|---|
| - | - | - | - | - | - | - | - | - | - | - | - |
uni-app x(4.18)
| Chrome | Safari | Android | iOS | 鸿蒙 | 微信小程序 |
|---|---|---|---|---|---|
| - | - | - | - | - | - |
其他
| 多语言 | 暗黑模式 | 宽屏模式 |
|---|---|---|
| √ | × | √ |
ly028-OCR 离线文字识别插件
📱 下载体验 APK
基于 PaddleOCR(PP-OCR) 的离线文字识别 UTS 原生插件,Android / iOS / HarmonyOS Next 三端完美支持,导出函数完全一致,一套代码三端运行。全离线运行,无需网络连接,数据不出设备。
特性
- 🔒 全离线 — 本地推理,无需联网,图片数据不出设备
- 📱 三端完美一致 — Android / iOS / HarmonyOS Next 导出 API 完全相同,调用端无平台差异
- 🔤 通用文字识别 — 支持中英文混合识别,自动方向检测(0°/180°)
- 🆔 身份证识别 — 正面(姓名/性别/民族/出生/住址/身份证号)+ 背面(签发机关/有效期限),自动正反面判定,五策略回退解析
- 🚗 车牌识别 — 支持多车牌同时检测,涵盖蓝牌/新能源/黄牌/白牌/黑牌/港澳/教练/警用/军牌/使馆/领事,IoU 去重,O/0 I/1 混淆修复
- 📄 驾驶证识别 — 准驾车型/姓名/性别/国籍/住址/证号/档案编号/有效期等 11 个字段结构化提取
- 🧩 模型热切换 — 支持多模型注册、切换、动态下载,无需重新打包
- 🧪 图片质量预检 — 亮度/模糊/文字边缘/方向检测,识别前即可给出建议
- ⚡ 异步非阻塞 — 所有耗时操作在后台线程执行,绝不阻塞 UI
- 🌐 多语言 — 内置国际化支持
平台兼容
| 平台 | 最低版本 | 架构 |
|---|---|---|
| Android | 7.0 (API 21) | arm64-v8a, armeabi-v7a |
| iOS | 12.0 | arm64 |
| HarmonyOS Next | API 9+ | arm64-v8a |
集成与调试
HBuilderX 自定义基座运行
- 插件市场购买插件后,导入到项目
- 自定义调试基座:菜单栏 → 运行 → 运行到手机或模拟器 → 制作自定义调试基座
- 制作完成后,选择自定义基座运行到真机即可调试所有功能
首次使用请确保已关联 uni-app 开发者证书,自定义基座有效期为 7 天,过期后需重新制作。
离线打包
插件为原生代码插件,离线打包需自行集成原生依赖。
模型资源准备
方式一:内置到应用(推荐)
将 OCR 模型文件放置到 static/models/ 目录下(可自定义目录名),模型包目录结构如下:
static/models/
└── tiny/ # 模型名称目录(可自定义,如 small、std 等)
├── det/
│ └── *.onnx # 文本检测模型(必选,任意 .onnx 文件名均可)
├── rec/
│ └── *.onnx # 文字识别模型(必选,任意 .onnx 文件名均可)
├── cls/
│ └── *.onnx # 方向分类模型(可选,建议包含,任意 .onnx 文件名均可)
└── *keys*.txt / *dict* # 字典文件(必选,引擎自动识别含 keys 的 .txt 或含 dict 的文件)
初始化时插件自动定位模型路径,无需手动配置。如需自定义目录名,在 initEngineAsync 的 options 中传入 modelAssetDir: "your-dir-name" 即可。
方式二:应用启动后动态下载
插件内置模型下载功能,支持从远程 URL 下载 ZIP 包并自动解压注册(参见"模型下载"章节)。
方式三:手动放置到设备存储
通过 getModelStoragePathsAsync 获取设备上的推荐模型存放路径,将模型包手动复制到该目录后调用 scanModelDirectoryAsync / autoDiscoverModelsAsync 扫描注册。
快速开始
import * as OCR from '@/uni_modules/ly028-OCR'
// 1. 初始化 OCR 引擎
OCR.initEngineAsync({
modelAssetDir: 'models', // static/models/ 目录(可自定义)
threadNum: 4, // 推理线程数
}, (res) => {
const result = JSON.parse(res)
if (result.code === 0) {
console.log('OCR 引擎初始化成功')
} else {
console.error('初始化失败:', result.msg)
}
})
// 2. 通用文字识别(从文件路径)
OCR.runOcrFileAsync('/path/to/image.jpg', (res) => {
const result = JSON.parse(res)
if (result.code === 0) {
const data = result.data
console.log('识别结果:', data.text)
console.log('文本框数:', data.boxes.length)
console.log('耗时:', data.elapsedMs, 'ms')
}
})
// 3. 释放引擎
OCR.releaseEngineAsync((res) => {
console.log('引擎已释放')
})
全量 API 参考
响应信封格式
所有 API 通过 callback 返回 JSON 字符串,前端 JSON.parse() 后检查 code:
// 成功
{"code": 0, "data": {...}}
// 失败
{"code": 1, "subCode": 400, "msg": "错误描述"}
状态码说明:
| subCode | 说明 |
|---|---|
| 400 | 请求参数错误 |
| 404 | 资源不存在 |
| 422 | 引擎未初始化 / 解码失败 |
| 500 | 内部错误 |
| 501 | 功能未实现 |
| 503 | 服务不可用(引擎忙) |
🔧 引擎生命周期
| API | 说明 |
|---|---|
initEngineAsync(options, callback) |
初始化 OCR 引擎 |
releaseEngineAsync(callback) |
释放引擎资源 |
releaseOCRAsync(callback) |
releaseEngineAsync 的别名(统一 API) |
initEngineAsync — OcrOptions 参数
type OcrOptions = {
modelDir?: string // 模型目录绝对路径(不常用,一般用 modelAssetDir)
modelAssetDir?: string | null // static/ 下的模型目录名,默认 "models"
threadNum?: number // 推理线程数,默认 4
language?: string // 语言
// — 检测调优参数 —
boxThresh?: number // det_db_box_thresh (0.0-1.0, 默认 0.3, 越低框越多)
detThresh?: number // det_db_thresh (0.0-1.0, 默认 0.3, 二值化阈值)
unclipRatio?: number // det_db_unclip_ratio (默认 2.0, 框扩展系数)
scoreMode?: string // det_db_score_mode: "slow"(精确) | "fast"(快速)
maxCandidates?: number // 最大输出框数 (默认 1000)
enableCls?: boolean // 是否启用方向分类 (默认 true)
enableRec?: boolean // 是否启用文字识别 (默认 true)
// — 图像预处理调优(漏检修复) —
minContourArea?: number // 输出图最小轮廓面积 (默认 2)
minBoxSize?: number // 原图最小框宽高 (默认 3)
limitSideLen?: number // 检测缩放最大边长 (默认 1280)
claheClipLimit?: number // CLAHE 对比度限制 (默认 2.5)
claheTileSize?: number // CLAHE 分块大小 (默认 8)
enhanceBrightnessThr?: number // CLAHE 亮度阈值 (默认 95)
enableEnhance?: boolean // 图像增强总开关 (默认 true)
}
示例: 调高检测灵敏度(适合小文字、密集文字)
OCR.initEngineAsync({
boxThresh: 0.2, // 降低检测阈值 → 更多候选框
detThresh: 0.2,
unclipRatio: 2.5, // 更大扩展系数
limitSideLen: 960, // 缩小检测图像 → 小文字更好检
enableEnhance: true,
}, callback)
🔤 通用文字识别
| API | 说明 |
|---|---|
runOcrFileAsync(filePath, callback) |
从文件路径识别 |
runDetectOnlyFileAsync(filePath, callback) |
仅文本检测(不识别文字,文件版) |
generalOCRAsync(imagePath, options?, callback?) |
通用 OCR(统一 API 别名) |
precheckFileAsync(filePath, callback) |
图片质量预检 |
runOcrFileAsync — 文件路径识别
适用于从相册选择或已有图片文件:
OCR.runOcrFileAsync('/path/to/image.jpg', (res) => {
const result = JSON.parse(res)
if (result.code === 0) {
const data = result.data
// data.text — 全文拼接文本
// data.boxes — 文本框数组
// data.elapsedMs — 耗时(毫秒)
// data.imageWidth — 图片宽度(前端坐标映射用)
// data.imageHeight — 图片高度
}
})
runDetectOnlyFileAsync — 仅文本检测
只检测文本区域位置,不执行文字识别(速度更快),适合需要自定义识别的场景:
OCR.runDetectOnlyFileAsync('/path/to/image.jpg', (res) => {
const result = JSON.parse(res)
if (result.code === 0) {
// result.data.boxes — 只含位置信息的检测框
}
})
precheckFileAsync — 图片质量预检
在 OCR 识别前进行质量评估,避免无效识别:
OCR.precheckFileAsync('/path/to/image.jpg', (res) => {
const result = JSON.parse(res)
if (result.code === 0) {
const q = result.data
// q.brightness: number — 亮度均值 (0-255)
// q.blurScore: number — Laplacian 方差(>200 清晰)
// q.textEdgeRatio: number — Canny 边缘占比(>0.05 有文字)
// q.isHorizontal: boolean — 是否横拍
// q.advice: string — 建议文案(空=质量合格)
console.log('质量建议:', q.advice)
}
})
generalOCRAsync — 统一 API 别名
runOcrFileAsync 的别名,更简洁的调用签名:
OCR.generalOCRAsync('/path/to/image.jpg', (res) => {
const result = JSON.parse(res)
console.log('识别结果:', result.data?.text)
})
🆔 身份证识别
| API | 说明 |
|---|---|
idCardOCRAsync(imagePath, callback) |
身份证识别(文件路径版) |
身份证识别结果已自动完成结构化解析,返回的
fields对象包含所有字段,side为自动判定的正反面。
身份证识别结果结构:
{
"code": 0,
"data": {
"fields": {
"name": "刘永",
"gender": "男",
"ethnicity": "汉",
"birthDate": "1990年01月01日",
"address": "湖南省长沙市岳麓区...",
"idNumber": "43010419900101001X",
"issuedBy": "长沙市公安局",
"validDate": "2017.08.01 至 2037.08.01"
},
"side": "front",
"sideConfidence": 0.95,
"confidence": 1.0,
"elapsedMs": 0
}
}
字段说明:
| 字段 | 说明 |
|---|---|
name |
姓名 |
gender |
性别(男/女) |
ethnicity |
民族 |
birthDate |
出生日期(YYYY年MM月DD日 标准化格式) |
address |
住址(跨框自动合并) |
idNumber |
身份证号(18 位,含校验位自动验证) |
issuedBy |
签发机关(背面提取) |
validDate |
有效期限(背面提取) |
自动正反面判定:
解析器通过关键词加权评分自动判定是正面还是背面:
| 面 | 权重关键词 |
|---|---|
| 正面 | 姓名(5)、公民身份号码(5)、性别(4)、民族(4)、出生(3)、住址(3) |
| 背面 | 签发(5)、有效(5)、机关(3)、期限(3)、长期(2) |
五策略回退解析:
身份证解析器采用五策略逐级回退,保证在 OCR 检测不完美时仍能尽可能提取到正确字段:
- 策略 1:单框内正则提取(如
"姓名刘永"→"刘永") - 策略 2:空间邻近搜索 — 按框坐标找最近的右侧/下方非标签框(含置信度加权评分)
- 策略 3:顺序下一框提取(如
["姓名", "刘永"]) - 策略 4:跨行搜索
- 策略 5:全文兜底扫描 + 孤立值推断(民族关键字、地址关键字等)
额外特性:
- 地址跨框合并:遇到标签后合并后续非标签框直到遇到新字段,支持换行续行
- 身份证号多框拼接:检测一行中多个纯数字框自动拼接
- 日期格式标准化:各种格式 →
YYYY年MM月DD日 - OCR 混淆修正:
O/0、I/1等常见误识别修复 - 校验位自动验证 + 性别从身份证号推断 + 出生日期交叉校验
🚗 车牌识别
| API | 说明 |
|---|---|
plateOCRAsync(imagePath, callback) |
车牌识别(文件路径版) |
车牌识别结果已自动完成结构化解析,返回结果中包含车牌号、颜色、类型及置信度。
快速示例:
OCR.plateOCRAsync('/path/to/car.jpg', (res) => {
const result = JSON.parse(res)
if (result.code === 0) {
const data = result.data
console.log('车牌号:', data.plateNumber)
console.log('颜色:', data.plateColor)
console.log('类型:', data.plateType)
console.log('置信度:', data.confidence)
}
})
单条结果结构:
{
"code": 0,
"data": {
"plateNumber": "京A12345",
"plateColor": "蓝牌",
"plateType": "蓝牌",
"confidence": 0.98,
"elapsedMs": 0
}
}
多条结果结构(多车牌场景):
{
"code": 0,
"data": {
"plates": [
{
"plateNumber": "京A12345",
"plateColor": "蓝牌",
"plateType": "蓝牌",
"confidence": 0.98,
"sourceConfidence": 0.98,
"box": [100, 200, 300, 400],
"rawText": "京A12345"
},
{
"plateNumber": "沪B67890",
"plateColor": "绿牌",
"plateType": "新能源",
"confidence": 0.92,
"sourceConfidence": 0.92,
"box": [400, 500, 600, 700],
"rawText": "沪B67890"
}
],
"count": 2,
"elapsedMs": 0
}
}
车牌类型自动判定:
| 类型 | 判定规则 | 颜色 |
|---|---|---|
| 新能源 | 8 位,第 2 位为 D/F | 绿牌 |
| 蓝牌 | 7 位,第 2 位为字母 | 蓝牌 |
| 黄牌 | 7 位 | 黄牌 |
| 港澳 | 末位为"港"/"澳" | 黑牌 |
| 使馆 | 以"使"开头 | 黑牌 |
| 教练 | 末位为"学" | 黄牌 |
| 警用 | 末位为"警" | 白牌 |
| 武警 | 以 WJ 开头 | 白牌 |
| 军牌 | 特定字母开头 | 白牌 |
OCR 混淆自动修复:
车牌解析器内置常用 OCR 混淆映射:
| 字符位置 | 混淆映射 |
|---|---|
| 第 2 位(省份字母) | 0→O, 1→I, 2→Z |
| 后续位(数字字母) | O→0, I/ l→1, Z→2, S→5, B→8, G→6 |
📄 驾驶证识别
驾驶证识别结果已自动完成结构化解析,返回 11 个字段。解析算法与身份证共享相同的五策略回退架构,对 OCR 检测不完美场景有良好的容错性。
结构化字段:
| 字段 | 说明 |
|---|---|
licenseType |
准驾车型(如 C1、A2) |
name |
姓名 |
gender |
性别 |
nationality |
国籍 |
birthDate |
出生日期 |
address |
住址(跨框合并) |
licenseNumber |
驾驶证号(18 位) |
archiveNumber |
档案编号(12 位数字) |
issueDate |
初次领证日期 |
validFrom |
有效起始日期 |
validTo |
有效截止日期(支持"长期") |
📦 模型管理
ly028-OCR 支持多模型注册、切换和动态下载,可在不更新应用的情况下切换不同精度的 OCR 模型。
模型扫描与发现
| API | 说明 |
|---|---|
scanModelDirectoryAsync(path, callback) |
扫描目录发现所有模型包 |
autoDiscoverModelsAsync(path, callback) |
扫描并自动注册所有发现的模型 |
registerModelAsync(configJson, callback) |
手动注册自定义模型 |
switchModelAsync(modelId, callback) |
切换到指定模型(需重新 initEngine) |
removeModelAsync(modelId, callback) |
注销模型(default 模型不可移除) |
getModelListAsync(callback) |
获取所有已注册模型列表 |
getModelStatusAsync(modelId, callback) |
获取单个模型状态 |
getCurrentModelAsync(callback) |
获取当前模型 ID |
getModelStoragePathsAsync(callback) |
获取模型存储路径 |
模型发现与使用完整示例
// 1. 获取设备推荐存储路径
OCR.getModelStoragePathsAsync((res) => {
const result = JSON.parse(res)
console.log('模型存放目录:', result.data.modelStorageDir)
console.log('下载目录:', result.data.downloadDir)
})
// 2. 扫描并自动注册
OCR.autoDiscoverModelsAsync('/sdcard/OCRModels/', (res) => {
const result = JSON.parse(res)
console.log('已注册模型数:', result.data.registered)
})
// 3. 查看已注册模型
OCR.getModelListAsync((res) => {
const result = JSON.parse(res)
// result.code === 0 ? result.data 为已注册模型数组
})
// 4. 切换到特定模型(然后重新 initEngine)
OCR.switchModelAsync('my-custom-model', (res) => {
if (JSON.parse(res).code === 0) {
OCR.initEngineAsync({ ... }, callback)
}
})
模型注册配置(registerModelAsync 的 configJson)
{
"modelId": "my-model",
"modelDir": "/path/to/model/dir",
"name": "高精度模型",
"version": "1.0",
"detModel": "det/inference.onnx",
"recModel": "rec/inference.onnx",
"clsModel": "cls/inference.onnx",
"recVocab": "rec/vocab.txt",
"boxThresh": 0.3,
"detThresh": 0.3,
"unclipRatio": 2.0,
"threadNum": 4,
"enableCls": true,
"enableRec": true
}
⬇️ 模型下载
| API | 说明 |
|---|---|
downloadModelZipAsync(url, modelName, callback) |
下载模型 ZIP 包并自动解压注册 |
onDownloadProgress(callback) |
注册下载进度监听 |
offDownloadProgress(callback) |
移除下载进度监听 |
示例
// 注册进度监听
OCR.onDownloadProgress((json) => {
const progress = JSON.parse(json)
// progress.name — 模型名称
// progress.pct — 0-100
// progress.status — "downloading" | "unzipping" | "done" | "error"
// progress.msg — 状态描述
console.log(`下载进度 ${progress.name}: ${progress.pct}%`)
})
// 开始下载
OCR.downloadModelZipAsync(
'https://example.com/models/small-model.zip',
'small',
(res) => {
const result = JSON.parse(res)
if (result.code === 0) {
console.log('模型下载并注册成功:', result.data.modelId)
}
}
)
// 不再需要时移除监听
OCR.offDownloadProgress(myHandler)
进度事件格式:
{
"name": "small",
"pct": 67,
"status": "downloading",
"downloaded": 8388608,
"total": 12582912,
"msg": "正在下载..."
}
完整返回值格式
通用 OCR 结果(OcrResult)
{
"code": 0,
"data": {
"text": "识别出的全文",
"boxes": [
{
"points": [{"x": 10, "y": 20}, {"x": 100, "y": 20}, {"x": 100, "y": 50}, {"x": 10, "y": 50}],
"text": "单行文字",
"confidence": 0.98,
"textConfidence": 0.95
}
],
"elapsedMs": 123,
"imageWidth": 1920,
"imageHeight": 1080
}
}
处理复杂框坐标(前端):
// OCR 引擎返回两种兼容的框坐标格式方便前端处理:
// 1. OcrTextBox: points = [{x,y}, {x,y}, {x,y}, {x,y}](四角坐标)
// 2. OcrBox: box = [xmin, ymin, xmax, ymax](轴对齐框)
// points = [x0,y0, x1,y1, x2,y2, x3,y3](旋转框 8 个值)
// 简单场景(无需旋转校正)直接用 box 坐标:
// const rect = { left: box[0], top: box[1], width: box[2]-box[0], height: box[3]-box[1] }
检测框类型定义
// OcrTextBox(通用 OCR 结果中的框格式)
type OcrTextBox = {
points: OcrPoint[] // [{x,y}, {x,y}, {x,y}, {x,y}]
text: string
confidence: number
textConfidence: number
}
// OcrBox(统一 API 的扁平格式,用于 JSON 序列化/反序列化)
type OcrBox = {
text: string
box: number[] // [xmin, ymin, xmax, ymax]
points?: number[] // [x0,y0,x1,y1,x2,y2,x3,y3](旋转框 8 个值)
confidence: number
textConfidence?: number
angle?: number // 0 或 180(方向分类结果)
fieldType?: string // 字段类型(idcard 模式时标记)
}
图片质量预检结果
{
"code": 0,
"data": {
"brightness": 180,
"blurScore": 350,
"textEdgeRatio": 0.15,
"isHorizontal": true,
"advice": ""
}
}
预检建议文案对照:
| advice | 说明 |
|---|---|
"" (空字符串) |
质量合格,可直接进行 OCR |
"图片过暗,建议调整亮度" |
brightness < 60 |
"图片过亮,建议减少曝光" |
brightness > 200 |
"图片模糊,建议保持稳定拍摄" |
blurScore < 200 |
"图片中未检测到文字,请确认内容" |
textEdgeRatio < 0.05 |
"请横屏拍摄" |
非横拍(文字识别推荐横拍) |
类型定义速查
所有类型均在 interface.uts 中 export type,在 TypeScript 中可直接引用:
import type {
OcrResult, OcrTextBox, OcrBox, OcrPoint, OcrRect,
IdCardFields, IdCardResult, IdCardOptions,
PlateResult, PlateInfo, MultiPlateResult, PlateOptions,
PrecheckQuality,
ModelConfig, DiscoveredModel, ModelStoragePaths,
DownloadableModel, DownloadProgress,
OcrOptions, OcrCallback, ResponseEnvelope
} from '@/uni_modules/ly028-OCR'
使用模式参考
模式一:从相册选图识别
uni.chooseImage({
count: 1,
sizeType: ['compressed'],
success: (res) => {
const filePath = res.tempFilePaths[0]
OCR.precheckFileAsync(filePath, (precheck) => {
const pq = JSON.parse(precheck)
if (pq.data?.advice) {
console.warn('质量警告:', pq.data.advice)
}
OCR.generalOCRAsync(filePath, (ocrRes) => {
const result = JSON.parse(ocrRes)
if (result.code === 0) {
this.ocrText = result.data.text
}
})
})
}
})
模式二:仅检测文本位置(不识别文字)
适用于需要自定义识别的场景(例如对检测到的文字区域做额外处理):
OCR.runDetectOnlyFileAsync('/path/to/image.jpg', (res) => {
const result = JSON.parse(res)
if (result.code === 0) {
const boxes = result.data.boxes // 只含位置信息
for (const box of boxes) {
console.log('文字区域坐标:', box.points)
}
}
})
注意事项
- 引擎生命周期:引擎只能初始化一次,
releaseEngineAsync后可再次初始化 - Model 文件:模型需放在
static/models/<模型名称>/目录下(如static/models/tiny/),模型目录内需包含检测、识别模型及字典文件,以及可选的方向分类模型 - 必选模型文件清单:
| 文件 | 用途 | 说明 |
|---|---|---|
det/*.onnx |
文本检测模型 | ✅ 必选,C++ 引擎自动识别子目录下任意 .onnx 文件 |
rec/*.onnx |
文字识别模型 | ✅ 必选,同上 |
*keys*.txt 或 *dict* |
字典文件 | ✅ 必选,引擎自动识别含 keys 的 .txt 文件或含 dict 的文件 |
cls/*.onnx |
方向分类模型 | ❌ 可选,工程推荐包含 |
- 异步回调:所有 API 均为异步+回调模式,在后台线程执行,不阻塞 UI
- 并发限制:内部有互斥锁,同一时间只允许一次识别操作
- 图片尺寸:建议图片最长边不超过 1280px(可通过
limitSideLen调整),超大图片自动缩放 - 结果解析:身份证和车牌的结构化解析由插件内置算法自动完成,OCR 引擎本身只做纯文字识别,返回结果中已包含结构化字段
- 图像增强:默认启用 CLAHE 图像增强(
enableEnhance: true),对低对比度、背光场景有显著改善,如不需要可关闭以提升速度 - 模型热切换:
switchModelAsync切换后必须重新initEngineAsync使配置生效 - 三端一致:Android / iOS / HarmonyOS Next 的导出函数名和参数签名完全一致,调用代码无需平台判断
隐私声明
- 本插件不采集任何用户数据
- 所有文字识别在设备端离线完成
- 无需网络权限,不上传任何图像数据
体验
📱 下载体验 APK
许可
- 插件市场购买后获得使用授权
- 禁止反编译、二次分发

收藏人数:
购买源码授权版(
试用
使用 HBuilderX 导入示例项目
赞赏(0)
下载 923
赞赏 0
下载 12411915
赞赏 1930
赞赏
京公网安备:11010802035340号