鸿蒙化适配 react-native-torch:模拟器无闪光灯,依赖硬件的三方库该怎么验证
本文记录把 react-native-torch(手电筒 / 相机闪光灯控制)适配到 HarmonyOS 的完整过程,涵盖版本对齐、原生实现走查、接入宿主、构建运行,以及依赖硬件的库在模拟器上究竟该怎么验证。
react-native-torch 的 API 只有两个,却是个典型例子:它控制的是一块模拟器上根本不存在的硬件。这类库的适配难点不在代码量,而在“什么算验证通过”。
一、先说结论
项 | 结果 |
上游最新版 | 1.2.0 |
是否带原生实现 | 是(ArkTS TurboModule,不是纯 JS) |
是否依赖硬件 | 是,依赖相机闪光灯 |
公开 API | switchState(enabled)、requestCameraPermission(title?, message?),都返回 Promise |
是否需要声明权限 | 不需要(连 ohos.permission.CAMERA 都用不上) |
原生实现体量 | ArkTS 侧 19 行,HAR 包 2,875 字节 |
编译是否通过 | ✅ assembleHap 成功,HAP 从 80,714,316 增至 80,867,227 字节(+152,911 ≈ 149 KB) |
原生是否注册 | ✅ 日志 TM created: RCTTorch |
软件层断言 | ✅ 13 / 13 通过(契约 6 + 参数守卫 7) |
硬件层结论 | ⚠️ 本机没有闪光灯,四次调用全部以 ERR_TORCH_UNAVAILABLE 拒绝——没有返回 true 假装成功 |
一句话结论:这个库不需要权限、实现只有 19 行、接入后正常注册;但它要控制的闪光灯在这台设备上并不存在,所以“灯亮不亮”无法在本轮验证——本轮真正验证到的,是它在硬件不支持时会明确报错,而不是假装成功。这两件事必须分开说。
二、判定过程
适配前先回答三个问题。
2.1 这个库需要鸿蒙化吗
看源码就知道必须:
// src/NativeTorch.ts
import type {TurboModule} from 'react-native';
import {TurboModuleRegistry} from 'react-native';
export interface Spec extends TurboModule {
switchState(enabled: boolean): Promise<boolean>;
requestCameraPermission(title?: string, message?: string): Promise<boolean>;
}
export default TurboModuleRegistry.getEnforcing<Spec>('RCTTorch');
getEnforcing 意味着取不到原生模块就直接抛错,没有 JS 兜底实现。而 react-native-torch 上游只提供 Android 与 iOS 原生代码,鸿蒙上没有 RCTTorch 这个名字的模块——所以必须补一个鸿蒙原生实现。
这里和纯 JS 库的分界线很清楚:纯 JS 库只要能跑通就直接可用,而这类库不补原生实现就必然报错。
2.2 上游版本核对
npm view react-native-torch version # 1.2.0
拿到鸿蒙适配包后对比到上游基线,版本一致,没有落后。
2.3 交付包里该有的信息都在
spec.json 里有 upstream 仓库地址,也有 upstreamCommit 基线 commit——两样都记了,意味着后续上游改动时能确定该合到哪个点,这次适配的可追溯性是完整的。
validation 里还明确写了能力边界,其中一条很关键:
返回 true 不表示设备一定有闪光灯。
交付包自己先把这件事说清楚了,这一点很重要——它没有让调用方误以为“switchState 返回 true = 灯亮了”。
三、适配实现:先查支持性,再下发命令
原生实现全文:
// harmony/torch/src/main/ets/RCTTorchTurboModule.ts
import camera from '@ohos.multimedia.camera';
import { UITurboModule } from '@rnoh/react-native-openharmony/ts';
export class RCTTorchTurboModule extends UITurboModule {
async switchState(enabled: boolean): Promise<boolean> {
const manager = camera.getCameraManager(this.ctx.uiAbilityContext);
const mode = enabled ? camera.TorchMode.ON : camera.TorchMode.OFF;
if (!manager.isTorchSupported() || !manager.isTorchModeSupported(mode)) {
throw new Error('ERR_TORCH_UNAVAILABLE: flashlight mode is not supported');
}
manager.setTorchMode(mode);
return true;
}
async requestCameraPermission(): Promise<boolean> {
// HarmonyOS CameraManager torch APIs do not require CAMERA permission
// or open a camera input.
return true;
}
}
三个设计点值得展开。
3.1 支持性检查必须在下发命令之前
if (!manager.isTorchSupported() || !manager.isTorchModeSupported(mode)) {
throw new Error('ERR_TORCH_UNAVAILABLE: ...');
}
manager.setTorchMode(mode);
这里做了两层检查:
- isTorchSupported() —— 这台设备有没有闪光灯;
- isTorchModeSupported(mode) —— 这个模式(ON / OFF)支不支持。
两层检查是必要的,因为有些设备的闪光灯只支持常亮、不支持闪烁(或反过来),“有闪光灯”并不等于“这个模式可用”。
检查失败时抛出的是带前缀的明确错误(ERR_TORCH_UNAVAILABLE),调用方能靠字符串识别并走降级逻辑。这一点比“静默返回 false”好得多——静默失败会让调用方以为操作成功了。
3.2 它不打开相机、不采集图像
实现里出现的相机 API 只有四个:
getCameraManager / isTorchSupported / isTorchModeSupported / setTorchMode
没有 createCameraInput、没有 createCaptureSession、没有预览流。这是静态就能查清的事实,也是理解“为什么不需要 CAMERA 权限”的钥匙——它只借用 CameraManager 的手电接口,不碰相机采集链路。
3.3 JS 侧的参数守卫是"被拒绝的 Promise"
上游 JS 层保留了类型校验:
async function switchState(enabled: boolean): Promise<boolean> {
if (typeof enabled !== 'boolean') {
throw new TypeError('Torch.switchState expects a boolean');
}
return NativeTorch.switchState(enabled);
}
注意它在 async 函数里,所以抛出的是一个被拒绝的 Promise,而不是同步异常。这意味着:
// ❌ 抓不到
try { Torch.switchState(1); } catch (e) {}
// ✅ 这样才抓得到
await Torch.switchState(1).catch(e => console.log(e.message));
这个细节在本轮验证中被实际确认过——参数守卫组的断言必须用 await 才能捕获到 TypeError。调用方如果按同步异常来写 try/catch,会漏掉所有参数错误。
四、requestCameraPermission() 为什么直接返回 true
这个方法最容易引起误解。它在 Android 上的语义是弹出系统相机授权框,所以返回值表示“用户授权了没有”。到了鸿蒙,实现直接写成:
async requestCameraPermission(): Promise<boolean> {
return true;
}
为什么可以这样写?
因为鸿蒙的手电筒 API 走的是 CameraManager,既不要求声明 ohos.permission.CAMERA,也不打开相机输入。既然没有权限可申请,也就没有授权框可弹——直接返回 true 表示"没有权限障碍,你继续调 switchState 就行"。
这个 true 的含义要读准:
它表示 | 它不表示 |
不存在权限障碍 | 设备一定有闪光灯 |
可以继续调用 switchState | 闪光灯能被点亮 |
保留了上游接口的兼容性 | Android 上的授权弹窗行为也一致 |
交付包 README 专门写了这个限定,这一点做得很诚实。如果调用方把 requestCameraPermission() === true 当成"硬件可用"的判据,就会在无闪光灯设备上做出错误决策——正确的判据是 switchState 是否成功,而不是权限方法是否返回 true。
五、接入宿主
宿主工程是一个已经运行起来的 RNOH 示例应用。接入这个库要改四处:
文件 | 改动 |
package.json | 加 "react-native-torch": "file:../react-native-torch" |
metro.config.js | watchFolders 增加该库的绝对路径 |
index.js | import 测试页并注册 |
harmony/entry/oh-package.json5 | 手工加 HAR 依赖 |
第四处是唯一需要留神的:
"@react-native-ohos/react-native-torch": "file:../../node_modules/react-native-torch/harmony/torch.har"
自动链接工具不会写这一处。它会把 package.json 的依赖关系和 Metro 的模块解析处理好,日志里也会报 linked 9 libraries,但模块级的 oh-package.json5 依赖需要手工补——漏了这一步,编译期就会在 CMake / ohpm 阶段报找不到包。
这一点交付包的 README 里也专门提醒了,写成了一句很实在的话:
若宿主 CMake 的 OH_MODULES_DIR 指向 entry/oh_modules,还需在 harmony/entry/oh-package.json5 添加 HAR 依赖……不能据 link 成功省略这一步。
这类“工具链不会代劳的一步”是接入环节最容易踩的坑:link-harmony 成功不代表接线完成,得认准编译产物里到底有没有这个包。
另一个刻意的选择:没有改 harmony/entry/src/main/module.json5。宿主的 requestPermissions 里只有:
"requestPermissions": [
{ "name": "ohos.permission.INTERNET" },
{ "name": "ohos.permission.VIBRATE" }
]
没有 ohos.permission.CAMERA——保持原样,因为本库不需要。这一点后来还成了一个有用的旁证,见第七节。
关于换测试页
宿主用一个启动参数(rnAppKey)决定加载哪个测试页。这个参数只在冷启动时生效,所以换页必须:
hdc shell aa force-stop com.rnoh084.demo
hdc shell aa start -b com.rnoh084.demo -a EntryAbility --ps rnAppKey TorchTestApp
force-stop 不能省。少了它,应用还活着,新参数不会重新读取,你会一直在看旧页面——而且页面长得没错,很容易误判。
六、构建与运行
# 1. 装依赖
npm install
# 2. 自动链接
node_modules\.bin\react-native link-harmony # linked 9 libraries
# 3. 装 HAR(在 harmony/ 目录)
ohpm install --all
# 4. 出 bundle
npm run dev
# 5. 打 HAP
hvigorw --mode module -p product=default -p module=entry@default assembleHap --no-daemon
编译结果:
项 | 数值 |
首次 assembleHap 耗时 | 6 分 39 秒 |
HAP 变化 | 80,714,316 → 80,867,227 字节(+152,911 ≈ 149 KB) |
原生注册日志 | #RNOH_ARK ... TM created: RCTTorch |
二次重编(改了一条断言) | 6 分 27 秒,HAP 80,867,224 字节 |
两个数量级上的观察:
- 149 KB vs 2,875 字节的 HAR:HAR 本身极小,但会连带拉起相机相关的能力声明与依赖,所以最终产物增量比 HAR 大两个数量级。别拿 HAR 体积估增量。
- 编译耗时基本由 ABI / CMake 阶段决定,跟这个库没关系:加 19 行 ArkTS 不会让编译从 2 分钟变成 6 分钟,这 6 分钟是 RNOH 工程本身的量级,做集成排期时要按这个数留时间。
日志里的 #RNOH_ARK 标签说明这是 ArkTS 侧 TurboModule(不是纯 C++ TurboModule,也不是 JSI)。这个区别在排查时有用:ArkTS 模块的问题看 ArkTS 日志,纯 C++ 模块才有 #RNOH_CPP 的 TurboModuleFactory.cpp 记录。
七、验证设计:依赖硬件的库在模拟器上怎么验
这是本文最重要的一节。
7.1 问题:灯亮不亮,模拟器答不了
模拟器(Pura X View,ohos-x64)没有闪光灯硬件。这意味着一件事:
switchState(true) 之后“灯是否真的亮了”,在模拟器上永远无法验证。
如果验证方案写成“调用 switchState(true),断言返回 true”,那么在这个环境下必然失败——但这个失败不是库的问题。反过来,如果为了让测试通过而放宽断言,又会遮盖真正该测的东西。
所以验证设计的关键是换一个落点。
7.2 换落点:从"硬件是否动作"换到"行为是否正确"
既然“能不能亮”验不了,那就验能验的、而且同样重要的:
硬件不支持时,它是明确失败,还是返回 true 假装成功?
这个问题在模拟器上完全可以验证,而且它比“灯能不能亮”更接近库的真实质量——因为调用方写降级逻辑时,依赖的正是“失败能被感知到”这件事。一个在无硬件时静默返回 true 的库,会让调用方以为灯亮了,这比报错糟糕得多。
于是把验证分成三层:
层 | 能验吗 | 内容 |
A. 契约与类型 | ✅ 完全可验 | 两个方法存在、返回 Promise、权限方法解析为 true |
B. JS 侧参数守卫 | ✅ 完全可验 | 非布尔参数必须以 TypeError 拒绝(6 种取值) |
C. 硬件相关行为 | ⚠️ 只记录,不断言“应当成功” | 实测结局,重点看是明确失败还是假装成功 |
A、B 两层进通过率,C 层如实记录。这样通过率是有意义的(13 / 13),同时不掩盖硬件未验证的事实。
7.3 A 组:契约与类型(6 项,全通过)
断言 | 结果 |
switchState 是 function | ✅ |
requestCameraPermission 是 function | ✅ |
requestCameraPermission() 返回 Promise | ✅ |
requestCameraPermission() 解析为 true | ✅ |
switchState(true) 返回 Promise | ✅ |
switchState(false) 返回 Promise | ✅ |
这里有个细节让这组在无硬件时依然成立:switchState 是 async 函数,无论内部成功还是抛错,返回的一定是 Promise。 因此,“返回 Promise”这条断言与硬件无关,可以放心验证。
7.4 B 组:参数守卫(7 项,全通过)
非布尔参数必须以 TypeError 拒绝:
传入值 | 结果 |
undefined | ✅ TypeError |
null | ✅ TypeError |
0 | ✅ TypeError |
1 | ✅ TypeError |
"true" | ✅ TypeError |
{} | ✅ TypeError |
非法参数之后,合法调用不再被参数守卫拦住 | ✅ |
最后一条值得单独说,因为它让我改过一次断言。
初版我把它写成"非法参数之后调用应当成功"。结果在本机失败——因为无闪光灯时,合法调用会以 ERR_TORCH_UNAVAILABLE 拒绝。那是正确的硬件行为,不是参数问题,但我的断言把两件事混在了一起。
改判据后:只检查结果里不再出现 TypeError,不管它最终是成功还是因硬件拒绝——这样断言只依赖"参数守卫"这一个契约,与环境能力无关。
教训:测试断言不能隐含"环境具备某能力"这个前提。
断言应该只依赖被测代码的契约,不依赖环境能力。如果确实要断言环境,就把它单独成组并如实记录,而不是混进通过率里——否则通过率会随设备变化,失去意义。
7.5 C 组:硬件相关行为(如实记录)
四次调用的实测结果:
硬件|switchState(true) 拒绝:ERR_TORCH_UNAVAILABLE: flashlight mode is not supported
硬件|switchState(false) 拒绝:ERR_TORCH_UNAVAILABLE: flashlight mode is not supported
硬件|switchState(true) 第二次 拒绝:ERR_TORCH_UNAVAILABLE: flashlight mode is not supported
硬件|switchState(false) 第二次 拒绝:ERR_TORCH_UNAVAILABLE: flashlight mode is not supported
硬件|两轮结果是否一致:是
硬件|四次调用是否都以 ERR_TORCH_UNAVAILABLE 拒绝:是
硬件|硬件不支持时是否返回 true 假装成功:否(明确失败)
后两条是页面从实测结果动态推导出来的,不是写死的文案——这样它们才是断言,而不是修饰。
两个结论:
- 本机确实没有闪光灯 —— 四次调用全部走到"不支持"分支,两轮一致;
- 库的行为是对的 —— 硬件不支持时明确拒绝,没有返回 true 假装成功。
第 2 条才是本轮真正验证到的东西,而它恰好是模拟器能验证、也最应该验证的那一条。
7.6 两个独立旁证
断言之外还找了两个不依赖测试页的旁证,用来交叉验证“确实没碰硬件”。
旁证一:相机服务侧没有任何日志。
在两次调用前后抓取 hilog,没有相机服务的任何记录。这和代码路径完全一致:isTorchSupported() 检查失败就抛错了,从未执行到 setTorchMode,所以系统侧没有动作可记录。
如果代码是“先下发命令再检查结果”,就会看到系统侧有响应——日志的有无,正好反证了检查发生在命令之前。
旁证二:没有 CAMERA 权限,错误类型却是“硬件不支持”。
宿主 module.json5 里没有声明 ohos.permission.CAMERA,而 switchState 返回的是:
ERR_TORCH_UNAVAILABLE: flashlight mode is not supported
是“不支持”,不是“无权限”。如果是权限问题,报错应该是权限拒绝;既然系统先给出了硬件不支持的判断,就说明这条调用路径根本没走到权限校验——独立印证了“鸿蒙手电 API 不需要相机权限”这个结论。
这两个旁证的价值在于:它们不来自我的测试代码。一个来自系统日志,一个来自工程配置,都是外部事实。
7.7 关于测试页本身
测试页沿用了一个统一的断言夹具:每条断言用 record(组名, 描述, 期望, 实际) 记录,最后按组汇总“通过 / 失败”。硬件行为单独成组,不计入通过率。页面根节点包在 SafeAreaView 里避免被状态栏遮挡,日志统一带 [torch-test] 前缀方便过滤。
另外,C 组的两个推导结论是运行时算出来的:
四次调用是否都以 ERR_TORCH_UNAVAILABLE 拒绝:是
硬件不支持时是否返回 true 假装成功:否(明确失败)
这样做的好处是——换一台有闪光灯的设备跑,这两行会自动变成“否”和“不适用”,而不会继续打印“是”。写死的文案会骗人,动态推导不会。
八、真机验证:哪些验过了,哪些没验
这一节必须写清楚,因为这是本轮最容易被含糊过去的地方。
8.1 已验证
项 | 证据 |
两个方法的契约与类型 | A 组 6 条断言全通过 |
JS 侧参数守卫(6 种非布尔值) | B 组 7 条断言全通过 |
原生模块成功注册 | hilog 中 TM created: RCTTorch |
模块是 ArkTS 实现 | 日志标签为 #RNOH_ARK |
硬件不支持时的失败模式 | C 组:四次调用全部以 ERR_TORCH_UNAVAILABLE 明确拒绝,未返回 true 假装成功,两轮一致 |
失败发生命令下发之前 | 相机服务侧无任何 hilog 记录 |
手电 API 不需要 CAMERA 权限 | 宿主无该权限声明,错误类型仍是“硬件不支持”而非“无权限” |
编译与产物 | assembleHap 成功,HAP +149 KB |
8.2 未验证(本轮无法验证)
项 | 为什么没验 |
闪光灯是否真的会亮 | 本机没有闪光灯硬件,无法验证 |
开启 / 关闭的实际硬件状态 | 需要真机,并需要查看系统侧的手电状态事件 |
相机被其他应用占用时的失败路径 | 需要真实硬件与占用场景 |
其他 ROM / 平板 / 无闪光灯真机的差异 | 只有一台模拟器 |
亮灯时的功耗与发热 | 无硬件 |
8.3 为什么要分开写
因为这两类事的可信度完全不同。
- 把"已验证"说成"适配成功、功能正常"是过度声明——灯到底亮不亮,我没验过;
- 把"未验证"含糊成"应该没问题",会让下一位使用者踩坑;
- 反过来,把"硬件不支持时正确报错"也归入未验证,又低估了本轮的实际成果。
准确的表述是:软件层全通过,硬件动作未验证,但硬件不支持时的失败模式已确认为正确。
九、已知限制
- 模拟器没有闪光灯,"点亮"无法验证。这是本轮最大的空白,需要真机补充。
- requestCameraPermission() 恒返回 true,是兼容桩而非硬件探测。它的返回值不能当作"硬件可用"的判据。调用方应改用 switchState 是否成功来判断。
- 无权限声明的结论只在本轮环境成立。本轮验证了"不声明 CAMERA 权限也能走到硬件检查",但没有验证其他 ROM 上是否同样如此。
- 未做跨平台对照。本轮只验证鸿蒙,没有对比 Android / iOS 上同一接口的行为差异(特别是 requestCameraPermission 的弹窗语义)。
- ERR_TORCH_UNAVAILABLE 是字符串前缀,不是错误码。调用方靠自己匹配字符串来识别,匹配时要考虑前缀而非全等,否则错误文案微调就会破坏降级逻辑。
- 单设备、单次运行。只在一台模拟器上跑过,没有压力测试、没有并发调用场景、没有反复快速切换。
- 未改动库代码,也未发现缺陷。本轮唯一一次失败是我自己测试页的断言写错了(隐含了"硬件存在"的前提),修正后通过。
十、常见问题
Q1:调用 switchState(true) 报 ERR_TORCH_UNAVAILABLE,是适配坏了吗?
不一定。先确认设备有没有闪光灯——模拟器上、平板上、很多无闪光灯设备上,这个错误是正确行为。这个库的设计就是"没有闪光灯就明确报错",而不是静默成功。
Q2:requestCameraPermission() 返回 true 了,为什么 switchState 还是失败?
因为这两件事无关。true 表示"不存在权限障碍",不表示设备有闪光灯。在鸿蒙上手电 API 根本不需要相机权限,所以这个方法只是个兼容桩。
Q3:需要声明 ohos.permission.CAMERA 吗?
本库不需要,本轮实测没有声明也能正常走到硬件检查。但如果你的应用自己还要做拍照、录像,那当然要声明——那是另一条采集链路的事。
Q4:为什么 try { Torch.switchState(1) } catch {} 抓不到参数错误?
因为 JS 守卫在 async 函数里,抛出的是被拒绝的 Promise。必须 await 或 .catch():
await Torch.switchState(1).catch(e => console.log(e.message));
Q5:link-harmony 报链接成功,编译还是找不到包?
检查 harmony/entry/oh-package.json5 有没有手工加上 HAR 依赖。自动链接工具不写模块级的这一处,漏了就报找不到包——这不是链接失败,是链接没覆盖到这里。
Q6:换了测试页代码,界面还是旧的?
rnAppKey 这类启动参数只在冷启动生效。先 aa force-stop 再 aa start,否则一直在看旧页面。
Q7:怎么在自己设备上确认闪光灯到底能不能用?
别信权限方法,直接试:
try {
await Torch.switchState(true);
console.log('下发成功'); // 注意:命令被接受,不代表灯一定亮了
} catch (e) {
console.log('不可用:', e.message);
}
注意日志里的措辞——即使返回成功,也只表示"命令被系统接受";要确认灯真的亮了,得看设备本身。这个区分在交付包的 README 里也写明了。
Q8:编译要 6 分多钟,是不是这个库太重?
不是。这 6 分钟是 RNOH 工程本身 ABI / CMake 阶段的量级,跟 19 行 ArkTS 无关。HAR 只有 2,875 字节,但最终 HAP 增了 149 KB——别拿 HAR 体积估算集成增量。
小结
react-native-torch 是个小库:两个 API、19 行原生实现、不需要任何权限。但它在方法论上给了一个很典型的题目——当库依赖的硬件在你手上不存在时,验证该怎么做?
这次的做法是:
- 把验证分层。 契约与参数守卫(A、B)完全可验,计入通过率;硬件行为(C)只记录、不断言“应当成功”。
- 换验证落点。 既然“灯亮不亮”验不了,就验**“硬件不支持时是否明确失败,而不是返回 true 假装成功”**——这一条模拟器能验,而且比“能不能亮”更接近库的质量。
- 找不依赖测试代码的旁证。 相机服务无日志(证明检查发生在命令下发之前)、无 CAMERA 权限却报“硬件不支持”(证明这条路径不需要相机权限)——两个都来自系统日志和工程配置,不是我自己的断言。
- 把“已验证”和“未验证”分开写。 软件层全通过是真的,硬件动作没验也是真的,不能混成一句“适配成功”。
另外两条从实践中得来的经验:
- 断言不能隐含“环境具备某能力”。 我初版把“非法参数之后调用应当成功”写成断言,在有闪光灯的机器上会过,在这台机器上必然失败。断言只该依赖被测代码的契约,否则通过率会随设备漂移,失去意义。
- requestCameraPermission() 返回 true 不是硬件探针。 它是权限语义上的“没有障碍”,把两者混为一谈,就会在无硬件设备上做出错误决策。
还有一点关于交付包:它自己在 README 里就写清了“返回 true 不表示设备一定有闪光灯”,并且诚实声明了哪些场景是 mock 验证、哪些没做真机覆盖。 一个交付包能主动划出自己的能力边界,比它多写几个 API 有用得多——因为使用者正是靠这句话避免误判的。
本篇用到的库
项 | 内容 |
三方库 | react-native-torch(上游 1.2.0 的鸿蒙适配版) |
适配仓库 | https://atomgit.com/oh-react-native/react-native-torch |
适配 TAG | 1.2.0-ohos-1.0.0 |
ohpm 包名 | @react-native-ohos/react-native-torch |
HAR | harmony/torch.har(2,875 字节) |
原生模块名 | RCTTorch(ArkTS TurboModule,日志标签 #RNOH_ARK) |
是否需要权限 | 不需要 |
上游仓库 | https://github.com/ludo/react-native-torch(基线 commit 957db6330a0afd09455e42ce6537e0ca69518e3c,MIT) |
宿主工程 | RNOH084Demo(测试页 rnAppKey = TorchTestApp) |
接入方式:
// harmony/entry/oh-package.json5(需手工添加)
"@react-native-ohos/react-native-torch": "file:../../node_modules/react-native-torch/harmony/torch.har"
import Torch from 'react-native-torch';
await Torch.requestCameraPermission(); // 鸿蒙上恒为 true,仅表"无权限障碍"
try {
await Torch.switchState(true); // 成功只代表命令被系统接受
} catch (e) {
// 无闪光灯设备会抛 ERR_TORCH_UNAVAILABLE
console.log(String(e));
}
await Torch.switchState(false);
# 换页启动测试页(force-stop 不能省,换页参数只在冷启动生效)
hdc shell aa force-stop com.rnoh084.demo
hdc shell aa start -b com.rnoh084.demo -a EntryAbility --ps rnAppKey TorchTestApp
验证环境
项 | 版本 |
React Native | 0.84.1 |
React | 19.2.3 |
RNOH(npm / ohpm) | @react-native-oh/react-native-harmony / @rnoh/react-native-openharmony 0.84.3 |
Node.js | v24.14.0 |
DevEco Studio | 26.0.0.621 |
HarmonyOS SDK | API 26(26.0.0.32) |
设备 | HarmonyOS 7.0.0(26.0.0) Beta2 模拟器 Pura X View(ohos-x64)——无闪光灯硬件 |
宿主 HAP 产物 | entry-default-signed.hap(80.87 MB) |
本次增量构建 | assembleHap 6 分 39 秒,HAP +149 KB |
验证规模 | 设备侧软件层 13 项断言全部通过;硬件行为组 4 次调用如实记录(全部 ERR_TORCH_UNAVAILABLE) |
欢迎加入 CPF-RN 鸿蒙社区:https://atomgit.com/CPF-RN
React Native for OpenHarmony 组织:https://atomgit.com/oh-react-native
RN 三方库鸿蒙适配清单:https://atomgit.com/oh-react-native/rn-ohos-adaptation-overview
本文记录把 react-native-torch(手电筒 / 相机闪光灯控制)适配到 HarmonyOS 的完整过程,涵盖版本对齐、原生实现走查、接入宿主、构建运行,以及依赖硬件的库在模拟器上究竟该怎么验证。
react-native-torch 的 API 只有两个,却是个典型例子:它控制的是一块模拟器上根本不存在的硬件。这类库的适配难点不在代码量,而在“什么算验证通过”。
一、先说结论
项 | 结果 |
上游最新版 | 1.2.0 |
是否带原生实现 | 是(ArkTS TurboModule,不是纯 JS) |
是否依赖硬件 | 是,依赖相机闪光灯 |
公开 API | switchState(enabled)、requestCameraPermission(title?, message?),都返回 Promise |
是否需要声明权限 | 不需要(连 ohos.permission.CAMERA 都用不上) |
原生实现体量 | ArkTS 侧 19 行,HAR 包 2,875 字节 |
编译是否通过 | ✅ assembleHap 成功,HAP 从 80,714,316 增至 80,867,227 字节(+152,911 ≈ 149 KB) |
原生是否注册 | ✅ 日志 TM created: RCTTorch |
软件层断言 | ✅ 13 / 13 通过(契约 6 + 参数守卫 7) |
硬件层结论 | ⚠️ 本机没有闪光灯,四次调用全部以 ERR_TORCH_UNAVAILABLE 拒绝——没有返回 true 假装成功 |
一句话结论:这个库不需要权限、实现只有 19 行、接入后正常注册;但它要控制的闪光灯在这台设备上并不存在,所以“灯亮不亮”无法在本轮验证——本轮真正验证到的,是它在硬件不支持时会明确报错,而不是假装成功。这两件事必须分开说。
二、判定过程
适配前先回答三个问题。
2.1 这个库需要鸿蒙化吗
看源码就知道必须:
// src/NativeTorch.ts
import type {TurboModule} from 'react-native';
import {TurboModuleRegistry} from 'react-native';
export interface Spec extends TurboModule {
switchState(enabled: boolean): Promise<boolean>;
requestCameraPermission(title?: string, message?: string): Promise<boolean>;
}
export default TurboModuleRegistry.getEnforcing<Spec>('RCTTorch');
getEnforcing 意味着取不到原生模块就直接抛错,没有 JS 兜底实现。而 react-native-torch 上游只提供 Android 与 iOS 原生代码,鸿蒙上没有 RCTTorch 这个名字的模块——所以必须补一个鸿蒙原生实现。
这里和纯 JS 库的分界线很清楚:纯 JS 库只要能跑通就直接可用,而这类库不补原生实现就必然报错。
2.2 上游版本核对
npm view react-native-torch version # 1.2.0
拿到鸿蒙适配包后对比到上游基线,版本一致,没有落后。
2.3 交付包里该有的信息都在
spec.json 里有 upstream 仓库地址,也有 upstreamCommit 基线 commit——两样都记了,意味着后续上游改动时能确定该合到哪个点,这次适配的可追溯性是完整的。
validation 里还明确写了能力边界,其中一条很关键:
返回 true 不表示设备一定有闪光灯。
交付包自己先把这件事说清楚了,这一点很重要——它没有让调用方误以为“switchState 返回 true = 灯亮了”。
三、适配实现:先查支持性,再下发命令
原生实现全文:
// harmony/torch/src/main/ets/RCTTorchTurboModule.ts
import camera from '@ohos.multimedia.camera';
import { UITurboModule } from '@rnoh/react-native-openharmony/ts';
export class RCTTorchTurboModule extends UITurboModule {
async switchState(enabled: boolean): Promise<boolean> {
const manager = camera.getCameraManager(this.ctx.uiAbilityContext);
const mode = enabled ? camera.TorchMode.ON : camera.TorchMode.OFF;
if (!manager.isTorchSupported() || !manager.isTorchModeSupported(mode)) {
throw new Error('ERR_TORCH_UNAVAILABLE: flashlight mode is not supported');
}
manager.setTorchMode(mode);
return true;
}
async requestCameraPermission(): Promise<boolean> {
// HarmonyOS CameraManager torch APIs do not require CAMERA permission
// or open a camera input.
return true;
}
}
三个设计点值得展开。
3.1 支持性检查必须在下发命令之前
if (!manager.isTorchSupported() || !manager.isTorchModeSupported(mode)) {
throw new Error('ERR_TORCH_UNAVAILABLE: ...');
}
manager.setTorchMode(mode);
这里做了两层检查:
- isTorchSupported() —— 这台设备有没有闪光灯;
- isTorchModeSupported(mode) —— 这个模式(ON / OFF)支不支持。
两层检查是必要的,因为有些设备的闪光灯只支持常亮、不支持闪烁(或反过来),“有闪光灯”并不等于“这个模式可用”。
检查失败时抛出的是带前缀的明确错误(ERR_TORCH_UNAVAILABLE),调用方能靠字符串识别并走降级逻辑。这一点比“静默返回 false”好得多——静默失败会让调用方以为操作成功了。
3.2 它不打开相机、不采集图像
实现里出现的相机 API 只有四个:
getCameraManager / isTorchSupported / isTorchModeSupported / setTorchMode
没有 createCameraInput、没有 createCaptureSession、没有预览流。这是静态就能查清的事实,也是理解“为什么不需要 CAMERA 权限”的钥匙——它只借用 CameraManager 的手电接口,不碰相机采集链路。
3.3 JS 侧的参数守卫是"被拒绝的 Promise"
上游 JS 层保留了类型校验:
async function switchState(enabled: boolean): Promise<boolean> {
if (typeof enabled !== 'boolean') {
throw new TypeError('Torch.switchState expects a boolean');
}
return NativeTorch.switchState(enabled);
}
注意它在 async 函数里,所以抛出的是一个被拒绝的 Promise,而不是同步异常。这意味着:
// ❌ 抓不到
try { Torch.switchState(1); } catch (e) {}
// ✅ 这样才抓得到
await Torch.switchState(1).catch(e => console.log(e.message));
这个细节在本轮验证中被实际确认过——参数守卫组的断言必须用 await 才能捕获到 TypeError。调用方如果按同步异常来写 try/catch,会漏掉所有参数错误。
四、requestCameraPermission() 为什么直接返回 true
这个方法最容易引起误解。它在 Android 上的语义是弹出系统相机授权框,所以返回值表示“用户授权了没有”。到了鸿蒙,实现直接写成:
async requestCameraPermission(): Promise<boolean> {
return true;
}
为什么可以这样写?
因为鸿蒙的手电筒 API 走的是 CameraManager,既不要求声明 ohos.permission.CAMERA,也不打开相机输入。既然没有权限可申请,也就没有授权框可弹——直接返回 true 表示"没有权限障碍,你继续调 switchState 就行"。
这个 true 的含义要读准:
它表示 | 它不表示 |
不存在权限障碍 | 设备一定有闪光灯 |
可以继续调用 switchState | 闪光灯能被点亮 |
保留了上游接口的兼容性 | Android 上的授权弹窗行为也一致 |
交付包 README 专门写了这个限定,这一点做得很诚实。如果调用方把 requestCameraPermission() === true 当成"硬件可用"的判据,就会在无闪光灯设备上做出错误决策——正确的判据是 switchState 是否成功,而不是权限方法是否返回 true。
五、接入宿主
宿主工程是一个已经运行起来的 RNOH 示例应用。接入这个库要改四处:
文件 | 改动 |
package.json | 加 "react-native-torch": "file:../react-native-torch" |
metro.config.js | watchFolders 增加该库的绝对路径 |
index.js | import 测试页并注册 |
harmony/entry/oh-package.json5 | 手工加 HAR 依赖 |
第四处是唯一需要留神的:
"@react-native-ohos/react-native-torch": "file:../../node_modules/react-native-torch/harmony/torch.har"
自动链接工具不会写这一处。它会把 package.json 的依赖关系和 Metro 的模块解析处理好,日志里也会报 linked 9 libraries,但模块级的 oh-package.json5 依赖需要手工补——漏了这一步,编译期就会在 CMake / ohpm 阶段报找不到包。
这一点交付包的 README 里也专门提醒了,写成了一句很实在的话:
若宿主 CMake 的 OH_MODULES_DIR 指向 entry/oh_modules,还需在 harmony/entry/oh-package.json5 添加 HAR 依赖……不能据 link 成功省略这一步。
这类“工具链不会代劳的一步”是接入环节最容易踩的坑:link-harmony 成功不代表接线完成,得认准编译产物里到底有没有这个包。
另一个刻意的选择:没有改 harmony/entry/src/main/module.json5。宿主的 requestPermissions 里只有:
"requestPermissions": [
{ "name": "ohos.permission.INTERNET" },
{ "name": "ohos.permission.VIBRATE" }
]
没有 ohos.permission.CAMERA——保持原样,因为本库不需要。这一点后来还成了一个有用的旁证,见第七节。
关于换测试页
宿主用一个启动参数(rnAppKey)决定加载哪个测试页。这个参数只在冷启动时生效,所以换页必须:
hdc shell aa force-stop com.rnoh084.demo
hdc shell aa start -b com.rnoh084.demo -a EntryAbility --ps rnAppKey TorchTestApp
force-stop 不能省。少了它,应用还活着,新参数不会重新读取,你会一直在看旧页面——而且页面长得没错,很容易误判。
六、构建与运行
# 1. 装依赖
npm install
# 2. 自动链接
node_modules\.bin\react-native link-harmony # linked 9 libraries
# 3. 装 HAR(在 harmony/ 目录)
ohpm install --all
# 4. 出 bundle
npm run dev
# 5. 打 HAP
hvigorw --mode module -p product=default -p module=entry@default assembleHap --no-daemon
编译结果:
项 | 数值 |
首次 assembleHap 耗时 | 6 分 39 秒 |
HAP 变化 | 80,714,316 → 80,867,227 字节(+152,911 ≈ 149 KB) |
原生注册日志 | #RNOH_ARK ... TM created: RCTTorch |
二次重编(改了一条断言) | 6 分 27 秒,HAP 80,867,224 字节 |
两个数量级上的观察:
- 149 KB vs 2,875 字节的 HAR:HAR 本身极小,但会连带拉起相机相关的能力声明与依赖,所以最终产物增量比 HAR 大两个数量级。别拿 HAR 体积估增量。
- 编译耗时基本由 ABI / CMake 阶段决定,跟这个库没关系:加 19 行 ArkTS 不会让编译从 2 分钟变成 6 分钟,这 6 分钟是 RNOH 工程本身的量级,做集成排期时要按这个数留时间。
日志里的 #RNOH_ARK 标签说明这是 ArkTS 侧 TurboModule(不是纯 C++ TurboModule,也不是 JSI)。这个区别在排查时有用:ArkTS 模块的问题看 ArkTS 日志,纯 C++ 模块才有 #RNOH_CPP 的 TurboModuleFactory.cpp 记录。
七、验证设计:依赖硬件的库在模拟器上怎么验
这是本文最重要的一节。
7.1 问题:灯亮不亮,模拟器答不了
模拟器(Pura X View,ohos-x64)没有闪光灯硬件。这意味着一件事:
switchState(true) 之后“灯是否真的亮了”,在模拟器上永远无法验证。
如果验证方案写成“调用 switchState(true),断言返回 true”,那么在这个环境下必然失败——但这个失败不是库的问题。反过来,如果为了让测试通过而放宽断言,又会遮盖真正该测的东西。
所以验证设计的关键是换一个落点。
7.2 换落点:从"硬件是否动作"换到"行为是否正确"
既然“能不能亮”验不了,那就验能验的、而且同样重要的:
硬件不支持时,它是明确失败,还是返回 true 假装成功?
这个问题在模拟器上完全可以验证,而且它比“灯能不能亮”更接近库的真实质量——因为调用方写降级逻辑时,依赖的正是“失败能被感知到”这件事。一个在无硬件时静默返回 true 的库,会让调用方以为灯亮了,这比报错糟糕得多。
于是把验证分成三层:
层 | 能验吗 | 内容 |
A. 契约与类型 | ✅ 完全可验 | 两个方法存在、返回 Promise、权限方法解析为 true |
B. JS 侧参数守卫 | ✅ 完全可验 | 非布尔参数必须以 TypeError 拒绝(6 种取值) |
C. 硬件相关行为 | ⚠️ 只记录,不断言“应当成功” | 实测结局,重点看是明确失败还是假装成功 |
A、B 两层进通过率,C 层如实记录。这样通过率是有意义的(13 / 13),同时不掩盖硬件未验证的事实。
7.3 A 组:契约与类型(6 项,全通过)
断言 | 结果 |
switchState 是 function | ✅ |
requestCameraPermission 是 function | ✅ |
requestCameraPermission() 返回 Promise | ✅ |
requestCameraPermission() 解析为 true | ✅ |
switchState(true) 返回 Promise | ✅ |
switchState(false) 返回 Promise | ✅ |
这里有个细节让这组在无硬件时依然成立:switchState 是 async 函数,无论内部成功还是抛错,返回的一定是 Promise。 因此,“返回 Promise”这条断言与硬件无关,可以放心验证。
7.4 B 组:参数守卫(7 项,全通过)
非布尔参数必须以 TypeError 拒绝:
传入值 | 结果 |
undefined | ✅ TypeError |
null | ✅ TypeError |
0 | ✅ TypeError |
1 | ✅ TypeError |
"true" | ✅ TypeError |
{} | ✅ TypeError |
非法参数之后,合法调用不再被参数守卫拦住 | ✅ |
最后一条值得单独说,因为它让我改过一次断言。
初版我把它写成"非法参数之后调用应当成功"。结果在本机失败——因为无闪光灯时,合法调用会以 ERR_TORCH_UNAVAILABLE 拒绝。那是正确的硬件行为,不是参数问题,但我的断言把两件事混在了一起。
改判据后:只检查结果里不再出现 TypeError,不管它最终是成功还是因硬件拒绝——这样断言只依赖"参数守卫"这一个契约,与环境能力无关。
教训:测试断言不能隐含"环境具备某能力"这个前提。
断言应该只依赖被测代码的契约,不依赖环境能力。如果确实要断言环境,就把它单独成组并如实记录,而不是混进通过率里——否则通过率会随设备变化,失去意义。
7.5 C 组:硬件相关行为(如实记录)
四次调用的实测结果:
硬件|switchState(true) 拒绝:ERR_TORCH_UNAVAILABLE: flashlight mode is not supported
硬件|switchState(false) 拒绝:ERR_TORCH_UNAVAILABLE: flashlight mode is not supported
硬件|switchState(true) 第二次 拒绝:ERR_TORCH_UNAVAILABLE: flashlight mode is not supported
硬件|switchState(false) 第二次 拒绝:ERR_TORCH_UNAVAILABLE: flashlight mode is not supported
硬件|两轮结果是否一致:是
硬件|四次调用是否都以 ERR_TORCH_UNAVAILABLE 拒绝:是
硬件|硬件不支持时是否返回 true 假装成功:否(明确失败)
后两条是页面从实测结果动态推导出来的,不是写死的文案——这样它们才是断言,而不是修饰。
两个结论:
- 本机确实没有闪光灯 —— 四次调用全部走到"不支持"分支,两轮一致;
- 库的行为是对的 —— 硬件不支持时明确拒绝,没有返回 true 假装成功。
第 2 条才是本轮真正验证到的东西,而它恰好是模拟器能验证、也最应该验证的那一条。
7.6 两个独立旁证
断言之外还找了两个不依赖测试页的旁证,用来交叉验证“确实没碰硬件”。
旁证一:相机服务侧没有任何日志。
在两次调用前后抓取 hilog,没有相机服务的任何记录。这和代码路径完全一致:isTorchSupported() 检查失败就抛错了,从未执行到 setTorchMode,所以系统侧没有动作可记录。
如果代码是“先下发命令再检查结果”,就会看到系统侧有响应——日志的有无,正好反证了检查发生在命令之前。
旁证二:没有 CAMERA 权限,错误类型却是“硬件不支持”。
宿主 module.json5 里没有声明 ohos.permission.CAMERA,而 switchState 返回的是:
ERR_TORCH_UNAVAILABLE: flashlight mode is not supported
是“不支持”,不是“无权限”。如果是权限问题,报错应该是权限拒绝;既然系统先给出了硬件不支持的判断,就说明这条调用路径根本没走到权限校验——独立印证了“鸿蒙手电 API 不需要相机权限”这个结论。
这两个旁证的价值在于:它们不来自我的测试代码。一个来自系统日志,一个来自工程配置,都是外部事实。
7.7 关于测试页本身
测试页沿用了一个统一的断言夹具:每条断言用 record(组名, 描述, 期望, 实际) 记录,最后按组汇总“通过 / 失败”。硬件行为单独成组,不计入通过率。页面根节点包在 SafeAreaView 里避免被状态栏遮挡,日志统一带 [torch-test] 前缀方便过滤。
另外,C 组的两个推导结论是运行时算出来的:
四次调用是否都以 ERR_TORCH_UNAVAILABLE 拒绝:是
硬件不支持时是否返回 true 假装成功:否(明确失败)
这样做的好处是——换一台有闪光灯的设备跑,这两行会自动变成“否”和“不适用”,而不会继续打印“是”。写死的文案会骗人,动态推导不会。
八、真机验证:哪些验过了,哪些没验
这一节必须写清楚,因为这是本轮最容易被含糊过去的地方。
8.1 已验证
项 | 证据 |
两个方法的契约与类型 | A 组 6 条断言全通过 |
JS 侧参数守卫(6 种非布尔值) | B 组 7 条断言全通过 |
原生模块成功注册 | hilog 中 TM created: RCTTorch |
模块是 ArkTS 实现 | 日志标签为 #RNOH_ARK |
硬件不支持时的失败模式 | C 组:四次调用全部以 ERR_TORCH_UNAVAILABLE 明确拒绝,未返回 true 假装成功,两轮一致 |
失败发生命令下发之前 | 相机服务侧无任何 hilog 记录 |
手电 API 不需要 CAMERA 权限 | 宿主无该权限声明,错误类型仍是“硬件不支持”而非“无权限” |
编译与产物 | assembleHap 成功,HAP +149 KB |
8.2 未验证(本轮无法验证)
项 | 为什么没验 |
闪光灯是否真的会亮 | 本机没有闪光灯硬件,无法验证 |
开启 / 关闭的实际硬件状态 | 需要真机,并需要查看系统侧的手电状态事件 |
相机被其他应用占用时的失败路径 | 需要真实硬件与占用场景 |
其他 ROM / 平板 / 无闪光灯真机的差异 | 只有一台模拟器 |
亮灯时的功耗与发热 | 无硬件 |
8.3 为什么要分开写
因为这两类事的可信度完全不同。
- 把"已验证"说成"适配成功、功能正常"是过度声明——灯到底亮不亮,我没验过;
- 把"未验证"含糊成"应该没问题",会让下一位使用者踩坑;
- 反过来,把"硬件不支持时正确报错"也归入未验证,又低估了本轮的实际成果。
准确的表述是:软件层全通过,硬件动作未验证,但硬件不支持时的失败模式已确认为正确。
九、已知限制
- 模拟器没有闪光灯,"点亮"无法验证。这是本轮最大的空白,需要真机补充。
- requestCameraPermission() 恒返回 true,是兼容桩而非硬件探测。它的返回值不能当作"硬件可用"的判据。调用方应改用 switchState 是否成功来判断。
- 无权限声明的结论只在本轮环境成立。本轮验证了"不声明 CAMERA 权限也能走到硬件检查",但没有验证其他 ROM 上是否同样如此。
- 未做跨平台对照。本轮只验证鸿蒙,没有对比 Android / iOS 上同一接口的行为差异(特别是 requestCameraPermission 的弹窗语义)。
- ERR_TORCH_UNAVAILABLE 是字符串前缀,不是错误码。调用方靠自己匹配字符串来识别,匹配时要考虑前缀而非全等,否则错误文案微调就会破坏降级逻辑。
- 单设备、单次运行。只在一台模拟器上跑过,没有压力测试、没有并发调用场景、没有反复快速切换。
- 未改动库代码,也未发现缺陷。本轮唯一一次失败是我自己测试页的断言写错了(隐含了"硬件存在"的前提),修正后通过。
十、常见问题
Q1:调用 switchState(true) 报 ERR_TORCH_UNAVAILABLE,是适配坏了吗?
不一定。先确认设备有没有闪光灯——模拟器上、平板上、很多无闪光灯设备上,这个错误是正确行为。这个库的设计就是"没有闪光灯就明确报错",而不是静默成功。
Q2:requestCameraPermission() 返回 true 了,为什么 switchState 还是失败?
因为这两件事无关。true 表示"不存在权限障碍",不表示设备有闪光灯。在鸿蒙上手电 API 根本不需要相机权限,所以这个方法只是个兼容桩。
Q3:需要声明 ohos.permission.CAMERA 吗?
本库不需要,本轮实测没有声明也能正常走到硬件检查。但如果你的应用自己还要做拍照、录像,那当然要声明——那是另一条采集链路的事。
Q4:为什么 try { Torch.switchState(1) } catch {} 抓不到参数错误?
因为 JS 守卫在 async 函数里,抛出的是被拒绝的 Promise。必须 await 或 .catch():
await Torch.switchState(1).catch(e => console.log(e.message));
Q5:link-harmony 报链接成功,编译还是找不到包?
检查 harmony/entry/oh-package.json5 有没有手工加上 HAR 依赖。自动链接工具不写模块级的这一处,漏了就报找不到包——这不是链接失败,是链接没覆盖到这里。
Q6:换了测试页代码,界面还是旧的?
rnAppKey 这类启动参数只在冷启动生效。先 aa force-stop 再 aa start,否则一直在看旧页面。
Q7:怎么在自己设备上确认闪光灯到底能不能用?
别信权限方法,直接试:
try {
await Torch.switchState(true);
console.log('下发成功'); // 注意:命令被接受,不代表灯一定亮了
} catch (e) {
console.log('不可用:', e.message);
}
注意日志里的措辞——即使返回成功,也只表示"命令被系统接受";要确认灯真的亮了,得看设备本身。这个区分在交付包的 README 里也写明了。
Q8:编译要 6 分多钟,是不是这个库太重?
不是。这 6 分钟是 RNOH 工程本身 ABI / CMake 阶段的量级,跟 19 行 ArkTS 无关。HAR 只有 2,875 字节,但最终 HAP 增了 149 KB——别拿 HAR 体积估算集成增量。
小结
react-native-torch 是个小库:两个 API、19 行原生实现、不需要任何权限。但它在方法论上给了一个很典型的题目——当库依赖的硬件在你手上不存在时,验证该怎么做?
这次的做法是:
- 把验证分层。 契约与参数守卫(A、B)完全可验,计入通过率;硬件行为(C)只记录、不断言“应当成功”。
- 换验证落点。 既然“灯亮不亮”验不了,就验**“硬件不支持时是否明确失败,而不是返回 true 假装成功”**——这一条模拟器能验,而且比“能不能亮”更接近库的质量。
- 找不依赖测试代码的旁证。 相机服务无日志(证明检查发生在命令下发之前)、无 CAMERA 权限却报“硬件不支持”(证明这条路径不需要相机权限)——两个都来自系统日志和工程配置,不是我自己的断言。
- 把“已验证”和“未验证”分开写。 软件层全通过是真的,硬件动作没验也是真的,不能混成一句“适配成功”。
另外两条从实践中得来的经验:
- 断言不能隐含“环境具备某能力”。 我初版把“非法参数之后调用应当成功”写成断言,在有闪光灯的机器上会过,在这台机器上必然失败。断言只该依赖被测代码的契约,否则通过率会随设备漂移,失去意义。
- requestCameraPermission() 返回 true 不是硬件探针。 它是权限语义上的“没有障碍”,把两者混为一谈,就会在无硬件设备上做出错误决策。
还有一点关于交付包:它自己在 README 里就写清了“返回 true 不表示设备一定有闪光灯”,并且诚实声明了哪些场景是 mock 验证、哪些没做真机覆盖。 一个交付包能主动划出自己的能力边界,比它多写几个 API 有用得多——因为使用者正是靠这句话避免误判的。
本篇用到的库
项 | 内容 |
三方库 | react-native-torch(上游 1.2.0 的鸿蒙适配版) |
适配仓库 | https://atomgit.com/oh-react-native/react-native-torch |
适配 TAG | 1.2.0-ohos-1.0.0 |
ohpm 包名 | @react-native-ohos/react-native-torch |
HAR | harmony/torch.har(2,875 字节) |
原生模块名 | RCTTorch(ArkTS TurboModule,日志标签 #RNOH_ARK) |
是否需要权限 | 不需要 |
上游仓库 | https://github.com/ludo/react-native-torch(基线 commit 957db6330a0afd09455e42ce6537e0ca69518e3c,MIT) |
宿主工程 | RNOH084Demo(测试页 rnAppKey = TorchTestApp) |
接入方式:
// harmony/entry/oh-package.json5(需手工添加)
"@react-native-ohos/react-native-torch": "file:../../node_modules/react-native-torch/harmony/torch.har"
import Torch from 'react-native-torch';
await Torch.requestCameraPermission(); // 鸿蒙上恒为 true,仅表"无权限障碍"
try {
await Torch.switchState(true); // 成功只代表命令被系统接受
} catch (e) {
// 无闪光灯设备会抛 ERR_TORCH_UNAVAILABLE
console.log(String(e));
}
await Torch.switchState(false);
# 换页启动测试页(force-stop 不能省,换页参数只在冷启动生效)
hdc shell aa force-stop com.rnoh084.demo
hdc shell aa start -b com.rnoh084.demo -a EntryAbility --ps rnAppKey TorchTestApp
验证环境
项 | 版本 |
React Native | 0.84.1 |
React | 19.2.3 |
RNOH(npm / ohpm) | @react-native-oh/react-native-harmony / @rnoh/react-native-openharmony 0.84.3 |
Node.js | v24.14.0 |
DevEco Studio | 26.0.0.621 |
HarmonyOS SDK | API 26(26.0.0.32) |
设备 | HarmonyOS 7.0.0(26.0.0) Beta2 模拟器 Pura X View(ohos-x64)——无闪光灯硬件 |
宿主 HAP 产物 | entry-default-signed.hap(80.87 MB) |
本次增量构建 | assembleHap 6 分 39 秒,HAP +149 KB |
验证规模 | 设备侧软件层 13 项断言全部通过;硬件行为组 4 次调用如实记录(全部 ERR_TORCH_UNAVAILABLE) |
欢迎加入 CPF-RN 鸿蒙社区:https://atomgit.com/CPF-RN
React Native for OpenHarmony 组织:https://atomgit.com/oh-react-native
RN 三方库鸿蒙适配清单:https://atomgit.com/oh-react-native/rn-ohos-adaptation-overview
收起阅读 »DCloud 发文前,先检查这三个环节
先核对登录账户和发布分类
这是一篇用于验证码友推广 DCloud 发布功能的测试文章。正式发布前,应核对当前登录账户,选择与正文相关的分类,并检查标题、正文、话题与配图。
按正文需要整理配图
配图上传后使用平台返回的附件编号插入正文,避免把本机路径提交到社区。发布前确认图片位置与预览一致,代码示例及列表应保留原来的结构。
const message = "ready";
console.log(message);
网络异常时先核对结果
如果提交超时,应先到我的文章列表核对结果。已经提交成功或结果尚未确认的文章不应再次提交,以免产生重复内容。本文仅用于功能验证,不包含推广链接或产品效果承诺。
先核对登录账户和发布分类
这是一篇用于验证码友推广 DCloud 发布功能的测试文章。正式发布前,应核对当前登录账户,选择与正文相关的分类,并检查标题、正文、话题与配图。
按正文需要整理配图
配图上传后使用平台返回的附件编号插入正文,避免把本机路径提交到社区。发布前确认图片位置与预览一致,代码示例及列表应保留原来的结构。
const message = "ready";
console.log(message);
网络异常时先核对结果
如果提交超时,应先到我的文章列表核对结果。已经提交成功或结果尚未确认的文章不应再次提交,以免产生重复内容。本文仅用于功能验证,不包含推广链接或产品效果承诺。
收起阅读 »HBuilderX 5.26 鸿蒙生成本地安装包以后,打包成APP白屏问题
overwrite: true,
});
} ```
overwrite: true,
});
} ``` 收起阅读 »
原创vue3+vite8+tauri2.12跨平台聊天实战
最新原创基于Tauri 2.12、Rust、Vite 8.3、Vue 3、Pinia 4及ElementPlus构建的跨平台仿微信桌面聊天程序。本次升级更新了全部依赖、重写封装Tauri多窗口管理、修复窗口重复创建问题及提升新窗口响应性能。
项目实现技术
- 跨平台框架:Tauri2.12.0
- 前端技术框架:vite^8.3.4+vue^3.5.43+vue-router^5.4.0
- 状态管理:pinia^4.0.3
- 本地存储:pinia-plugin-persistedstate^4.7.1
- 组件库:element-plus^2.14.7
- 样式管理:sass^1.105.0
- 短视频滑动组件:swiper^14.3.0
项目目录结构
使用最新跨平台框架tauri2.12结合vite8+vue3创建项目模板。
tauri-wechat客户端聊天项目已经正式的发布到我的原创小店,欢迎下载使用哈~
Tauri2.12+Vite8+ElementPlus桌面聊天Exe程序
作者:xiaoyan2017
链接:https://www.cnblogs.com/xiaoyan2017/p/23249495
来源:博客园
本文原创发布于博客园 ,转载请注明出处,谢谢合作
最新原创基于Tauri 2.12、Rust、Vite 8.3、Vue 3、Pinia 4及ElementPlus构建的跨平台仿微信桌面聊天程序。本次升级更新了全部依赖、重写封装Tauri多窗口管理、修复窗口重复创建问题及提升新窗口响应性能。
项目实现技术
- 跨平台框架:Tauri2.12.0
- 前端技术框架:vite^8.3.4+vue^3.5.43+vue-router^5.4.0
- 状态管理:pinia^4.0.3
- 本地存储:pinia-plugin-persistedstate^4.7.1
- 组件库:element-plus^2.14.7
- 样式管理:sass^1.105.0
- 短视频滑动组件:swiper^14.3.0
项目目录结构
使用最新跨平台框架tauri2.12结合vite8+vue3创建项目模板。
tauri-wechat客户端聊天项目已经正式的发布到我的原创小店,欢迎下载使用哈~
Tauri2.12+Vite8+ElementPlus桌面聊天Exe程序
作者:xiaoyan2017
链接:https://www.cnblogs.com/xiaoyan2017/p/23249495
来源:博客园
本文原创发布于博客园 ,转载请注明出处,谢谢合作
专业金融K线图表原生插件 Chen KChart Pro
Chen-KChartPro
UniApp Android/iOS 专业 K 线图原生插件
Chen-KChartPro 是基于 Flutter KChart 渲染引擎封装的 uni-app App 原生插件,提供 Android 和 iOS 双端一致的 K 线、深度图、技术指标、绘图工具及交易叠加能力。
插件提供两种使用方式:
- 使用
<chen-kchart-pro>将 KChart 作为原生组件嵌入.nvue页面,页面、行情数据和工具栏由 uni-app 业务层控制。 - 使用
openDemo()直接打开完整 Flutter KChart Demo,用于功能体验和效果对照。
如有接入问题、功能需求或商业合作,请添加VX:
Chen-Taurus-0510(添加时请备注 KChart)。
在线体验
| 类型 | 地址 | 说明 |
|---|---|---|
| Web 效果预览 | 打开 KChart 在线演示 | 用于 PC、H5 预览 KChart 功能和交互效果 |
| Android Demo | 下载 KChart APK | 下载并安装到 Android 手机体验完整 Demo |
平台要求
| 平台 | 最低版本 | 接入方式 |
|---|---|---|
| Android | API 21 | AAR 本地原生插件 |
| iOS | iOS 13.0 | Framework 本地原生插件 |
当前插件版本:1.0.0。
主要功能
- 支持蜡烛图、分时图、深度图和成交量图。
- 支持 MA、EMA、BOLL、SAR、MACD、KDJ、RSI、WR、CCI、OBV、StochRSI 等指标。
- 支持拖动浏览、双指缩放、十字线、回到最新、指定时间跳转和自定义价格范围。
- 支持趋势线、趋势角、箭头、垂直线、水平线、水平射线、射线和十字线绘图。
- 支持磁铁吸附、连续绘图、撤销、重做、清空和绘图数据恢复。
- 支持买卖 B/S 标记、订单线、持仓线、开仓价、强平价、止盈止损和未实现盈亏。
- 支持历史行情分页、实时 K 线更新、亮色/深色主题和色觉友好模式。
- 支持通过 Props、ref 方法和事件与 uni-app 页面进行双向交互。
插件接入
1. 复制插件文件
将以下目录完整复制到业务项目:
nativeplugins/Chen-KChartPro/
uni_modules/chen-kchart/
其中:
nativeplugins/Chen-KChartPro包含 Android/iOS 原生插件产物。uni_modules/chen-kchart包含可直接在.nvue页面使用的组件封装。
2. 配置本地原生插件
在 HBuilderX 中打开:
manifest.json -> App 原生插件配置 -> 选择本地插件
勾选 Chen-KChartPro,保存后重新制作 Android/iOS 自定义基座。新增或更新本地原生插件后,旧基座不会自动包含新插件,必须重新制作基座或重新云打包。
3. 页面中使用组件
该组件用于 uni-app App 端 .nvue 页面。将组件放入页面并设置明确的宽高:
<template>
<view class="page">
<chen-kchart-pro
ref="chart"
:data="bars"
:options="chartOptions"
theme="light"
mainIndicator="MA"
secondaryIndicator="KDJ"
:showVolume="true"
:tradeMarkers="tradeMarkers"
:tradingOverlay="tradingOverlay"
chartStyle="width:750rpx;height:900rpx;"
@ready="onChartReady"
@crosshair="onCrosshair"
@viewport-change="onViewportChange"
@load-more="onLoadMore"
@error="onChartError"
/>
</view>
</template>
<script>
export default {
data() {
return {
bars: [],
tradeMarkers: [],
tradingOverlay: {},
chartOptions: {
showNowPrice: true,
showInfoDialog: true,
enableDrawingTools: true,
precision: 4,
minScale: 0.35,
maxScale: 5
}
}
},
methods: {
onChartReady() {
this.$refs.chart.setData(this.bars)
},
onCrosshair(event) {
console.log('crosshair:', event)
},
onViewportChange(event) {
console.log('viewport:', event)
},
onLoadMore(event) {
if (event && event.history) this.loadHistory()
},
onChartError(event) {
console.error('KChart error:', event)
}
}
}
</script>
K 线数据格式
每根 K 线至少需要以下字段:
{
time: 1790481600000, // Unix 毫秒时间戳
open: 50000,
high: 50200,
low: 49800,
close: 50100,
vol: 123.45
}
数据要求:
time使用 Unix 毫秒时间戳,建议直接使用行情服务返回的 UTC 时间戳。open、high、low、close、vol必须是有效数字。- 完整数据应按时间从旧到新排列。
- 业务端负责根据当前周期请求或生成 OHLCV,插件不负责将逐笔成交聚合为 K 线。
- 切换交易对或周期后,应重新请求数据并调用
setData()。
组件属性
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
data |
Array | [] |
K 线数据,按时间从旧到新排列 |
options |
Object | {} |
图表配置 |
theme |
String | light |
light 或 dark |
mainIndicator |
String | MA |
MA/BOLL/EMA/SAR/NONE |
secondaryIndicator |
String | KDJ |
KDJ/MACD/RSI/WR/CCI/OBV/STOCH_RSI/NONE |
showVolume |
Boolean | true |
是否显示成交量区域 |
lineMode |
Boolean | false |
是否显示分时线模式 |
mode |
String | KLINE |
KLINE 或 DEPTH |
depth |
Object | {bids: [], asks: []} |
深度图买卖盘数据 |
tradeMarkers |
Array | [] |
买入/卖出成交标记 |
tradingOverlay |
Object | {} |
订单、持仓、TP/SL 等交易叠加数据 |
chartStyle |
String | width:750rpx;height:900rpx; |
原生图表组件宽高样式 |
options 配置
| 参数 | 类型 | 说明 |
|---|---|---|
theme |
String | light/dark |
mainIndicator |
String | 主图指标 |
secondaryIndicator |
String | 副图指标 |
showVolume |
Boolean | 是否显示成交量 |
isLine |
Boolean | 是否启用分时模式 |
mode |
String | KLINE/DEPTH |
showNowPrice |
Boolean | 是否显示最新价线 |
showInfoDialog |
Boolean | 是否显示十字线详情 |
enableDrawingTools |
Boolean | 是否启用绘图能力 |
colorBlind |
Boolean | 是否启用色觉友好配色 |
candleBodyMode |
String | solid/hollow,实心或空心 K 线 |
precision |
Number | 价格小数精度,范围 0-12 |
minScale |
Number | 最小缩放比例 |
maxScale |
Number | 最大缩放比例 |
indicatorConfigs |
Array | 指标计算参数、线色、序列显隐和启停配置 |
指标设置
indicatorConfigs 会直接传入纯 Flutter KChart 指标计算和渲染层,支持
MA/EMA/BOLL/SAR/VOL/MACD/KDJ/RSI/WR/CCI/OBV/STOCH_RSI:
chartOptions: {
indicatorConfigs: [
{
id: 'BOLL',
parameters: { period: 20, multiplier: 2 },
colors: { mb: '#2962ff', up: '#ff9800', dn: '#7e57c2' },
hiddenSeries: [],
enabled: true
},
{
id: 'KDJ',
parameters: { period: 9, kSmoothing: 3, dSmoothing: 3 },
colors: { k: '#2962ff', d: '#ff9800', j: '#7e57c2' },
hiddenSeries: ['j']
}
]
}
绑定的 options 支持深度响应式更新,也可以显式调用
this.$refs.chart.setOptions(this.chartOptions)。完整设置面板实现可直接复用
pages/sample/kchart-module.nvue 中的指标设置数据结构与方法。
数据更新方法
| 方法 | 参数 | 使用场景 |
|---|---|---|
setData(bars) |
Array | 首次加载、刷新、切换交易对或周期时,全量替换数据 |
appendData(bars) |
Array | 合并历史或新增数据,插件会按时间重新排序 |
updateLastData(bar) |
Object | 实时更新当前最新 K 线,时间相同则替换,否则追加 |
// 第一次加载或切换周期
this.$refs.chart.setData(bars)
// 加载更早历史数据
this.$refs.chart.appendData(olderBars)
// WebSocket/轮询更新最新 K 线
this.$refs.chart.updateLastData(realtimeBar)
注意:
appendData()不应传入已经存在的重复时间数据,业务端应先按time去重。updateLastData()只用于当前最后一根或新生成的一根 K 线,不要传入早于当前最后时间的数据。- Props 响应式更新和 ref 方法更新应选择一种,不要在同一次行情更新中同时修改
:data并调用updateLastData(),否则会同时触发全量和增量更新。 - 用户正在浏览历史区域时,实时更新不会强制拉回最新位置;可通过
viewport-change的isAtLatest判断当前状态。
图表控制方法
基础配置
| 方法 | 参数 | 说明 |
|---|---|---|
setOptions(options) |
Object | 批量更新图表配置 |
setTheme(theme) |
String | 设置 light/dark 主题 |
setMainIndicator(value) |
String | 设置主图指标 |
setSecondaryIndicator(value) |
String | 设置副图指标 |
setShowVolume(value) |
Boolean | 显示或隐藏成交量 |
setLineMode(value) |
Boolean | 切换分时模式 |
setChartMode(value) |
String | 切换 KLINE/DEPTH |
深度与交易
| 方法 | 参数 | 说明 |
|---|---|---|
setDepthData(data) |
Object | 设置 {bids, asks} 深度数据 |
setTradeMarkers(markers) |
Array | 设置成交 B/S 标记 |
setTradingOverlay(overlay) |
Object | 设置订单、持仓、止盈止损等交易线 |
绘图工具
| 方法 | 参数 | 说明 |
|---|---|---|
setDrawingTool(type) |
String | 设置当前绘图工具,NONE 取消当前工具 |
setDrawingMode(enabled) |
Boolean | 开启或关闭绘图模式 |
setDrawingOptions(options) |
Object | 设置连续绘图、磁铁、颜色、线宽等 |
setDrawings(drawings) |
Array | 恢复序列化的绘图数据 |
clearDrawings() |
无 | 清除全部绘图 |
undoDrawing() |
无 | 撤销 |
redoDrawing() |
无 | 重做 |
支持的绘图类型:
trendLine、trendAngle、arrow、verticalLine、horizontalLine、
horizontalRay、ray、crossLine、NONE
setDrawingOptions() 支持:
this.$refs.chart.setDrawingOptions({
continuous: true,
magnet: true,
magnetThreshold: 50,
color: '#f2b84b',
strokeWidth: 2,
lineStyle: 'solid', // solid/dashed/dotted
visible: true
})
视口与十字线
| 方法 | 参数 | 说明 |
|---|---|---|
zoomIn() |
无 | 放大图表 |
zoomOut() |
无 | 缩小图表 |
resetViewport() |
无 | 重置缩放并回到最新位置 |
fitContent() |
无 | 自适应显示全部数据 |
scrollToLatest() |
无 | 滚动到最新 K 线 |
jumpToIndex(index) |
Number | 跳转到指定数据索引 |
jumpToTime(time) |
Number | 跳转到指定 Unix 毫秒时间 |
setLogicalRange(range) |
Object | 设置 {from, to} 逻辑范围 |
setPriceRange(range) |
Object | 设置 {min, max} 价格范围 |
clearPriceRange() |
无 | 恢复自动价格范围 |
showCrosshair(value) |
Object | 使用 {time, price} 或 {index, price} 显示十字线 |
hideCrosshair() |
无 | 隐藏十字线 |
深度与交易数据
深度图
this.$refs.chart.setDepthData({
bids: [
{ price: 50000, volume: 10 },
{ price: 49990, volume: 18 }
],
asks: [
{ price: 50010, volume: 8 },
{ price: 50020, volume: 15 }
]
})
volume 建议传入从盘口中心向外累计后的数量。
成交标记
this.$refs.chart.setTradeMarkers([
{
id: 'buy-1',
time: 1790481600000,
price: 50000,
side: 'buy', // buy/sell
label: 'B',
style: 'badge', // badge/arrow
color: '#00c087',
size: 12,
visible: true
}
])
订单与持仓线
this.$refs.chart.setTradingOverlay({
visible: true,
showOrders: true,
showPositions: true,
showLiquidationPrice: true,
showTakeProfitStopLoss: true,
showTradeHistory: true,
lines: [
{
id: 'position-1',
price: 50000,
kind: 'position',
side: 'buy',
label: '多仓',
quantity: '0.50 BTC',
unrealizedPnl: 42.8,
unrealizedPnlPercent: 1.72
},
{
id: 'tp-1',
price: 51000,
kind: 'takeProfit',
side: 'sell',
label: '止盈',
draggable: true,
dashed: true
}
]
})
kind 支持 order、position、liquidation、takeProfit、stopLoss。订单 status 支持 pending、open、partiallyFilled、filled、canceled、rejected。
组件事件
| 事件 | 返回数据 | 说明 |
|---|---|---|
ready |
图表状态 | 原生图表初始化完成 |
crosshair |
index/time/price/x/y 及当前 OHLCV |
十字线选中变化 |
crosshair-hidden |
无 | 十字线隐藏 |
crossline-label-tap |
{price} |
点击十字线价格标签 |
viewport-change |
可见索引、时间、价格范围及 isAtLatest |
可见区域变化 |
scale-change |
{scale} |
缩放比例变化 |
drawing-change |
count/canUndo/canRedo/drawings |
绘图数据或历史状态变化 |
load-more |
{history, prefetch} |
滑动到历史边缘,请求更早数据 |
trade-marker-tap |
{id, time, price, side} |
点击成交标记 |
trading-line-tap |
{id, price, kind, side} |
点击交易线 |
trading-line-drag-end |
{id, price} |
确认拖拽改价后触发 |
error |
{method, message} |
原生调用或数据处理异常 |
可拖动交易线在松手后会先显示价格修改确认弹窗,用户确认后才触发 trading-line-drag-end。
打开完整 Flutter Demo
通过 js_sdk 调用
import { openDemo } from '@/nativeplugins/Chen-KChartPro/js_sdk/index.js'
openDemo({}, result => {
console.log('open demo:', result.success)
})
直接引用原生模块
const kchartModule = uni.requireNativePlugin('Chen-KChartPro')
kchartModule.openDemo({}, result => {
console.log(result)
})
打包说明
- 本地调试必须使用包含
Chen-KChartPro的自定义基座,普通标准基座不包含该插件。 - 正式发布时需要在
manifest.json中勾选插件后重新云打包。 - Android 插件保留
armeabi-v7a、arm64-v8a、x86_64三种 ABI。 - 不要把 Flutter、Android 的
build目录或 uni-app 的旧unpackage缓存当作插件依赖提交。 - 如果替换了插件原生产物,需要重新制作自定义基座后再验证。
完整示例
项目中的完整 uni-app 自定义交易页可参考:
pages/sample/kchart-module.nvue
可复用组件封装位于:
uni_modules/chen-kchart/components/chen-kchart-pro/chen-kchart-pro.nvue Chen-KChartPro
UniApp Android/iOS 专业 K 线图原生插件
Chen-KChartPro 是基于 Flutter KChart 渲染引擎封装的 uni-app App 原生插件,提供 Android 和 iOS 双端一致的 K 线、深度图、技术指标、绘图工具及交易叠加能力。
插件提供两种使用方式:
- 使用
<chen-kchart-pro>将 KChart 作为原生组件嵌入.nvue页面,页面、行情数据和工具栏由 uni-app 业务层控制。 - 使用
openDemo()直接打开完整 Flutter KChart Demo,用于功能体验和效果对照。
如有接入问题、功能需求或商业合作,请添加VX:
Chen-Taurus-0510(添加时请备注 KChart)。
在线体验
| 类型 | 地址 | 说明 |
|---|---|---|
| Web 效果预览 | 打开 KChart 在线演示 | 用于 PC、H5 预览 KChart 功能和交互效果 |
| Android Demo | 下载 KChart APK | 下载并安装到 Android 手机体验完整 Demo |
平台要求
| 平台 | 最低版本 | 接入方式 |
|---|---|---|
| Android | API 21 | AAR 本地原生插件 |
| iOS | iOS 13.0 | Framework 本地原生插件 |
当前插件版本:1.0.0。
主要功能
- 支持蜡烛图、分时图、深度图和成交量图。
- 支持 MA、EMA、BOLL、SAR、MACD、KDJ、RSI、WR、CCI、OBV、StochRSI 等指标。
- 支持拖动浏览、双指缩放、十字线、回到最新、指定时间跳转和自定义价格范围。
- 支持趋势线、趋势角、箭头、垂直线、水平线、水平射线、射线和十字线绘图。
- 支持磁铁吸附、连续绘图、撤销、重做、清空和绘图数据恢复。
- 支持买卖 B/S 标记、订单线、持仓线、开仓价、强平价、止盈止损和未实现盈亏。
- 支持历史行情分页、实时 K 线更新、亮色/深色主题和色觉友好模式。
- 支持通过 Props、ref 方法和事件与 uni-app 页面进行双向交互。
插件接入
1. 复制插件文件
将以下目录完整复制到业务项目:
nativeplugins/Chen-KChartPro/
uni_modules/chen-kchart/
其中:
nativeplugins/Chen-KChartPro包含 Android/iOS 原生插件产物。uni_modules/chen-kchart包含可直接在.nvue页面使用的组件封装。
2. 配置本地原生插件
在 HBuilderX 中打开:
manifest.json -> App 原生插件配置 -> 选择本地插件
勾选 Chen-KChartPro,保存后重新制作 Android/iOS 自定义基座。新增或更新本地原生插件后,旧基座不会自动包含新插件,必须重新制作基座或重新云打包。
3. 页面中使用组件
该组件用于 uni-app App 端 .nvue 页面。将组件放入页面并设置明确的宽高:
<template>
<view class="page">
<chen-kchart-pro
ref="chart"
:data="bars"
:options="chartOptions"
theme="light"
mainIndicator="MA"
secondaryIndicator="KDJ"
:showVolume="true"
:tradeMarkers="tradeMarkers"
:tradingOverlay="tradingOverlay"
chartStyle="width:750rpx;height:900rpx;"
@ready="onChartReady"
@crosshair="onCrosshair"
@viewport-change="onViewportChange"
@load-more="onLoadMore"
@error="onChartError"
/>
</view>
</template>
<script>
export default {
data() {
return {
bars: [],
tradeMarkers: [],
tradingOverlay: {},
chartOptions: {
showNowPrice: true,
showInfoDialog: true,
enableDrawingTools: true,
precision: 4,
minScale: 0.35,
maxScale: 5
}
}
},
methods: {
onChartReady() {
this.$refs.chart.setData(this.bars)
},
onCrosshair(event) {
console.log('crosshair:', event)
},
onViewportChange(event) {
console.log('viewport:', event)
},
onLoadMore(event) {
if (event && event.history) this.loadHistory()
},
onChartError(event) {
console.error('KChart error:', event)
}
}
}
</script>
K 线数据格式
每根 K 线至少需要以下字段:
{
time: 1790481600000, // Unix 毫秒时间戳
open: 50000,
high: 50200,
low: 49800,
close: 50100,
vol: 123.45
}
数据要求:
time使用 Unix 毫秒时间戳,建议直接使用行情服务返回的 UTC 时间戳。open、high、low、close、vol必须是有效数字。- 完整数据应按时间从旧到新排列。
- 业务端负责根据当前周期请求或生成 OHLCV,插件不负责将逐笔成交聚合为 K 线。
- 切换交易对或周期后,应重新请求数据并调用
setData()。
组件属性
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
data |
Array | [] |
K 线数据,按时间从旧到新排列 |
options |
Object | {} |
图表配置 |
theme |
String | light |
light 或 dark |
mainIndicator |
String | MA |
MA/BOLL/EMA/SAR/NONE |
secondaryIndicator |
String | KDJ |
KDJ/MACD/RSI/WR/CCI/OBV/STOCH_RSI/NONE |
showVolume |
Boolean | true |
是否显示成交量区域 |
lineMode |
Boolean | false |
是否显示分时线模式 |
mode |
String | KLINE |
KLINE 或 DEPTH |
depth |
Object | {bids: [], asks: []} |
深度图买卖盘数据 |
tradeMarkers |
Array | [] |
买入/卖出成交标记 |
tradingOverlay |
Object | {} |
订单、持仓、TP/SL 等交易叠加数据 |
chartStyle |
String | width:750rpx;height:900rpx; |
原生图表组件宽高样式 |
options 配置
| 参数 | 类型 | 说明 |
|---|---|---|
theme |
String | light/dark |
mainIndicator |
String | 主图指标 |
secondaryIndicator |
String | 副图指标 |
showVolume |
Boolean | 是否显示成交量 |
isLine |
Boolean | 是否启用分时模式 |
mode |
String | KLINE/DEPTH |
showNowPrice |
Boolean | 是否显示最新价线 |
showInfoDialog |
Boolean | 是否显示十字线详情 |
enableDrawingTools |
Boolean | 是否启用绘图能力 |
colorBlind |
Boolean | 是否启用色觉友好配色 |
candleBodyMode |
String | solid/hollow,实心或空心 K 线 |
precision |
Number | 价格小数精度,范围 0-12 |
minScale |
Number | 最小缩放比例 |
maxScale |
Number | 最大缩放比例 |
indicatorConfigs |
Array | 指标计算参数、线色、序列显隐和启停配置 |
指标设置
indicatorConfigs 会直接传入纯 Flutter KChart 指标计算和渲染层,支持
MA/EMA/BOLL/SAR/VOL/MACD/KDJ/RSI/WR/CCI/OBV/STOCH_RSI:
chartOptions: {
indicatorConfigs: [
{
id: 'BOLL',
parameters: { period: 20, multiplier: 2 },
colors: { mb: '#2962ff', up: '#ff9800', dn: '#7e57c2' },
hiddenSeries: [],
enabled: true
},
{
id: 'KDJ',
parameters: { period: 9, kSmoothing: 3, dSmoothing: 3 },
colors: { k: '#2962ff', d: '#ff9800', j: '#7e57c2' },
hiddenSeries: ['j']
}
]
}
绑定的 options 支持深度响应式更新,也可以显式调用
this.$refs.chart.setOptions(this.chartOptions)。完整设置面板实现可直接复用
pages/sample/kchart-module.nvue 中的指标设置数据结构与方法。
数据更新方法
| 方法 | 参数 | 使用场景 |
|---|---|---|
setData(bars) |
Array | 首次加载、刷新、切换交易对或周期时,全量替换数据 |
appendData(bars) |
Array | 合并历史或新增数据,插件会按时间重新排序 |
updateLastData(bar) |
Object | 实时更新当前最新 K 线,时间相同则替换,否则追加 |
// 第一次加载或切换周期
this.$refs.chart.setData(bars)
// 加载更早历史数据
this.$refs.chart.appendData(olderBars)
// WebSocket/轮询更新最新 K 线
this.$refs.chart.updateLastData(realtimeBar)
注意:
appendData()不应传入已经存在的重复时间数据,业务端应先按time去重。updateLastData()只用于当前最后一根或新生成的一根 K 线,不要传入早于当前最后时间的数据。- Props 响应式更新和 ref 方法更新应选择一种,不要在同一次行情更新中同时修改
:data并调用updateLastData(),否则会同时触发全量和增量更新。 - 用户正在浏览历史区域时,实时更新不会强制拉回最新位置;可通过
viewport-change的isAtLatest判断当前状态。
图表控制方法
基础配置
| 方法 | 参数 | 说明 |
|---|---|---|
setOptions(options) |
Object | 批量更新图表配置 |
setTheme(theme) |
String | 设置 light/dark 主题 |
setMainIndicator(value) |
String | 设置主图指标 |
setSecondaryIndicator(value) |
String | 设置副图指标 |
setShowVolume(value) |
Boolean | 显示或隐藏成交量 |
setLineMode(value) |
Boolean | 切换分时模式 |
setChartMode(value) |
String | 切换 KLINE/DEPTH |
深度与交易
| 方法 | 参数 | 说明 |
|---|---|---|
setDepthData(data) |
Object | 设置 {bids, asks} 深度数据 |
setTradeMarkers(markers) |
Array | 设置成交 B/S 标记 |
setTradingOverlay(overlay) |
Object | 设置订单、持仓、止盈止损等交易线 |
绘图工具
| 方法 | 参数 | 说明 |
|---|---|---|
setDrawingTool(type) |
String | 设置当前绘图工具,NONE 取消当前工具 |
setDrawingMode(enabled) |
Boolean | 开启或关闭绘图模式 |
setDrawingOptions(options) |
Object | 设置连续绘图、磁铁、颜色、线宽等 |
setDrawings(drawings) |
Array | 恢复序列化的绘图数据 |
clearDrawings() |
无 | 清除全部绘图 |
undoDrawing() |
无 | 撤销 |
redoDrawing() |
无 | 重做 |
支持的绘图类型:
trendLine、trendAngle、arrow、verticalLine、horizontalLine、
horizontalRay、ray、crossLine、NONE
setDrawingOptions() 支持:
this.$refs.chart.setDrawingOptions({
continuous: true,
magnet: true,
magnetThreshold: 50,
color: '#f2b84b',
strokeWidth: 2,
lineStyle: 'solid', // solid/dashed/dotted
visible: true
})
视口与十字线
| 方法 | 参数 | 说明 |
|---|---|---|
zoomIn() |
无 | 放大图表 |
zoomOut() |
无 | 缩小图表 |
resetViewport() |
无 | 重置缩放并回到最新位置 |
fitContent() |
无 | 自适应显示全部数据 |
scrollToLatest() |
无 | 滚动到最新 K 线 |
jumpToIndex(index) |
Number | 跳转到指定数据索引 |
jumpToTime(time) |
Number | 跳转到指定 Unix 毫秒时间 |
setLogicalRange(range) |
Object | 设置 {from, to} 逻辑范围 |
setPriceRange(range) |
Object | 设置 {min, max} 价格范围 |
clearPriceRange() |
无 | 恢复自动价格范围 |
showCrosshair(value) |
Object | 使用 {time, price} 或 {index, price} 显示十字线 |
hideCrosshair() |
无 | 隐藏十字线 |
深度与交易数据
深度图
this.$refs.chart.setDepthData({
bids: [
{ price: 50000, volume: 10 },
{ price: 49990, volume: 18 }
],
asks: [
{ price: 50010, volume: 8 },
{ price: 50020, volume: 15 }
]
})
volume 建议传入从盘口中心向外累计后的数量。
成交标记
this.$refs.chart.setTradeMarkers([
{
id: 'buy-1',
time: 1790481600000,
price: 50000,
side: 'buy', // buy/sell
label: 'B',
style: 'badge', // badge/arrow
color: '#00c087',
size: 12,
visible: true
}
])
订单与持仓线
this.$refs.chart.setTradingOverlay({
visible: true,
showOrders: true,
showPositions: true,
showLiquidationPrice: true,
showTakeProfitStopLoss: true,
showTradeHistory: true,
lines: [
{
id: 'position-1',
price: 50000,
kind: 'position',
side: 'buy',
label: '多仓',
quantity: '0.50 BTC',
unrealizedPnl: 42.8,
unrealizedPnlPercent: 1.72
},
{
id: 'tp-1',
price: 51000,
kind: 'takeProfit',
side: 'sell',
label: '止盈',
draggable: true,
dashed: true
}
]
})
kind 支持 order、position、liquidation、takeProfit、stopLoss。订单 status 支持 pending、open、partiallyFilled、filled、canceled、rejected。
组件事件
| 事件 | 返回数据 | 说明 |
|---|---|---|
ready |
图表状态 | 原生图表初始化完成 |
crosshair |
index/time/price/x/y 及当前 OHLCV |
十字线选中变化 |
crosshair-hidden |
无 | 十字线隐藏 |
crossline-label-tap |
{price} |
点击十字线价格标签 |
viewport-change |
可见索引、时间、价格范围及 isAtLatest |
可见区域变化 |
scale-change |
{scale} |
缩放比例变化 |
drawing-change |
count/canUndo/canRedo/drawings |
绘图数据或历史状态变化 |
load-more |
{history, prefetch} |
滑动到历史边缘,请求更早数据 |
trade-marker-tap |
{id, time, price, side} |
点击成交标记 |
trading-line-tap |
{id, price, kind, side} |
点击交易线 |
trading-line-drag-end |
{id, price} |
确认拖拽改价后触发 |
error |
{method, message} |
原生调用或数据处理异常 |
可拖动交易线在松手后会先显示价格修改确认弹窗,用户确认后才触发 trading-line-drag-end。
打开完整 Flutter Demo
通过 js_sdk 调用
import { openDemo } from '@/nativeplugins/Chen-KChartPro/js_sdk/index.js'
openDemo({}, result => {
console.log('open demo:', result.success)
})
直接引用原生模块
const kchartModule = uni.requireNativePlugin('Chen-KChartPro')
kchartModule.openDemo({}, result => {
console.log(result)
})
打包说明
- 本地调试必须使用包含
Chen-KChartPro的自定义基座,普通标准基座不包含该插件。 - 正式发布时需要在
manifest.json中勾选插件后重新云打包。 - Android 插件保留
armeabi-v7a、arm64-v8a、x86_64三种 ABI。 - 不要把 Flutter、Android 的
build目录或 uni-app 的旧unpackage缓存当作插件依赖提交。 - 如果替换了插件原生产物,需要重新制作自定义基座后再验证。
完整示例
项目中的完整 uni-app 自定义交易页可参考:
pages/sample/kchart-module.nvue
可复用组件封装位于:
uni_modules/chen-kchart/components/chen-kchart-pro/chen-kchart-pro.nvue 收起阅读 »
【公告】旧版 HBuilder(红色图标) Android 云端打包服务下线通知
亲爱的开发者:
旧版HBuilder(红色图标)已于2018年停止维护,为保证老用户正常使用,HBuilder对应的Android云端打包服务器一直还坚守在岗位上(iOS打包机由于iOS系统兼容问题,前期已经下线)。随着开发者基本上已升级到HBuilderX,HBuilder云端打包服务器计划于2026年10月30日正式下线。具体事项公告如下:
一、下线时间与影响范围
- 下线时间:2026 年 10 月 30 日 24:00
- 影响范围:届时,基于旧版 HBuilder 发起的 Android 云端打包请求 将无法继续处理。
二、迁移与升级建议
为避免影响您的业务正常迭代与版本发布,请使用旧版 HBuilder 的开发者尽快迁移至下一代开发工具 HBuilderX:
- 下载安装:请前往官网下载并安装最新版本的 HBuilderX。
- 项目导入:将原 HBuilder 项目直接导入 HBuilderX 中,项目结构与代码完全兼容。
请相关开发者提前安排升级计划,感谢您的理解与配合!
DCloud 官方团队
亲爱的开发者:
旧版HBuilder(红色图标)已于2018年停止维护,为保证老用户正常使用,HBuilder对应的Android云端打包服务器一直还坚守在岗位上(iOS打包机由于iOS系统兼容问题,前期已经下线)。随着开发者基本上已升级到HBuilderX,HBuilder云端打包服务器计划于2026年10月30日正式下线。具体事项公告如下:
一、下线时间与影响范围
- 下线时间:2026 年 10 月 30 日 24:00
- 影响范围:届时,基于旧版 HBuilder 发起的 Android 云端打包请求 将无法继续处理。
二、迁移与升级建议
为避免影响您的业务正常迭代与版本发布,请使用旧版 HBuilder 的开发者尽快迁移至下一代开发工具 HBuilderX:
- 下载安装:请前往官网下载并安装最新版本的 HBuilderX。
- 项目导入:将原 HBuilder 项目直接导入 HBuilderX 中,项目结构与代码完全兼容。
请相关开发者提前安排升级计划,感谢您的理解与配合!
DCloud 官方团队
收起阅读 »HBuilderX 云打包上传 iOS IPA 报错 90085,执行 xcode-select --install 后解决
解决 HBuilderX iOS 云打包上传 Transporter 报错 90085 的方法:
我之前使用 HBuilderX 5.07 打包 iOS IPA,通过 Mac 上的 Transporter 上传 App Store Connect 时,遇到了这个错误:
No architectures in the binary. Lipo failed to detect any architectures in the bundle executable. (90085)
一开始检查了 IPA 主程序,确认主程序是 arm64 架构,Transporter 也已经是 1.4.5,但仍然无法正常上传。
最后解决方法:
打开 Mac 终端。
执行命令:
xcode-select --install
按照系统提示安装 Xcode Command Line Tools。
安装完成后,重新使用 Transporter 上传原来的 IPA。
我这边实测,安装完成后重新上传就可以了!
如果你也遇到了同样的 90085 架构错误,可以先尝试这个方法,不一定需要先修改项目代码、反复重新打包或逐个检查 Framework。
说明:这是我自己的实测结果,不保证所有开发环境都适用。如果安装后仍然报错,再继续检查打包产物和上传环境。
补充说明:
我使用的 Transporter 已经是 1.4.5,检查 IPA 主程序后确认是 arm64 架构,但仍然出现 90085 错误。
后来在 Mac 终端执行 xcode-select --install,按照系统提示安装 Xcode Command Line Tools。安装完成后,重新使用 Transporter 上传 IPA,之前的 90085 错误就没有再出现,上传进入了 Apple 处理阶段。
目前这是我自己实测有效的方法,分享给遇到相同问题的开发者参考。不同电脑环境可能存在差异,如果此方法无效,还需要进一步排查。
解决 HBuilderX iOS 云打包上传 Transporter 报错 90085 的方法:
我之前使用 HBuilderX 5.07 打包 iOS IPA,通过 Mac 上的 Transporter 上传 App Store Connect 时,遇到了这个错误:
No architectures in the binary. Lipo failed to detect any architectures in the bundle executable. (90085)
一开始检查了 IPA 主程序,确认主程序是 arm64 架构,Transporter 也已经是 1.4.5,但仍然无法正常上传。
最后解决方法:
打开 Mac 终端。
执行命令:
xcode-select --install
按照系统提示安装 Xcode Command Line Tools。
安装完成后,重新使用 Transporter 上传原来的 IPA。
我这边实测,安装完成后重新上传就可以了!
如果你也遇到了同样的 90085 架构错误,可以先尝试这个方法,不一定需要先修改项目代码、反复重新打包或逐个检查 Framework。
说明:这是我自己的实测结果,不保证所有开发环境都适用。如果安装后仍然报错,再继续检查打包产物和上传环境。
补充说明:
我使用的 Transporter 已经是 1.4.5,检查 IPA 主程序后确认是 arm64 架构,但仍然出现 90085 错误。
后来在 Mac 终端执行 xcode-select --install,按照系统提示安装 Xcode Command Line Tools。安装完成后,重新使用 Transporter 上传 IPA,之前的 90085 错误就没有再出现,上传进入了 Apple 处理阶段。
目前这是我自己实测有效的方法,分享给遇到相同问题的开发者参考。不同电脑环境可能存在差异,如果此方法无效,还需要进一步排查。
收起阅读 »【免费】Android / iOS / 鸿蒙 DLNA 投屏插件,一套 API 推到电视播放
【免费】Android / iOS / 鸿蒙 DLNA 投屏插件,一套 API 推到电视播放
插件 ID:
msh-dlna-cast
形态:UTS 插件 · 免费 · Vue3 App
适用:把手机里的视频 / 图片推到同一 Wi-Fi 下的电视、盒子播放
做短视频、教育、相册类 App 时,经常会被问一句:「能不能投到电视上看?」
以前要自己啃 DLNA / SSDP / SOAP,Android、iOS、鸿蒙还各写一套。现在用 msh-dlna-cast,三端同一套 JavaScript 接口,搜索设备 → 选中 → 播放 → 暂停 / 进度 / 音量,业务代码只写一份。
插件市场搜索:msh-dlna-cast(或点文末链接)
能做什么
| 能力 | 说明 |
|---|---|
| 搜设备 | 局域网 SSDP 搜索;iOS 同时搜 AirPlay |
| 推媒体 | http / https 地址,或手机本地文件(插件自建临时 HTTP,不用先上传) |
| 播控 | 播放、暂停、继续、从头播、停止 |
| 进度 | 查询时长 / 进度,seek,快进 / 快退 10 秒 |
| 音量 | 0–100 绝对音量、静音 |
| 状态 | onStateChange 实时回调状态、进度、音量 |
平台:Android 5.0+ · iOS 15+ · 鸿蒙 NEXT(API 12+)
协议:三端都支持 DLNA;iOS 额外支持 AirPlay
不支持:屏幕镜像、Miracast、Chromecast、小程序 / H5 / uni-app x
5 分钟接入
1. 导入插件
从插件市场导入,或把 uni_modules/msh-dlna-cast 放进工程。uni_modules 会自动识别,不必在 manifest.json 里勾选本地插件。
2. 做自定义调试基座(必做)
插件带原生配置(权限、引擎),标准基座无效。HBuilderX 会提示:
uts插件[msh-dlna-cast]依赖的原生配置或三方SDK在运行至标准基座时不能生效
路径:运行 → 运行到手机或模拟器 → 制作自定义调试基座 → 用该基座真机跑。
模拟器收不到组播,请用真机。
3. 写代码
import * as dlna from '@/uni_modules/msh-dlna-cast'
// 发现设备(同一 id 重复发现会覆盖并再次回调)
dlna.onDeviceFound((res) => {
if (res.code !== 0) return
console.log(res.data.name, res.data.protocol) // DLNA / AIRPLAY
})
// 状态变化
dlna.onStateChange((res) => {
if (res.code !== 0) return
const { state, position, duration, volume, mute } = res.data
console.log(state, position, duration, volume, mute)
})
// 开始搜索(timeout 默认 8000ms)
dlna.startSearch({ timeout: 8000 }, (res) => {
if (res.code === 0) console.log('搜索已启动')
if (res.code === 1001) console.log('超时未发现设备')
})
// 选中设备后播放
dlna.selectDevice({ id: deviceId }, (res) => {
if (res.code !== 0) return
dlna.play({
url: 'https://player.alicdn.com/video/aliyunmedia.mp4',
title: '示例视频',
mimeType: 'video/mp4'
}, (playRes) => {
console.log(playRes.code, playRes.data.url)
})
})
// 常用控制
dlna.pause(() => {})
dlna.resume(() => {})
dlna.seek({ position: 30000 }, () => {}) // 毫秒
dlna.setVolume({ volume: 30 }, () => {})
dlna.setMute({ mute: true }, () => {})
dlna.stop(() => {})
回调统一为 { code, message, data },成功时 code === 0。
Android 请开启明文 HTTP(电视控制地址多为 HTTP):
{
"app-plus": {
"android": {
"usesCleartextTraffic": true
}
}
}
本地视频怎么投
play 的 url 传本地绝对路径(以 / 开头,不要带 file://)。插件在手机上起临时 HTTP,回调里的 data.url 就是电视能访问的地址。
uni.chooseVideo({
sourceType: ['album'],
compressed: false,
success: (res) => {
let path = res.tempFilePath || ''
if (typeof plus !== 'undefined' && plus.io?.convertLocalFileSystemURL) {
path = plus.io.convertLocalFileSystemURL(path)
}
if (path.indexOf('file://') === 0) path = path.substring(7)
dlna.play({ url: path, title: '本地视频' }, (r) => {
if (r.code === 0) console.log('终端地址', r.data.url)
})
}
})
Demo 怎么跑通
仓库 / 插件包里附带 dlna-cast-demo,界面大致如下:
- 确认顶部本机 IP 已显示(说明连上 Wi-Fi)
- 点「搜索设备」,列表里选中电视
- 用「示例视频」或粘贴自己的地址,点「播放」
- 试暂停、进度条、快进快退、音量、静音
示例素材(国内可直连):
- 视频:
https://player.alicdn.com/video/aliyunmedia.mp4 - 图片:
https://www.w3school.com.cn/i/eg_tulip.jpg
发帖时可附 Demo 截图:顶部 IP、搜索按钮、播放控件、最近回调。
iOS 特别说明(DLNA vs AirPlay)
| DLNA | AirPlay | |
|---|---|---|
| 选中后 | 直接连上,可 play |
先点系统投屏按钮,选好接收端,再 play |
| 音量 | setVolume |
走系统音量;接收端不跟随时中间音量返回 1009 |
首次运行务必允许「本地网络」。iOS 16+ 搜不到设备,常见原因是缺组播能力:向 Apple 申请 Multicast Networking,描述文件具备后再写入 UTS.entitlements,并重新做自定义基座。
AirPlay 是推媒体,不是屏幕镜像。
使用前 checklist
- [ ] 手机与电视同一 Wi-Fi,路由器未开 AP 隔离
- [ ] 电视已开启 DLNA / 媒体渲染(或对应投屏开关)
- [ ] 已用自定义基座真机运行
- [ ] Android 已开
usesCleartextTraffic - [ ] iOS 已允许本地网络
常见问题速查
| 现象 | 怎么处理 |
|---|---|
1001 一台都没有 |
同一 Wi-Fi?AP 隔离关了?电视 DLNA 开了? |
| 提示标准基座不能生效 | 制作自定义调试基座 |
1008 |
iOS 本地网络 / 组播;Android 组播权限 |
1007 |
手机没连 Wi-Fi |
1010 |
换过网络,重新搜索并选设备 |
| 本地文件电视打不开 | 电视访问不到手机临时 HTTP,再查同一 Wi-Fi |
AirPlay 调音量 1009 |
用接收端自身音量 |
完整错误码、接口表见插件 readme / 使用手册。
本期边界(避免踩坑)
不做:屏幕镜像、字幕、播放列表、多机同时播、倍速。
系统 DLNA 的 Play 只保证 1 倍速,多数电视会忽略其他 Speed。
获取方式
- 插件市场搜索:msh-dlna-cast
- 插件链接:(上架后填入)
https://ext.dcloud.net.cn/plugin?id=29885 - 免费,无广告,不采集、不上传个人数据;网络行为只发生在局域网
有问题欢迎评论区留言,请尽量带上:平台版本、手机与电视型号、完整 { code, message, data } 回调。祝投屏顺利 📺
【免费】Android / iOS / 鸿蒙 DLNA 投屏插件,一套 API 推到电视播放
插件 ID:
msh-dlna-cast
形态:UTS 插件 · 免费 · Vue3 App
适用:把手机里的视频 / 图片推到同一 Wi-Fi 下的电视、盒子播放
做短视频、教育、相册类 App 时,经常会被问一句:「能不能投到电视上看?」
以前要自己啃 DLNA / SSDP / SOAP,Android、iOS、鸿蒙还各写一套。现在用 msh-dlna-cast,三端同一套 JavaScript 接口,搜索设备 → 选中 → 播放 → 暂停 / 进度 / 音量,业务代码只写一份。
插件市场搜索:msh-dlna-cast(或点文末链接)
能做什么
| 能力 | 说明 |
|---|---|
| 搜设备 | 局域网 SSDP 搜索;iOS 同时搜 AirPlay |
| 推媒体 | http / https 地址,或手机本地文件(插件自建临时 HTTP,不用先上传) |
| 播控 | 播放、暂停、继续、从头播、停止 |
| 进度 | 查询时长 / 进度,seek,快进 / 快退 10 秒 |
| 音量 | 0–100 绝对音量、静音 |
| 状态 | onStateChange 实时回调状态、进度、音量 |
平台:Android 5.0+ · iOS 15+ · 鸿蒙 NEXT(API 12+)
协议:三端都支持 DLNA;iOS 额外支持 AirPlay
不支持:屏幕镜像、Miracast、Chromecast、小程序 / H5 / uni-app x
5 分钟接入
1. 导入插件
从插件市场导入,或把 uni_modules/msh-dlna-cast 放进工程。uni_modules 会自动识别,不必在 manifest.json 里勾选本地插件。
2. 做自定义调试基座(必做)
插件带原生配置(权限、引擎),标准基座无效。HBuilderX 会提示:
uts插件[msh-dlna-cast]依赖的原生配置或三方SDK在运行至标准基座时不能生效
路径:运行 → 运行到手机或模拟器 → 制作自定义调试基座 → 用该基座真机跑。
模拟器收不到组播,请用真机。
3. 写代码
import * as dlna from '@/uni_modules/msh-dlna-cast'
// 发现设备(同一 id 重复发现会覆盖并再次回调)
dlna.onDeviceFound((res) => {
if (res.code !== 0) return
console.log(res.data.name, res.data.protocol) // DLNA / AIRPLAY
})
// 状态变化
dlna.onStateChange((res) => {
if (res.code !== 0) return
const { state, position, duration, volume, mute } = res.data
console.log(state, position, duration, volume, mute)
})
// 开始搜索(timeout 默认 8000ms)
dlna.startSearch({ timeout: 8000 }, (res) => {
if (res.code === 0) console.log('搜索已启动')
if (res.code === 1001) console.log('超时未发现设备')
})
// 选中设备后播放
dlna.selectDevice({ id: deviceId }, (res) => {
if (res.code !== 0) return
dlna.play({
url: 'https://player.alicdn.com/video/aliyunmedia.mp4',
title: '示例视频',
mimeType: 'video/mp4'
}, (playRes) => {
console.log(playRes.code, playRes.data.url)
})
})
// 常用控制
dlna.pause(() => {})
dlna.resume(() => {})
dlna.seek({ position: 30000 }, () => {}) // 毫秒
dlna.setVolume({ volume: 30 }, () => {})
dlna.setMute({ mute: true }, () => {})
dlna.stop(() => {})
回调统一为 { code, message, data },成功时 code === 0。
Android 请开启明文 HTTP(电视控制地址多为 HTTP):
{
"app-plus": {
"android": {
"usesCleartextTraffic": true
}
}
}
本地视频怎么投
play 的 url 传本地绝对路径(以 / 开头,不要带 file://)。插件在手机上起临时 HTTP,回调里的 data.url 就是电视能访问的地址。
uni.chooseVideo({
sourceType: ['album'],
compressed: false,
success: (res) => {
let path = res.tempFilePath || ''
if (typeof plus !== 'undefined' && plus.io?.convertLocalFileSystemURL) {
path = plus.io.convertLocalFileSystemURL(path)
}
if (path.indexOf('file://') === 0) path = path.substring(7)
dlna.play({ url: path, title: '本地视频' }, (r) => {
if (r.code === 0) console.log('终端地址', r.data.url)
})
}
})
Demo 怎么跑通
仓库 / 插件包里附带 dlna-cast-demo,界面大致如下:
- 确认顶部本机 IP 已显示(说明连上 Wi-Fi)
- 点「搜索设备」,列表里选中电视
- 用「示例视频」或粘贴自己的地址,点「播放」
- 试暂停、进度条、快进快退、音量、静音
示例素材(国内可直连):
- 视频:
https://player.alicdn.com/video/aliyunmedia.mp4 - 图片:
https://www.w3school.com.cn/i/eg_tulip.jpg
发帖时可附 Demo 截图:顶部 IP、搜索按钮、播放控件、最近回调。
iOS 特别说明(DLNA vs AirPlay)
| DLNA | AirPlay | |
|---|---|---|
| 选中后 | 直接连上,可 play |
先点系统投屏按钮,选好接收端,再 play |
| 音量 | setVolume |
走系统音量;接收端不跟随时中间音量返回 1009 |
首次运行务必允许「本地网络」。iOS 16+ 搜不到设备,常见原因是缺组播能力:向 Apple 申请 Multicast Networking,描述文件具备后再写入 UTS.entitlements,并重新做自定义基座。
AirPlay 是推媒体,不是屏幕镜像。
使用前 checklist
- [ ] 手机与电视同一 Wi-Fi,路由器未开 AP 隔离
- [ ] 电视已开启 DLNA / 媒体渲染(或对应投屏开关)
- [ ] 已用自定义基座真机运行
- [ ] Android 已开
usesCleartextTraffic - [ ] iOS 已允许本地网络
常见问题速查
| 现象 | 怎么处理 |
|---|---|
1001 一台都没有 |
同一 Wi-Fi?AP 隔离关了?电视 DLNA 开了? |
| 提示标准基座不能生效 | 制作自定义调试基座 |
1008 |
iOS 本地网络 / 组播;Android 组播权限 |
1007 |
手机没连 Wi-Fi |
1010 |
换过网络,重新搜索并选设备 |
| 本地文件电视打不开 | 电视访问不到手机临时 HTTP,再查同一 Wi-Fi |
AirPlay 调音量 1009 |
用接收端自身音量 |
完整错误码、接口表见插件 readme / 使用手册。
本期边界(避免踩坑)
不做:屏幕镜像、字幕、播放列表、多机同时播、倍速。
系统 DLNA 的 Play 只保证 1 倍速,多数电视会忽略其他 Speed。
获取方式
- 插件市场搜索:msh-dlna-cast
- 插件链接:(上架后填入)
https://ext.dcloud.net.cn/plugin?id=29885 - 免费,无广告,不采集、不上传个人数据;网络行为只发生在局域网
有问题欢迎评论区留言,请尽量带上:平台版本、手机与电视型号、完整 { code, message, data } 回调。祝投屏顺利 📺
开发者资料索引:页面阅读与核对清单
开发者资料索引:页面阅读与核对清单
整理日期:20260929
本次整理 300 条公开页面,地址以纯文本列出,方便按需浏览和复核。
阅读建议:先查看页面标题与正文,再结合其他来源核对信息。页面内容和更新时间以原站展示为准。
资料地址:
- http://wap.baiduzzc.org.cn/article/news/k-UhDAyZ6iGqQRJnQWoR1p.html
- http://wap.baiduzzc.org.cn/article/news/k-kgcGBJMISBqBQipHOTEV.html
- http://wap.baiduzzc.org.cn/article/news/k-A8SbKTW5YO4VJKHWp0fo.html
- http://wap.baiduzzc.org.cn/article/news/k-VIEC21H9kKzeRw8RdXuw.html
- http://wap.baiduzzc.org.cn/article/news/k-lRbSChZBVkuLUxeJNv64.html
- http://wap.baiduzzc.org.cn/article/news/k-mvYyImnEutNrwF1TwBfZ.html
- http://wap.baiduzzc.org.cn/article/news/k-7kvo2VtgFGom4RkYaVIU.html
- http://wap.baiduzzc.org.cn/article/news/k-8iwBH13Jml2cTaUfTbMW.html
- http://wap.baiduzzc.org.cn/article/news/k-l4P7wNln97Zxumjhvoi9.html
- http://wap.baiduzzc.org.cn/article/news/k-0uPqwj5KLWKteCmXXqI7.html
- http://wap.baiduzzc.org.cn/article/news/k-9DLawRF5ckGVcs3bBeEE.html
- http://wap.baiduzzc.org.cn/article/news/k-RwUZXztY1yJqPGpSuIbs.html
- http://wap.baiduzzc.org.cn/article/news/k-UTxdswqB2CHOVQ0RXsP3.html
- http://wap.baiduzzc.org.cn/article/news/k-BoOXN9oOHXw9toCl2mdX.html
- http://wap.baiduzzc.org.cn/article/news/k-Mz3zdVpgwrf1hAddXOB1.html
- http://wap.baiduzzc.org.cn/article/news/k-BCRlNWUflpOa5DVRcKTJ.html
- http://wap.baiduzzc.org.cn/article/news/k-PcVeasV3mYkCJLpn2JcQ.html
- http://wap.baiduzzc.org.cn/article/news/k-ZFkQVlWtk3oCAgdb6iCy.html
- http://wap.baiduzzc.org.cn/article/news/k-NI6ozu7ta3SJmk8lSrvj.html
- http://wap.baiduzzc.org.cn/article/news/k-pkG3pXkyfTuIvZtQV5dR.html
- http://wap.baiduzzc.org.cn/article/news/k-x8xwJt46zUdxZLZyqGKA.html
- http://wap.baiduzzc.org.cn/article/news/k-umbCPixBMW4SoM1HKDSt.html
- http://wap.baiduzzc.org.cn/article/news/k-Jwy76DhIKbiyjlsSmtHe.html
- http://wap.baiduzzc.org.cn/article/news/k-jfafWQbt2g18dFuQYoTI.html
- http://wap.baiduzzc.org.cn/article/news/k-FYXGh4F7dA3K914PFK0a.html
- http://wap.baiduzzc.org.cn/article/news/k-2mF5UQJiqLnZUf2tki4O.html
- http://wap.baiduzzc.org.cn/article/news/k-MDuaeOfvxdfsQU0xRoNH.html
- http://wap.baiduzzc.org.cn/article/news/k-4YUrgrExEWNeBrmmXU3Y.html
- http://wap.baiduzzc.org.cn/article/news/k-Vd0c6ZAzVuWy5HDKQhyf.html
- http://wap.baiduzzc.org.cn/article/news/k-JubsRxXh73slxhIjwATS.html
- http://wap.baiduzzc.org.cn/article/news/k-fpDVIYDzJtgXEM7wGXAj.html
- http://wap.baiduzzc.org.cn/article/news/k-ROHysbO0lSfPgRrk0D4D.html
- http://wap.baiduzzc.org.cn/article/news/k-0T6xrAoEijjToBvuyL7f.html
- http://wap.baiduzzc.org.cn/article/news/k-ge4Qv7ctgHhdNo6EHTZu.html
- http://wap.baiduzzc.org.cn/article/news/k-ZYUVyKb0CG7n084Lrgf7.html
- http://wap.baiduzzc.org.cn/article/news/k-PDz3RU5I28peykALdmaM.html
- http://wap.baiduzzc.org.cn/article/news/k-IDfJZznBDcdje5x0jIJO.html
- http://wap.baiduzzc.org.cn/article/news/k-bowUtkY8Vh1efcFdTVuJ.html
- http://wap.baiduzzc.org.cn/article/news/k-ipQvBvIKKhAsyPIO3DJ0.html
- http://wap.baiduzzc.org.cn/article/news/k-Q2z8ZTaHnCc1NEgHWdFx.html
- http://wap.baiduzzc.org.cn/article/news/k-Qaht0xLilBEVvpl53lxs.html
- http://wap.baiduzzc.org.cn/article/news/k-sVp0ZF8ixcjLcDt3VVdq.html
- http://wap.baiduzzc.org.cn/article/news/k-N3jmLabzlnoWLuoNMciE.html
- http://wap.baiduzzc.org.cn/article/news/k-HrO7hpbbPy8usYPrFDoM.html
- http://wap.baiduzzc.org.cn/article/news/k-yQQV4kGgYLYLt5knkKOO.html
- http://wap.baiduzzc.org.cn/article/news/k-PUHLXerP2YgYUwIZaU7j.html
- http://wap.baiduzzc.org.cn/article/news/k-dukVXgdNSV8FViKCitTr.html
- http://wap.baiduzzc.org.cn/article/news/k-5eaFYodjdxWaQXbEDE8b.html
- http://wap.baiduzzc.org.cn/article/news/k-hGy45TOeLo7IDzw4CcPv.html
- http://wap.baiduzzc.org.cn/article/news/k-cmvpaH7lW2RBdXIcLP0F.html
- http://wap.baiduzzc.org.cn/article/news/k-VgP1XH9CwRtvUhRO7yRt.html
- http://wap.baiduzzc.org.cn/article/news/k-dw7tJCmHPqK0DFzuyN8t.html
- http://wap.baiduzzc.org.cn/article/news/k-1J7pcJF7t13qxZx5eupC.html
- http://wap.baiduzzc.org.cn/article/news/k-2goQLvTOiaHmlPEfSHsM.html
- http://wap.baiduzzc.org.cn/article/news/k-dJsM7gZlKapDU1A2XczG.html
- http://wap.baiduzzc.org.cn/article/news/k-b7y2c8k6quuItHt76A9L.html
- http://wap.baiduzzc.org.cn/article/news/k-rd884aRYavAxjfz26FD4.html
- http://wap.baiduzzc.org.cn/article/news/k-ZRDIbTpyZc1mf7ZIP36N.html
- http://wap.baiduzzc.org.cn/article/news/k-8SOjjHBpAJJ1k1Rf3FIy.html
- http://wap.baiduzzc.org.cn/article/news/k-5s23NyWfesdZLTCdkhyf.html
- http://wap.baiduzzc.org.cn/article/news/k-PNJmXbEM1uSQcQPJxUd3.html
- http://wap.baiduzzc.org.cn/article/news/k-KhYvH3wJVfaS2vc7Soww.html
- http://wap.baiduzzc.org.cn/article/news/k-JpdFEugwarJoDG5c9blw.html
- http://wap.baiduzzc.org.cn/article/news/k-59DQgC4q1UZKHi22uBHd.html
- http://wap.baiduzzc.org.cn/article/news/k-2PzVuL8CmAOmtJZ4T7Lc.html
- http://wap.baiduzzc.org.cn/article/news/k-jL7OVavS6jQzbRVtJPSB.html
- http://wap.baiduzzc.org.cn/article/news/k-ZsysOT4PcWpMRSQh7mbV.html
- http://wap.baiduzzc.org.cn/article/news/k-hXvh02zmJ4Or2evHj9Im.html
- http://wap.baiduzzc.org.cn/article/news/k-JngZi4fUfblcV09PQTMW.html
- http://wap.baiduzzc.org.cn/article/news/k-2h71HiGqFOpxCeQfxIaG.html
- http://wap.baiduzzc.org.cn/article/news/k-8YRyACjSPldbRL7Ulik0.html
- http://wap.baiduzzc.org.cn/article/news/k-YyCGFnWy6dCf5v16afJ8.html
- http://wap.baiduzzc.org.cn/article/news/k-JGydAMwih3eB6aZKCInU.html
- http://wap.baiduzzc.org.cn/article/news/k-fZbjQLlKfHy3vWgplQTF.html
- http://wap.baiduzzc.org.cn/article/news/k-JFIjIR6KlYT5psvNVa6P.html
- http://wap.baiduzzc.org.cn/article/news/k-ioSoIEwKYllrjYzjo0Th.html
- http://wap.baiduzzc.org.cn/article/news/k-DDdLKOmonXhyc76dlGg8.html
- http://wap.baiduzzc.org.cn/article/news/k-tIqqTvyDRnhcuUmoHrGp.html
- http://wap.baiduzzc.org.cn/article/news/k-9B3UQqfyCFobd7rHsBid.html
- http://wap.baiduzzc.org.cn/article/news/k-dKk19C0IbO8aHfxnrJ1u.html
- http://wap.baiduzzc.org.cn/article/news/k-y8CVGMDFFTaxKhq46RSB.html
- http://wap.baiduzzc.org.cn/article/news/k-g1CiqcNZYY3XJZ3aPecJ.html
- http://wap.baiduzzc.org.cn/article/news/k-eAzgOKIKBuh7KJIbmUlO.html
- http://wap.baiduzzc.org.cn/article/news/k-IZSKXDQimZAy8ymiapqc.html
- http://wap.baiduzzc.org.cn/article/news/k-3IFezgarFacPlKaKrMjd.html
- http://wap.baiduzzc.org.cn/article/news/k-ig1LAI0E8nB4sbqm4vF6.html
- http://wap.baiduzzc.org.cn/article/news/k-3EJlWw3u4gwsvVT2yv0j.html
- http://wap.baiduzzc.org.cn/article/news/k-wMljPG3RRLLg7S2BzWbl.html
- http://wap.baiduzzc.org.cn/article/news/k-d1fh7swcdMmLZMPQmLua.html
- http://wap.baiduzzc.org.cn/article/news/k-sPujNeusebuKFZQznCVn.html
- http://wap.baiduzzc.org.cn/article/news/k-LvwwZ6tjHAxwnUIFWAc0.html
- http://wap.baiduzzc.org.cn/article/news/k-i53Ui4fcHgKEsHl4WDOc.html
- http://wap.baiduzzc.org.cn/article/news/k-Nle1jDtNb3N3vjsQlXD5.html
- http://wap.baiduzzc.org.cn/article/news/k-0qPNffdtOhk42QmEoDk3.html
- http://wap.baiduzzc.org.cn/article/news/k-GuaqR7pPHr72ETelD0dR.html
- http://wap.baiduzzc.org.cn/article/news/k-etMUqFfwtRv53mca3KeM.html
- http://wap.baiduzzc.org.cn/article/news/k-iYR0QDt6OslNjctUg6Ak.html
- http://wap.baiduzzc.org.cn/article/news/k-70ZckzQuCR3xfxmQnCvR.html
- http://wap.baiduzzc.org.cn/article/news/k-Mbh1ULlWhUgsEYFn1vmu.html
- http://wap.baiduzzc.org.cn/article/news/k-Ls5J66iNWchmVUjs0vlo.html
- http://wap.baiduzzc.org.cn/article/news/k-46een9Zsu4Ts5Yzh20ee.html
- http://wap.baiduzzc.org.cn/article/news/k-bB99GSUFrJ1xkd8EvZIr.html
- http://wap.baiduzzc.org.cn/article/news/k-IqIpHIgkbSIjUt9ifPGX.html
- http://wap.baiduzzc.org.cn/article/news/k-zWnWiRtLMhpJPyRFqiCr.html
- http://wap.baiduzzc.org.cn/article/news/k-c3KvpMfXhTiHF1ukG7gc.html
- http://wap.baiduzzc.org.cn/article/news/k-H5e6yyboCk6KPXsS96Up.html
- http://wap.baiduzzc.org.cn/article/news/k-HmIoAhhz9Pbia5IkF4bB.html
- http://wap.baiduzzc.org.cn/article/news/k-U5ccAZRlB5dkPKGhx454.html
- http://wap.baiduzzc.org.cn/article/news/k-5v2W9DufN2fMjz5ZbQRc.html
- http://wap.baiduzzc.org.cn/article/news/k-mJqYhBGn9gmWCeebKMQt.html
- http://wap.baiduzzc.org.cn/article/news/k-RP81JbmocRfkEjf2SzGN.html
- http://wap.baiduzzc.org.cn/article/news/k-g5NefoIfKzZb20xbcbrO.html
- http://wap.baiduzzc.org.cn/article/news/k-JqxKukJ855isPFWyfEtC.html
- http://wap.baiduzzc.org.cn/article/news/k-htzacMb8mJzeTnMsgqXl.html
- http://wap.baiduzzc.org.cn/article/news/k-6oB4P4f7wkoboReJ3TKL.html
- http://wap.baiduzzc.org.cn/article/news/k-PGhBn4GFEO22RrKLPFsj.html
- http://wap.baiduzzc.org.cn/article/news/k-KfWUV0dT6RfPCrfbIqjn.html
- http://wap.baiduzzc.org.cn/article/news/k-4ye8C6Yj27CjcxjyEOu0.html
- http://wap.baiduzzc.org.cn/article/news/k-JKpOVLPRymjn6c4O1vGM.html
- http://wap.baiduzzc.org.cn/article/news/k-MZpJjrjbCH5VuQgjKgRK.html
- http://wap.baiduzzc.org.cn/article/news/k-rTz0LoyW76NebeQeDBh8.html
- http://wap.baiduzzc.org.cn/article/news/k-EYANRqmqZbhNz5ST5Ed6.html
- http://wap.baiduzzc.org.cn/article/news/k-A4ip70jUmhdsJXdXv8wb.html
- http://wap.baiduzzc.org.cn/article/news/k-aPyXbjvqRWDw21VuiVDA.html
- http://wap.baiduzzc.org.cn/article/news/k-2vONbLFAj96vrjDnORwv.html
- http://wap.baiduzzc.org.cn/article/news/k-47qxfN2rIBOBAzrhPl1U.html
- http://wap.baiduzzc.org.cn/article/news/k-J9QP7YOwm5rmFp9htm6t.html
- http://wap.baiduzzc.org.cn/article/news/k-J1FZedAhNgBTvqbSRXJV.html
- http://wap.baiduzzc.org.cn/article/news/k-5e5hkGrd4ppXGa6sxuXd.html
- http://wap.baiduzzc.org.cn/article/news/k-5n8m0ykeCEJJPSaDjUdt.html
- http://wap.baiduzzc.org.cn/article/news/k-BadtrLG5hnoOrUWsw3YB.html
- http://wap.baiduzzc.org.cn/article/news/k-OMOeZhHZmfTYsaA5yMIf.html
- http://wap.baiduzzc.org.cn/article/news/k-D1CXqJ96IOTJges34ib7.html
- http://wap.baiduzzc.org.cn/article/news/k-cDk1LMBbYdlVr6kRSMrF.html
- http://wap.baiduzzc.org.cn/article/news/k-cWcVFtdpEm1kSi6gT0nf.html
- http://wap.baiduzzc.org.cn/article/news/k-G4wphXZGCnSHBrki9fWh.html
- http://wap.baiduzzc.org.cn/article/news/k-083iUW5787chsO4toKPX.html
- http://wap.baiduzzc.org.cn/article/news/k-Qhdjs8Xqz30vlceUte5B.html
- http://wap.baiduzzc.org.cn/article/news/k-VdwsSK3a6YYYvZDVToFn.html
- http://wap.baiduzzc.org.cn/article/news/k-Jb2EGXD2MaISty9kk1LC.html
- http://wap.baiduzzc.org.cn/article/news/k-EcJg4T1IjGIZRWHHycVH.html
- http://wap.baiduzzc.org.cn/article/news/k-dmWCoFYncXDLnBWH6oen.html
- http://wap.baiduzzc.org.cn/article/news/k-OC64zXviVO0BnYjwBht9.html
- http://wap.baiduzzc.org.cn/article/news/k-G14PcklgOFkc7tP8Naqs.html
- http://wap.baiduzzc.org.cn/article/news/k-RJwca2REUkjJcNH1ceTf.html
- http://wap.baiduzzc.org.cn/article/news/k-ToQKGclXxsRgQN4OYyQi.html
- http://wap.baiduzzc.org.cn/article/news/k-JeF6KsjO0donomKsr1pw.html
- http://wap.baiduzzc.org.cn/article/news/k-5GbLUealmo3w4Ig4TtNf.html
- http://wap.baiduzzc.org.cn/article/news/k-UW0FfoagrbHe8Lmrgmpy.html
- http://wap.baiduzzc.org.cn/article/news/k-B3Fzh2POkuwyFElkT8Xg.html
- http://wap.baiduzzc.org.cn/article/news/k-jVTZbh0QEuqvabW1Gxro.html
- http://wap.baiduzzc.org.cn/article/news/k-PpkW1H8xAuYIXNoxlzY1.html
- http://wap.baiduzzc.org.cn/article/news/k-FrStzpt0aj1U7ZH79jhL.html
- http://wap.baiduzzc.org.cn/article/news/k-MRcO7DLwkSBtHI3rRtQm.html
- http://wap.baiduzzc.org.cn/article/news/k-YRTnUEPHSdMvXrIFW8Q7.html
- http://wap.baiduzzc.org.cn/article/news/k-dpDjP77YwEQk2ateF09h.html
- http://wap.baiduzzc.org.cn/article/news/k-DuLfsxv8M6eBqkxlqaQY.html
- http://wap.baiduzzc.org.cn/article/news/k-2Yrr9wIb74ceLWUUnq6C.html
- http://wap.baiduzzc.org.cn/article/news/k-O6rKUaGK2iVKcPuyea33.html
- http://wap.baiduzzc.org.cn/article/news/k-tTZ6qaT6YAXVxVQrErOm.html
- http://wap.baiduzzc.org.cn/article/news/k-IeTvThdIfaP6Z1QiaSDe.html
- http://wap.baiduzzc.org.cn/article/news/k-2KNlWbx66HnxPiGgcNHV.html
- http://wap.baiduzzc.org.cn/article/news/k-RjSEkMHEba7jIJzSYDmU.html
- http://wap.baiduzzc.org.cn/article/news/k-sDvFQHSwnFIhLCW2NJgg.html
- http://wap.baiduzzc.org.cn/article/news/k-Vheay5dYCWPJsB1Z03Jx.html
- http://wap.baiduzzc.org.cn/article/news/k-i12Wc3JdLtaUIN6rhUSt.html
- http://wap.baiduzzc.org.cn/article/news/k-m0Wvjz6dkheNdPtqpMkb.html
- http://wap.baiduzzc.org.cn/article/news/k-o2G1IuwBWq1lx0vm5Wkw.html
- http://wap.baiduzzc.org.cn/article/news/k-KuyYXhPxEuz08ofhMe0f.html
- http://wap.baiduzzc.org.cn/article/news/k-dcp1zNQf5Lat8DhmXGSW.html
- http://wap.baiduzzc.org.cn/article/news/k-j0kYECRx0qslxbsvD10e.html
- http://wap.baiduzzc.org.cn/article/news/k-6SIHQBFGOKMJsDLNbA5D.html
- http://wap.baiduzzc.org.cn/article/news/k-JBuLAMvudWOqhtmPoN0M.html
- http://wap.baiduzzc.org.cn/article/news/k-iFw2DLEtpP5xnNOKEBIe.html
- http://wap.baiduzzc.org.cn/article/news/k-bqgqlXTJj5BqUVjcqwhx.html
- http://wap.baiduzzc.org.cn/article/news/k-GJL05IZtwv2WyT0zgFAL.html
- http://wap.baiduzzc.org.cn/article/news/k-Kp6cta93UNHqVlRjDfoX.html
- http://wap.baiduzzc.org.cn/article/news/k-tPlVlc1bc71DopygfnAQ.html
- http://wap.baiduzzc.org.cn/article/news/k-PC07tiieE0fQiCVqF9bQ.html
- http://wap.baiduzzc.org.cn/article/news/k-WabkeNhHofHV5QhuOFsL.html
- http://wap.baiduzzc.org.cn/article/news/k-dHbUH8OJnj2NW6rSkUw4.html
- http://wap.baiduzzc.org.cn/article/news/k-i9F3Elr1mDQ7JcXaXSWj.html
- http://wap.baiduzzc.org.cn/article/news/k-rPtqydrMptsnJ9SAnS92.html
- http://wap.baiduzzc.org.cn/article/news/k-uyCZAlivVqY0hbCbufKj.html
- http://wap.baiduzzc.org.cn/article/news/k-a7apqHnzBsnHcMwgLk8Q.html
- http://wap.baiduzzc.org.cn/article/news/k-X9upWvBQpM2H7UVs2pv6.html
- http://wap.baiduzzc.org.cn/article/news/k-ihXofbmxDGbdAbhYR4cX.html
- http://wap.baiduzzc.org.cn/article/news/k-lnaWCCJCrYpkKodtxcf4.html
- http://wap.baiduzzc.org.cn/article/news/k-dSzyPa9rFvUODC4KPBui.html
- http://wap.baiduzzc.org.cn/article/news/k-9qC3VoegR1evU2hZ4uIG.html
- http://wap.baiduzzc.org.cn/article/news/k-zU5sW2IabsSMWtALbqJ4.html
- http://wap.baiduzzc.org.cn/article/news/k-K7AuAt7KA0AydOXpp8bd.html
- http://wap.baiduzzc.org.cn/article/news/k-HEYeiBpzh4qbX39JewWo.html
- http://wap.baiduzzc.org.cn/article/news/k-B26XLpgyRDYoIxrSIRtO.html
- http://wap.baiduzzc.org.cn/article/news/k-dYd7rxDSwtu6PkW7d4yn.html
- http://wap.baiduzzc.org.cn/article/news/k-U673daf7SrVA5g8Lq06H.html
- http://wap.baiduzzc.org.cn/article/news/k-EQ4Tg02BFyt4OeDXiHh0.html
- http://wap.baiduzzc.org.cn/article/news/k-M0NPYJL9GLdUVc0KBd2c.html
- http://wap.baiduzzc.org.cn/article/news/k-QFfSM7woaFuxMn8orODH.html
- http://wap.baiduzzc.org.cn/article/news/k-xY4znjauKrQXJ59RJfga.html
- http://wap.baiduzzc.org.cn/article/news/k-MdgQYzHZfmvav42WLLeN.html
- http://wap.baiduzzc.org.cn/article/news/k-voNUTlgbm5St3qc5PLBD.html
- http://wap.baiduzzc.org.cn/article/news/k-K0QQVj0FcLuCLaPpdgcy.html
- http://wap.baiduzzc.org.cn/article/news/k-AblKD81LeXsGLY13HV1d.html
- http://wap.baiduzzc.org.cn/article/news/k-0uhlSBzWhkmb5Me2ouIc.html
- http://wap.baiduzzc.org.cn/article/news/k-Zp16WhmwUnKeeRe70Lp4.html
- http://wap.baiduzzc.org.cn/article/news/k-eJSQaNqZBRifImk0UclA.html
- http://wap.baiduzzc.org.cn/article/news/k-cF4ZEepZTV1evQTAJeqq.html
- http://wap.baiduzzc.org.cn/article/news/k-srlHW3DnnjR2uQgb7RgA.html
- http://wap.baiduzzc.org.cn/article/news/k-DXZdet5ZZPWBhkomVXzS.html
- http://wap.baiduzzc.org.cn/article/news/k-JlnneqqCXhveifg2MJL3.html
- http://wap.baiduzzc.org.cn/article/news/k-N6f7oKj1EXOjq1Yb5xCT.html
- http://wap.baiduzzc.org.cn/article/news/k-cV42sLVdqAldmXy8ydT6.html
- http://wap.baiduzzc.org.cn/article/news/k-IlfThoimqpYBNnjaeQV6.html
- http://wap.baiduzzc.org.cn/article/news/k-0iUN9NrpG4LjYaoNO9WP.html
- http://wap.baiduzzc.org.cn/article/news/k-EwPnZGS4oymiACy57KvW.html
- http://wap.baiduzzc.org.cn/article/news/k-xTvPC8Lk3MBs30wtAxGX.html
- http://wap.baiduzzc.org.cn/article/news/k-JplRIsBaa98ZFaHkFcd8.html
- http://wap.baiduzzc.org.cn/article/news/k-GEAO1JXp4HW2zFHwMKVI.html
- http://wap.baiduzzc.org.cn/article/news/k-9qSNIN84fOaLNieCTp0v.html
- http://wap.baiduzzc.org.cn/article/news/k-5FxEdTLdMnRuWIWIec2O.html
- http://wap.baiduzzc.org.cn/article/news/k-EgPIa9RdQdXGyhEt0IIe.html
- http://wap.baiduzzc.org.cn/article/news/k-wwzLJdrFdKXoXcBPxbzr.html
- http://wap.baiduzzc.org.cn/article/news/k-kYc5fTWKrkQXKihxeY62.html
- http://wap.baiduzzc.org.cn/article/news/k-Fl5zWWuXa03AHioJtoOZ.html
- http://wap.baiduzzc.org.cn/article/news/k-qVySdVMKwpeO8SMfPdvM.html
- http://wap.baiduzzc.org.cn/article/news/k-MWlaFJTujnBZXoSKeFZp.html
- http://wap.baiduzzc.org.cn/article/news/k-g8jEWuupsaQHcEiWbk08.html
- http://wap.baiduzzc.org.cn/article/news/k-FlkFXXie6zWtPmliJuWX.html
- http://wap.baiduzzc.org.cn/article/news/k-c735JI5YzE91OH6Owp7x.html
- http://wap.baiduzzc.org.cn/article/news/k-aw0xxhPGfGztdiNAD8Fx.html
- http://wap.baiduzzc.org.cn/article/news/k-kbvzOBuPjMuSfj6xEpze.html
- http://wap.baiduzzc.org.cn/article/news/k-cMGwSsmc6a4Mhe7uHkr0.html
- http://wap.baiduzzc.org.cn/article/news/k-wQUW1q6zE2uWunNO23cD.html
- http://wap.baiduzzc.org.cn/article/news/k-pxJa84cpKY9MquiiTd6M.html
- http://wap.baiduzzc.org.cn/article/news/k-fKzab0wEUVKHfY68yFjZ.html
- http://wap.baiduzzc.org.cn/article/news/k-Tog2chyC8Q1TWp62BXId.html
- http://wap.baiduzzc.org.cn/article/news/k-1yiLxT9ejz9bRVQUFBJD.html
- http://wap.baiduzzc.org.cn/article/news/k-rA8QouuxU21vnn3MRKh8.html
- http://wap.baiduzzc.org.cn/article/news/k-bnhOCa4haTVrIdDSciYJ.html
- http://wap.baiduzzc.org.cn/article/news/k-jdoYJowcmal4mllnY2BW.html
- http://wap.baiduzzc.org.cn/article/news/k-P3y6EzW1aRa8v2DYSlt5.html
- http://wap.baiduzzc.org.cn/article/news/k-2m35Ten1gnBFpdgfAKfz.html
- http://wap.baiduzzc.org.cn/article/news/k-WU6GW2Kc48XMYeCuyeus.html
- http://wap.baiduzzc.org.cn/article/news/k-WhFjpmkmbKhw5MvJI42h.html
- http://wap.baiduzzc.org.cn/article/news/k-sNhvfR3cisAqyrJYF0hK.html
- http://wap.baiduzzc.org.cn/article/news/k-Z7M9i8fs5jIgsgPE73fg.html
- http://wap.baiduzzc.org.cn/article/news/k-aAkri1i3uBgY4hwVmNhi.html
- http://wap.baiduzzc.org.cn/article/news/k-uyY1BUGCfSoOQXMEFIDy.html
- http://wap.baiduzzc.org.cn/article/news/k-NIPJHZI4glfWjpda68lc.html
- http://wap.baiduzzc.org.cn/article/news/k-v530nHq9M0I1WZirbX8V.html
- http://wap.baiduzzc.org.cn/article/news/k-I28lVZNoBhnLQNgvyffj.html
- http://wap.baiduzzc.org.cn/article/news/k-f3ISs7NO97hnykCLz2Gt.html
- http://wap.baiduzzc.org.cn/article/news/k-Uct7LVQoerCgH6yPFi5B.html
- http://wap.baiduzzc.org.cn/article/news/k-1uIvDqwrMQ1UZmOenJx7.html
- http://wap.baiduzzc.org.cn/article/news/k-fAd9LckdnKy31zPG1Hfv.html
- http://wap.baiduzzc.org.cn/article/news/k-pGZfdJJ32S2LQdPsgsIw.html
- http://wap.baiduzzc.org.cn/article/news/k-nRCApWgMfloce3UU9XNh.html
- http://wap.baiduzzc.org.cn/article/news/k-bCe7qGiRVdnZWgVLHV1O.html
- http://wap.baiduzzc.org.cn/article/news/k-oVrGSbSCOc9iIbwKTUNc.html
- http://wap.baiduzzc.org.cn/article/news/k-jRD0WpnTyfECdhkQebGK.html
- http://wap.baiduzzc.org.cn/article/news/k-pymvukHQyG9MYO8vWsFZ.html
- http://wap.baiduzzc.org.cn/article/news/k-20eom9AZ1GK0hOQYd0fY.html
- http://wap.baiduzzc.org.cn/article/news/k-fTKpeRsA65jJcrvzjmhv.html
- http://wap.baiduzzc.org.cn/article/news/k-ApGg3ymixXVYXyT8OLMq.html
- http://wap.baiduzzc.org.cn/article/news/k-zPTFnv7vVbaPwG2gyIsa.html
- http://wap.baiduzzc.org.cn/article/news/k-EskTpKzE5tyesbnkrB7K.html
- http://wap.baiduzzc.org.cn/article/news/k-gjotjNLzyDyplidNiRFz.html
- http://wap.baiduzzc.org.cn/article/news/k-sc09PlQqHyNVJpCvAVXl.html
- http://wap.baiduzzc.org.cn/article/news/k-BI7wy2qna5woWDrMOSCm.html
- http://wap.baiduzzc.org.cn/article/news/k-G1VMuWuDZiuJygZhYnll.html
- http://wap.baiduzzc.org.cn/article/news/k-LcBTBaL4lYVIcId0bBka.html
- http://wap.baiduzzc.org.cn/article/news/k-HSfhIytuPHpBPoVjxH3E.html
- http://wap.baiduzzc.org.cn/article/news/k-fQOvEcpzsdeKvYLeTnpG.html
- http://wap.baiduzzc.org.cn/article/news/k-2KoujcY1QqSFIqwrCwTe.html
- http://wap.baiduzzc.org.cn/article/news/k-YVcsl3iGLyli68oW69B9.html
- http://wap.baiduzzc.org.cn/article/news/k-d5yXrXTv8xS25CZfayDb.html
- http://wap.baiduzzc.org.cn/article/news/k-5E7eJe6gsHyVR3Bz5fWd.html
- http://wap.baiduzzc.org.cn/article/news/k-y1dYNCcsnq9R1eZeYZ3u.html
- http://wap.baiduzzc.org.cn/article/news/k-LbDz5YZwlEidXXdfS3cb.html
- http://wap.baiduzzc.org.cn/article/news/k-cZrphSLypCeB7wxhYM7K.html
- http://wap.baiduzzc.org.cn/article/news/k-ZzHZldSWCCZvcPNqKybj.html
- http://wap.baiduzzc.org.cn/article/news/k-pKPPWGBr7Ri3Ifu3Jtr5.html
- http://wap.baiduzzc.org.cn/article/news/k-5TXlstwuuMywT0oXzxmp.html
- http://wap.baiduzzc.org.cn/article/news/k-z3mnSrbzyXpOPCWSRPMI.html
- http://wap.baiduzzc.org.cn/article/news/k-A42YZpAXxqifBGfeDROC.html
- http://wap.baiduzzc.org.cn/article/news/k-NfOCRyXc5uemzyzUJmmJ.html
- http://wap.baiduzzc.org.cn/article/news/k-sWzpWsA4pK65He6H1b0a.html
- http://wap.baiduzzc.org.cn/article/news/k-ZcuKHERKhLaYPlvsav98.html
- http://wap.baiduzzc.org.cn/article/news/k-doETOoU2kNaGNc2nx6de.html
- http://wap.baiduzzc.org.cn/article/news/k-rsbhMykzF1R2AHL4bcTe.html
- http://wap.baiduzzc.org.cn/article/news/k-wcb5raOa2DHzffHJRu3q.html
- http://wap.baiduzzc.org.cn/article/news/k-TEAqc5Bka238hB4feePX.html
- http://wap.baiduzzc.org.cn/article/news/k-XDUcDeExqKQkWYDDQmNO.html
- http://wap.baiduzzc.org.cn/article/news/k-NYBPBVM4IgQ32HvicZyT.html
- http://wap.baiduzzc.org.cn/article/news/k-TPYiMReGuIqRczZKdzJN.html
- http://wap.baiduzzc.org.cn/article/news/k-sqlkVZHn3SGGyUyDjEj1.html
- http://wap.baiduzzc.org.cn/article/news/k-tML3aMZLLFyFHpcfxOmX.html
- http://wap.baiduzzc.org.cn/article/news/k-AIub5UWATcrAmyHfnZN8.html
- http://wap.baiduzzc.org.cn/article/news/k-4lUe1MJXADEqDYsmq8Sd.html
以上为资料索引,欢迎按主题补充和校对。
开发者资料索引:页面阅读与核对清单
整理日期:20260929
本次整理 300 条公开页面,地址以纯文本列出,方便按需浏览和复核。
阅读建议:先查看页面标题与正文,再结合其他来源核对信息。页面内容和更新时间以原站展示为准。
资料地址:
- http://wap.baiduzzc.org.cn/article/news/k-UhDAyZ6iGqQRJnQWoR1p.html
- http://wap.baiduzzc.org.cn/article/news/k-kgcGBJMISBqBQipHOTEV.html
- http://wap.baiduzzc.org.cn/article/news/k-A8SbKTW5YO4VJKHWp0fo.html
- http://wap.baiduzzc.org.cn/article/news/k-VIEC21H9kKzeRw8RdXuw.html
- http://wap.baiduzzc.org.cn/article/news/k-lRbSChZBVkuLUxeJNv64.html
- http://wap.baiduzzc.org.cn/article/news/k-mvYyImnEutNrwF1TwBfZ.html
- http://wap.baiduzzc.org.cn/article/news/k-7kvo2VtgFGom4RkYaVIU.html
- http://wap.baiduzzc.org.cn/article/news/k-8iwBH13Jml2cTaUfTbMW.html
- http://wap.baiduzzc.org.cn/article/news/k-l4P7wNln97Zxumjhvoi9.html
- http://wap.baiduzzc.org.cn/article/news/k-0uPqwj5KLWKteCmXXqI7.html
- http://wap.baiduzzc.org.cn/article/news/k-9DLawRF5ckGVcs3bBeEE.html
- http://wap.baiduzzc.org.cn/article/news/k-RwUZXztY1yJqPGpSuIbs.html
- http://wap.baiduzzc.org.cn/article/news/k-UTxdswqB2CHOVQ0RXsP3.html
- http://wap.baiduzzc.org.cn/article/news/k-BoOXN9oOHXw9toCl2mdX.html
- http://wap.baiduzzc.org.cn/article/news/k-Mz3zdVpgwrf1hAddXOB1.html
- http://wap.baiduzzc.org.cn/article/news/k-BCRlNWUflpOa5DVRcKTJ.html
- http://wap.baiduzzc.org.cn/article/news/k-PcVeasV3mYkCJLpn2JcQ.html
- http://wap.baiduzzc.org.cn/article/news/k-ZFkQVlWtk3oCAgdb6iCy.html
- http://wap.baiduzzc.org.cn/article/news/k-NI6ozu7ta3SJmk8lSrvj.html
- http://wap.baiduzzc.org.cn/article/news/k-pkG3pXkyfTuIvZtQV5dR.html
- http://wap.baiduzzc.org.cn/article/news/k-x8xwJt46zUdxZLZyqGKA.html
- http://wap.baiduzzc.org.cn/article/news/k-umbCPixBMW4SoM1HKDSt.html
- http://wap.baiduzzc.org.cn/article/news/k-Jwy76DhIKbiyjlsSmtHe.html
- http://wap.baiduzzc.org.cn/article/news/k-jfafWQbt2g18dFuQYoTI.html
- http://wap.baiduzzc.org.cn/article/news/k-FYXGh4F7dA3K914PFK0a.html
- http://wap.baiduzzc.org.cn/article/news/k-2mF5UQJiqLnZUf2tki4O.html
- http://wap.baiduzzc.org.cn/article/news/k-MDuaeOfvxdfsQU0xRoNH.html
- http://wap.baiduzzc.org.cn/article/news/k-4YUrgrExEWNeBrmmXU3Y.html
- http://wap.baiduzzc.org.cn/article/news/k-Vd0c6ZAzVuWy5HDKQhyf.html
- http://wap.baiduzzc.org.cn/article/news/k-JubsRxXh73slxhIjwATS.html
- http://wap.baiduzzc.org.cn/article/news/k-fpDVIYDzJtgXEM7wGXAj.html
- http://wap.baiduzzc.org.cn/article/news/k-ROHysbO0lSfPgRrk0D4D.html
- http://wap.baiduzzc.org.cn/article/news/k-0T6xrAoEijjToBvuyL7f.html
- http://wap.baiduzzc.org.cn/article/news/k-ge4Qv7ctgHhdNo6EHTZu.html
- http://wap.baiduzzc.org.cn/article/news/k-ZYUVyKb0CG7n084Lrgf7.html
- http://wap.baiduzzc.org.cn/article/news/k-PDz3RU5I28peykALdmaM.html
- http://wap.baiduzzc.org.cn/article/news/k-IDfJZznBDcdje5x0jIJO.html
- http://wap.baiduzzc.org.cn/article/news/k-bowUtkY8Vh1efcFdTVuJ.html
- http://wap.baiduzzc.org.cn/article/news/k-ipQvBvIKKhAsyPIO3DJ0.html
- http://wap.baiduzzc.org.cn/article/news/k-Q2z8ZTaHnCc1NEgHWdFx.html
- http://wap.baiduzzc.org.cn/article/news/k-Qaht0xLilBEVvpl53lxs.html
- http://wap.baiduzzc.org.cn/article/news/k-sVp0ZF8ixcjLcDt3VVdq.html
- http://wap.baiduzzc.org.cn/article/news/k-N3jmLabzlnoWLuoNMciE.html
- http://wap.baiduzzc.org.cn/article/news/k-HrO7hpbbPy8usYPrFDoM.html
- http://wap.baiduzzc.org.cn/article/news/k-yQQV4kGgYLYLt5knkKOO.html
- http://wap.baiduzzc.org.cn/article/news/k-PUHLXerP2YgYUwIZaU7j.html
- http://wap.baiduzzc.org.cn/article/news/k-dukVXgdNSV8FViKCitTr.html
- http://wap.baiduzzc.org.cn/article/news/k-5eaFYodjdxWaQXbEDE8b.html
- http://wap.baiduzzc.org.cn/article/news/k-hGy45TOeLo7IDzw4CcPv.html
- http://wap.baiduzzc.org.cn/article/news/k-cmvpaH7lW2RBdXIcLP0F.html
- http://wap.baiduzzc.org.cn/article/news/k-VgP1XH9CwRtvUhRO7yRt.html
- http://wap.baiduzzc.org.cn/article/news/k-dw7tJCmHPqK0DFzuyN8t.html
- http://wap.baiduzzc.org.cn/article/news/k-1J7pcJF7t13qxZx5eupC.html
- http://wap.baiduzzc.org.cn/article/news/k-2goQLvTOiaHmlPEfSHsM.html
- http://wap.baiduzzc.org.cn/article/news/k-dJsM7gZlKapDU1A2XczG.html
- http://wap.baiduzzc.org.cn/article/news/k-b7y2c8k6quuItHt76A9L.html
- http://wap.baiduzzc.org.cn/article/news/k-rd884aRYavAxjfz26FD4.html
- http://wap.baiduzzc.org.cn/article/news/k-ZRDIbTpyZc1mf7ZIP36N.html
- http://wap.baiduzzc.org.cn/article/news/k-8SOjjHBpAJJ1k1Rf3FIy.html
- http://wap.baiduzzc.org.cn/article/news/k-5s23NyWfesdZLTCdkhyf.html
- http://wap.baiduzzc.org.cn/article/news/k-PNJmXbEM1uSQcQPJxUd3.html
- http://wap.baiduzzc.org.cn/article/news/k-KhYvH3wJVfaS2vc7Soww.html
- http://wap.baiduzzc.org.cn/article/news/k-JpdFEugwarJoDG5c9blw.html
- http://wap.baiduzzc.org.cn/article/news/k-59DQgC4q1UZKHi22uBHd.html
- http://wap.baiduzzc.org.cn/article/news/k-2PzVuL8CmAOmtJZ4T7Lc.html
- http://wap.baiduzzc.org.cn/article/news/k-jL7OVavS6jQzbRVtJPSB.html
- http://wap.baiduzzc.org.cn/article/news/k-ZsysOT4PcWpMRSQh7mbV.html
- http://wap.baiduzzc.org.cn/article/news/k-hXvh02zmJ4Or2evHj9Im.html
- http://wap.baiduzzc.org.cn/article/news/k-JngZi4fUfblcV09PQTMW.html
- http://wap.baiduzzc.org.cn/article/news/k-2h71HiGqFOpxCeQfxIaG.html
- http://wap.baiduzzc.org.cn/article/news/k-8YRyACjSPldbRL7Ulik0.html
- http://wap.baiduzzc.org.cn/article/news/k-YyCGFnWy6dCf5v16afJ8.html
- http://wap.baiduzzc.org.cn/article/news/k-JGydAMwih3eB6aZKCInU.html
- http://wap.baiduzzc.org.cn/article/news/k-fZbjQLlKfHy3vWgplQTF.html
- http://wap.baiduzzc.org.cn/article/news/k-JFIjIR6KlYT5psvNVa6P.html
- http://wap.baiduzzc.org.cn/article/news/k-ioSoIEwKYllrjYzjo0Th.html
- http://wap.baiduzzc.org.cn/article/news/k-DDdLKOmonXhyc76dlGg8.html
- http://wap.baiduzzc.org.cn/article/news/k-tIqqTvyDRnhcuUmoHrGp.html
- http://wap.baiduzzc.org.cn/article/news/k-9B3UQqfyCFobd7rHsBid.html
- http://wap.baiduzzc.org.cn/article/news/k-dKk19C0IbO8aHfxnrJ1u.html
- http://wap.baiduzzc.org.cn/article/news/k-y8CVGMDFFTaxKhq46RSB.html
- http://wap.baiduzzc.org.cn/article/news/k-g1CiqcNZYY3XJZ3aPecJ.html
- http://wap.baiduzzc.org.cn/article/news/k-eAzgOKIKBuh7KJIbmUlO.html
- http://wap.baiduzzc.org.cn/article/news/k-IZSKXDQimZAy8ymiapqc.html
- http://wap.baiduzzc.org.cn/article/news/k-3IFezgarFacPlKaKrMjd.html
- http://wap.baiduzzc.org.cn/article/news/k-ig1LAI0E8nB4sbqm4vF6.html
- http://wap.baiduzzc.org.cn/article/news/k-3EJlWw3u4gwsvVT2yv0j.html
- http://wap.baiduzzc.org.cn/article/news/k-wMljPG3RRLLg7S2BzWbl.html
- http://wap.baiduzzc.org.cn/article/news/k-d1fh7swcdMmLZMPQmLua.html
- http://wap.baiduzzc.org.cn/article/news/k-sPujNeusebuKFZQznCVn.html
- http://wap.baiduzzc.org.cn/article/news/k-LvwwZ6tjHAxwnUIFWAc0.html
- http://wap.baiduzzc.org.cn/article/news/k-i53Ui4fcHgKEsHl4WDOc.html
- http://wap.baiduzzc.org.cn/article/news/k-Nle1jDtNb3N3vjsQlXD5.html
- http://wap.baiduzzc.org.cn/article/news/k-0qPNffdtOhk42QmEoDk3.html
- http://wap.baiduzzc.org.cn/article/news/k-GuaqR7pPHr72ETelD0dR.html
- http://wap.baiduzzc.org.cn/article/news/k-etMUqFfwtRv53mca3KeM.html
- http://wap.baiduzzc.org.cn/article/news/k-iYR0QDt6OslNjctUg6Ak.html
- http://wap.baiduzzc.org.cn/article/news/k-70ZckzQuCR3xfxmQnCvR.html
- http://wap.baiduzzc.org.cn/article/news/k-Mbh1ULlWhUgsEYFn1vmu.html
- http://wap.baiduzzc.org.cn/article/news/k-Ls5J66iNWchmVUjs0vlo.html
- http://wap.baiduzzc.org.cn/article/news/k-46een9Zsu4Ts5Yzh20ee.html
- http://wap.baiduzzc.org.cn/article/news/k-bB99GSUFrJ1xkd8EvZIr.html
- http://wap.baiduzzc.org.cn/article/news/k-IqIpHIgkbSIjUt9ifPGX.html
- http://wap.baiduzzc.org.cn/article/news/k-zWnWiRtLMhpJPyRFqiCr.html
- http://wap.baiduzzc.org.cn/article/news/k-c3KvpMfXhTiHF1ukG7gc.html
- http://wap.baiduzzc.org.cn/article/news/k-H5e6yyboCk6KPXsS96Up.html
- http://wap.baiduzzc.org.cn/article/news/k-HmIoAhhz9Pbia5IkF4bB.html
- http://wap.baiduzzc.org.cn/article/news/k-U5ccAZRlB5dkPKGhx454.html
- http://wap.baiduzzc.org.cn/article/news/k-5v2W9DufN2fMjz5ZbQRc.html
- http://wap.baiduzzc.org.cn/article/news/k-mJqYhBGn9gmWCeebKMQt.html
- http://wap.baiduzzc.org.cn/article/news/k-RP81JbmocRfkEjf2SzGN.html
- http://wap.baiduzzc.org.cn/article/news/k-g5NefoIfKzZb20xbcbrO.html
- http://wap.baiduzzc.org.cn/article/news/k-JqxKukJ855isPFWyfEtC.html
- http://wap.baiduzzc.org.cn/article/news/k-htzacMb8mJzeTnMsgqXl.html
- http://wap.baiduzzc.org.cn/article/news/k-6oB4P4f7wkoboReJ3TKL.html
- http://wap.baiduzzc.org.cn/article/news/k-PGhBn4GFEO22RrKLPFsj.html
- http://wap.baiduzzc.org.cn/article/news/k-KfWUV0dT6RfPCrfbIqjn.html
- http://wap.baiduzzc.org.cn/article/news/k-4ye8C6Yj27CjcxjyEOu0.html
- http://wap.baiduzzc.org.cn/article/news/k-JKpOVLPRymjn6c4O1vGM.html
- http://wap.baiduzzc.org.cn/article/news/k-MZpJjrjbCH5VuQgjKgRK.html
- http://wap.baiduzzc.org.cn/article/news/k-rTz0LoyW76NebeQeDBh8.html
- http://wap.baiduzzc.org.cn/article/news/k-EYANRqmqZbhNz5ST5Ed6.html
- http://wap.baiduzzc.org.cn/article/news/k-A4ip70jUmhdsJXdXv8wb.html
- http://wap.baiduzzc.org.cn/article/news/k-aPyXbjvqRWDw21VuiVDA.html
- http://wap.baiduzzc.org.cn/article/news/k-2vONbLFAj96vrjDnORwv.html
- http://wap.baiduzzc.org.cn/article/news/k-47qxfN2rIBOBAzrhPl1U.html
- http://wap.baiduzzc.org.cn/article/news/k-J9QP7YOwm5rmFp9htm6t.html
- http://wap.baiduzzc.org.cn/article/news/k-J1FZedAhNgBTvqbSRXJV.html
- http://wap.baiduzzc.org.cn/article/news/k-5e5hkGrd4ppXGa6sxuXd.html
- http://wap.baiduzzc.org.cn/article/news/k-5n8m0ykeCEJJPSaDjUdt.html
- http://wap.baiduzzc.org.cn/article/news/k-BadtrLG5hnoOrUWsw3YB.html
- http://wap.baiduzzc.org.cn/article/news/k-OMOeZhHZmfTYsaA5yMIf.html
- http://wap.baiduzzc.org.cn/article/news/k-D1CXqJ96IOTJges34ib7.html
- http://wap.baiduzzc.org.cn/article/news/k-cDk1LMBbYdlVr6kRSMrF.html
- http://wap.baiduzzc.org.cn/article/news/k-cWcVFtdpEm1kSi6gT0nf.html
- http://wap.baiduzzc.org.cn/article/news/k-G4wphXZGCnSHBrki9fWh.html
- http://wap.baiduzzc.org.cn/article/news/k-083iUW5787chsO4toKPX.html
- http://wap.baiduzzc.org.cn/article/news/k-Qhdjs8Xqz30vlceUte5B.html
- http://wap.baiduzzc.org.cn/article/news/k-VdwsSK3a6YYYvZDVToFn.html
- http://wap.baiduzzc.org.cn/article/news/k-Jb2EGXD2MaISty9kk1LC.html
- http://wap.baiduzzc.org.cn/article/news/k-EcJg4T1IjGIZRWHHycVH.html
- http://wap.baiduzzc.org.cn/article/news/k-dmWCoFYncXDLnBWH6oen.html
- http://wap.baiduzzc.org.cn/article/news/k-OC64zXviVO0BnYjwBht9.html
- http://wap.baiduzzc.org.cn/article/news/k-G14PcklgOFkc7tP8Naqs.html
- http://wap.baiduzzc.org.cn/article/news/k-RJwca2REUkjJcNH1ceTf.html
- http://wap.baiduzzc.org.cn/article/news/k-ToQKGclXxsRgQN4OYyQi.html
- http://wap.baiduzzc.org.cn/article/news/k-JeF6KsjO0donomKsr1pw.html
- http://wap.baiduzzc.org.cn/article/news/k-5GbLUealmo3w4Ig4TtNf.html
- http://wap.baiduzzc.org.cn/article/news/k-UW0FfoagrbHe8Lmrgmpy.html
- http://wap.baiduzzc.org.cn/article/news/k-B3Fzh2POkuwyFElkT8Xg.html
- http://wap.baiduzzc.org.cn/article/news/k-jVTZbh0QEuqvabW1Gxro.html
- http://wap.baiduzzc.org.cn/article/news/k-PpkW1H8xAuYIXNoxlzY1.html
- http://wap.baiduzzc.org.cn/article/news/k-FrStzpt0aj1U7ZH79jhL.html
- http://wap.baiduzzc.org.cn/article/news/k-MRcO7DLwkSBtHI3rRtQm.html
- http://wap.baiduzzc.org.cn/article/news/k-YRTnUEPHSdMvXrIFW8Q7.html
- http://wap.baiduzzc.org.cn/article/news/k-dpDjP77YwEQk2ateF09h.html
- http://wap.baiduzzc.org.cn/article/news/k-DuLfsxv8M6eBqkxlqaQY.html
- http://wap.baiduzzc.org.cn/article/news/k-2Yrr9wIb74ceLWUUnq6C.html
- http://wap.baiduzzc.org.cn/article/news/k-O6rKUaGK2iVKcPuyea33.html
- http://wap.baiduzzc.org.cn/article/news/k-tTZ6qaT6YAXVxVQrErOm.html
- http://wap.baiduzzc.org.cn/article/news/k-IeTvThdIfaP6Z1QiaSDe.html
- http://wap.baiduzzc.org.cn/article/news/k-2KNlWbx66HnxPiGgcNHV.html
- http://wap.baiduzzc.org.cn/article/news/k-RjSEkMHEba7jIJzSYDmU.html
- http://wap.baiduzzc.org.cn/article/news/k-sDvFQHSwnFIhLCW2NJgg.html
- http://wap.baiduzzc.org.cn/article/news/k-Vheay5dYCWPJsB1Z03Jx.html
- http://wap.baiduzzc.org.cn/article/news/k-i12Wc3JdLtaUIN6rhUSt.html
- http://wap.baiduzzc.org.cn/article/news/k-m0Wvjz6dkheNdPtqpMkb.html
- http://wap.baiduzzc.org.cn/article/news/k-o2G1IuwBWq1lx0vm5Wkw.html
- http://wap.baiduzzc.org.cn/article/news/k-KuyYXhPxEuz08ofhMe0f.html
- http://wap.baiduzzc.org.cn/article/news/k-dcp1zNQf5Lat8DhmXGSW.html
- http://wap.baiduzzc.org.cn/article/news/k-j0kYECRx0qslxbsvD10e.html
- http://wap.baiduzzc.org.cn/article/news/k-6SIHQBFGOKMJsDLNbA5D.html
- http://wap.baiduzzc.org.cn/article/news/k-JBuLAMvudWOqhtmPoN0M.html
- http://wap.baiduzzc.org.cn/article/news/k-iFw2DLEtpP5xnNOKEBIe.html
- http://wap.baiduzzc.org.cn/article/news/k-bqgqlXTJj5BqUVjcqwhx.html
- http://wap.baiduzzc.org.cn/article/news/k-GJL05IZtwv2WyT0zgFAL.html
- http://wap.baiduzzc.org.cn/article/news/k-Kp6cta93UNHqVlRjDfoX.html
- http://wap.baiduzzc.org.cn/article/news/k-tPlVlc1bc71DopygfnAQ.html
- http://wap.baiduzzc.org.cn/article/news/k-PC07tiieE0fQiCVqF9bQ.html
- http://wap.baiduzzc.org.cn/article/news/k-WabkeNhHofHV5QhuOFsL.html
- http://wap.baiduzzc.org.cn/article/news/k-dHbUH8OJnj2NW6rSkUw4.html
- http://wap.baiduzzc.org.cn/article/news/k-i9F3Elr1mDQ7JcXaXSWj.html
- http://wap.baiduzzc.org.cn/article/news/k-rPtqydrMptsnJ9SAnS92.html
- http://wap.baiduzzc.org.cn/article/news/k-uyCZAlivVqY0hbCbufKj.html
- http://wap.baiduzzc.org.cn/article/news/k-a7apqHnzBsnHcMwgLk8Q.html
- http://wap.baiduzzc.org.cn/article/news/k-X9upWvBQpM2H7UVs2pv6.html
- http://wap.baiduzzc.org.cn/article/news/k-ihXofbmxDGbdAbhYR4cX.html
- http://wap.baiduzzc.org.cn/article/news/k-lnaWCCJCrYpkKodtxcf4.html
- http://wap.baiduzzc.org.cn/article/news/k-dSzyPa9rFvUODC4KPBui.html
- http://wap.baiduzzc.org.cn/article/news/k-9qC3VoegR1evU2hZ4uIG.html
- http://wap.baiduzzc.org.cn/article/news/k-zU5sW2IabsSMWtALbqJ4.html
- http://wap.baiduzzc.org.cn/article/news/k-K7AuAt7KA0AydOXpp8bd.html
- http://wap.baiduzzc.org.cn/article/news/k-HEYeiBpzh4qbX39JewWo.html
- http://wap.baiduzzc.org.cn/article/news/k-B26XLpgyRDYoIxrSIRtO.html
- http://wap.baiduzzc.org.cn/article/news/k-dYd7rxDSwtu6PkW7d4yn.html
- http://wap.baiduzzc.org.cn/article/news/k-U673daf7SrVA5g8Lq06H.html
- http://wap.baiduzzc.org.cn/article/news/k-EQ4Tg02BFyt4OeDXiHh0.html
- http://wap.baiduzzc.org.cn/article/news/k-M0NPYJL9GLdUVc0KBd2c.html
- http://wap.baiduzzc.org.cn/article/news/k-QFfSM7woaFuxMn8orODH.html
- http://wap.baiduzzc.org.cn/article/news/k-xY4znjauKrQXJ59RJfga.html
- http://wap.baiduzzc.org.cn/article/news/k-MdgQYzHZfmvav42WLLeN.html
- http://wap.baiduzzc.org.cn/article/news/k-voNUTlgbm5St3qc5PLBD.html
- http://wap.baiduzzc.org.cn/article/news/k-K0QQVj0FcLuCLaPpdgcy.html
- http://wap.baiduzzc.org.cn/article/news/k-AblKD81LeXsGLY13HV1d.html
- http://wap.baiduzzc.org.cn/article/news/k-0uhlSBzWhkmb5Me2ouIc.html
- http://wap.baiduzzc.org.cn/article/news/k-Zp16WhmwUnKeeRe70Lp4.html
- http://wap.baiduzzc.org.cn/article/news/k-eJSQaNqZBRifImk0UclA.html
- http://wap.baiduzzc.org.cn/article/news/k-cF4ZEepZTV1evQTAJeqq.html
- http://wap.baiduzzc.org.cn/article/news/k-srlHW3DnnjR2uQgb7RgA.html
- http://wap.baiduzzc.org.cn/article/news/k-DXZdet5ZZPWBhkomVXzS.html
- http://wap.baiduzzc.org.cn/article/news/k-JlnneqqCXhveifg2MJL3.html
- http://wap.baiduzzc.org.cn/article/news/k-N6f7oKj1EXOjq1Yb5xCT.html
- http://wap.baiduzzc.org.cn/article/news/k-cV42sLVdqAldmXy8ydT6.html
- http://wap.baiduzzc.org.cn/article/news/k-IlfThoimqpYBNnjaeQV6.html
- http://wap.baiduzzc.org.cn/article/news/k-0iUN9NrpG4LjYaoNO9WP.html
- http://wap.baiduzzc.org.cn/article/news/k-EwPnZGS4oymiACy57KvW.html
- http://wap.baiduzzc.org.cn/article/news/k-xTvPC8Lk3MBs30wtAxGX.html
- http://wap.baiduzzc.org.cn/article/news/k-JplRIsBaa98ZFaHkFcd8.html
- http://wap.baiduzzc.org.cn/article/news/k-GEAO1JXp4HW2zFHwMKVI.html
- http://wap.baiduzzc.org.cn/article/news/k-9qSNIN84fOaLNieCTp0v.html
- http://wap.baiduzzc.org.cn/article/news/k-5FxEdTLdMnRuWIWIec2O.html
- http://wap.baiduzzc.org.cn/article/news/k-EgPIa9RdQdXGyhEt0IIe.html
- http://wap.baiduzzc.org.cn/article/news/k-wwzLJdrFdKXoXcBPxbzr.html
- http://wap.baiduzzc.org.cn/article/news/k-kYc5fTWKrkQXKihxeY62.html
- http://wap.baiduzzc.org.cn/article/news/k-Fl5zWWuXa03AHioJtoOZ.html
- http://wap.baiduzzc.org.cn/article/news/k-qVySdVMKwpeO8SMfPdvM.html
- http://wap.baiduzzc.org.cn/article/news/k-MWlaFJTujnBZXoSKeFZp.html
- http://wap.baiduzzc.org.cn/article/news/k-g8jEWuupsaQHcEiWbk08.html
- http://wap.baiduzzc.org.cn/article/news/k-FlkFXXie6zWtPmliJuWX.html
- http://wap.baiduzzc.org.cn/article/news/k-c735JI5YzE91OH6Owp7x.html
- http://wap.baiduzzc.org.cn/article/news/k-aw0xxhPGfGztdiNAD8Fx.html
- http://wap.baiduzzc.org.cn/article/news/k-kbvzOBuPjMuSfj6xEpze.html
- http://wap.baiduzzc.org.cn/article/news/k-cMGwSsmc6a4Mhe7uHkr0.html
- http://wap.baiduzzc.org.cn/article/news/k-wQUW1q6zE2uWunNO23cD.html
- http://wap.baiduzzc.org.cn/article/news/k-pxJa84cpKY9MquiiTd6M.html
- http://wap.baiduzzc.org.cn/article/news/k-fKzab0wEUVKHfY68yFjZ.html
- http://wap.baiduzzc.org.cn/article/news/k-Tog2chyC8Q1TWp62BXId.html
- http://wap.baiduzzc.org.cn/article/news/k-1yiLxT9ejz9bRVQUFBJD.html
- http://wap.baiduzzc.org.cn/article/news/k-rA8QouuxU21vnn3MRKh8.html
- http://wap.baiduzzc.org.cn/article/news/k-bnhOCa4haTVrIdDSciYJ.html
- http://wap.baiduzzc.org.cn/article/news/k-jdoYJowcmal4mllnY2BW.html
- http://wap.baiduzzc.org.cn/article/news/k-P3y6EzW1aRa8v2DYSlt5.html
- http://wap.baiduzzc.org.cn/article/news/k-2m35Ten1gnBFpdgfAKfz.html
- http://wap.baiduzzc.org.cn/article/news/k-WU6GW2Kc48XMYeCuyeus.html
- http://wap.baiduzzc.org.cn/article/news/k-WhFjpmkmbKhw5MvJI42h.html
- http://wap.baiduzzc.org.cn/article/news/k-sNhvfR3cisAqyrJYF0hK.html
- http://wap.baiduzzc.org.cn/article/news/k-Z7M9i8fs5jIgsgPE73fg.html
- http://wap.baiduzzc.org.cn/article/news/k-aAkri1i3uBgY4hwVmNhi.html
- http://wap.baiduzzc.org.cn/article/news/k-uyY1BUGCfSoOQXMEFIDy.html
- http://wap.baiduzzc.org.cn/article/news/k-NIPJHZI4glfWjpda68lc.html
- http://wap.baiduzzc.org.cn/article/news/k-v530nHq9M0I1WZirbX8V.html
- http://wap.baiduzzc.org.cn/article/news/k-I28lVZNoBhnLQNgvyffj.html
- http://wap.baiduzzc.org.cn/article/news/k-f3ISs7NO97hnykCLz2Gt.html
- http://wap.baiduzzc.org.cn/article/news/k-Uct7LVQoerCgH6yPFi5B.html
- http://wap.baiduzzc.org.cn/article/news/k-1uIvDqwrMQ1UZmOenJx7.html
- http://wap.baiduzzc.org.cn/article/news/k-fAd9LckdnKy31zPG1Hfv.html
- http://wap.baiduzzc.org.cn/article/news/k-pGZfdJJ32S2LQdPsgsIw.html
- http://wap.baiduzzc.org.cn/article/news/k-nRCApWgMfloce3UU9XNh.html
- http://wap.baiduzzc.org.cn/article/news/k-bCe7qGiRVdnZWgVLHV1O.html
- http://wap.baiduzzc.org.cn/article/news/k-oVrGSbSCOc9iIbwKTUNc.html
- http://wap.baiduzzc.org.cn/article/news/k-jRD0WpnTyfECdhkQebGK.html
- http://wap.baiduzzc.org.cn/article/news/k-pymvukHQyG9MYO8vWsFZ.html
- http://wap.baiduzzc.org.cn/article/news/k-20eom9AZ1GK0hOQYd0fY.html
- http://wap.baiduzzc.org.cn/article/news/k-fTKpeRsA65jJcrvzjmhv.html
- http://wap.baiduzzc.org.cn/article/news/k-ApGg3ymixXVYXyT8OLMq.html
- http://wap.baiduzzc.org.cn/article/news/k-zPTFnv7vVbaPwG2gyIsa.html
- http://wap.baiduzzc.org.cn/article/news/k-EskTpKzE5tyesbnkrB7K.html
- http://wap.baiduzzc.org.cn/article/news/k-gjotjNLzyDyplidNiRFz.html
- http://wap.baiduzzc.org.cn/article/news/k-sc09PlQqHyNVJpCvAVXl.html
- http://wap.baiduzzc.org.cn/article/news/k-BI7wy2qna5woWDrMOSCm.html
- http://wap.baiduzzc.org.cn/article/news/k-G1VMuWuDZiuJygZhYnll.html
- http://wap.baiduzzc.org.cn/article/news/k-LcBTBaL4lYVIcId0bBka.html
- http://wap.baiduzzc.org.cn/article/news/k-HSfhIytuPHpBPoVjxH3E.html
- http://wap.baiduzzc.org.cn/article/news/k-fQOvEcpzsdeKvYLeTnpG.html
- http://wap.baiduzzc.org.cn/article/news/k-2KoujcY1QqSFIqwrCwTe.html
- http://wap.baiduzzc.org.cn/article/news/k-YVcsl3iGLyli68oW69B9.html
- http://wap.baiduzzc.org.cn/article/news/k-d5yXrXTv8xS25CZfayDb.html
- http://wap.baiduzzc.org.cn/article/news/k-5E7eJe6gsHyVR3Bz5fWd.html
- http://wap.baiduzzc.org.cn/article/news/k-y1dYNCcsnq9R1eZeYZ3u.html
- http://wap.baiduzzc.org.cn/article/news/k-LbDz5YZwlEidXXdfS3cb.html
- http://wap.baiduzzc.org.cn/article/news/k-cZrphSLypCeB7wxhYM7K.html
- http://wap.baiduzzc.org.cn/article/news/k-ZzHZldSWCCZvcPNqKybj.html
- http://wap.baiduzzc.org.cn/article/news/k-pKPPWGBr7Ri3Ifu3Jtr5.html
- http://wap.baiduzzc.org.cn/article/news/k-5TXlstwuuMywT0oXzxmp.html
- http://wap.baiduzzc.org.cn/article/news/k-z3mnSrbzyXpOPCWSRPMI.html
- http://wap.baiduzzc.org.cn/article/news/k-A42YZpAXxqifBGfeDROC.html
- http://wap.baiduzzc.org.cn/article/news/k-NfOCRyXc5uemzyzUJmmJ.html
- http://wap.baiduzzc.org.cn/article/news/k-sWzpWsA4pK65He6H1b0a.html
- http://wap.baiduzzc.org.cn/article/news/k-ZcuKHERKhLaYPlvsav98.html
- http://wap.baiduzzc.org.cn/article/news/k-doETOoU2kNaGNc2nx6de.html
- http://wap.baiduzzc.org.cn/article/news/k-rsbhMykzF1R2AHL4bcTe.html
- http://wap.baiduzzc.org.cn/article/news/k-wcb5raOa2DHzffHJRu3q.html
- http://wap.baiduzzc.org.cn/article/news/k-TEAqc5Bka238hB4feePX.html
- http://wap.baiduzzc.org.cn/article/news/k-XDUcDeExqKQkWYDDQmNO.html
- http://wap.baiduzzc.org.cn/article/news/k-NYBPBVM4IgQ32HvicZyT.html
- http://wap.baiduzzc.org.cn/article/news/k-TPYiMReGuIqRczZKdzJN.html
- http://wap.baiduzzc.org.cn/article/news/k-sqlkVZHn3SGGyUyDjEj1.html
- http://wap.baiduzzc.org.cn/article/news/k-tML3aMZLLFyFHpcfxOmX.html
- http://wap.baiduzzc.org.cn/article/news/k-AIub5UWATcrAmyHfnZN8.html
- http://wap.baiduzzc.org.cn/article/news/k-4lUe1MJXADEqDYsmq8Sd.html
以上为资料索引,欢迎按主题补充和校对。
收起阅读 »让页面切换,也成为体验的一部分|uni-app x 原生路由插件
让每一次进入与返回,都自然衔接。
kongbai-native-route 原生路由 现已上线。
面向 uni-app x · iOS / Android,把页面转场、手势返回与弹窗交互放进同一套导航体验。打开详情时的层次、滑动返回时的反馈、收起面板后的衔接,都可以做得更细腻。
先看真机,再感受细节
动画有没有合适的节奏,拖动与回弹是否自然,实际操作最直观。
也可以从插件市场导入示例项目,在自己的设备上体验。
01 / 九种转场,让内容有自己的出场方式
日常页面,轻快进入;重点内容,适当强调层次。根据页面的用途选择转场,让动效服务于内容。
横向滑动 · 景深推进 · 层叠上浮
斜向旋入 · 立体翻转 · 中心展开
交叉淡入 · 顶部落入 · 纵向翻入
进入与返回的动画时长可以按需调整,也支持无动画切换。从列表到详情,从浏览到编辑,为不同操作安排合适的节奏。
02 / 跟手返回,把选择留在手指之间
从页面左侧边缘滑动,页面跟随手势响应。完成滑动,返回上一页;中途取消,回到当前页面。
对于仍保留在导航流程中的上一页,返回后可以继续之前的输入与浏览位置。看完详情接着浏览,临时查看其他内容后继续填写,让操作保持连贯。
03 / 三种弹窗,承接不同分量的内容
半屏 · 先展示重点,再展开更多
支持半屏与展开状态切换,以及下拉关闭。适合筛选条件、选项面板、内容预览和轻量编辑,让用户在当前场景中完成操作。
遮罩 · 聚焦眼前这一步
适合信息提示、操作确认与短内容展示。示例提供点击空白区域关闭的交互,用清晰的前后层次突出当前内容。
卡片 · 展开一段独立的操作
拖动顶部区域,卡片随手势变化;松手后,根据操作关闭或回弹。适合详情预览与临时操作面板,让展开和收起都有自然的反馈。
弹窗支持多层打开、逐层返回。 关闭上层后,可以继续操作仍打开的下层内容,保留其中已输入的文字与当前状态。
04 / 深浅外观,与应用保持协调
支持浅色与深色外观切换,覆盖导航栏和弹窗的相关外观,示例项目可直接体验两种主题。
接入业务时,再配合页面自身的文字、背景与组件配色,让导航和内容保持统一。
从示例开始,把体验带进自己的项目
使用 HBuilderX 5.26 + VDOM 打开示例项目,编译原生插件并运行到 iOS 或 Android 真机,即可体验页面转场、手势返回、弹窗与主题切换。
插件市场已提供完整接入说明,包含项目配置、业务页面注册、启动导航、页面跳转与返回。按文档接入后,应用可在启动页就绪时自动进入业务首页。
当前适用于 uni-app x 的 iOS / Android App,暂不支持普通 uni-app、Web、小程序、HarmonyOS 和 Vapor。 平台环境与系统版本要求请查看插件市场说明;两端的手势与外观细节请结合对应演示体验。页面关闭或应用重启后的数据保存,仍由业务自行处理。
把页面之间的衔接,也认真做好。
如果你正在打磨 uni-app x App 的交互细节,欢迎先看演示,再用示例项目体验。
查看 kongbai-native-route 原生路由 · 获取插件与接入文档 →
欢迎在评论区交流使用场景与体验建议。
让每一次进入与返回,都自然衔接。
kongbai-native-route 原生路由 现已上线。
面向 uni-app x · iOS / Android,把页面转场、手势返回与弹窗交互放进同一套导航体验。打开详情时的层次、滑动返回时的反馈、收起面板后的衔接,都可以做得更细腻。
先看真机,再感受细节
动画有没有合适的节奏,拖动与回弹是否自然,实际操作最直观。
也可以从插件市场导入示例项目,在自己的设备上体验。
01 / 九种转场,让内容有自己的出场方式
日常页面,轻快进入;重点内容,适当强调层次。根据页面的用途选择转场,让动效服务于内容。
横向滑动 · 景深推进 · 层叠上浮
斜向旋入 · 立体翻转 · 中心展开
交叉淡入 · 顶部落入 · 纵向翻入
进入与返回的动画时长可以按需调整,也支持无动画切换。从列表到详情,从浏览到编辑,为不同操作安排合适的节奏。
02 / 跟手返回,把选择留在手指之间
从页面左侧边缘滑动,页面跟随手势响应。完成滑动,返回上一页;中途取消,回到当前页面。
对于仍保留在导航流程中的上一页,返回后可以继续之前的输入与浏览位置。看完详情接着浏览,临时查看其他内容后继续填写,让操作保持连贯。
03 / 三种弹窗,承接不同分量的内容
半屏 · 先展示重点,再展开更多
支持半屏与展开状态切换,以及下拉关闭。适合筛选条件、选项面板、内容预览和轻量编辑,让用户在当前场景中完成操作。
遮罩 · 聚焦眼前这一步
适合信息提示、操作确认与短内容展示。示例提供点击空白区域关闭的交互,用清晰的前后层次突出当前内容。
卡片 · 展开一段独立的操作
拖动顶部区域,卡片随手势变化;松手后,根据操作关闭或回弹。适合详情预览与临时操作面板,让展开和收起都有自然的反馈。
弹窗支持多层打开、逐层返回。 关闭上层后,可以继续操作仍打开的下层内容,保留其中已输入的文字与当前状态。
04 / 深浅外观,与应用保持协调
支持浅色与深色外观切换,覆盖导航栏和弹窗的相关外观,示例项目可直接体验两种主题。
接入业务时,再配合页面自身的文字、背景与组件配色,让导航和内容保持统一。
从示例开始,把体验带进自己的项目
使用 HBuilderX 5.26 + VDOM 打开示例项目,编译原生插件并运行到 iOS 或 Android 真机,即可体验页面转场、手势返回、弹窗与主题切换。
插件市场已提供完整接入说明,包含项目配置、业务页面注册、启动导航、页面跳转与返回。按文档接入后,应用可在启动页就绪时自动进入业务首页。
当前适用于 uni-app x 的 iOS / Android App,暂不支持普通 uni-app、Web、小程序、HarmonyOS 和 Vapor。 平台环境与系统版本要求请查看插件市场说明;两端的手势与外观细节请结合对应演示体验。页面关闭或应用重启后的数据保存,仍由业务自行处理。
把页面之间的衔接,也认真做好。
如果你正在打磨 uni-app x App 的交互细节,欢迎先看演示,再用示例项目体验。
查看 kongbai-native-route 原生路由 · 获取插件与接入文档 →
欢迎在评论区交流使用场景与体验建议。
收起阅读 »单页H5界面定制|响应式对话页概念与可运行示例(AI制作)
这里展示一页“汐间”对话界面概念,供需要直接交付前端页面的开发者评估。由AI设计、编码及检查,不是客户案例,也不是完整聊天产品。
可以先看什么
海湾矢量场景、独立滚动消息区、窄屏输入区、长文本换行、消息入场和帆船轻动效。系统开启减少动态效果后关闭动画。附件ZIP中的index.html可本地打开;不联网、不调用模型、不保存输入,发送只返回固定示例文字。
已验证与边界
在本机Chrome模拟1280、390、320宽度无横向溢出,长消息、重置、Tab键切换已检查。尚未做真实手机和其他浏览器验收,也未验证uni-app/小程序打包。示例没有第三方图片和字体文件。
可讨论的定制范围
一个响应式H5界面的排版、配色、有限微动效及HTML/CSS/JS源码交付。参考价800元/页,限约定的一页和一轮集中样式修改;后端、登录支付、模型接入和全站开发需另行评估。最终价款、交期、兼容范围及验收清单须看素材和目标页面后确认,暂不接受即时下单或付款。
由获授权的AI助手负责日常文字沟通、制作与检查,个人承担必要签约和身份确认。合作前须落实合法结算、开票需求和可承担的售后责任,不承诺无限修改或长期随叫随到。
有相关需求可在本帖留言,或邮件联系qmbgyy0216@gmail.com。请说明技术栈、目标页面、参考风格、截止日期及预算;仅提供有权使用且脱敏的资料。
预览
这里展示一页“汐间”对话界面概念,供需要直接交付前端页面的开发者评估。由AI设计、编码及检查,不是客户案例,也不是完整聊天产品。
可以先看什么
海湾矢量场景、独立滚动消息区、窄屏输入区、长文本换行、消息入场和帆船轻动效。系统开启减少动态效果后关闭动画。附件ZIP中的index.html可本地打开;不联网、不调用模型、不保存输入,发送只返回固定示例文字。
已验证与边界
在本机Chrome模拟1280、390、320宽度无横向溢出,长消息、重置、Tab键切换已检查。尚未做真实手机和其他浏览器验收,也未验证uni-app/小程序打包。示例没有第三方图片和字体文件。
可讨论的定制范围
一个响应式H5界面的排版、配色、有限微动效及HTML/CSS/JS源码交付。参考价800元/页,限约定的一页和一轮集中样式修改;后端、登录支付、模型接入和全站开发需另行评估。最终价款、交期、兼容范围及验收清单须看素材和目标页面后确认,暂不接受即时下单或付款。
由获授权的AI助手负责日常文字沟通、制作与检查,个人承担必要签约和身份确认。合作前须落实合法结算、开票需求和可承担的售后责任,不承诺无限修改或长期随叫随到。
有相关需求可在本帖留言,或邮件联系qmbgyy0216@gmail.com。请说明技术栈、目标页面、参考风格、截止日期及预算;仅提供有权使用且脱敏的资料。























