From a2e6989d325caacd05f17d77ac13e9757c061361 Mon Sep 17 00:00:00 2001 From: lqq <1960002614@qq.com> Date: Thu, 18 Dec 2025 17:09:31 +0800 Subject: [PATCH 01/22] =?UTF-8?q?docs:=20[Issues:=20#IDE3MY]=20=E6=96=B0?= =?UTF-8?q?=E5=A2=9Ewatermelondb=E7=9A=84=E6=8C=87=E5=AF=BC=E6=96=87?= =?UTF-8?q?=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- zh-cn/watermelondb.md | 361 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 361 insertions(+) create mode 100644 zh-cn/watermelondb.md diff --git a/zh-cn/watermelondb.md b/zh-cn/watermelondb.md new file mode 100644 index 000000000..0120a22f2 --- /dev/null +++ b/zh-cn/watermelondb.md @@ -0,0 +1,361 @@ +> 模板版本:v0.3.0 + +

+

react-native-camera

+

+ +本项目基于 [react-native-camera@v3.40.0](https://github.com/react-native-camera/react-native-camera/tree/v3.40.0) 开发。 + +请到三方库的 Releases 发布地址查看配套的版本信息:[@react-native-ohos/react-native-camera Releases](https://github.com/react-native-oh-library/react-native-camera/releases)。对于未发布到npm的旧版本,请参考[安装指南](/zh-cn/tgz-usage.md)安装tgz包。 + +| 三方库版本 | 发布信息 | 支持RN版本 | +| ---------- | -------------------------------------------------------------------------------------------------------------------------- | ---------- | +| v3.40.0 | [@react-native-ohos/react-native-camera Releases](https://github.com/react-native-oh-library/react-native-camera/releases) | 0.72/0.77 | + +## 1. 安装与使用 + +进入到工程目录并输入以下命令: + +#### **npm** + +```bash +npm install @react-native-ohos/react-native-camera +``` + +#### **yarn** + +```bash +yarn add @react-native-ohos/react-native-camera +``` + +下面的代码展示了这个库的基本使用场景: + +> [!WARNING] 使用时 import 的库名不变。 + +```js +import * as React from 'react'; +import { Text, View } from 'react-native'; +import { RNCamera } from 'react-native-camera'; +import { + StyleSheet, +} from 'react-native'; + +export function CameraExample() { + return ( + + { + const options = { + quality: 0.5, + base64: true, + width: 300, + height: 300, + }; + this.camera.takePictureAsync(options).then((photoResult) => { + console.log("takepicture result:" + photoResult?.path) + }); + + } + }> + 测试takepicture + + + + { + this.camera = ref; + }} + style={styles.cameraPreview} + > + + + ); +} + +const styles = StyleSheet.create({ + container: { + flex: 1, + paddingTop: 10, + backgroundColor: '#000', + }, + button: { + width: 160, + height: 36, + backgroundColor: 'hsl(190, 50%, 70%)', + paddingHorizontal: 16, + paddingVertical: 8, + borderRadius: 8, + }, + margin20: { + marginLeft: 20 + }, + buttonText: { + width: '100%', + height: '100%', + fontWeight: 'bold', + }, + cameraPreview: { width: '100%', aspectRatio: 56 / 100 } +}); + +export default CameraExample; +``` + +## 2. Manual Link + +此步骤为手动配置原生依赖项的指导。 + +首先需要使用 DevEco Studio 打开项目里的 HarmonyOS 工程 `harmony`。 + +### 2.1. Overrides RN SDK + +为了让工程依赖同一个版本的 RN SDK,需要在工程根目录的 `oh-package.json5` 添加 overrides 字段,指向工程需要使用的 RN SDK 版本。替换的版本既可以是一个具体的版本号,也可以是一个模糊版本,还可以是本地存在的 HAR 包或源码目录。 + +关于该字段的作用请阅读[官方说明](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/ide-oh-package-json5-V5#zh-cn_topic_0000001792256137_overrides): + +```json +{ + "overrides": { + "@rnoh/react-native-openharmony": "^0.72.38" // ohpm 在线版本 + // "@rnoh/react-native-openharmony" : "./react_native_openharmony.har" // 指向本地 har 包的路径 + // "@rnoh/react-native-openharmony" : "./react_native_openharmony" // 指向源码路径 + } +} +``` + +### 2.2. 引入原生端代码 + +目前有两种方法: + +1. 通过 har 包引入(在 IDE 完善相关功能后该方法会被遗弃,目前首选此方法); +2. 直接链接源码。 + +方法一:通过 har 包引入(推荐) + +> [!TIP] har 包位于三方库安装路径的 `harmony` 文件夹下。 + +打开 `entry/oh-package.json5`,添加以下依赖 + +```json +"dependencies": { + "@react-native-ohos/react-native-camera": "file:../../node_modules/@react-native-ohos/react-native-camera/harmony/reactNativeCamera.har" + } +``` + +点击右上角的 `sync` 按钮 + +或者在终端执行: + +```bash +cd entry +ohpm install +``` + +方法二:直接链接源码 + +> [!TIP] 如需使用直接链接源码,请参考[直接链接源码说明](/zh-cn/link-source-code.md) + +### 2.3.配置 CMakeLists 和引入 NativeCameraPackage + +打开 `entry/src/main/cpp/CMakeLists.txt`,添加: + +```diff ++ set(OH_MODULES "${CMAKE_CURRENT_SOURCE_DIR}/../../../oh_modules") + +# RNOH_BEGIN: manual_package_linking_1 ++ add_subdirectory("${OH_MODULES}/@react-native-ohos/react-native-camera/src/main/cpp" ./reactNativeCamera) +# RNOH_END: manual_package_linking_1 + +# RNOH_BEGIN: manual_package_linking_2 ++ target_link_libraries(rnoh_app PUBLIC rnoh_native_camera) +# RNOH_END: manual_package_linking_2 +``` + +打开 `entry/src/main/cpp/PackageProvider.cpp`,添加: + +```diff +#include "RNOH/PackageProvider.h" +#include "generated/RNOHGeneratedPackage.h" ++ #include "NativeCameraPackage.h" + +using namespace rnoh; + +std::vector> PackageProvider::getPackages(Package::Context ctx) { + return { + std::make_shared(ctx), ++ std::make_shared(ctx) + }; +} +``` + +### 2.4.在 ArkTs 侧引入 react-native-camera 组件 + +找到 `function buildCustomRNComponent()`,一般位于 `entry/src/main/ets/pages/index.ets` 或 `entry/src/main/ets/rn/LoadBundle.ets`,添加: + +```diff +... ++ import { ReactCameraView } from '@react-native-ohos/react-native-camera'; + +@Builder +export function buildCustomRNComponent(ctx: ComponentBuilderContext) { +... ++ if (ctx.componentName === ReactCameraView.NAME) { ++ ReactCameraView({ ++ ctx: ctx.rnComponentContext, ++ tag: ctx.tag, ++ }) ++ } +... +} +... +``` + +> [!TIP] 本库使用了混合方案,需要添加组件名。 + +在 `entry/src/main/ets/pages/index.ets` 或 `entry/src/main/ets/rn/LoadBundle.ets` 找到常量 `arkTsComponentNames` 在其数组里添加组件名 + +```diff +const arkTsComponentNames: Array = [ + SampleView.NAME, + GeneratedSampleView.NAME, + PropsDisplayer.NAME, ++ ReactCameraView.NAME + ]; +``` + +### 2.5.在 ArkTs 侧引入 ReactNativeCameraPackage + +打开 `entry/src/main/ets/RNPackagesFactory.ts`,添加: + +```diff +... ++ import { ReactNativeCameraPackage, FaceDectorPackage } from '@react-native-ohos/react-native-camera/ts'; +export function createRNPackages(ctx: RNPackageContext): RNPackage[] { + return [ + new SamplePackage(ctx), ++ new ReactNativeCameraPackage(ctx), ++ new FaceDectorPackage(ctx) + ]; +} +``` + +### 2.6.运行 + +点击右上角的 `sync` 按钮 + +或者在终端执行: + +```bash +cd entry +ohpm install +``` + +然后编译、运行即可。 + +## 3. 约束与限制 + +### 3.1. 兼容性 + +要使用此库,需要使用正确的 React-Native 和 RNOH 版本。另外,还需要使用配套的 DevEco Studio 和 手机 ROM。 + +请到三方库相应的 Releases 发布地址查看 Release 配套的版本信息:[@react-native-ohos/react-native-camera Releases](https://github.com/react-native-oh-library/react-native-camera/releases) + +### 3.2. 权限要求 + +#### 在 entry 目录下的module.json5中添加权限 + +打开 `entry/src/main/module.json5`,添加: + +```diff +... +"requestPermissions": [ ++ { ++ "name": "ohos.permission.CAMERA", ++ "reason": "$string:camera_reason", ++ "usedScene": { ++ "abilities": [ ++ "EntryAbility" ++ ], ++ "when":"inuse" ++ } ++ }, ++ { ++ "name": "ohos.permission.MICROPHONE", ++ "reason": "$string:microphone_reason", ++ "usedScene": { ++ "abilities": [ ++ "EntryAbility" ++ ], ++ "when":"inuse" ++ } ++ }, +] +``` + +#### 在 entry 目录下添加申请以上权限的原因 + +打开 `entry/src/main/resources/base/element/string.json`,添加: + +```diff +... +{ + "string": [ ++ { ++ "name": "camera_reason", ++ "value": "使用相机" ++ }, ++ { ++ "name": "microphone_reason", ++ "value": "使用麦克风" ++ }, + ] +} +``` + +## 4. 属性 + +> [!TIP] "Platform"列表示该属性在原三方库上支持的平台。 + +> [!TIP] "HarmonyOS Support"列为 yes 表示 HarmonyOS 平台支持该属性;no 则表示不支持;partially 表示部分支持。使用方法跨平台一致,效果对标 iOS 或 Android 的效果。 + +| Name | Description | Type | Required | Platform | HarmonyOS Support | +| ------------------------ | -------------------------- | -------- | -------- | ----------- | ----------------- | +| pipe | 对查询结果进行链式转换操作 | function | no | iOS/Android | yes | +| fetch() | 立即执行查询获取结果 | function | no | iOS/Android | yes | +| then |支持Promise链式调用查询结果 | function | no | iOS/Android | yes | +|observe |监听查询结果的实时变化 | function | no | iOS/Android | yes | +| observeWithColumns| 监听特定字段变化的查询结果 | function | no | iOS/Android | yes | +| fetchCount| 获取查询结果的数量统计 | function | no | iOS/Android | yes | +| get count| 获取查询结果总数 | function | no | iOS/Android | yes | +| observeCount| 监听查询结果数量的变化 | function | no | iOS/Android | yes | +| fetchIds| 获取查询结果的ID列表 | function | no | iOS/Android | yes | +| unsafeFetchRaw | 不安全地获取原始查询数据 | function | no | iOS/Android | yes | +| experimentalSubscribe| 实验性查询订阅功能 | function | no | iOS/Android | yes | +| experimentalSubscribeWithColumns| 实验性字段变化订阅 | function | no | Android | yes | +| experimentalSubscribeToCount| 实验性数量变化订阅 | function | no | Android | yes | +| markAllAsDeleted| 标记所有查询结果为软删除 | function | no | Android | yes | +| destroyAllPermanently| 永久删除所有查询结果 | string | no | function | yes | +| get modelClass|获取查询对应的模型类 | function | no | iOS/Android | yes | +|get table| 获取查询的主表名称 | function | no | iOS/Android | yes | +|get secondaryTables| 获取查询涉及的关联表 | function | no | iOS/Android | yes | +|get allTables| 获取查询所有相关表 | function | no | Android | yes | +| get associations| 获取查询的关联关系 | function | no | Android | yes | +| serialize| 序列化查询为可传输格式 | string | no | function | yes | +| tableName| 定义数据库表的名称 | function | no | iOS/Android | yes | +|columnName| 定义数据库字段的名称 | function | no | iOS/Android | yes | +|appSchema| 定义整个应用数据库的结构模式 | function | no | iOS/Android | yes | +|validateColumnsSchema| 验证字段模式定义的合法性 | function | no | Android | yes | +| tableSchema| 定义单个数据表的结构和字段配置 | function | no | Android |yes | + +## 5.遗留问题 + +- [ ] onFacesDetected能力无法实现 [issue#1](https://github.com/react-native-oh-library/react-native-camera/issues/8) + +## 6.其他 + +- notAuthorizedView属性,pendingAuthorizationView属性是android独有的属性,申请授权时显示的UI,harmony已经提供授权时的弹窗。 +- ratio是android独有的属性,在ios无效果 [源库readme位置](https://github.com/react-native-camera/react-native-camera/blob/v3.40.0/docs/RNCamera.md#android-ratio) + +## 7.开源协议 + +本项目基于 [MIT License (MIT)](https://github.com/react-native-camera/react-native-camera/blob/master/LICENSE) ,请自由地享受和参与开源。 + -- Gitee From 96e22fecfb4be203d2d7c7f67058260b829f2ca6 Mon Sep 17 00:00:00 2001 From: lqq <1960002614@qq.com> Date: Thu, 18 Dec 2025 19:48:39 +0800 Subject: [PATCH 02/22] =?UTF-8?q?docs:=20[Issues:=20#IDE3MY]=20=E6=96=B0?= =?UTF-8?q?=E5=A2=9Ewatermelondb=E7=9A=84=E6=8C=87=E5=AF=BC=E6=96=87?= =?UTF-8?q?=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- zh-cn/watermelondb.md | 120 +++++++++++++++++++++++++++++++----------- 1 file changed, 90 insertions(+), 30 deletions(-) diff --git a/zh-cn/watermelondb.md b/zh-cn/watermelondb.md index 0120a22f2..8aa9e03de 100644 --- a/zh-cn/watermelondb.md +++ b/zh-cn/watermelondb.md @@ -317,36 +317,96 @@ ohpm install > [!TIP] "HarmonyOS Support"列为 yes 表示 HarmonyOS 平台支持该属性;no 则表示不支持;partially 表示部分支持。使用方法跨平台一致,效果对标 iOS 或 Android 的效果。 -| Name | Description | Type | Required | Platform | HarmonyOS Support | -| ------------------------ | -------------------------- | -------- | -------- | ----------- | ----------------- | -| pipe | 对查询结果进行链式转换操作 | function | no | iOS/Android | yes | -| fetch() | 立即执行查询获取结果 | function | no | iOS/Android | yes | -| then |支持Promise链式调用查询结果 | function | no | iOS/Android | yes | -|observe |监听查询结果的实时变化 | function | no | iOS/Android | yes | -| observeWithColumns| 监听特定字段变化的查询结果 | function | no | iOS/Android | yes | -| fetchCount| 获取查询结果的数量统计 | function | no | iOS/Android | yes | -| get count| 获取查询结果总数 | function | no | iOS/Android | yes | -| observeCount| 监听查询结果数量的变化 | function | no | iOS/Android | yes | -| fetchIds| 获取查询结果的ID列表 | function | no | iOS/Android | yes | -| unsafeFetchRaw | 不安全地获取原始查询数据 | function | no | iOS/Android | yes | -| experimentalSubscribe| 实验性查询订阅功能 | function | no | iOS/Android | yes | -| experimentalSubscribeWithColumns| 实验性字段变化订阅 | function | no | Android | yes | -| experimentalSubscribeToCount| 实验性数量变化订阅 | function | no | Android | yes | -| markAllAsDeleted| 标记所有查询结果为软删除 | function | no | Android | yes | -| destroyAllPermanently| 永久删除所有查询结果 | string | no | function | yes | -| get modelClass|获取查询对应的模型类 | function | no | iOS/Android | yes | -|get table| 获取查询的主表名称 | function | no | iOS/Android | yes | -|get secondaryTables| 获取查询涉及的关联表 | function | no | iOS/Android | yes | -|get allTables| 获取查询所有相关表 | function | no | Android | yes | -| get associations| 获取查询的关联关系 | function | no | Android | yes | -| serialize| 序列化查询为可传输格式 | string | no | function | yes | -| tableName| 定义数据库表的名称 | function | no | iOS/Android | yes | -|columnName| 定义数据库字段的名称 | function | no | iOS/Android | yes | -|appSchema| 定义整个应用数据库的结构模式 | function | no | iOS/Android | yes | -|validateColumnsSchema| 验证字段模式定义的合法性 | function | no | Android | yes | -| tableSchema| 定义单个数据表的结构和字段配置 | function | no | Android |yes | - -## 5.遗留问题 +| Name | Description | Type | Required | Platform | HarmonyOS Support | +| -------------------------------- | ------------------------------ | -------- | -------- | ----------- | ----------------- | +|Collection类 | | | | | | +| db | 获取该集合所属的数据库实例引用 | function | no | iOS/Android | yes | +| find | 根据指定条件查询集合中匹配的记录 | function | no | iOS/Android | yes | +| findAndObserve | 查询记录并建立响应式观察以监听数据变更 | function | no | iOS/Android | yes | +| create |在集合中创建并保存新纪录 | function | no | iOS/Android | yes | +|prepareCreate | 预创建记录对象但不立即持久化到数据库 | function | no | iOS/Android | yes | +| prepareCreateFromDirtyRaw | 从原始未经验证的数据预创建记录对象 | function | no | iOS/Android | yes | +| disposableFromDirtyRaw | 基于原始脏数据创建一次性使用的临时记录 | function | no | iOS/Android | yes | +| table| 获取该集合对应的数据库表名称 | function | no | iOS/Android | yes | +| schema | 获取该集合对应的数据表结构定义 | function | no | iOS/Android | yes | +| experimentalSubscribe| 订阅集合级别的数据变更通知 | function | no | iOS/Android | yes | +| Database类| | | | | | +| get | 根据模型类型和ID从数据库获取特定记录 | function | no | iOS/Android | yes | +| localStorage| 获取数据库的本地存储适配器用于键值对数据 | function | no | Android | yes | |batch | 创建批量操作以高效执行多个数据库写入 | function | no | Android | yes | +| write | 在写入十五中执行数据库修改操作 | function | no | Android | yes | +|read | 在只读事务中执行数据库查询操作 | string | no | function | yes | +| action | 执行需要读写权限的数据库动作 | function | no | iOS/Android | yes | +|withChangesForTables | 创建监听指定表数据变更的观察者 | function | no | iOS/Android | yes | +| experimentalSubscribe | 订阅数据库级别的变更通知 | function | no | iOS/Android | yes | +| unsafeResetDatabase | 完全重置数据库并清除所有数据 | function | no | Android | yes | +| _ensurelnWtiter | 获取查询的关联关系 | function | no | Android | yes | +| _fataError | 序列化查询为可传输格式 | string | no | function | yes | +| Relation类 | 序列化查询为可传输格式 | string | no | function | yes| +|get id | 定义数据库表的名称 | function | no | iOS/Android | yes | +| set id | 定义数据库字段的名称 | function | no | iOS/Android | yes | +| fetch | 定义整个应用数据库的结构模式 | function | no | iOS/Android | yes | +| then | 验证字段模式定义的合法性 | function | no | Android | yes | +| set | 定义单个数据表的结构和字段配置 | function | no | Android | yes | +| observe | 定义单个数据表的结构和字段配置 | function | no | Android | yes | +| Model类 | | | | | | +| associations | 对查询结果进行链式转换操作 | function | no | iOS/Android | yes | +| prepareMarkAsDeleted | 立即执行查询获取结果 | function | no | iOS/Android | yes | +| _getChanges | 支持Promise链式调用查询结果 | function | no | iOS/Android | yes | +| observe | 监听查询结果的实时变化 | function | no | iOS/Android | yes | +| observeWithColumns | 监听特定字段变化的查询结果 | function | no | iOS/Android | yes | +| fetchCount | 获取查询结果的数量统计 | function | no | iOS/Android | yes | +| get count | 获取查询结果总数 | function | no | iOS/Android | yes | +| observeCount | 监听查询结果数量的变化 | function | no | iOS/Android | yes | +| fetchIds | 获取查询结果的ID列表 | function | no | iOS/Android | yes | +| unsafeFetchRaw | 不安全地获取原始查询数据 | function | no | iOS/Android | yes | +| experimentalSubscribe | 实验性查询订阅功能 | function | no | iOS/Android | yes | +| experimentalSubscribeWithColumns | 实验性字段变化订阅 | function | no | Android | yes | +| experimentalSubscribeToCount | 实验性数量变化订阅 | function | no | Android | yes | +| markAllAsDeleted | 标记所有查询结果为软删除 | function | no | Android | yes | +| destroyAllPermanently | 永久删除所有查询结果 | string | no | function | yes | +| get modelClass | 获取查询对应的模型类 | function | no | iOS/Android | yes | +| get table | 获取查询的主表名称 | function | no | iOS/Android | yes | +| get secondaryTables | 获取查询涉及的关联表 | function | no | iOS/Android | yes | +| get allTables | 获取查询所有相关表 | function | no | Android | yes | +| get associations | 获取查询的关联关系 | function | no | Android | yes | +| serialize | 序列化查询为可传输格式 | string | no | function | yes | +| tableName | 定义数据库表的名称 | function | no | iOS/Android | yes | +| columnName | 定义数据库字段的名称 | function | no | iOS/Android | yes | +| appSchema | 定义整个应用数据库的结构模式 | function | no | iOS/Android | yes | +| validateColumnsSchema | 验证字段模式定义的合法性 | function | no | Android | yes | +| tableSchema | 定义单个数据表的结构和字段配置 | function | no | Android | yes | +| Query类 | | | | | | +| pipe | 对查询结果进行链式转换操作 | function | no | iOS/Android | yes | +| fetch() | 立即执行查询获取结果 | function | no | iOS/Android | yes | +| then | 支持Promise链式调用查询结果 | function | no | iOS/Android | yes | +| observe | 监听查询结果的实时变化 | function | no | iOS/Android | yes | +| observeWithColumns | 监听特定字段变化的查询结果 | function | no | iOS/Android | yes | +| fetchCount | 获取查询结果的数量统计 | function | no | iOS/Android | yes | +| get count | 获取查询结果总数 | function | no | iOS/Android | yes | +| observeCount | 监听查询结果数量的变化 | function | no | iOS/Android | yes | +| fetchIds | 获取查询结果的ID列表 | function | no | iOS/Android | yes | +| unsafeFetchRaw | 不安全地获取原始查询数据 | function | no | iOS/Android | yes | +| experimentalSubscribe | 实验性查询订阅功能 | function | no | iOS/Android | yes | +| experimentalSubscribeWithColumns | 实验性字段变化订阅 | function | no | iOS/Android | yes | +| experimentalSubscribeToCount | 实验性数量变化订阅 | function | no | iOS/Android | yes | +| markAllAsDeleted | 标记所有查询结果为软删除 | function | no | iOS/Android | yes | +| destroyAllPermanently | 永久删除所有查询结果 |function | no | iOS/Android | yes | +| get modelClass | 获取查询对应的模型类 | function | no | iOS/Android | yes | +| get table | 获取查询的主表名称 | function | no | iOS/Android | yes | +| get secondaryTables | 获取查询涉及的关联表 | function | no | iOS/Android | yes | +| get allTables | 获取查询所有相关表 | function | no | iOS/Android | yes | +| get associations | 获取查询的关联关系 | function | no | iOS/Android | yes | +| serialize | 序列化查询为可传输格式 | function | no | iOS/Android | yes | +|Schema类| +| tableName | 定义数据库表的名称 | function | no | iOS/Android | yes | +| columnName | 定义数据库字段的名称 | function | no | iOS/Android | yes | +| appSchema | 定义整个应用数据库的结构模式 | function | no | iOS/Android | yes | +| validateColumnsSchema | 验证字段模式定义的合法性 | function | no |iOS/Android | yes | +| tableSchema | 定义单个数据表的结构和字段配置 | function | no | iOS/Android | yes | + +## + +5.遗留问题 - [ ] onFacesDetected能力无法实现 [issue#1](https://github.com/react-native-oh-library/react-native-camera/issues/8) -- Gitee From d623eb2c2f17e9dec71820f2649e64c3051186f7 Mon Sep 17 00:00:00 2001 From: lqq <1960002614@qq.com> Date: Thu, 18 Dec 2025 20:13:38 +0800 Subject: [PATCH 03/22] =?UTF-8?q?docs:=20[Issues:=20#IDE3MY]=20=E6=96=B0?= =?UTF-8?q?=E5=A2=9Ewatermelondb=E7=9A=84=E6=8C=87=E5=AF=BC=E6=96=87?= =?UTF-8?q?=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- zh-cn/watermelondb.md | 86 +++++++++++++++++++++++-------------------- 1 file changed, 47 insertions(+), 39 deletions(-) diff --git a/zh-cn/watermelondb.md b/zh-cn/watermelondb.md index 8aa9e03de..35a478f41 100644 --- a/zh-cn/watermelondb.md +++ b/zh-cn/watermelondb.md @@ -332,49 +332,57 @@ ohpm install | experimentalSubscribe| 订阅集合级别的数据变更通知 | function | no | iOS/Android | yes | | Database类| | | | | | | get | 根据模型类型和ID从数据库获取特定记录 | function | no | iOS/Android | yes | -| localStorage| 获取数据库的本地存储适配器用于键值对数据 | function | no | Android | yes | |batch | 创建批量操作以高效执行多个数据库写入 | function | no | Android | yes | -| write | 在写入十五中执行数据库修改操作 | function | no | Android | yes | -|read | 在只读事务中执行数据库查询操作 | string | no | function | yes | +| localStorage| 获取数据库的本地存储适配器用于键值对数据 | function | no | iOS/Android| yes | +|batch | 创建批量操作以高效执行多个数据库写入 | function | no | Android | yes | +| write | 在写入十五中执行数据库修改操作 | function | no | iOS/Android| yes | +|read | 在只读事务中执行数据库查询操作 | function | no | iOS/Android| yes | | action | 执行需要读写权限的数据库动作 | function | no | iOS/Android | yes | |withChangesForTables | 创建监听指定表数据变更的观察者 | function | no | iOS/Android | yes | | experimentalSubscribe | 订阅数据库级别的变更通知 | function | no | iOS/Android | yes | -| unsafeResetDatabase | 完全重置数据库并清除所有数据 | function | no | Android | yes | -| _ensurelnWtiter | 获取查询的关联关系 | function | no | Android | yes | -| _fataError | 序列化查询为可传输格式 | string | no | function | yes | -| Relation类 | 序列化查询为可传输格式 | string | no | function | yes| -|get id | 定义数据库表的名称 | function | no | iOS/Android | yes | -| set id | 定义数据库字段的名称 | function | no | iOS/Android | yes | -| fetch | 定义整个应用数据库的结构模式 | function | no | iOS/Android | yes | -| then | 验证字段模式定义的合法性 | function | no | Android | yes | -| set | 定义单个数据表的结构和字段配置 | function | no | Android | yes | -| observe | 定义单个数据表的结构和字段配置 | function | no | Android | yes | +| unsafeResetDatabase | 完全重置数据库并清除所有数据 | function | no | iOS/Android | yes | +| _ensurelnWtiter | 确保当前在写入线程中执行操作 | function | no |iOS/Android | yes | +| _fataError | 处理数据库致命错误并终止连接 | function | no | iOS/Android | yes | +| Relation类 | | | | | | +|get id | 获取关联目标模型的唯一标识符 | function | no | iOS/Android | yes | +| set id | 设置关联目标模型的标识符值 | function | no | iOS/Android | yes | +| fetch | 异步获取关联记录的完整数据承诺 | function | no | iOS/Android | yes | +| then | 支持Promise链式调用,处理异步获取的关联数据 | function | no | Android | yes | +| set |建立与另一个模型实例的关联关系 | function | no | Android | yes | +| observe | 监听关联模型数据的实时变化 | function | no | Android | yes | | Model类 | | | | | | -| associations | 对查询结果进行链式转换操作 | function | no | iOS/Android | yes | -| prepareMarkAsDeleted | 立即执行查询获取结果 | function | no | iOS/Android | yes | -| _getChanges | 支持Promise链式调用查询结果 | function | no | iOS/Android | yes | -| observe | 监听查询结果的实时变化 | function | no | iOS/Android | yes | -| observeWithColumns | 监听特定字段变化的查询结果 | function | no | iOS/Android | yes | -| fetchCount | 获取查询结果的数量统计 | function | no | iOS/Android | yes | -| get count | 获取查询结果总数 | function | no | iOS/Android | yes | -| observeCount | 监听查询结果数量的变化 | function | no | iOS/Android | yes | -| fetchIds | 获取查询结果的ID列表 | function | no | iOS/Android | yes | -| unsafeFetchRaw | 不安全地获取原始查询数据 | function | no | iOS/Android | yes | -| experimentalSubscribe | 实验性查询订阅功能 | function | no | iOS/Android | yes | -| experimentalSubscribeWithColumns | 实验性字段变化订阅 | function | no | Android | yes | -| experimentalSubscribeToCount | 实验性数量变化订阅 | function | no | Android | yes | -| markAllAsDeleted | 标记所有查询结果为软删除 | function | no | Android | yes | -| destroyAllPermanently | 永久删除所有查询结果 | string | no | function | yes | -| get modelClass | 获取查询对应的模型类 | function | no | iOS/Android | yes | -| get table | 获取查询的主表名称 | function | no | iOS/Android | yes | -| get secondaryTables | 获取查询涉及的关联表 | function | no | iOS/Android | yes | -| get allTables | 获取查询所有相关表 | function | no | Android | yes | -| get associations | 获取查询的关联关系 | function | no | Android | yes | -| serialize | 序列化查询为可传输格式 | string | no | function | yes | -| tableName | 定义数据库表的名称 | function | no | iOS/Android | yes | -| columnName | 定义数据库字段的名称 | function | no | iOS/Android | yes | -| appSchema | 定义整个应用数据库的结构模式 | function | no | iOS/Android | yes | -| validateColumnsSchema | 验证字段模式定义的合法性 | function | no | Android | yes | -| tableSchema | 定义单个数据表的结构和字段配置 | function | no | Android | yes | +| associations | 定义模型与其他数据表之间的关联关系映射 | function | no | iOS/Android | yes | +| prepareMarkAsDeleted | 标记为删除状态 | function | no | iOS/Android | yes | +| _getChanges | 获取模型变更观察对象 | function | no | iOS/Android | yes | +| | 获取实例的唯一标识符 | function | no | iOS/Android | yes | +| |获取数据同步状态 | function | no | iOS/Android | yes | +| | 更新模型并立即提交 | function | no | iOS/Android | yes | +| | 准备更新 | function | no | iOS/Android | yes | +| | 准备永久销毁但不执行 | function | no | iOS/Android | yes | +| | 删除模型并提交 | function | no | iOS/Android | yes | +| | 永久删除模型记录 | function | no | iOS/Android | yes | +| | 软删除功能 | function | no | iOS/Android | yes | +| |永久删除功能 | function | no | iOS/Android| yes | +| |监听模型数据变化 | function | no | iOS/Android| yes | +| | 获取所属数据集合 | function | no | iOS/Android| yes | +| | 获取所有集合映射 | function | no | iOS/Android| yes | +| |获取数据库实例 | function | no | iOS/Android | yes | +| | 获取数据库实例 | function | no | iOS/Android | yes | +| | 返回自身实例 | function | no | iOS/Android | yes | +| | 在写入线程执行操作 | function | no | iOS/Android| yes | +| | 在读取线程执行查询 | function | no | iOS/Android| yes | +| |在事务中执行子操作 | function | no | iOS/Android| yes | +| | 获取对应数据表名 | function | no | iOS/Android | yes | +| | 准备创建模型 | function | no | iOS/Android | yes | +| | 从脏数据创建模型 | function | no | iOS/Android | yes | +| | 创建一次性模型 | function | no | iOS/Android| yes | +| | 实验性订阅模型变更 | function | no | iOS/Android| yes | +| | 内部通知模型变更 | function | no | iOS/Android | yes | +| _notifyDestroyed | 内部通知模型销毁 | function | no | iOS/Android | yes | +| _getRaw | 内部获取原始字段值 | function | no | iOS/Android | yes | +| _setRaw | 内部设置字段值并标记变更 | function | no | iOS/Android | yes | +|_dangerouslySetRawWithoutMarkingColumnChange | 内部不安全设置字段值 | function | no | iOS/Android | yes | +| _ensureCanSetRaw | 内部确保可设置原始值 | function | no | iOS/Android | yes | +| _ensureNotDisposable| 内部确保非一次性实例 | function | no | iOS/Android | yes | | Query类 | | | | | | | pipe | 对查询结果进行链式转换操作 | function | no | iOS/Android | yes | | fetch() | 立即执行查询获取结果 | function | no | iOS/Android | yes | -- Gitee From 43f4f2569defc1d3e46f3c3e6ba63a3eb3834a0e Mon Sep 17 00:00:00 2001 From: lqq <1960002614@qq.com> Date: Fri, 19 Dec 2025 09:18:35 +0800 Subject: [PATCH 04/22] =?UTF-8?q?docs:=20[Issues:=20#IDE3MY]=20=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0watermelondb=E7=9A=84=E6=8C=87=E5=AF=BC=E6=96=87?= =?UTF-8?q?=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- zh-cn/watermelondb.md | 56 +++++++++++++++++++++---------------------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/zh-cn/watermelondb.md b/zh-cn/watermelondb.md index 35a478f41..c027272bd 100644 --- a/zh-cn/watermelondb.md +++ b/zh-cn/watermelondb.md @@ -333,7 +333,7 @@ ohpm install | Database类| | | | | | | get | 根据模型类型和ID从数据库获取特定记录 | function | no | iOS/Android | yes | | localStorage| 获取数据库的本地存储适配器用于键值对数据 | function | no | iOS/Android| yes | -|batch | 创建批量操作以高效执行多个数据库写入 | function | no | Android | yes | +|batch | 创建批量操作以高效执行多个数据库写入 | function | no | iOS/Android | yes | | write | 在写入十五中执行数据库修改操作 | function | no | iOS/Android| yes | |read | 在只读事务中执行数据库查询操作 | function | no | iOS/Android| yes | | action | 执行需要读写权限的数据库动作 | function | no | iOS/Android | yes | @@ -346,37 +346,37 @@ ohpm install |get id | 获取关联目标模型的唯一标识符 | function | no | iOS/Android | yes | | set id | 设置关联目标模型的标识符值 | function | no | iOS/Android | yes | | fetch | 异步获取关联记录的完整数据承诺 | function | no | iOS/Android | yes | -| then | 支持Promise链式调用,处理异步获取的关联数据 | function | no | Android | yes | -| set |建立与另一个模型实例的关联关系 | function | no | Android | yes | -| observe | 监听关联模型数据的实时变化 | function | no | Android | yes | +| then | 支持Promise链式调用,处理异步获取的关联数据 | function | no | iOS/Android | yes | +| set |建立与另一个模型实例的关联关系 | function | no | iOS/Android | yes | +| observe | 监听关联模型数据的实时变化 | function | no | iOS/Android | yes | | Model类 | | | | | | | associations | 定义模型与其他数据表之间的关联关系映射 | function | no | iOS/Android | yes | | prepareMarkAsDeleted | 标记为删除状态 | function | no | iOS/Android | yes | | _getChanges | 获取模型变更观察对象 | function | no | iOS/Android | yes | -| | 获取实例的唯一标识符 | function | no | iOS/Android | yes | -| |获取数据同步状态 | function | no | iOS/Android | yes | -| | 更新模型并立即提交 | function | no | iOS/Android | yes | -| | 准备更新 | function | no | iOS/Android | yes | -| | 准备永久销毁但不执行 | function | no | iOS/Android | yes | -| | 删除模型并提交 | function | no | iOS/Android | yes | -| | 永久删除模型记录 | function | no | iOS/Android | yes | -| | 软删除功能 | function | no | iOS/Android | yes | -| |永久删除功能 | function | no | iOS/Android| yes | -| |监听模型数据变化 | function | no | iOS/Android| yes | -| | 获取所属数据集合 | function | no | iOS/Android| yes | -| | 获取所有集合映射 | function | no | iOS/Android| yes | -| |获取数据库实例 | function | no | iOS/Android | yes | -| | 获取数据库实例 | function | no | iOS/Android | yes | -| | 返回自身实例 | function | no | iOS/Android | yes | -| | 在写入线程执行操作 | function | no | iOS/Android| yes | -| | 在读取线程执行查询 | function | no | iOS/Android| yes | -| |在事务中执行子操作 | function | no | iOS/Android| yes | -| | 获取对应数据表名 | function | no | iOS/Android | yes | -| | 准备创建模型 | function | no | iOS/Android | yes | -| | 从脏数据创建模型 | function | no | iOS/Android | yes | -| | 创建一次性模型 | function | no | iOS/Android| yes | -| | 实验性订阅模型变更 | function | no | iOS/Android| yes | -| | 内部通知模型变更 | function | no | iOS/Android | yes | +| get id | 获取实例的唯一标识符 | function | no | iOS/Android | yes | +| get syncStatus |获取数据同步状态 | function | no | iOS/Android | yes | +| update | 更新模型并立即提交 | function | no | iOS/Android | yes | +| prepareUpdate | 准备更新 | function | no | iOS/Android | yes | +| prepareDestroyPermanently | 准备永久销毁但不执行 | function | no | iOS/Android | yes | +| markAsDeleted | 删除模型并提交 | function | no | iOS/Android | yes | +| destroyPermanently | 永久删除模型记录 | function | no | iOS/Android | yes | +| experimentalMarkAsDeleted | 软删除功能 | function | no | iOS/Android | yes | +| experimentalDestroyPermanently |永久删除功能 | function | no | iOS/Android| yes | +| observe |监听模型数据变化 | function | no | iOS/Android| yes | +| collection | 获取所属数据集合 | function | no | iOS/Android| yes | +| get collections | 获取所有集合映射 | function | no | iOS/Android| yes | +| get database |获取数据库实例 | function | no | iOS/Android | yes | +| get db | 获取数据库实例 | function | no | iOS/Android | yes | +|get asModel | 返回自身实例 | function | no | iOS/Android | yes | +|callWriter | 在写入线程执行操作 | function | no | iOS/Android| yes | +| callReader | 在读取线程执行查询 | function | no | iOS/Android| yes | +| subAction |在事务中执行子操作 | function | no | iOS/Android| yes | +| get table | 获取对应数据表名 | function | no | iOS/Android | yes | +| prepareCreate | 准备创建模型 | function | no | iOS/Android | yes | +| _prepareCreateFromDirtyRaw | 从脏数据创建模型 | function | no | iOS/Android | yes | +| _disposableFromDirtyRaw | 创建一次性模型 | function | no | iOS/Android| yes | +| experimentalSubscribe | 实验性订阅模型变更 | function | no | iOS/Android| yes | +| _notifyChanged | 内部通知模型变更 | function | no | iOS/Android | yes | | _notifyDestroyed | 内部通知模型销毁 | function | no | iOS/Android | yes | | _getRaw | 内部获取原始字段值 | function | no | iOS/Android | yes | | _setRaw | 内部设置字段值并标记变更 | function | no | iOS/Android | yes | -- Gitee From 2f6baf66c1a8c6bed9c01ab7456b61c2b32929a1 Mon Sep 17 00:00:00 2001 From: lqq <1960002614@qq.com> Date: Fri, 19 Dec 2025 14:27:17 +0800 Subject: [PATCH 05/22] =?UTF-8?q?docs:=20[Issues:=20#IDE3MY]=20=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0watermelondb=E7=9A=84=E6=8C=87=E5=AF=BC=E6=96=87?= =?UTF-8?q?=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- zh-cn/watermelondb.md | 897 ++++++++++++++++++++++++++++++------------ 1 file changed, 647 insertions(+), 250 deletions(-) diff --git a/zh-cn/watermelondb.md b/zh-cn/watermelondb.md index c027272bd..000b0a282 100644 --- a/zh-cn/watermelondb.md +++ b/zh-cn/watermelondb.md @@ -1,16 +1,16 @@ > 模板版本:v0.3.0

-

react-native-camera

+

watermelondb

-本项目基于 [react-native-camera@v3.40.0](https://github.com/react-native-camera/react-native-camera/tree/v3.40.0) 开发。 +本项目基于 [watermelondb@[v0.28.1-0](https://github.com/Nozbe/WatermelonDB)]开发。 -请到三方库的 Releases 发布地址查看配套的版本信息:[@react-native-ohos/react-native-camera Releases](https://github.com/react-native-oh-library/react-native-camera/releases)。对于未发布到npm的旧版本,请参考[安装指南](/zh-cn/tgz-usage.md)安装tgz包。 +请到三方库的 Releases 发布地址查看配套的版本信息:[@react-native-ohos/watermelondb Releases]([Nozbe/WatermelonDB:🍉 适用于强大 React 和 React Native 应用 ⚡️ 的反应式与异步数据库](https://github.com/Nozbe/WatermelonDB))。对于未发布到 npm 的旧版本,请参考[安装指南](/zh-cn/tgz-usage.md)安装 tgz 包。 -| 三方库版本 | 发布信息 | 支持RN版本 | -| ---------- | -------------------------------------------------------------------------------------------------------------------------- | ---------- | -| v3.40.0 | [@react-native-ohos/react-native-camera Releases](https://github.com/react-native-oh-library/react-native-camera/releases) | 0.72/0.77 | +| 三方库版本 | 发布信息 | 支持 RN 版本 | +| ---------- | -------------------------------------------------------------------------------------------------------------------------- | ------------ | +| v0.28.1-0 | [@react-native-ohos/watermelondb Releases](https://github.com/) | 0.72/0.77 | ## 1. 安装与使用 @@ -19,13 +19,13 @@ #### **npm** ```bash -npm install @react-native-ohos/react-native-camera +npm install ``` #### **yarn** ```bash -yarn add @react-native-ohos/react-native-camera +yarn add ``` 下面的代码展示了这个库的基本使用场景: @@ -33,71 +33,487 @@ yarn add @react-native-ohos/react-native-camera > [!WARNING] 使用时 import 的库名不变。 ```js -import * as React from 'react'; -import { Text, View } from 'react-native'; -import { RNCamera } from 'react-native-camera'; +import React, { useCallback, useEffect, useMemo, useState } from 'react'; import { - StyleSheet, + Alert, + FlatList, + Pressable, + SafeAreaView, + ScrollView, + StyleSheet, + Text, + View, } from 'react-native'; +import { Database } from '@react-native-ohos/watermelondb'; +import SQLiteAdapter from '@react-native-ohos/watermelondb/adapters/sqlite'; +import { DatabaseProvider, useDatabase } from '@react-native-ohos/watermelondb/react'; +import { mySchema } from './models/schema'; +import { dbModels } from './models/index.js'; + +const adapter = new SQLiteAdapter({ + dbName: 'WatermelonRelationDemo', + schema: mySchema, + jsi: false, + log: (sql) => console.log('SQL执行:', sql), + onSetUpError: (error) => { + console.error('[WatermelonDemo*] 初始化数据库失败', error); + }, +}); -export function CameraExample() { - return ( - - { - const options = { - quality: 0.5, - base64: true, - width: 300, - height: 300, - }; - this.camera.takePictureAsync(options).then((photoResult) => { - console.log("takepicture result:" + photoResult?.path) - }); - - } - }> - 测试takepicture - - - - { - this.camera = ref; - }} - style={styles.cameraPreview} - > - - - ); -} +const database = new Database({ + adapter, + modelClasses: dbModels, +}); -const styles = StyleSheet.create({ - container: { - flex: 1, - paddingTop: 10, - backgroundColor: '#000', +const SAMPLE_MOVIES = [ + { + title: '星际穿越', + posterImage: + 'https://image.tmdb.org/t/p/original/rAiYTfKGqDCRIIqo664sY9XZIvQ.jpg', + genre: '科幻', + description: '一段跨越宇宙与时间、寻找新家园的旅程。', + }, + { + title: '疯狂动物城', + posterImage: + 'https://image.tmdb.org/t/p/original/hlK0e0wAQ3VLuJcsfIYPvb4JVud.jpg', + genre: '动画', + description: '一只兔子警官与狐狸搭档,揭开阴谋、守护城市。', + }, + { + title: '速度与激情 10', + posterImage: + 'https://image.tmdb.org/t/p/original/qDRGPAcQoW8Wuig9bvoLpHwf1gU.jpg', + genre: '动作', + description: '家人永远是第一位,飙车、爆炸、热血永不停歇。', + }, +]; + +const SAMPLE_REVIEWS = [ + '剧情紧凑,完全停不下来!', + '配乐太棒了,影院体验绝佳。', + '主角魅力满分,期待续作。', + '画面惊艳,值得二刷三刷。', + '故事内核很温暖,看完心情很好。', +]; + +const randomItem = (items) => items[Math.floor(Math.random() * items.length)]; + +const formatDate = (value) => { + if (!value) { + return '未知'; + } + try { + const date = value instanceof Date ? value : new Date(value); + return date.toISOString().slice(0, 10); + } catch (error) { + return '未知'; + } +}; + +const ActionButton = ({ label, onPress, type = 'default' }) => { + const background = + type === 'danger' + ? styles.dangerButton + : type === 'secondary' + ? styles.secondaryButton + : styles.primaryButton; + return ( + + {label} + + ); +}; + +const MovieCard = ({ movie, onAddReview, onRename, onDelete }) => { + const [reviews, setReviews] = useState([]); + const [movieInfo, setMovieInfo] = useState(movie.getMovie()); + + useEffect(() => { + const subscription = movie.reviews.observe().subscribe({ + next: (list) => setReviews(list), + error: (error) => console.warn('订阅评论失败', error), + }); + return () => subscription.unsubscribe(); + }, [movie]); + + // 订阅 movie 对象的变化,以便在更新时刷新 UI + useEffect(() => { + const subscription = movie.observe().subscribe({ + next: (updatedMovie) => { + const newInfo = updatedMovie.getMovie(); + console.log('%c watermelondbConsoleLogger movie card updated:', 'color: #0e93e0;background: #aaefe5;', { + id: updatedMovie.id, + newTitle: newInfo.title, + }); + setMovieInfo(newInfo); + }, + error: (error) => console.warn('订阅电影变化失败', error), + }); + return () => subscription.unsubscribe(); + }, [movie]); + + const info = movieInfo; + + return ( + + {info.title} + {info.genre} + {info.description} + 上映日期:{formatDate(info.releaseDateAt)} + 短评:{reviews.length} 条 + {reviews.slice(0, 2).map((review) => ( + + · {review.body} + + ))} + {reviews.length > 2 ? ( + · …… + ) : null} + + onAddReview(movie)} /> + onRename(movie)} /> + onDelete(movie)} /> + + + ); +}; + +const MovieScreen = () => { + const databaseInstance = useDatabase(); + const moviesCollection = useMemo( + () => databaseInstance.collections.get('movies'), + [databaseInstance], + ); + + const [movies, setMovies] = useState([]); + const [loading, setLoading] = useState(true); + + useEffect(() => { + const subscription = moviesCollection.query().observe().subscribe({ + next: (list) => { + console.log('%c watermelondbConsoleLogger movies list updated:', 'color: #0e93e0;background: #aaefe5;', { + count: list.length, + titles: list.map(m => m.title), + }); + setMovies(list); + setLoading(false); + }, + error: (error) => console.error('订阅电影列表失败', error), + }); + return () => subscription.unsubscribe(); + }, [moviesCollection]); + + const seedDemoData = useCallback(async () => { + console.log( + '%c watermelondbConsoleLogger moviesCollection:', + 'color: #0e93e0;background: #aaefe5;', + moviesCollection, + ); + const current = await moviesCollection.query().fetch(); + console.log( + '%c watermelondbConsoleLogger current:', + 'color: #0e93e0;background: #aaefe5;', + current, + ); + if (current.length > 0) { + Alert.alert('提示', '数据库中已经有电影数据,无需重复导入。'); + return; + } + await databaseInstance.write(async () => { + await Promise.all( + SAMPLE_MOVIES.map((payload, index) => + moviesCollection.create((movie) => { + movie.title = payload.title; + movie.genre = payload.genre; + movie.posterImage = payload.posterImage; + movie.description = payload.description; + movie.releaseDateAt = new Date(Date.now() - index * 24 * 60 * 60 * 1000); + }), + ), + ); + }); + }, [databaseInstance, moviesCollection]); + + const addRandomMovie = useCallback(async () => { + const payload = randomItem(SAMPLE_MOVIES); + await databaseInstance.write(async () => { + await moviesCollection.create((movie) => { + movie.title = `${payload.title} · ${Math.floor(Math.random() * 100)}`; + movie.genre = payload.genre; + movie.posterImage = payload.posterImage; + movie.description = payload.description; + movie.releaseDateAt = new Date(); + }); + }); + }, [databaseInstance, moviesCollection]); + + const addReview = useCallback( + async (movie) => { + await databaseInstance.write(async () => { + await movie.addReview(randomItem(SAMPLE_REVIEWS)); + }); }, - button: { - width: 160, - height: 36, - backgroundColor: 'hsl(190, 50%, 70%)', - paddingHorizontal: 16, - paddingVertical: 8, - borderRadius: 8, + [databaseInstance], + ); + + const renameMovie = useCallback( + async (movie) => { + try { + console.log('%c watermelondbConsoleLogger renameMovie before:', 'color: #0e93e0;background: #aaefe5;', movie.title); + await databaseInstance.write(async () => { + await movie.update((record) => { + const baseTitle = record.title.split(' · ')[0]; + const newTitle = `${baseTitle} · v${Math.floor(Math.random() * 10 + 1)}`; + console.log('%c watermelondbConsoleLogger renameMovie updating:', 'color: #0e93e0;background: #aaefe5;', { + oldTitle: record.title, + baseTitle, + newTitle, + }); + record.title = newTitle; + }); + }); + console.log('%c watermelondbConsoleLogger renameMovie after:', 'color: #0e93e0;background: #aaefe5;', movie.title); + } catch (error) { + console.error('[WatermelonDemo] 随机改名失败', error); + Alert.alert('错误', `随机改名失败: ${error.message}`); + } }, - margin20: { - marginLeft: 20 + [databaseInstance], + ); + + const deleteMovie = useCallback( + async (movie) => { + await databaseInstance.write(async () => movie.deleteMovie()); }, - buttonText: { - width: '100%', - height: '100%', - fontWeight: 'bold', + [databaseInstance], + ); + + const clearAll = useCallback(async () => { + await databaseInstance.write(async () => { + const allMovies = await moviesCollection.query().fetch(); + await Promise.all(allMovies.map((movie) => movie.deleteMovie())); + }); + }, [databaseInstance, moviesCollection]); + + // 1. 测试关联查询:查询指定电影的所有评论 + const testRelationQuery = useCallback(async () => { + if (movies.length === 0) { + Alert.alert("提示", "请先添加电影数据"); + return; + } + try { + const targetMovie = movies[0]; // 取第一个电影 + const reviews = await targetMovie.reviews.fetch(); // 测试 Relation.fetch() + const reviewTexts = reviews.map(r => r.body).join("\n- "); + Alert.alert( + `电影《${targetMovie.title}》的评论`, + reviewTexts || "暂无评论" + ); + } catch (error) { + console.error("关联查询测试失败", error); + Alert.alert("错误", "关联查询测试失败:" + error.message); + } + }, [movies]); + + // 2. 测试批量添加评论(测试 has_many 关联批量操作) + const testBulkAddReviews = useCallback(async () => { + if (movies.length === 0) { + Alert.alert("提示", "请先添加电影数据"); + return; + } + try { + const targetMovie = movies[0]; + await databaseInstance.write(async () => { + // 批量添加3条评论 + for (let i = 0; i < 3; i++) { + await targetMovie.addReview(`批量测试评论 ${i + 1}:${randomItem(SAMPLE_REVIEWS)}`); + } + }); + Alert.alert("成功", `已为《${targetMovie.title}》批量添加3条评论`); + } catch (error) { + console.error("批量添加评论测试失败", error); + Alert.alert("错误", "批量添加评论失败:" + error.message); + } + }, [movies, databaseInstance]); + + // 3. 测试事务回滚(故意制造错误,验证事务原子性) + const testTransactionRollback = useCallback(async () => { + if (movies.length === 0) { + Alert.alert("提示", "请先添加电影数据"); + return; + } + try { + const targetMovie = movies[0]; + await databaseInstance.write(async () => { + // 第一步:正常添加评论 + await targetMovie.addReview("事务测试:这是一条正常评论"); + // 第二步:故意抛出错误,验证事务回滚(上面的评论应被撤销) + throw new Error("故意触发事务回滚测试"); + }); + } catch (error) { + console.warn("事务回滚测试触发", error.message); + // 验证评论是否被回滚 + const reviews = await targetMovie.reviews.fetch(); + const hasTestReview = reviews.some(r => r.body.includes("事务测试")); + Alert.alert( + "事务回滚测试结果", + hasTestReview + ? "测试失败:事务未回滚" + : "测试成功:事务已回滚(错误前的操作被撤销)" + ); + } + }, [movies, databaseInstance]); + + // 在 MovieScreen 组件的 useCallback 方法区域添加 + const deleteReview = useCallback( + async (review) => { + try { + await databaseInstance.write(async () => { + await review.deleteReview(); // 调用 Review 模型的 deleteReview 方法 + }); + Alert.alert("成功", "评论已删除"); + } catch (error) { + console.error("删除评论失败", error); + Alert.alert("错误", `删除评论失败: ${error.message}`); + } }, - cameraPreview: { width: '100%', aspectRatio: 56 / 100 } -}); + [databaseInstance] + ); + + return ( + + + WatermelonDB 示例 + + + + + + + + + 当前共有 {movies.length} 部电影 + {loading ? ( + 正在加载数据... + ) : movies.length === 0 ? ( + 暂无电影,请先导入示例数据。 + ) : ( + movies.map((movie) => ( + + )) + )} + + + ); +}; + +export default function WatermelonRelaDemo() { + console.log('%c watermelondbConsoleLogger WatermelonDemo:', 'color: #0e93e0;background: #aaefe5;', 'WatermelonDemo'); + return ( + + + + ); +} -export default CameraExample; +const styles = StyleSheet.create({ + container: { + flex: 1, + backgroundColor: '#0b132b', + }, + content: { + padding: 16, + paddingBottom: 32, + }, + title: { + fontSize: 24, + fontWeight: 'bold', + color: '#f9f9f9', + marginBottom: 8, + }, + subtitle: { + fontSize: 14, + color: '#c5c9d3', + marginBottom: 16, + lineHeight: 20, + }, + actionRow: { + flexDirection: 'row', + flexWrap: 'wrap', + gap: 12, + marginBottom: 16, + }, + countText: { + color: '#f9f9f9', + marginBottom: 12, + }, + loadingText: { + color: '#c5c9d3', + fontStyle: 'italic', + }, + card: { + backgroundColor: '#1c2541', + borderRadius: 12, + padding: 16, + marginBottom: 16, + }, + movieTitle: { + fontSize: 18, + fontWeight: 'bold', + color: '#ffffff', + }, + movieGenre: { + color: '#5bc0be', + marginTop: 4, + }, + movieDesc: { + color: '#d1d5db', + marginTop: 8, + lineHeight: 18, + }, + movieMeta: { + color: '#9aa0ac', + marginTop: 4, + }, + reviewItem: { + color: '#c5c9d3', + marginTop: 4, + }, + cardActions: { + flexDirection: 'row', + flexWrap: 'wrap', + gap: 10, + marginTop: 12, + }, + buttonWrapper: { + paddingVertical: 8, + paddingHorizontal: 14, + borderRadius: 999, + }, + primaryButton: { + backgroundColor: '#5bc0be', + }, + secondaryButton: { + backgroundColor: '#3a506b', + }, + dangerButton: { + backgroundColor: '#ef476f', + }, + buttonText: { + color: '#fff', + fontWeight: '600', + }, +}); ``` ## 2. Manual Link @@ -115,9 +531,7 @@ export default CameraExample; ```json { "overrides": { - "@rnoh/react-native-openharmony": "^0.72.38" // ohpm 在线版本 - // "@rnoh/react-native-openharmony" : "./react_native_openharmony.har" // 指向本地 har 包的路径 - // "@rnoh/react-native-openharmony" : "./react_native_openharmony" // 指向源码路径 + "@rnoh/react-native-openharmony": "file:../node_modules/@react-native-oh/react-native-harmony/react_native_openharmony.har" } } ``` @@ -137,7 +551,8 @@ export default CameraExample; ```json "dependencies": { - "@react-native-ohos/react-native-camera": "file:../../node_modules/@react-native-ohos/react-native-camera/harmony/reactNativeCamera.har" + "@rnoh/react-native-openharmony": "0.72.32", + "@react-native-ohos/watermelondb": "file:../../../harmony/watermelondb" } ``` @@ -154,19 +569,49 @@ ohpm install > [!TIP] 如需使用直接链接源码,请参考[直接链接源码说明](/zh-cn/link-source-code.md) -### 2.3.配置 CMakeLists 和引入 NativeCameraPackage +### 2.3.配置 CMakeLists 和引入 WatermelonDBPackage 打开 `entry/src/main/cpp/CMakeLists.txt`,添加: ```diff -+ set(OH_MODULES "${CMAKE_CURRENT_SOURCE_DIR}/../../../oh_modules") +project(rnapp) +cmake_minimum_required(VERSION 3.4.1) +set(CMAKE_SKIP_BUILD_RPATH TRUE) +set(RNOH_APP_DIR "${CMAKE_CURRENT_SOURCE_DIR}") +set(NODE_MODULES "${CMAKE_CURRENT_SOURCE_DIR}/../../../../../node_modules") +set(OH_MODULE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../../oh_modules") +set(RNOH_CPP_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../../oh_modules/@rnoh/react-native-openharmony/src/main/cpp") +set(RNOH_GENERATED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/generated") +set(LOG_VERBOSITY_LEVEL 1) +set(CMAKE_ASM_FLAGS "-Wno-error=unused-command-line-argument -Qunused-arguments") +set(CMAKE_CXX_FLAGS "-fstack-protector-strong -Wl,-z,relro,-z,now,-z,noexecstack -s -fPIE -pie") +set(OH_MODULES "${CMAKE_CURRENT_SOURCE_DIR}/../../../oh_modules") + +set(WITH_HITRACE_SYSTRACE 1) # for other CMakeLists.txt files to use +add_compile_definitions(WITH_HITRACE_SYSTRACE) + +add_subdirectory("${RNOH_CPP_DIR}" ./rn) # RNOH_BEGIN: manual_package_linking_1 -+ add_subdirectory("${OH_MODULES}/@react-native-ohos/react-native-camera/src/main/cpp" ./reactNativeCamera) # RNOH_END: manual_package_linking_1 +file(GLOB GENERATED_CPP_FILES "${CMAKE_CURRENT_SOURCE_DIR}/generated/*.cpp") # this line is needed by codegen v1 + +add_subdirectory("${OH_MODULES}/@react-native-ohos/watermelondb/src/main/cpp" ./watermelondb) + + + +add_library(rnoh_app SHARED + ${GENERATED_CPP_FILES} + "./PackageProvider.cpp" + "${RNOH_CPP_DIR}/RNOHAppNapiBridge.cpp" +) +target_link_libraries(rnoh_app PUBLIC rnoh) + # RNOH_BEGIN: manual_package_linking_2 -+ target_link_libraries(rnoh_app PUBLIC rnoh_native_camera) +target_link_libraries(rnoh_app PUBLIC watermelondb) + + # RNOH_END: manual_package_linking_2 ``` @@ -175,69 +620,37 @@ ohpm install ```diff #include "RNOH/PackageProvider.h" #include "generated/RNOHGeneratedPackage.h" -+ #include "NativeCameraPackage.h" + #include "WatermelondbPackage.h" using namespace rnoh; -std::vector> PackageProvider::getPackages(Package::Context ctx) { - return { - std::make_shared(ctx), -+ std::make_shared(ctx) - }; +std::vector> PackageProvider::getPackages( + Package::Context ctx) { + return { + std::make_shared(ctx), // generated by codegen v1 + std::make_shared(ctx) + }; } ``` -### 2.4.在 ArkTs 侧引入 react-native-camera 组件 +### 2.4.在 ArkTs 侧引入 watermelondb 组件 -找到 `function buildCustomRNComponent()`,一般位于 `entry/src/main/ets/pages/index.ets` 或 `entry/src/main/ets/rn/LoadBundle.ets`,添加: +找到 `entry/src/main/ets/RNPackagesFactory.ts`,添加: ```diff ... -+ import { ReactCameraView } from '@react-native-ohos/react-native-camera'; - -@Builder -export function buildCustomRNComponent(ctx: ComponentBuilderContext) { ++ import type {RNPackageContext, RNPackage} from '@rnoh/react-native-openharmony/ts'; ++ import { WatermelonDBPackage } from '@react-native-ohos/watermelondb'; + ++ export function createRNPackages(ctx: RNPackageContext): RNPackage[] { ++ return [ ++ new WatermelonDBPackage(ctx) ++ ]; ++ } ... -+ if (ctx.componentName === ReactCameraView.NAME) { -+ ReactCameraView({ -+ ctx: ctx.rnComponentContext, -+ tag: ctx.tag, -+ }) -+ } -... -} ... ``` -> [!TIP] 本库使用了混合方案,需要添加组件名。 - -在 `entry/src/main/ets/pages/index.ets` 或 `entry/src/main/ets/rn/LoadBundle.ets` 找到常量 `arkTsComponentNames` 在其数组里添加组件名 - -```diff -const arkTsComponentNames: Array = [ - SampleView.NAME, - GeneratedSampleView.NAME, - PropsDisplayer.NAME, -+ ReactCameraView.NAME - ]; -``` - -### 2.5.在 ArkTs 侧引入 ReactNativeCameraPackage - -打开 `entry/src/main/ets/RNPackagesFactory.ts`,添加: - -```diff -... -+ import { ReactNativeCameraPackage, FaceDectorPackage } from '@react-native-ohos/react-native-camera/ts'; -export function createRNPackages(ctx: RNPackageContext): RNPackage[] { - return [ - new SamplePackage(ctx), -+ new ReactNativeCameraPackage(ctx), -+ new FaceDectorPackage(ctx) - ]; -} -``` - ### 2.6.运行 点击右上角的 `sync` 按钮 @@ -251,7 +664,7 @@ ohpm install 然后编译、运行即可。 -## 3. 约束与限制 +## 3. 约束与限制 ### 3.1. 兼容性 @@ -261,7 +674,7 @@ ohpm install ### 3.2. 权限要求 -#### 在 entry 目录下的module.json5中添加权限 +#### 在 entry 目录下的 module.json5 中添加权限 打开 `entry/src/main/module.json5`,添加: @@ -269,25 +682,8 @@ ohpm install ... "requestPermissions": [ + { -+ "name": "ohos.permission.CAMERA", -+ "reason": "$string:camera_reason", -+ "usedScene": { -+ "abilities": [ -+ "EntryAbility" -+ ], -+ "when":"inuse" -+ } -+ }, -+ { -+ "name": "ohos.permission.MICROPHONE", -+ "reason": "$string:microphone_reason", -+ "usedScene": { -+ "abilities": [ -+ "EntryAbility" -+ ], -+ "when":"inuse" -+ } -+ }, ++ "name": "ohos.permission.INTERNET", ++ } ] ``` @@ -300,12 +696,16 @@ ohpm install { "string": [ + { -+ "name": "camera_reason", -+ "value": "使用相机" ++ "name": "module_desc", ++ "value": "moudle description" + }, + { -+ "name": "microphone_reason", -+ "value": "使用麦克风" ++ "name": "EntryAbility_desc", ++ "value": "description" ++ }, ++ { ++ "name": "EntryAbility_label", ++ "value": "WatermelonDB" + }, ] } @@ -317,113 +717,110 @@ ohpm install > [!TIP] "HarmonyOS Support"列为 yes 表示 HarmonyOS 平台支持该属性;no 则表示不支持;partially 表示部分支持。使用方法跨平台一致,效果对标 iOS 或 Android 的效果。 -| Name | Description | Type | Required | Platform | HarmonyOS Support | -| -------------------------------- | ------------------------------ | -------- | -------- | ----------- | ----------------- | -|Collection类 | | | | | | -| db | 获取该集合所属的数据库实例引用 | function | no | iOS/Android | yes | -| find | 根据指定条件查询集合中匹配的记录 | function | no | iOS/Android | yes | -| findAndObserve | 查询记录并建立响应式观察以监听数据变更 | function | no | iOS/Android | yes | -| create |在集合中创建并保存新纪录 | function | no | iOS/Android | yes | -|prepareCreate | 预创建记录对象但不立即持久化到数据库 | function | no | iOS/Android | yes | -| prepareCreateFromDirtyRaw | 从原始未经验证的数据预创建记录对象 | function | no | iOS/Android | yes | -| disposableFromDirtyRaw | 基于原始脏数据创建一次性使用的临时记录 | function | no | iOS/Android | yes | -| table| 获取该集合对应的数据库表名称 | function | no | iOS/Android | yes | -| schema | 获取该集合对应的数据表结构定义 | function | no | iOS/Android | yes | -| experimentalSubscribe| 订阅集合级别的数据变更通知 | function | no | iOS/Android | yes | -| Database类| | | | | | -| get | 根据模型类型和ID从数据库获取特定记录 | function | no | iOS/Android | yes | -| localStorage| 获取数据库的本地存储适配器用于键值对数据 | function | no | iOS/Android| yes | -|batch | 创建批量操作以高效执行多个数据库写入 | function | no | iOS/Android | yes | -| write | 在写入十五中执行数据库修改操作 | function | no | iOS/Android| yes | -|read | 在只读事务中执行数据库查询操作 | function | no | iOS/Android| yes | -| action | 执行需要读写权限的数据库动作 | function | no | iOS/Android | yes | -|withChangesForTables | 创建监听指定表数据变更的观察者 | function | no | iOS/Android | yes | -| experimentalSubscribe | 订阅数据库级别的变更通知 | function | no | iOS/Android | yes | -| unsafeResetDatabase | 完全重置数据库并清除所有数据 | function | no | iOS/Android | yes | -| _ensurelnWtiter | 确保当前在写入线程中执行操作 | function | no |iOS/Android | yes | -| _fataError | 处理数据库致命错误并终止连接 | function | no | iOS/Android | yes | -| Relation类 | | | | | | -|get id | 获取关联目标模型的唯一标识符 | function | no | iOS/Android | yes | -| set id | 设置关联目标模型的标识符值 | function | no | iOS/Android | yes | -| fetch | 异步获取关联记录的完整数据承诺 | function | no | iOS/Android | yes | -| then | 支持Promise链式调用,处理异步获取的关联数据 | function | no | iOS/Android | yes | -| set |建立与另一个模型实例的关联关系 | function | no | iOS/Android | yes | -| observe | 监听关联模型数据的实时变化 | function | no | iOS/Android | yes | -| Model类 | | | | | | -| associations | 定义模型与其他数据表之间的关联关系映射 | function | no | iOS/Android | yes | -| prepareMarkAsDeleted | 标记为删除状态 | function | no | iOS/Android | yes | -| _getChanges | 获取模型变更观察对象 | function | no | iOS/Android | yes | -| get id | 获取实例的唯一标识符 | function | no | iOS/Android | yes | -| get syncStatus |获取数据同步状态 | function | no | iOS/Android | yes | -| update | 更新模型并立即提交 | function | no | iOS/Android | yes | -| prepareUpdate | 准备更新 | function | no | iOS/Android | yes | -| prepareDestroyPermanently | 准备永久销毁但不执行 | function | no | iOS/Android | yes | -| markAsDeleted | 删除模型并提交 | function | no | iOS/Android | yes | -| destroyPermanently | 永久删除模型记录 | function | no | iOS/Android | yes | -| experimentalMarkAsDeleted | 软删除功能 | function | no | iOS/Android | yes | -| experimentalDestroyPermanently |永久删除功能 | function | no | iOS/Android| yes | -| observe |监听模型数据变化 | function | no | iOS/Android| yes | -| collection | 获取所属数据集合 | function | no | iOS/Android| yes | -| get collections | 获取所有集合映射 | function | no | iOS/Android| yes | -| get database |获取数据库实例 | function | no | iOS/Android | yes | -| get db | 获取数据库实例 | function | no | iOS/Android | yes | -|get asModel | 返回自身实例 | function | no | iOS/Android | yes | -|callWriter | 在写入线程执行操作 | function | no | iOS/Android| yes | -| callReader | 在读取线程执行查询 | function | no | iOS/Android| yes | -| subAction |在事务中执行子操作 | function | no | iOS/Android| yes | -| get table | 获取对应数据表名 | function | no | iOS/Android | yes | -| prepareCreate | 准备创建模型 | function | no | iOS/Android | yes | -| _prepareCreateFromDirtyRaw | 从脏数据创建模型 | function | no | iOS/Android | yes | -| _disposableFromDirtyRaw | 创建一次性模型 | function | no | iOS/Android| yes | -| experimentalSubscribe | 实验性订阅模型变更 | function | no | iOS/Android| yes | -| _notifyChanged | 内部通知模型变更 | function | no | iOS/Android | yes | -| _notifyDestroyed | 内部通知模型销毁 | function | no | iOS/Android | yes | -| _getRaw | 内部获取原始字段值 | function | no | iOS/Android | yes | -| _setRaw | 内部设置字段值并标记变更 | function | no | iOS/Android | yes | -|_dangerouslySetRawWithoutMarkingColumnChange | 内部不安全设置字段值 | function | no | iOS/Android | yes | -| _ensureCanSetRaw | 内部确保可设置原始值 | function | no | iOS/Android | yes | -| _ensureNotDisposable| 内部确保非一次性实例 | function | no | iOS/Android | yes | -| Query类 | | | | | | -| pipe | 对查询结果进行链式转换操作 | function | no | iOS/Android | yes | -| fetch() | 立即执行查询获取结果 | function | no | iOS/Android | yes | -| then | 支持Promise链式调用查询结果 | function | no | iOS/Android | yes | -| observe | 监听查询结果的实时变化 | function | no | iOS/Android | yes | -| observeWithColumns | 监听特定字段变化的查询结果 | function | no | iOS/Android | yes | -| fetchCount | 获取查询结果的数量统计 | function | no | iOS/Android | yes | -| get count | 获取查询结果总数 | function | no | iOS/Android | yes | -| observeCount | 监听查询结果数量的变化 | function | no | iOS/Android | yes | -| fetchIds | 获取查询结果的ID列表 | function | no | iOS/Android | yes | -| unsafeFetchRaw | 不安全地获取原始查询数据 | function | no | iOS/Android | yes | -| experimentalSubscribe | 实验性查询订阅功能 | function | no | iOS/Android | yes | -| experimentalSubscribeWithColumns | 实验性字段变化订阅 | function | no | iOS/Android | yes | -| experimentalSubscribeToCount | 实验性数量变化订阅 | function | no | iOS/Android | yes | -| markAllAsDeleted | 标记所有查询结果为软删除 | function | no | iOS/Android | yes | -| destroyAllPermanently | 永久删除所有查询结果 |function | no | iOS/Android | yes | -| get modelClass | 获取查询对应的模型类 | function | no | iOS/Android | yes | -| get table | 获取查询的主表名称 | function | no | iOS/Android | yes | -| get secondaryTables | 获取查询涉及的关联表 | function | no | iOS/Android | yes | -| get allTables | 获取查询所有相关表 | function | no | iOS/Android | yes | -| get associations | 获取查询的关联关系 | function | no | iOS/Android | yes | -| serialize | 序列化查询为可传输格式 | function | no | iOS/Android | yes | -|Schema类| -| tableName | 定义数据库表的名称 | function | no | iOS/Android | yes | -| columnName | 定义数据库字段的名称 | function | no | iOS/Android | yes | -| appSchema | 定义整个应用数据库的结构模式 | function | no | iOS/Android | yes | -| validateColumnsSchema | 验证字段模式定义的合法性 | function | no |iOS/Android | yes | -| tableSchema | 定义单个数据表的结构和字段配置 | function | no | iOS/Android | yes | - -## - -5.遗留问题 - -- [ ] onFacesDetected能力无法实现 [issue#1](https://github.com/react-native-oh-library/react-native-camera/issues/8) +| Name | Description | Type | Required | Platform | HarmonyOS Support | +| --------------------------------------------- | --------------------------------------------- | -------- | -------- | ----------- | ----------------- | +| Collection 类 | | | | | | +| db | 获取该集合所属的数据库实例引用 | function | no | iOS/Android | yes | +| find | 根据指定条件查询集合中匹配的记录 | function | no | iOS/Android | yes | +| findAndObserve | 查询记录并建立响应式观察以监听数据变更 | function | no | iOS/Android | yes | +| create | 在集合中创建并保存新纪录 | function | no | iOS/Android | yes | +| prepareCreate | 预创建记录对象但不立即持久化到数据库 | function | no | iOS/Android | yes | +| prepareCreateFromDirtyRaw | 从原始未经验证的数据预创建记录对象 | function | no | iOS/Android | yes | +| disposableFromDirtyRaw | 基于原始脏数据创建一次性使用的临时记录 | function | no | iOS/Android | yes | +| table | 获取该集合对应的数据库表名称 | function | no | iOS/Android | yes | +| schema | 获取该集合对应的数据表结构定义 | function | no | iOS/Android | yes | +| experimentalSubscribe | 订阅集合级别的数据变更通知 | function | no | iOS/Android | yes | +| Database 类 | | | | | | +| get | 根据模型类型和 ID 从数据库获取特定记录 | function | no | iOS/Android | yes | +| localStorage | 获取数据库的本地存储适配器用于键值对数据 | function | no | iOS/Android | yes | +| batch | 创建批量操作以高效执行多个数据库写入 | function | no | iOS/Android | yes | +| write | 在写入十五中执行数据库修改操作 | function | no | iOS/Android | yes | +| read | 在只读事务中执行数据库查询操作 | function | no | iOS/Android | yes | +| action | 执行需要读写权限的数据库动作 | function | no | iOS/Android | yes | +| withChangesForTables | 创建监听指定表数据变更的观察者 | function | no | iOS/Android | yes | +| experimentalSubscribe | 订阅数据库级别的变更通知 | function | no | iOS/Android | yes | +| unsafeResetDatabase | 完全重置数据库并清除所有数据 | function | no | iOS/Android | yes | +| \_ensurelnWtiter | 确保当前在写入线程中执行操作 | function | no | iOS/Android | yes | +| \_fataError | 处理数据库致命错误并终止连接 | function | no | iOS/Android | yes | +| Relation 类 | | | | | | +| get id | 获取关联目标模型的唯一标识符 | function | no | iOS/Android | yes | +| set id | 设置关联目标模型的标识符值 | function | no | iOS/Android | yes | +| fetch | 异步获取关联记录的完整数据承诺 | function | no | iOS/Android | yes | +| then | 支持 Promise 链式调用,处理异步获取的关联数据 | function | no | iOS/Android | yes | +| set | 建立与另一个模型实例的关联关系 | function | no | iOS/Android | yes | +| observe | 监听关联模型数据的实时变化 | function | no | iOS/Android | yes | +| Model 类 | | | | | | +| associations | 定义模型与其他数据表之间的关联关系映射 | function | no | iOS/Android | yes | +| prepareMarkAsDeleted | 标记为删除状态 | function | no | iOS/Android | yes | +| \_getChanges | 获取模型变更观察对象 | function | no | iOS/Android | yes | +| get id | 获取实例的唯一标识符 | function | no | iOS/Android | yes | +| get syncStatus | 获取数据同步状态 | function | no | iOS/Android | yes | +| update | 更新模型并立即提交 | function | no | iOS/Android | yes | +| prepareUpdate | 准备更新 | function | no | iOS/Android | yes | +| prepareDestroyPermanently | 准备永久销毁但不执行 | function | no | iOS/Android | yes | +| markAsDeleted | 删除模型并提交 | function | no | iOS/Android | yes | +| destroyPermanently | 永久删除模型记录 | function | no | iOS/Android | yes | +| experimentalMarkAsDeleted | 软删除功能 | function | no | iOS/Android | yes | +| experimentalDestroyPermanently | 永久删除功能 | function | no | iOS/Android | yes | +| observe | 监听模型数据变化 | function | no | iOS/Android | yes | +| collection | 获取所属数据集合 | function | no | iOS/Android | yes | +| get collections | 获取所有集合映射 | function | no | iOS/Android | yes | +| get database | 获取数据库实例 | function | no | iOS/Android | yes | +| get db | 获取数据库实例 | function | no | iOS/Android | yes | +| get asModel | 返回自身实例 | function | no | iOS/Android | yes | +| callWriter | 在写入线程执行操作 | function | no | iOS/Android | yes | +| callReader | 在读取线程执行查询 | function | no | iOS/Android | yes | +| subAction | 在事务中执行子操作 | function | no | iOS/Android | yes | +| get table | 获取对应数据表名 | function | no | iOS/Android | yes | +| prepareCreate | 准备创建模型 | function | no | iOS/Android | yes | +| \_prepareCreateFromDirtyRaw | 从脏数据创建模型 | function | no | iOS/Android | yes | +| \_disposableFromDirtyRaw | 创建一次性模型 | function | no | iOS/Android | yes | +| experimentalSubscribe | 实验性订阅模型变更 | function | no | iOS/Android | yes | +| \_notifyChanged | 内部通知模型变更 | function | no | iOS/Android | yes | +| \_notifyDestroyed | 内部通知模型销毁 | function | no | iOS/Android | yes | +| \_getRaw | 内部获取原始字段值 | function | no | iOS/Android | yes | +| \_setRaw | 内部设置字段值并标记变更 | function | no | iOS/Android | yes | +| \_dangerouslySetRawWithoutMarkingColumnChange | 内部不安全设置字段值 | function | no | iOS/Android | yes | +| \_ensureCanSetRaw | 内部确保可设置原始值 | function | no | iOS/Android | yes | +| \_ensureNotDisposable | 内部确保非一次性实例 | function | no | iOS/Android | yes | +| Query 类 | | | | | | +| pipe | 对查询结果进行链式转换操作 | function | no | iOS/Android | yes | +| fetch | 立即执行查询获取结果 | function | no | iOS/Android | yes | +| then | 支持 Promise 链式调用查询结果 | function | no | iOS/Android | yes | +| observe | 监听查询结果的实时变化 | function | no | iOS/Android | yes | +| observeWithColumns | 监听特定字段变化的查询结果 | function | no | iOS/Android | yes | +| fetchCount | 获取查询结果的数量统计 | function | no | iOS/Android | yes | +| get count | 获取查询结果总数 | function | no | iOS/Android | yes | +| observeCount | 监听查询结果数量的变化 | function | no | iOS/Android | yes | +| fetchIds | 获取查询结果的 ID 列表 | function | no | iOS/Android | yes | +| unsafeFetchRaw | 不安全地获取原始查询数据 | function | no | iOS/Android | yes | +| experimentalSubscribe | 实验性查询订阅功能 | function | no | iOS/Android | yes | +| experimentalSubscribeWithColumns | 实验性字段变化订阅 | function | no | iOS/Android | yes | +| experimentalSubscribeToCount | 实验性数量变化订阅 | function | no | iOS/Android | yes | +| markAllAsDeleted | 标记所有查询结果为软删除 | function | no | iOS/Android | yes | +| destroyAllPermanently | 永久删除所有查询结果 | function | no | iOS/Android | yes | +| get modelClass | 获取查询对应的模型类 | function | no | iOS/Android | yes | +| get table | 获取查询的主表名称 | function | no | iOS/Android | yes | +| get secondaryTables | 获取查询涉及的关联表 | function | no | iOS/Android | yes | +| get allTables | 获取查询所有相关表 | function | no | iOS/Android | yes | +| get associations | 获取查询的关联关系 | function | no | iOS/Android | yes | +| serialize | 序列化查询为可传输格式 | function | no | iOS/Android | yes | +| Schema 类 | +| tableName | 定义数据库表的名称 | function | no | iOS/Android | yes | +| columnName | 定义数据库字段的名称 | function | no | iOS/Android | yes | +| appSchema | 定义整个应用数据库的结构模式 | function | no | iOS/Android | yes | +| validateColumnsSchema | 验证字段模式定义的合法性 | function | no | iOS/Android | yes | +| tableSchema | 定义单个数据表的结构和字段配置 | function | no | iOS/Android | yes | + +## 5.遗留问题 + +- [ ] onFacesDetected 能力无法实现 [issue#1](https://github.com/react-native-oh-library/react-native-camera/issues/8) ## 6.其他 -- notAuthorizedView属性,pendingAuthorizationView属性是android独有的属性,申请授权时显示的UI,harmony已经提供授权时的弹窗。 -- ratio是android独有的属性,在ios无效果 [源库readme位置](https://github.com/react-native-camera/react-native-camera/blob/v3.40.0/docs/RNCamera.md#android-ratio) +- notAuthorizedView 属性,pendingAuthorizationView 属性是 android 独有的属性,申请授权时显示的 UI,harmony 已经提供授权时的弹窗。 +- ratio 是 android 独有的属性,在 ios 无效果 [源库 readme 位置](https://github.com/react-native-camera/react-native-camera/blob/v3.40.0/docs/RNCamera.md#android-ratio) ## 7.开源协议 -本项目基于 [MIT License (MIT)](https://github.com/react-native-camera/react-native-camera/blob/master/LICENSE) ,请自由地享受和参与开源。 - +本项目基于 [MIT License (MIT)](https://github.com/Nozbe/WatermelonDB/blob/master/LICENSE) ,请自由地享受和参与开源。 -- Gitee From d5371bb2b3d0325dffa1ef27b81bd2417fdd9f36 Mon Sep 17 00:00:00 2001 From: lqq <1960002614@qq.com> Date: Fri, 19 Dec 2025 14:30:08 +0800 Subject: [PATCH 06/22] =?UTF-8?q?docs:=20[Issues:=20#IDE3MY]=20=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0watermelondb=E7=9A=84=E6=8C=87=E5=AF=BC=E6=96=87?= =?UTF-8?q?=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- zh-cn/watermelondb.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/zh-cn/watermelondb.md b/zh-cn/watermelondb.md index 000b0a282..8073f8f06 100644 --- a/zh-cn/watermelondb.md +++ b/zh-cn/watermelondb.md @@ -19,13 +19,13 @@ #### **npm** ```bash -npm install +npm install ``` #### **yarn** ```bash -yarn add +yarn add ``` 下面的代码展示了这个库的基本使用场景: @@ -734,7 +734,7 @@ ohpm install | get | 根据模型类型和 ID 从数据库获取特定记录 | function | no | iOS/Android | yes | | localStorage | 获取数据库的本地存储适配器用于键值对数据 | function | no | iOS/Android | yes | | batch | 创建批量操作以高效执行多个数据库写入 | function | no | iOS/Android | yes | -| write | 在写入十五中执行数据库修改操作 | function | no | iOS/Android | yes | +| write | 在写入事务中执行数据库修改操作 | function | no | iOS/Android | yes | | read | 在只读事务中执行数据库查询操作 | function | no | iOS/Android | yes | | action | 执行需要读写权限的数据库动作 | function | no | iOS/Android | yes | | withChangesForTables | 创建监听指定表数据变更的观察者 | function | no | iOS/Android | yes | @@ -824,3 +824,4 @@ ohpm install ## 7.开源协议 本项目基于 [MIT License (MIT)](https://github.com/Nozbe/WatermelonDB/blob/master/LICENSE) ,请自由地享受和参与开源。 + -- Gitee From 555ff00c5e3bde9b158bc556a9e8c52e9763c464 Mon Sep 17 00:00:00 2001 From: zhouyong <550468167@qq.com> Date: Mon, 22 Dec 2025 10:47:56 +0800 Subject: [PATCH 07/22] =?UTF-8?q?fix:=20[Issues:=20#IDE3MY]=20=E4=BF=AE?= =?UTF-8?q?=E6=94=B9watermelondb=E7=9A=84=E6=8C=87=E5=AF=BC=E6=96=87?= =?UTF-8?q?=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- zh-cn/watermelondb.md | 65 ++++++++++++++++++++++++++----------------- 1 file changed, 40 insertions(+), 25 deletions(-) diff --git a/zh-cn/watermelondb.md b/zh-cn/watermelondb.md index 8073f8f06..4116c094f 100644 --- a/zh-cn/watermelondb.md +++ b/zh-cn/watermelondb.md @@ -16,22 +16,22 @@ 进入到工程目录并输入以下命令: -#### **npm** +#### npm ```bash -npm install +npm install @react-native-ohos/watermelondb ``` -#### **yarn** +#### yarn ```bash -yarn add +yarn add @react-native-ohos/watermelondb ``` 下面的代码展示了这个库的基本使用场景: > [!WARNING] 使用时 import 的库名不变。 - +### watermelondb example ```js import React, { useCallback, useEffect, useMemo, useState } from 'react'; import { @@ -516,13 +516,13 @@ const styles = StyleSheet.create({ }); ``` -## 2. Manual Link +## Link 此步骤为手动配置原生依赖项的指导。 首先需要使用 DevEco Studio 打开项目里的 HarmonyOS 工程 `harmony`。 -### 2.1. Overrides RN SDK +### 1.在工程根目录的 `oh-package.json` 添加 overrides字段 为了让工程依赖同一个版本的 RN SDK,需要在工程根目录的 `oh-package.json5` 添加 overrides 字段,指向工程需要使用的 RN SDK 版本。替换的版本既可以是一个具体的版本号,也可以是一个模糊版本,还可以是本地存在的 HAR 包或源码目录。 @@ -531,12 +531,12 @@ const styles = StyleSheet.create({ ```json { "overrides": { - "@rnoh/react-native-openharmony": "file:../node_modules/@react-native-oh/react-native-harmony/react_native_openharmony.har" + "@rnoh/react-native-openharmony" : "./react_native_openharmony" } } ``` -### 2.2. 引入原生端代码 +### 2. 引入原生端代码 目前有两种方法: @@ -569,7 +569,7 @@ ohpm install > [!TIP] 如需使用直接链接源码,请参考[直接链接源码说明](/zh-cn/link-source-code.md) -### 2.3.配置 CMakeLists 和引入 WatermelonDBPackage +### 3. 配置 CMakeLists 和引入 WatermelonDBPackage 打开 `entry/src/main/cpp/CMakeLists.txt`,添加: @@ -633,7 +633,7 @@ std::vector> PackageProvider::getPackages( } ``` -### 2.4.在 ArkTs 侧引入 watermelondb 组件 +### 4. 在 ArkTs 侧引入 watermelondb 组件 找到 `entry/src/main/ets/RNPackagesFactory.ts`,添加: @@ -651,7 +651,7 @@ std::vector> PackageProvider::getPackages( ... ``` -### 2.6.运行 +### 5.运行 点击右上角的 `sync` 按钮 @@ -664,15 +664,15 @@ ohpm install 然后编译、运行即可。 -## 3. 约束与限制 - -### 3.1. 兼容性 +## 约束与限制 -要使用此库,需要使用正确的 React-Native 和 RNOH 版本。另外,还需要使用配套的 DevEco Studio 和 手机 ROM。 +### 兼容性 -请到三方库相应的 Releases 发布地址查看 Release 配套的版本信息:[@react-native-ohos/react-native-camera Releases](https://github.com/react-native-oh-library/react-native-camera/releases) +本文档内容基于以下版本验证通过: +1. RNOH:0.72.90; SDK:HarmonyOS NEXT Developer DB3; IDE: DevEco Studio: 5.0.5.220; ROM:NEXT.0.0.105; +2. RNOH:0.77.18; SDK:HarmonyOS 6.0.0 Release; IDE: DevEco Studio 6.0.0.858; ROM:6.0.0.112; -### 3.2. 权限要求 +### 权限要求 #### 在 entry 目录下的 module.json5 中添加权限 @@ -711,15 +711,15 @@ ohpm install } ``` -## 4. 属性 +## 属性 > [!TIP] "Platform"列表示该属性在原三方库上支持的平台。 > [!TIP] "HarmonyOS Support"列为 yes 表示 HarmonyOS 平台支持该属性;no 则表示不支持;partially 表示部分支持。使用方法跨平台一致,效果对标 iOS 或 Android 的效果。 +**Collection 类** | Name | Description | Type | Required | Platform | HarmonyOS Support | | --------------------------------------------- | --------------------------------------------- | -------- | -------- | ----------- | ----------------- | -| Collection 类 | | | | | | | db | 获取该集合所属的数据库实例引用 | function | no | iOS/Android | yes | | find | 根据指定条件查询集合中匹配的记录 | function | no | iOS/Android | yes | | findAndObserve | 查询记录并建立响应式观察以监听数据变更 | function | no | iOS/Android | yes | @@ -730,7 +730,10 @@ ohpm install | table | 获取该集合对应的数据库表名称 | function | no | iOS/Android | yes | | schema | 获取该集合对应的数据表结构定义 | function | no | iOS/Android | yes | | experimentalSubscribe | 订阅集合级别的数据变更通知 | function | no | iOS/Android | yes | -| Database 类 | | | | | | + +**Database 类** +| Name | Description | Type | Required | Platform | HarmonyOS Support | +| --------------------------------------------- | --------------------------------------------- | -------- | -------- | ----------- | ----------------- | | get | 根据模型类型和 ID 从数据库获取特定记录 | function | no | iOS/Android | yes | | localStorage | 获取数据库的本地存储适配器用于键值对数据 | function | no | iOS/Android | yes | | batch | 创建批量操作以高效执行多个数据库写入 | function | no | iOS/Android | yes | @@ -742,14 +745,20 @@ ohpm install | unsafeResetDatabase | 完全重置数据库并清除所有数据 | function | no | iOS/Android | yes | | \_ensurelnWtiter | 确保当前在写入线程中执行操作 | function | no | iOS/Android | yes | | \_fataError | 处理数据库致命错误并终止连接 | function | no | iOS/Android | yes | -| Relation 类 | | | | | | + +**Relation 类** +| Name | Description | Type | Required | Platform | HarmonyOS Support | +| --------------------------------------------- | --------------------------------------------- | -------- | -------- | ----------- | ----------------- | | get id | 获取关联目标模型的唯一标识符 | function | no | iOS/Android | yes | | set id | 设置关联目标模型的标识符值 | function | no | iOS/Android | yes | | fetch | 异步获取关联记录的完整数据承诺 | function | no | iOS/Android | yes | | then | 支持 Promise 链式调用,处理异步获取的关联数据 | function | no | iOS/Android | yes | | set | 建立与另一个模型实例的关联关系 | function | no | iOS/Android | yes | | observe | 监听关联模型数据的实时变化 | function | no | iOS/Android | yes | -| Model 类 | | | | | | + +**Model 类** +| Name | Description | Type | Required | Platform | HarmonyOS Support | +| --------------------------------------------- | --------------------------------------------- | -------- | -------- | ----------- | ----------------- | | associations | 定义模型与其他数据表之间的关联关系映射 | function | no | iOS/Android | yes | | prepareMarkAsDeleted | 标记为删除状态 | function | no | iOS/Android | yes | | \_getChanges | 获取模型变更观察对象 | function | no | iOS/Android | yes | @@ -783,7 +792,10 @@ ohpm install | \_dangerouslySetRawWithoutMarkingColumnChange | 内部不安全设置字段值 | function | no | iOS/Android | yes | | \_ensureCanSetRaw | 内部确保可设置原始值 | function | no | iOS/Android | yes | | \_ensureNotDisposable | 内部确保非一次性实例 | function | no | iOS/Android | yes | -| Query 类 | | | | | | + +**Query 类** +| Name | Description | Type | Required | Platform | HarmonyOS Support | +| --------------------------------------------- | --------------------------------------------- | -------- | -------- | ----------- | ----------------- | | pipe | 对查询结果进行链式转换操作 | function | no | iOS/Android | yes | | fetch | 立即执行查询获取结果 | function | no | iOS/Android | yes | | then | 支持 Promise 链式调用查询结果 | function | no | iOS/Android | yes | @@ -805,7 +817,10 @@ ohpm install | get allTables | 获取查询所有相关表 | function | no | iOS/Android | yes | | get associations | 获取查询的关联关系 | function | no | iOS/Android | yes | | serialize | 序列化查询为可传输格式 | function | no | iOS/Android | yes | -| Schema 类 | + +**Schema 类** +| Name | Description | Type | Required | Platform | HarmonyOS Support | +| --------------------------------------------- | --------------------------------------------- | -------- | -------- | ----------- | ----------------- | | tableName | 定义数据库表的名称 | function | no | iOS/Android | yes | | columnName | 定义数据库字段的名称 | function | no | iOS/Android | yes | | appSchema | 定义整个应用数据库的结构模式 | function | no | iOS/Android | yes | -- Gitee From dd5b5587be15f06e837d8207d14d7a66ef5bd468 Mon Sep 17 00:00:00 2001 From: zhouyong <550468167@qq.com> Date: Mon, 22 Dec 2025 11:42:32 +0800 Subject: [PATCH 08/22] =?UTF-8?q?docs:=20[Issues:=20#IDE3MY]=20=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0watermelondb=E7=9A=84=E8=8B=B1=E6=96=87=E6=8C=87?= =?UTF-8?q?=E5=AF=BC=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- en/watermelondb.md | 829 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 829 insertions(+) create mode 100644 en/watermelondb.md diff --git a/en/watermelondb.md b/en/watermelondb.md new file mode 100644 index 000000000..60ccf9c3d --- /dev/null +++ b/en/watermelondb.md @@ -0,0 +1,829 @@ +> Template version:v0.3.0 + +

+

watermelondb

+

+ +Please refer to the Releases page of the third-party library for the matching version information: +| Third-party library version | Release information | Supported RN version | +| ---------- | -------------------------------------------------------------------------------------------------------------------------- | ------------ | +| v0.28.1-0 | [@react-native-ohos/watermelondb Releases](https://github.com/) | 0.72/0.77 | + +## 1. Installation and Usage + +Go to the project directory and execute the following instruction: + +#### npm + +```bash +npm install @react-native-ohos/watermelondb +``` + +#### yarn + +```bash +yarn add @react-native-ohos/watermelondb +``` + +The following code demonstrates the basic usage scenarios of this library: + +> [!WARNING] The library name imported remains unchanged during usage. +### watermelondb example +```js +import React, { useCallback, useEffect, useMemo, useState } from 'react'; +import { + Alert, + FlatList, + Pressable, + SafeAreaView, + ScrollView, + StyleSheet, + Text, + View, +} from 'react-native'; +import { Database } from '@react-native-ohos/watermelondb'; +import SQLiteAdapter from '@react-native-ohos/watermelondb/adapters/sqlite'; +import { DatabaseProvider, useDatabase } from '@react-native-ohos/watermelondb/react'; +import { mySchema } from './models/schema'; +import { dbModels } from './models/index.js'; + +const adapter = new SQLiteAdapter({ + dbName: 'WatermelonRelationDemo', + schema: mySchema, + jsi: false, + log: (sql) => console.log('SQL执行:', sql), + onSetUpError: (error) => { + console.error('[WatermelonDemo*] 初始化数据库失败', error); + }, +}); + +const database = new Database({ + adapter, + modelClasses: dbModels, +}); + +const SAMPLE_MOVIES = [ + { + title: '星际穿越', + posterImage: + 'https://image.tmdb.org/t/p/original/rAiYTfKGqDCRIIqo664sY9XZIvQ.jpg', + genre: '科幻', + description: '一段跨越宇宙与时间、寻找新家园的旅程。', + }, + { + title: '疯狂动物城', + posterImage: + 'https://image.tmdb.org/t/p/original/hlK0e0wAQ3VLuJcsfIYPvb4JVud.jpg', + genre: '动画', + description: '一只兔子警官与狐狸搭档,揭开阴谋、守护城市。', + }, + { + title: '速度与激情 10', + posterImage: + 'https://image.tmdb.org/t/p/original/qDRGPAcQoW8Wuig9bvoLpHwf1gU.jpg', + genre: '动作', + description: '家人永远是第一位,飙车、爆炸、热血永不停歇。', + }, +]; + +const SAMPLE_REVIEWS = [ + '剧情紧凑,完全停不下来!', + '配乐太棒了,影院体验绝佳。', + '主角魅力满分,期待续作。', + '画面惊艳,值得二刷三刷。', + '故事内核很温暖,看完心情很好。', +]; + +const randomItem = (items) => items[Math.floor(Math.random() * items.length)]; + +const formatDate = (value) => { + if (!value) { + return '未知'; + } + try { + const date = value instanceof Date ? value : new Date(value); + return date.toISOString().slice(0, 10); + } catch (error) { + return '未知'; + } +}; + +const ActionButton = ({ label, onPress, type = 'default' }) => { + const background = + type === 'danger' + ? styles.dangerButton + : type === 'secondary' + ? styles.secondaryButton + : styles.primaryButton; + return ( + + {label} + + ); +}; + +const MovieCard = ({ movie, onAddReview, onRename, onDelete }) => { + const [reviews, setReviews] = useState([]); + const [movieInfo, setMovieInfo] = useState(movie.getMovie()); + + useEffect(() => { + const subscription = movie.reviews.observe().subscribe({ + next: (list) => setReviews(list), + error: (error) => console.warn('订阅评论失败', error), + }); + return () => subscription.unsubscribe(); + }, [movie]); + + // 订阅 movie 对象的变化,以便在更新时刷新 UI + useEffect(() => { + const subscription = movie.observe().subscribe({ + next: (updatedMovie) => { + const newInfo = updatedMovie.getMovie(); + console.log('%c watermelondbConsoleLogger movie card updated:', 'color: #0e93e0;background: #aaefe5;', { + id: updatedMovie.id, + newTitle: newInfo.title, + }); + setMovieInfo(newInfo); + }, + error: (error) => console.warn('订阅电影变化失败', error), + }); + return () => subscription.unsubscribe(); + }, [movie]); + + const info = movieInfo; + + return ( + + {info.title} + {info.genre} + {info.description} + 上映日期:{formatDate(info.releaseDateAt)} + 短评:{reviews.length} 条 + {reviews.slice(0, 2).map((review) => ( + + · {review.body} + + ))} + {reviews.length > 2 ? ( + · …… + ) : null} + + onAddReview(movie)} /> + onRename(movie)} /> + onDelete(movie)} /> + + + ); +}; + +const MovieScreen = () => { + const databaseInstance = useDatabase(); + const moviesCollection = useMemo( + () => databaseInstance.collections.get('movies'), + [databaseInstance], + ); + + const [movies, setMovies] = useState([]); + const [loading, setLoading] = useState(true); + + useEffect(() => { + const subscription = moviesCollection.query().observe().subscribe({ + next: (list) => { + console.log('%c watermelondbConsoleLogger movies list updated:', 'color: #0e93e0;background: #aaefe5;', { + count: list.length, + titles: list.map(m => m.title), + }); + setMovies(list); + setLoading(false); + }, + error: (error) => console.error('订阅电影列表失败', error), + }); + return () => subscription.unsubscribe(); + }, [moviesCollection]); + + const seedDemoData = useCallback(async () => { + console.log( + '%c watermelondbConsoleLogger moviesCollection:', + 'color: #0e93e0;background: #aaefe5;', + moviesCollection, + ); + const current = await moviesCollection.query().fetch(); + console.log( + '%c watermelondbConsoleLogger current:', + 'color: #0e93e0;background: #aaefe5;', + current, + ); + if (current.length > 0) { + Alert.alert('提示', '数据库中已经有电影数据,无需重复导入。'); + return; + } + await databaseInstance.write(async () => { + await Promise.all( + SAMPLE_MOVIES.map((payload, index) => + moviesCollection.create((movie) => { + movie.title = payload.title; + movie.genre = payload.genre; + movie.posterImage = payload.posterImage; + movie.description = payload.description; + movie.releaseDateAt = new Date(Date.now() - index * 24 * 60 * 60 * 1000); + }), + ), + ); + }); + }, [databaseInstance, moviesCollection]); + + const addRandomMovie = useCallback(async () => { + const payload = randomItem(SAMPLE_MOVIES); + await databaseInstance.write(async () => { + await moviesCollection.create((movie) => { + movie.title = `${payload.title} · ${Math.floor(Math.random() * 100)}`; + movie.genre = payload.genre; + movie.posterImage = payload.posterImage; + movie.description = payload.description; + movie.releaseDateAt = new Date(); + }); + }); + }, [databaseInstance, moviesCollection]); + + const addReview = useCallback( + async (movie) => { + await databaseInstance.write(async () => { + await movie.addReview(randomItem(SAMPLE_REVIEWS)); + }); + }, + [databaseInstance], + ); + + const renameMovie = useCallback( + async (movie) => { + try { + console.log('%c watermelondbConsoleLogger renameMovie before:', 'color: #0e93e0;background: #aaefe5;', movie.title); + await databaseInstance.write(async () => { + await movie.update((record) => { + const baseTitle = record.title.split(' · ')[0]; + const newTitle = `${baseTitle} · v${Math.floor(Math.random() * 10 + 1)}`; + console.log('%c watermelondbConsoleLogger renameMovie updating:', 'color: #0e93e0;background: #aaefe5;', { + oldTitle: record.title, + baseTitle, + newTitle, + }); + record.title = newTitle; + }); + }); + console.log('%c watermelondbConsoleLogger renameMovie after:', 'color: #0e93e0;background: #aaefe5;', movie.title); + } catch (error) { + console.error('[WatermelonDemo] 随机改名失败', error); + Alert.alert('错误', `随机改名失败: ${error.message}`); + } + }, + [databaseInstance], + ); + + const deleteMovie = useCallback( + async (movie) => { + await databaseInstance.write(async () => movie.deleteMovie()); + }, + [databaseInstance], + ); + + const clearAll = useCallback(async () => { + await databaseInstance.write(async () => { + const allMovies = await moviesCollection.query().fetch(); + await Promise.all(allMovies.map((movie) => movie.deleteMovie())); + }); + }, [databaseInstance, moviesCollection]); + + // 1. 测试关联查询:查询指定电影的所有评论 + const testRelationQuery = useCallback(async () => { + if (movies.length === 0) { + Alert.alert("提示", "请先添加电影数据"); + return; + } + try { + const targetMovie = movies[0]; // 取第一个电影 + const reviews = await targetMovie.reviews.fetch(); // 测试 Relation.fetch() + const reviewTexts = reviews.map(r => r.body).join("\n- "); + Alert.alert( + `电影《${targetMovie.title}》的评论`, + reviewTexts || "暂无评论" + ); + } catch (error) { + console.error("关联查询测试失败", error); + Alert.alert("错误", "关联查询测试失败:" + error.message); + } + }, [movies]); + + // 2. 测试批量添加评论(测试 has_many 关联批量操作) + const testBulkAddReviews = useCallback(async () => { + if (movies.length === 0) { + Alert.alert("提示", "请先添加电影数据"); + return; + } + try { + const targetMovie = movies[0]; + await databaseInstance.write(async () => { + // 批量添加3条评论 + for (let i = 0; i < 3; i++) { + await targetMovie.addReview(`批量测试评论 ${i + 1}:${randomItem(SAMPLE_REVIEWS)}`); + } + }); + Alert.alert("成功", `已为《${targetMovie.title}》批量添加3条评论`); + } catch (error) { + console.error("批量添加评论测试失败", error); + Alert.alert("错误", "批量添加评论失败:" + error.message); + } + }, [movies, databaseInstance]); + + // 3. 测试事务回滚(故意制造错误,验证事务原子性) + const testTransactionRollback = useCallback(async () => { + if (movies.length === 0) { + Alert.alert("提示", "请先添加电影数据"); + return; + } + try { + const targetMovie = movies[0]; + await databaseInstance.write(async () => { + // 第一步:正常添加评论 + await targetMovie.addReview("事务测试:这是一条正常评论"); + // 第二步:故意抛出错误,验证事务回滚(上面的评论应被撤销) + throw new Error("故意触发事务回滚测试"); + }); + } catch (error) { + console.warn("事务回滚测试触发", error.message); + // 验证评论是否被回滚 + const reviews = await targetMovie.reviews.fetch(); + const hasTestReview = reviews.some(r => r.body.includes("事务测试")); + Alert.alert( + "事务回滚测试结果", + hasTestReview + ? "测试失败:事务未回滚" + : "测试成功:事务已回滚(错误前的操作被撤销)" + ); + } + }, [movies, databaseInstance]); + + // 在 MovieScreen 组件的 useCallback 方法区域添加 + const deleteReview = useCallback( + async (review) => { + try { + await databaseInstance.write(async () => { + await review.deleteReview(); // 调用 Review 模型的 deleteReview 方法 + }); + Alert.alert("成功", "评论已删除"); + } catch (error) { + console.error("删除评论失败", error); + Alert.alert("错误", `删除评论失败: ${error.message}`); + } + }, + [databaseInstance] + ); + + return ( + + + WatermelonDB 示例 + + + + + + + + + 当前共有 {movies.length} 部电影 + {loading ? ( + 正在加载数据... + ) : movies.length === 0 ? ( + 暂无电影,请先导入示例数据。 + ) : ( + movies.map((movie) => ( + + )) + )} + + + ); +}; + +export default function WatermelonRelaDemo() { + console.log('%c watermelondbConsoleLogger WatermelonDemo:', 'color: #0e93e0;background: #aaefe5;', 'WatermelonDemo'); + return ( + + + + ); +} + +const styles = StyleSheet.create({ + container: { + flex: 1, + backgroundColor: '#0b132b', + }, + content: { + padding: 16, + paddingBottom: 32, + }, + title: { + fontSize: 24, + fontWeight: 'bold', + color: '#f9f9f9', + marginBottom: 8, + }, + subtitle: { + fontSize: 14, + color: '#c5c9d3', + marginBottom: 16, + lineHeight: 20, + }, + actionRow: { + flexDirection: 'row', + flexWrap: 'wrap', + gap: 12, + marginBottom: 16, + }, + countText: { + color: '#f9f9f9', + marginBottom: 12, + }, + loadingText: { + color: '#c5c9d3', + fontStyle: 'italic', + }, + card: { + backgroundColor: '#1c2541', + borderRadius: 12, + padding: 16, + marginBottom: 16, + }, + movieTitle: { + fontSize: 18, + fontWeight: 'bold', + color: '#ffffff', + }, + movieGenre: { + color: '#5bc0be', + marginTop: 4, + }, + movieDesc: { + color: '#d1d5db', + marginTop: 8, + lineHeight: 18, + }, + movieMeta: { + color: '#9aa0ac', + marginTop: 4, + }, + reviewItem: { + color: '#c5c9d3', + marginTop: 4, + }, + cardActions: { + flexDirection: 'row', + flexWrap: 'wrap', + gap: 10, + marginTop: 12, + }, + buttonWrapper: { + paddingVertical: 8, + paddingHorizontal: 14, + borderRadius: 999, + }, + primaryButton: { + backgroundColor: '#5bc0be', + }, + secondaryButton: { + backgroundColor: '#3a506b', + }, + dangerButton: { + backgroundColor: '#ef476f', + }, + buttonText: { + color: '#fff', + fontWeight: '600', + }, +}); +``` + +## Link + +This step is a guide for manually configuring native dependencies. +First, you need to open the HarmonyOS project harmony in the project using DevEco Studio. + +### 1.Add overrides field to oh-package.json in the project root directory + +```json +{ + "overrides": { + "@rnoh/react-native-openharmony" : "./react_native_openharmony" + } +} +``` + +### 2. There are currently two methods: + +Import via har package (this method will be deprecated after the IDE improves related functions, and it is the preferred method currently); +Link source code directly. + +Method 1: Import via har package (recommended) + +> [!TIP] The HAR package can be found in the harmony subfolder of the third-party library's installation directory. + +Open the entry/oh-package.json5 file and append the following dependencies: + +```json +"dependencies": { + "@rnoh/react-native-openharmony": "0.72.32", + "@react-native-ohos/watermelondb": "file:../../../harmony/watermelondb" + } +``` + +Click the sync button at the top right corner. + +Or run this command in the terminal: + +```bash +cd entry +ohpm install +``` + +Method 2: Link the source code directly + +> [!TIP] For direct source code linking, refer to [Direct Source Code Linking Instructions](/zh-cn/link-source-code.md) + +### 3.Configure CMakeLists and import WatermelonDBPackage + +open `entry/src/main/cpp/CMakeLists.txt`,add: + +```diff +project(rnapp) +cmake_minimum_required(VERSION 3.4.1) +set(CMAKE_SKIP_BUILD_RPATH TRUE) +set(RNOH_APP_DIR "${CMAKE_CURRENT_SOURCE_DIR}") +set(NODE_MODULES "${CMAKE_CURRENT_SOURCE_DIR}/../../../../../node_modules") +set(OH_MODULE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../../oh_modules") +set(RNOH_CPP_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../../oh_modules/@rnoh/react-native-openharmony/src/main/cpp") +set(RNOH_GENERATED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/generated") +set(LOG_VERBOSITY_LEVEL 1) +set(CMAKE_ASM_FLAGS "-Wno-error=unused-command-line-argument -Qunused-arguments") +set(CMAKE_CXX_FLAGS "-fstack-protector-strong -Wl,-z,relro,-z,now,-z,noexecstack -s -fPIE -pie") +set(OH_MODULES "${CMAKE_CURRENT_SOURCE_DIR}/../../../oh_modules") + +set(WITH_HITRACE_SYSTRACE 1) # for other CMakeLists.txt files to use +add_compile_definitions(WITH_HITRACE_SYSTRACE) + +add_subdirectory("${RNOH_CPP_DIR}" ./rn) + +# RNOH_BEGIN: manual_package_linking_1 +# RNOH_END: manual_package_linking_1 + +file(GLOB GENERATED_CPP_FILES "${CMAKE_CURRENT_SOURCE_DIR}/generated/*.cpp") # this line is needed by codegen v1 + +add_subdirectory("${OH_MODULES}/@react-native-ohos/watermelondb/src/main/cpp" ./watermelondb) + + + +add_library(rnoh_app SHARED + ${GENERATED_CPP_FILES} + "./PackageProvider.cpp" + "${RNOH_CPP_DIR}/RNOHAppNapiBridge.cpp" +) +target_link_libraries(rnoh_app PUBLIC rnoh) + +# RNOH_BEGIN: manual_package_linking_2 +target_link_libraries(rnoh_app PUBLIC watermelondb) + + +# RNOH_END: manual_package_linking_2 +``` + +open `entry/src/main/cpp/PackageProvider.cpp`,add: + +```diff +#include "RNOH/PackageProvider.h" +#include "generated/RNOHGeneratedPackage.h" + #include "WatermelondbPackage.h" + +using namespace rnoh; + +std::vector> PackageProvider::getPackages( + Package::Context ctx) { + return { + std::make_shared(ctx), // generated by codegen v1 + std::make_shared(ctx) + }; +} +``` + +### 4. Import the watermelondb module on the ArkTS side + +open `entry/src/main/ets/RNPackagesFactory.ts`,add: + +```diff +... ++ import type {RNPackageContext, RNPackage} from '@rnoh/react-native-openharmony/ts'; ++ import { WatermelonDBPackage } from '@react-native-ohos/watermelondb'; + ++ export function createRNPackages(ctx: RNPackageContext): RNPackage[] { ++ return [ ++ new WatermelonDBPackage(ctx) ++ ]; ++ } +... +... +``` + +### 5.Run + +Click the sync button in the upper right corner. + +Alternatively, execute the following command in the terminal: + +```bash +cd entry +ohpm install +``` + +Then compile and run the project. + +## Constraints and Limitations + +### Compatibility Support + +The content of this document has been verified based on the following versions: +1. RNOH:0.72.90; SDK:HarmonyOS NEXT Developer DB3; IDE: DevEco Studio: 5.0.5.220; ROM:NEXT.0.0.105; +2. RNOH:0.77.18; SDK:HarmonyOS 6.0.0 Release; IDE: DevEco Studio 6.0.0.858; ROM:6.0.0.112; + +#### Add permissions to module.json5 under the entry directory. + +Open `entry/src/main/module.json5`,add: + +```diff +... +"requestPermissions": [ ++ { ++ "name": "ohos.permission.INTERNET", ++ } +] +``` + +#### Add the reason for applying for the above permissions in the entry directory + +Open `entry/src/main/resources/base/element/string.json`,add: + +```diff +... +{ + "string": [ ++ { ++ "name": "module_desc", ++ "value": "moudle description" ++ }, ++ { ++ "name": "EntryAbility_desc", ++ "value": "description" ++ }, ++ { ++ "name": "EntryAbility_label", ++ "value": "WatermelonDB" ++ }, + ] +} +``` + +## Properties + +> [!TIP] "Platform"This column shows which platforms the property supports in the original third-party library。 + +> [!TIP] "HarmonyOS Support"A value of "yes" in this column means the HarmonyOS platform supports the property; "no" means it does not; "partially" means partial support. The usage method is consistent across platforms, and the effect is comparable to that on iOS or Android。 + +**Collection class** +| Name | Description | Type | Required | Platform | HarmonyOS Support | +| --------------------------------------------- | --------------------------------------------- | -------- | -------- | ----------- | ----------------- | +| db | Get the reference to the database instance that this collection belongs to | function | no | iOS/Android | yes | +| find | Query for matching records in the collection based on specified conditions | function | no | iOS/Android | yes | +| findAndObserve | Query records and establish responsive observation to monitor data changes | function | no | iOS/Android | yes | +| create | Create and save new records in the collection | function | no | iOS/Android | yes | +| prepareCreate | Pre - create a record object without immediately persisting it to the database | function | no | iOS/Android | yes | +| prepareCreateFromDirtyRaw | Pre - create a record object from raw unvalidated data | function | no | iOS/Android | yes | +| disposableFromDirtyRaw | Create a one - time temporary record based on raw dirty data | function | no | iOS/Android | yes | +| table | Get the name of the database table corresponding to this collection | function | no | iOS/Android | yes | +| schema | Get the data table structure definition corresponding to this collection | function | no | iOS/Android | yes | +| experimentalSubscribe | Subscribe to collection - level data change notifications | function | no | iOS/Android | yes | + +**Database class** +| Name | Description | Type | Required | Platform | HarmonyOS Support | +| --------------------------------------------- | --------------------------------------------- | -------- | -------- | ----------- | ----------------- | +| get | Retrieve a specific record from the database according to the model class and ID | function | no | iOS/Android | yes | +| localStorage | Get the local storage adapter of the database for key - value pair data | function | no | iOS/Android | yes | +| batch | 创Create batch operations to efficiently perform multiple database writes建批量操作以高效执行多个数据库写入 | function | no | iOS/Android | yes | +| write | Perform database modification operations in a write transaction | function | no | iOS/Android | yes | +| read | Perform database query operations in a read - only transaction | function | no | iOS/Android | yes | +| action | Perform database actions that require read and write permissions | function | no | iOS/Android | yes | +| withChangesForTables | Create an observer that monitors data changes in specified tables | function | no | iOS/Android | yes | +| experimentalSubscribe | Subscribe to database - level change notifications | function | no | iOS/Android | yes | +| unsafeResetDatabase | Completely reset the database and clear all data | function | no | iOS/Android | yes | +| \_ensurelnWtiter | Ensure that the current operation is executed in the write thread | function | no | iOS/Android | yes | +| \_fataError | Handle database fatal errors and terminate the connection | function | no | iOS/Android | yes | + +**Relation class** +| Name | Description | Type | Required | Platform | HarmonyOS Support | +| --------------------------------------------- | --------------------------------------------- | -------- | -------- | ----------- | ----------------- | +| get id | Get the unique identifier of the associated target model | function | no | iOS/Android | yes | +| set id | Set the identifier value of the associated target model | function | no | iOS/Android | yes | +| fetch | Asynchronously obtain the promise of complete data of the associated record | function | no | iOS/Android | yes | +| then | Support Promise chain calls to process asynchronously obtained associated data | function | no | iOS/Android | yes | +| set | Establish an association relationship with another model instance | function | no | iOS/Android | yes | +| observe | Monitor real - time changes in associated model data | function | no | iOS/Android | yes | + +**Model class** +| Name | Description | Type | Required | Platform | HarmonyOS Support | +| --------------------------------------------- | --------------------------------------------- | -------- | -------- | ----------- | ----------------- | +| associations | Define the association relationship mapping between the model and other data tables | function | no | iOS/Android | yes | +| prepareMarkAsDeleted | Mark as deleted status | function | no | iOS/Android | yes | +| \_getChanges | Get the model change observation object | function | no | iOS/Android | yes | +| get id | Get the unique identifier of the instance | function | no | iOS/Android | yes | +| get syncStatus | Get the data synchronization status | function | no | iOS/Android | yes | +| update | Update the model and submit immediately | function | no | iOS/Android | yes | +| prepareUpdate | Prepare for update | function | no | iOS/Android | yes | +| prepareDestroyPermanently | Prepare for permanent destruction without execution | function | no | iOS/Android | yes | +| markAsDeleted | Delete the model and submit | function | no | iOS/Android | yes | +| destroyPermanently | Permanently delete model records | function | no | iOS/Android | yes | +| experimentalMarkAsDeleted | Soft delete function | function | no | iOS/Android | yes | +| experimentalDestroyPermanently | Permanent delete function | function | no | iOS/Android | yes | +| observe | Monitor model data changes | function | no | iOS/Android | yes | +| collection | Get the associated data set | function | no | iOS/Android | yes | +| get collections | Get all collection mappings | function | no | iOS/Android | yes | +| get database | Get the database instance | function | no | iOS/Android | yes | +| get db | Get the database instance | function | no | iOS/Android | yes | +| get asModel | Return its own instance | function | no | iOS/Android | yes | +| callWriter | Perform operations in the write thread | function | no | iOS/Android | yes | +| callReader | Perform queries in the read thread | function | no | iOS/Android | yes | +| subAction | Perform sub - operations in a transaction | function | no | iOS/Android | yes | +| get table | Get the corresponding data table name | function | no | iOS/Android | yes | +| prepareCreate | Prepare to create a model | function | no | iOS/Android | yes | +| \_prepareCreateFromDirtyRaw | Create a model from dirty data | function | no | iOS/Android | yes | +| \_disposableFromDirtyRaw | Create a one - time model | function | no | iOS/Android | yes | +| experimentalSubscribe | Experimental subscription to model changes | function | no | iOS/Android | yes | +| \_notifyChanged | Internally notify model changes | function | no | iOS/Android | yes | +| \_notifyDestroyed | Internally notify model destruction | function | no | iOS/Android | yes | +| \_getRaw | Internally get the original field value | function | no | iOS/Android | yes | +| \_setRaw | Internally set field values and mark changes | function | no | iOS/Android | yes | +| \_dangerouslySetRawWithoutMarkingColumnChange | Internally unsafely set field values | function | no | iOS/Android | yes | +| \_ensureCanSetRaw | Internally ensure that raw values can be set | function | no | iOS/Android | yes | +| \_ensureNotDisposable | Internally ensure non - disposable instances | function | no | iOS/Android | yes | + +**Query class** +| Name | Description | Type | Required | Platform | HarmonyOS Support | +| --------------------------------------------- | --------------------------------------------- | -------- | -------- | ----------- | ----------------- | +| pipe | Perform chain conversion operations on query results | function | no | iOS/Android | yes | +| fetch | Execute the query immediately to get results | function | no | iOS/Android | yes | +| then | Support Promise chain calls for query results | function | no | iOS/Android | yes | +| observe | Monitor real - time changes in query results | function | no | iOS/Android | yes | +| observeWithColumns | Monitor query results of specific field changes | function | no | iOS/Android | yes | +| fetchCount | Get the quantity statistics of query results | function | no | iOS/Android | yes | +| get count | Get the total number of query results | function | no | iOS/Android | yes | +| observeCount | Monitor changes in the number of query results | function | no | iOS/Android | yes | +| fetchIds | Get the ID list of query results | function | no | iOS/Android | yes | +| unsafeFetchRaw | Unsafely get raw query data | function | no | iOS/Android | yes | +| experimentalSubscribe | Experimental query subscription function | function | no | iOS/Android | yes | +| experimentalSubscribeWithColumns | Experimental field change subscription | function | no | iOS/Android | yes | +| experimentalSubscribeToCount | Experimental quantity change subscription | function | no | iOS/Android | yes | +| markAllAsDeleted | Mark all query results as soft deleted | function | no | iOS/Android | yes | +| destroyAllPermanently | Permanently delete all query results | function | no | iOS/Android | yes | +| get modelClass | Get the model class corresponding to the query | function | no | iOS/Android | yes | +| get table | Get the main table name of the query | function | no | iOS/Android | yes | +| get secondaryTables | Get the associated tables involved in the query | function | no | iOS/Android | yes | +| get allTables | Get all tables related to the query | function | no | iOS/Android | yes | +| get associations | Get the association relationships of the query | function | no | iOS/Android | yes | +| serialize | Serialize the query into a transferable format | function | no | iOS/Android | yes | + +**Schema class** +| Name | Description | Type | Required | Platform | HarmonyOS Support | +| --------------------------------------------- | --------------------------------------------- | -------- | -------- | ----------- | ----------------- | +| tableName | Define the name of the database table | function | no | iOS/Android | yes | +| columnName | Define the name of the database field | function | no | iOS/Android | yes | +| appSchema | Define the structural schema of the entire application database | function | no | iOS/Android | yes | +| validateColumnsSchema | Verify the validity of the field schema definition | function | no | iOS/Android | yes | +| tableSchema | Define the structure and field configuration of a single data table | function | no | iOS/Android | yes | + +## Pending Issues + +- [ ] The onFacesDetected capability cannot be implemented. [issue#1](https://github.com/react-native-oh-library/react-native-camera/issues/8) + +## Others + +- Attributes: The notAuthorizedView and pendingAuthorizationView attributes are exclusive to Android. They refer to the UIs displayed when applying for authorization, while HarmonyOS already provides a pop-up window for the authorization process. +- This attribute is exclusive to the Android platform and does not take effect on iOS systems. [Refer to the README file of the source library for details](https://github.com/react-native-camera/react-native-camera/blob/v3.40.0/docs/RNCamera.md#android-ratio) + +## Open Source License + +This project is based on [The MIT License (MIT)](https://github.com/Nozbe/WatermelonDB/blob/master/LICENSE) ,Feel free to enjoy and contribute to the open source project。 -- Gitee From 32d0be0134c622874e3dd11bebf4f186b118dcef Mon Sep 17 00:00:00 2001 From: zhouyong <550468167@qq.com> Date: Mon, 22 Dec 2025 11:56:35 +0800 Subject: [PATCH 09/22] =?UTF-8?q?fix:=20[Issues:=20#IDE3MY]=20=E4=BF=AE?= =?UTF-8?q?=E6=94=B9watermelondb=E7=9A=84=E8=8B=B1=E6=96=87=E6=8C=87?= =?UTF-8?q?=E5=AF=BC=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- en/watermelondb.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/en/watermelondb.md b/en/watermelondb.md index 60ccf9c3d..b4d7413fc 100644 --- a/en/watermelondb.md +++ b/en/watermelondb.md @@ -4,7 +4,11 @@

watermelondb

-Please refer to the Releases page of the third-party library for the matching version information: +This project is developed based on [watermelondb@[v0.28.1-0](https://github.com/Nozbe/WatermelonDB)]. + +Please go to the Releases page of the third-party library to check the matching version information: [@react-native-ohos/watermelondb Releases](Nozbe/WatermelonDB: 🍉 Reactive & asynchronous database for powerful React and React Native apps ⚡️(https://github.com/Nozbe/WatermelonDB)). For older versions that have not been released to npm, please refer to the Installation [Install Guide](/zh-cn/tgz-usage.md)。 + + information: | Third-party library version | Release information | Supported RN version | | ---------- | -------------------------------------------------------------------------------------------------------------------------- | ------------ | | v0.28.1-0 | [@react-native-ohos/watermelondb Releases](https://github.com/) | 0.72/0.77 | @@ -724,7 +728,7 @@ Open `entry/src/main/resources/base/element/string.json`,add: | --------------------------------------------- | --------------------------------------------- | -------- | -------- | ----------- | ----------------- | | get | Retrieve a specific record from the database according to the model class and ID | function | no | iOS/Android | yes | | localStorage | Get the local storage adapter of the database for key - value pair data | function | no | iOS/Android | yes | -| batch | 创Create batch operations to efficiently perform multiple database writes建批量操作以高效执行多个数据库写入 | function | no | iOS/Android | yes | +| batch | Create batch operations to efficiently perform multiple database writes | function | no | iOS/Android | yes | | write | Perform database modification operations in a write transaction | function | no | iOS/Android | yes | | read | Perform database query operations in a read - only transaction | function | no | iOS/Android | yes | | action | Perform database actions that require read and write permissions | function | no | iOS/Android | yes | -- Gitee From e4a173a083d1de0b6c7a18e4c39c591922571bf6 Mon Sep 17 00:00:00 2001 From: zhouyong <550468167@qq.com> Date: Mon, 22 Dec 2025 14:17:31 +0800 Subject: [PATCH 10/22] =?UTF-8?q?fix:=20[Issues:=20#IDE3MY]=20=E4=BF=AE?= =?UTF-8?q?=E6=94=B9watermelondb=E7=9A=84=E6=8C=87=E5=AF=BC=E6=96=87?= =?UTF-8?q?=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- en/watermelondb.md | 13 ++++--------- zh-cn/watermelondb.md | 13 ++++--------- 2 files changed, 8 insertions(+), 18 deletions(-) diff --git a/en/watermelondb.md b/en/watermelondb.md index b4d7413fc..d19e603ef 100644 --- a/en/watermelondb.md +++ b/en/watermelondb.md @@ -591,7 +591,7 @@ add_subdirectory("${RNOH_CPP_DIR}" ./rn) file(GLOB GENERATED_CPP_FILES "${CMAKE_CURRENT_SOURCE_DIR}/generated/*.cpp") # this line is needed by codegen v1 -add_subdirectory("${OH_MODULES}/@react-native-ohos/watermelondb/src/main/cpp" ./watermelondb) ++ add_subdirectory("${OH_MODULES}/@react-native-ohos/watermelondb/src/main/cpp" ./watermelondb) @@ -603,7 +603,7 @@ add_library(rnoh_app SHARED target_link_libraries(rnoh_app PUBLIC rnoh) # RNOH_BEGIN: manual_package_linking_2 -target_link_libraries(rnoh_app PUBLIC watermelondb) ++ target_link_libraries(rnoh_app PUBLIC watermelondb) # RNOH_END: manual_package_linking_2 @@ -614,7 +614,7 @@ open `entry/src/main/cpp/PackageProvider.cpp`,add: ```diff #include "RNOH/PackageProvider.h" #include "generated/RNOHGeneratedPackage.h" - #include "WatermelondbPackage.h" ++ #include "WatermelondbPackage.h" using namespace rnoh; @@ -622,7 +622,7 @@ std::vector> PackageProvider::getPackages( Package::Context ctx) { return { std::make_shared(ctx), // generated by codegen v1 - std::make_shared(ctx) ++ std::make_shared(ctx) }; } ``` @@ -821,13 +821,8 @@ Open `entry/src/main/resources/base/element/string.json`,add: ## Pending Issues -- [ ] The onFacesDetected capability cannot be implemented. [issue#1](https://github.com/react-native-oh-library/react-native-camera/issues/8) - ## Others -- Attributes: The notAuthorizedView and pendingAuthorizationView attributes are exclusive to Android. They refer to the UIs displayed when applying for authorization, while HarmonyOS already provides a pop-up window for the authorization process. -- This attribute is exclusive to the Android platform and does not take effect on iOS systems. [Refer to the README file of the source library for details](https://github.com/react-native-camera/react-native-camera/blob/v3.40.0/docs/RNCamera.md#android-ratio) - ## Open Source License This project is based on [The MIT License (MIT)](https://github.com/Nozbe/WatermelonDB/blob/master/LICENSE) ,Feel free to enjoy and contribute to the open source project。 diff --git a/zh-cn/watermelondb.md b/zh-cn/watermelondb.md index 4116c094f..0853c895e 100644 --- a/zh-cn/watermelondb.md +++ b/zh-cn/watermelondb.md @@ -597,7 +597,7 @@ add_subdirectory("${RNOH_CPP_DIR}" ./rn) file(GLOB GENERATED_CPP_FILES "${CMAKE_CURRENT_SOURCE_DIR}/generated/*.cpp") # this line is needed by codegen v1 -add_subdirectory("${OH_MODULES}/@react-native-ohos/watermelondb/src/main/cpp" ./watermelondb) ++ add_subdirectory("${OH_MODULES}/@react-native-ohos/watermelondb/src/main/cpp" ./watermelondb) @@ -609,7 +609,7 @@ add_library(rnoh_app SHARED target_link_libraries(rnoh_app PUBLIC rnoh) # RNOH_BEGIN: manual_package_linking_2 -target_link_libraries(rnoh_app PUBLIC watermelondb) ++ target_link_libraries(rnoh_app PUBLIC watermelondb) # RNOH_END: manual_package_linking_2 @@ -620,7 +620,7 @@ target_link_libraries(rnoh_app PUBLIC watermelondb) ```diff #include "RNOH/PackageProvider.h" #include "generated/RNOHGeneratedPackage.h" - #include "WatermelondbPackage.h" ++ #include "WatermelondbPackage.h" using namespace rnoh; @@ -628,7 +628,7 @@ std::vector> PackageProvider::getPackages( Package::Context ctx) { return { std::make_shared(ctx), // generated by codegen v1 - std::make_shared(ctx) ++ std::make_shared(ctx) }; } ``` @@ -829,13 +829,8 @@ ohpm install ## 5.遗留问题 -- [ ] onFacesDetected 能力无法实现 [issue#1](https://github.com/react-native-oh-library/react-native-camera/issues/8) - ## 6.其他 -- notAuthorizedView 属性,pendingAuthorizationView 属性是 android 独有的属性,申请授权时显示的 UI,harmony 已经提供授权时的弹窗。 -- ratio 是 android 独有的属性,在 ios 无效果 [源库 readme 位置](https://github.com/react-native-camera/react-native-camera/blob/v3.40.0/docs/RNCamera.md#android-ratio) - ## 7.开源协议 本项目基于 [MIT License (MIT)](https://github.com/Nozbe/WatermelonDB/blob/master/LICENSE) ,请自由地享受和参与开源。 -- Gitee From 57fb25721a140f6b2e67b5489a26ec3cd8ced5e3 Mon Sep 17 00:00:00 2001 From: zhouyong <550468167@qq.com> Date: Mon, 22 Dec 2025 14:46:49 +0800 Subject: [PATCH 11/22] =?UTF-8?q?fix:=20[Issues:=20#IDE3MY]=20=E4=BF=AE?= =?UTF-8?q?=E6=94=B9watermelondb=E7=9A=84=E6=8C=87=E5=AF=BC=E6=96=87?= =?UTF-8?q?=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- en/watermelondb.md | 2 +- zh-cn/watermelondb.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/en/watermelondb.md b/en/watermelondb.md index d19e603ef..7555b0b4a 100644 --- a/en/watermelondb.md +++ b/en/watermelondb.md @@ -546,7 +546,7 @@ Open the entry/oh-package.json5 file and append the following dependencies: ```json "dependencies": { "@rnoh/react-native-openharmony": "0.72.32", - "@react-native-ohos/watermelondb": "file:../../../harmony/watermelondb" + "@react-native-ohos/watermelondb": "file:../../node_modules/@react-native-ohos/watermelondb/harmony/watermelondb.har" } ``` diff --git a/zh-cn/watermelondb.md b/zh-cn/watermelondb.md index 0853c895e..acfe46e34 100644 --- a/zh-cn/watermelondb.md +++ b/zh-cn/watermelondb.md @@ -552,7 +552,7 @@ const styles = StyleSheet.create({ ```json "dependencies": { "@rnoh/react-native-openharmony": "0.72.32", - "@react-native-ohos/watermelondb": "file:../../../harmony/watermelondb" + "@react-native-ohos/watermelondb": "file:../../node_modules/@react-native-ohos/watermelondb/harmony/watermelondb.har" } ``` -- Gitee From ccff5646e8a29ff11027934a7ac7fd719ce3f555 Mon Sep 17 00:00:00 2001 From: lqq <1960002614@qq.com> Date: Mon, 22 Dec 2025 15:21:22 +0800 Subject: [PATCH 12/22] =?UTF-8?q?fix:=20[Issues:=20#IDE3MY]=20=E4=BF=AE?= =?UTF-8?q?=E6=94=B9watermelondb=E7=9A=84=E6=8C=87=E5=AF=BC=E6=96=87?= =?UTF-8?q?=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- en/watermelondb.md | 22 +++++++++++----------- zh-cn/watermelondb.md | 22 +++++++++++----------- 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/en/watermelondb.md b/en/watermelondb.md index 7555b0b4a..f97e2cd60 100644 --- a/en/watermelondb.md +++ b/en/watermelondb.md @@ -726,12 +726,12 @@ Open `entry/src/main/resources/base/element/string.json`,add: **Database class** | Name | Description | Type | Required | Platform | HarmonyOS Support | | --------------------------------------------- | --------------------------------------------- | -------- | -------- | ----------- | ----------------- | -| get | Retrieve a specific record from the database according to the model class and ID | function | no | iOS/Android | yes | +| get | Retrieve a specific record from the database according to the model class and ID | function | no | iOS/Android | yes | | localStorage | Get the local storage adapter of the database for key - value pair data | function | no | iOS/Android | yes | | batch | Create batch operations to efficiently perform multiple database writes | function | no | iOS/Android | yes | -| write | Perform database modification operations in a write transaction | function | no | iOS/Android | yes | -| read | Perform database query operations in a read - only transaction | function | no | iOS/Android | yes | -| action | Perform database actions that require read and write permissions | function | no | iOS/Android | yes | +| write | Perform database modification operations in a write transaction | function | no | iOS/Android | yes | +| read | Perform database query operations in a read - only transaction | function | no | iOS/Android | yes | +| action | Perform database actions that require read and write permissions | function | no | iOS/Android | yes | | withChangesForTables | Create an observer that monitors data changes in specified tables | function | no | iOS/Android | yes | | experimentalSubscribe | Subscribe to database - level change notifications | function | no | iOS/Android | yes | | unsafeResetDatabase | Completely reset the database and clear all data | function | no | iOS/Android | yes | @@ -744,7 +744,7 @@ Open `entry/src/main/resources/base/element/string.json`,add: | get id | Get the unique identifier of the associated target model | function | no | iOS/Android | yes | | set id | Set the identifier value of the associated target model | function | no | iOS/Android | yes | | fetch | Asynchronously obtain the promise of complete data of the associated record | function | no | iOS/Android | yes | -| then | Support Promise chain calls to process asynchronously obtained associated data | function | no | iOS/Android | yes | +| then | Support Promise chain calls to process asynchronously obtained associated data | function | no | iOS/Android | yes | | set | Establish an association relationship with another model instance | function | no | iOS/Android | yes | | observe | Monitor real - time changes in associated model data | function | no | iOS/Android | yes | @@ -769,9 +769,9 @@ Open `entry/src/main/resources/base/element/string.json`,add: | get database | Get the database instance | function | no | iOS/Android | yes | | get db | Get the database instance | function | no | iOS/Android | yes | | get asModel | Return its own instance | function | no | iOS/Android | yes | -| callWriter | Perform operations in the write thread | function | no | iOS/Android | yes | -| callReader | Perform queries in the read thread | function | no | iOS/Android | yes | -| subAction | Perform sub - operations in a transaction | function | no | iOS/Android | yes | +| callWriter | Perform operations in the write thread | function | no | iOS/Android | yes | +| callReader | Perform queries in the read thread | function | no | iOS/Android | yes | +| subAction | Perform sub - operations in a transaction | function | no | iOS/Android | yes | | get table | Get the corresponding data table name | function | no | iOS/Android | yes | | prepareCreate | Prepare to create a model | function | no | iOS/Android | yes | | \_prepareCreateFromDirtyRaw | Create a model from dirty data | function | no | iOS/Android | yes | @@ -788,9 +788,9 @@ Open `entry/src/main/resources/base/element/string.json`,add: **Query class** | Name | Description | Type | Required | Platform | HarmonyOS Support | | --------------------------------------------- | --------------------------------------------- | -------- | -------- | ----------- | ----------------- | -| pipe | Perform chain conversion operations on query results | function | no | iOS/Android | yes | +| pipe | Perform chain conversion operations on query results | function | no | iOS/Android | yes | | fetch | Execute the query immediately to get results | function | no | iOS/Android | yes | -| then | Support Promise chain calls for query results | function | no | iOS/Android | yes | +| then | Support Promise chain calls for query results | function | no | iOS/Android | yes | | observe | Monitor real - time changes in query results | function | no | iOS/Android | yes | | observeWithColumns | Monitor query results of specific field changes | function | no | iOS/Android | yes | | fetchCount | Get the quantity statistics of query results | function | no | iOS/Android | yes | @@ -813,7 +813,7 @@ Open `entry/src/main/resources/base/element/string.json`,add: **Schema class** | Name | Description | Type | Required | Platform | HarmonyOS Support | | --------------------------------------------- | --------------------------------------------- | -------- | -------- | ----------- | ----------------- | -| tableName | Define the name of the database table | function | no | iOS/Android | yes | +| tableName | Define the name of the database table | function | no | iOS/Android | yes | | columnName | Define the name of the database field | function | no | iOS/Android | yes | | appSchema | Define the structural schema of the entire application database | function | no | iOS/Android | yes | | validateColumnsSchema | Verify the validity of the field schema definition | function | no | iOS/Android | yes | diff --git a/zh-cn/watermelondb.md b/zh-cn/watermelondb.md index acfe46e34..65629777c 100644 --- a/zh-cn/watermelondb.md +++ b/zh-cn/watermelondb.md @@ -734,12 +734,12 @@ ohpm install **Database 类** | Name | Description | Type | Required | Platform | HarmonyOS Support | | --------------------------------------------- | --------------------------------------------- | -------- | -------- | ----------- | ----------------- | -| get | 根据模型类型和 ID 从数据库获取特定记录 | function | no | iOS/Android | yes | +| get | 根据模型类型和 ID 从数据库获取特定记录 | function | no | iOS/Android | yes | | localStorage | 获取数据库的本地存储适配器用于键值对数据 | function | no | iOS/Android | yes | | batch | 创建批量操作以高效执行多个数据库写入 | function | no | iOS/Android | yes | -| write | 在写入事务中执行数据库修改操作 | function | no | iOS/Android | yes | -| read | 在只读事务中执行数据库查询操作 | function | no | iOS/Android | yes | -| action | 执行需要读写权限的数据库动作 | function | no | iOS/Android | yes | +| write | 在写入事务中执行数据库修改操作 | function | no | iOS/Android | yes | +| read | 在只读事务中执行数据库查询操作 | function | no | iOS/Android | yes | +| action | 执行需要读写权限的数据库动作 | function | no | iOS/Android | yes | | withChangesForTables | 创建监听指定表数据变更的观察者 | function | no | iOS/Android | yes | | experimentalSubscribe | 订阅数据库级别的变更通知 | function | no | iOS/Android | yes | | unsafeResetDatabase | 完全重置数据库并清除所有数据 | function | no | iOS/Android | yes | @@ -752,7 +752,7 @@ ohpm install | get id | 获取关联目标模型的唯一标识符 | function | no | iOS/Android | yes | | set id | 设置关联目标模型的标识符值 | function | no | iOS/Android | yes | | fetch | 异步获取关联记录的完整数据承诺 | function | no | iOS/Android | yes | -| then | 支持 Promise 链式调用,处理异步获取的关联数据 | function | no | iOS/Android | yes | +| then | 支持 Promise 链式调用,处理异步获取的关联数据 | function | no | iOS/Android | yes | | set | 建立与另一个模型实例的关联关系 | function | no | iOS/Android | yes | | observe | 监听关联模型数据的实时变化 | function | no | iOS/Android | yes | @@ -777,9 +777,9 @@ ohpm install | get database | 获取数据库实例 | function | no | iOS/Android | yes | | get db | 获取数据库实例 | function | no | iOS/Android | yes | | get asModel | 返回自身实例 | function | no | iOS/Android | yes | -| callWriter | 在写入线程执行操作 | function | no | iOS/Android | yes | -| callReader | 在读取线程执行查询 | function | no | iOS/Android | yes | -| subAction | 在事务中执行子操作 | function | no | iOS/Android | yes | +| callWriter | 在写入线程执行操作 | function | no | iOS/Android | yes | +| callReader | 在读取线程执行查询 | function | no | iOS/Android | yes | +| subAction | 在事务中执行子操作 | function | no | iOS/Android | yes | | get table | 获取对应数据表名 | function | no | iOS/Android | yes | | prepareCreate | 准备创建模型 | function | no | iOS/Android | yes | | \_prepareCreateFromDirtyRaw | 从脏数据创建模型 | function | no | iOS/Android | yes | @@ -796,9 +796,9 @@ ohpm install **Query 类** | Name | Description | Type | Required | Platform | HarmonyOS Support | | --------------------------------------------- | --------------------------------------------- | -------- | -------- | ----------- | ----------------- | -| pipe | 对查询结果进行链式转换操作 | function | no | iOS/Android | yes | +| pipe | 对查询结果进行链式转换操作 | function | no | iOS/Android | yes | | fetch | 立即执行查询获取结果 | function | no | iOS/Android | yes | -| then | 支持 Promise 链式调用查询结果 | function | no | iOS/Android | yes | +| then | 支持 Promise 链式调用查询结果 | function | no | iOS/Android | yes | | observe | 监听查询结果的实时变化 | function | no | iOS/Android | yes | | observeWithColumns | 监听特定字段变化的查询结果 | function | no | iOS/Android | yes | | fetchCount | 获取查询结果的数量统计 | function | no | iOS/Android | yes | @@ -821,7 +821,7 @@ ohpm install **Schema 类** | Name | Description | Type | Required | Platform | HarmonyOS Support | | --------------------------------------------- | --------------------------------------------- | -------- | -------- | ----------- | ----------------- | -| tableName | 定义数据库表的名称 | function | no | iOS/Android | yes | +| tableName | 定义数据库表的名称 | function | no | iOS/Android | yes | | columnName | 定义数据库字段的名称 | function | no | iOS/Android | yes | | appSchema | 定义整个应用数据库的结构模式 | function | no | iOS/Android | yes | | validateColumnsSchema | 验证字段模式定义的合法性 | function | no | iOS/Android | yes | -- Gitee From 50eedc5db256ee3e3ec7fca8e77daac8b00f1bc3 Mon Sep 17 00:00:00 2001 From: zhouyong <550468167@qq.com> Date: Mon, 22 Dec 2025 15:57:23 +0800 Subject: [PATCH 13/22] =?UTF-8?q?fix:=20[Issues:=20#IDE3MY]=20=E4=BF=AE?= =?UTF-8?q?=E6=94=B9watermelondb=E7=9A=84=E6=8C=87=E5=AF=BC=E6=96=87?= =?UTF-8?q?=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- en/watermelondb.md | 22 +++++++++++----------- zh-cn/watermelondb.md | 22 +++++++++++----------- 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/en/watermelondb.md b/en/watermelondb.md index f97e2cd60..e82239a35 100644 --- a/en/watermelondb.md +++ b/en/watermelondb.md @@ -726,12 +726,12 @@ Open `entry/src/main/resources/base/element/string.json`,add: **Database class** | Name | Description | Type | Required | Platform | HarmonyOS Support | | --------------------------------------------- | --------------------------------------------- | -------- | -------- | ----------- | ----------------- | -| get | Retrieve a specific record from the database according to the model class and ID | function | no | iOS/Android | yes | +| get<T extends Model> | Retrieve a specific record from the database according to the model class and ID | function | no | iOS/Android | yes | | localStorage | Get the local storage adapter of the database for key - value pair data | function | no | iOS/Android | yes | | batch | Create batch operations to efficiently perform multiple database writes | function | no | iOS/Android | yes | -| write | Perform database modification operations in a write transaction | function | no | iOS/Android | yes | -| read | Perform database query operations in a read - only transaction | function | no | iOS/Android | yes | -| action | Perform database actions that require read and write permissions | function | no | iOS/Android | yes | +| write<T> | Perform database modification operations in a write transaction | function | no | iOS/Android | yes | +| read<T> | Perform database query operations in a read - only transaction | function | no | iOS/Android | yes | +| action<T> | Perform database actions that require read and write permissions | function | no | iOS/Android | yes | | withChangesForTables | Create an observer that monitors data changes in specified tables | function | no | iOS/Android | yes | | experimentalSubscribe | Subscribe to database - level change notifications | function | no | iOS/Android | yes | | unsafeResetDatabase | Completely reset the database and clear all data | function | no | iOS/Android | yes | @@ -744,7 +744,7 @@ Open `entry/src/main/resources/base/element/string.json`,add: | get id | Get the unique identifier of the associated target model | function | no | iOS/Android | yes | | set id | Set the identifier value of the associated target model | function | no | iOS/Android | yes | | fetch | Asynchronously obtain the promise of complete data of the associated record | function | no | iOS/Android | yes | -| then | Support Promise chain calls to process asynchronously obtained associated data | function | no | iOS/Android | yes | +| then<U> | Support Promise chain calls to process asynchronously obtained associated data | function | no | iOS/Android | yes | | set | Establish an association relationship with another model instance | function | no | iOS/Android | yes | | observe | Monitor real - time changes in associated model data | function | no | iOS/Android | yes | @@ -769,9 +769,9 @@ Open `entry/src/main/resources/base/element/string.json`,add: | get database | Get the database instance | function | no | iOS/Android | yes | | get db | Get the database instance | function | no | iOS/Android | yes | | get asModel | Return its own instance | function | no | iOS/Android | yes | -| callWriter | Perform operations in the write thread | function | no | iOS/Android | yes | -| callReader | Perform queries in the read thread | function | no | iOS/Android | yes | -| subAction | Perform sub - operations in a transaction | function | no | iOS/Android | yes | +| callWriter<T> | Perform operations in the write thread | function | no | iOS/Android | yes | +| callReader<T> | Perform queries in the read thread | function | no | iOS/Android | yes | +| subAction<T> | Perform sub - operations in a transaction | function | no | iOS/Android | yes | | get table | Get the corresponding data table name | function | no | iOS/Android | yes | | prepareCreate | Prepare to create a model | function | no | iOS/Android | yes | | \_prepareCreateFromDirtyRaw | Create a model from dirty data | function | no | iOS/Android | yes | @@ -788,9 +788,9 @@ Open `entry/src/main/resources/base/element/string.json`,add: **Query class** | Name | Description | Type | Required | Platform | HarmonyOS Support | | --------------------------------------------- | --------------------------------------------- | -------- | -------- | ----------- | ----------------- | -| pipe | Perform chain conversion operations on query results | function | no | iOS/Android | yes | +| pipe<T> | Perform chain conversion operations on query results | function | no | iOS/Android | yes | | fetch | Execute the query immediately to get results | function | no | iOS/Android | yes | -| then | Support Promise chain calls for query results | function | no | iOS/Android | yes | +| then<U> | Support Promise chain calls for query results | function | no | iOS/Android | yes | | observe | Monitor real - time changes in query results | function | no | iOS/Android | yes | | observeWithColumns | Monitor query results of specific field changes | function | no | iOS/Android | yes | | fetchCount | Get the quantity statistics of query results | function | no | iOS/Android | yes | @@ -813,7 +813,7 @@ Open `entry/src/main/resources/base/element/string.json`,add: **Schema class** | Name | Description | Type | Required | Platform | HarmonyOS Support | | --------------------------------------------- | --------------------------------------------- | -------- | -------- | ----------- | ----------------- | -| tableName | Define the name of the database table | function | no | iOS/Android | yes | +| tableName<T extends Model> | Define the name of the database table | function | no | iOS/Android | yes | | columnName | Define the name of the database field | function | no | iOS/Android | yes | | appSchema | Define the structural schema of the entire application database | function | no | iOS/Android | yes | | validateColumnsSchema | Verify the validity of the field schema definition | function | no | iOS/Android | yes | diff --git a/zh-cn/watermelondb.md b/zh-cn/watermelondb.md index 65629777c..b6b5a2a1c 100644 --- a/zh-cn/watermelondb.md +++ b/zh-cn/watermelondb.md @@ -734,12 +734,12 @@ ohpm install **Database 类** | Name | Description | Type | Required | Platform | HarmonyOS Support | | --------------------------------------------- | --------------------------------------------- | -------- | -------- | ----------- | ----------------- | -| get | 根据模型类型和 ID 从数据库获取特定记录 | function | no | iOS/Android | yes | +| get<T extends Model> | 根据模型类型和 ID 从数据库获取特定记录 | function | no | iOS/Android | yes | | localStorage | 获取数据库的本地存储适配器用于键值对数据 | function | no | iOS/Android | yes | | batch | 创建批量操作以高效执行多个数据库写入 | function | no | iOS/Android | yes | -| write | 在写入事务中执行数据库修改操作 | function | no | iOS/Android | yes | -| read | 在只读事务中执行数据库查询操作 | function | no | iOS/Android | yes | -| action | 执行需要读写权限的数据库动作 | function | no | iOS/Android | yes | +| write<T> | 在写入事务中执行数据库修改操作 | function | no | iOS/Android | yes | +| read<T> | 在只读事务中执行数据库查询操作 | function | no | iOS/Android | yes | +| action<T> | 执行需要读写权限的数据库动作 | function | no | iOS/Android | yes | | withChangesForTables | 创建监听指定表数据变更的观察者 | function | no | iOS/Android | yes | | experimentalSubscribe | 订阅数据库级别的变更通知 | function | no | iOS/Android | yes | | unsafeResetDatabase | 完全重置数据库并清除所有数据 | function | no | iOS/Android | yes | @@ -752,7 +752,7 @@ ohpm install | get id | 获取关联目标模型的唯一标识符 | function | no | iOS/Android | yes | | set id | 设置关联目标模型的标识符值 | function | no | iOS/Android | yes | | fetch | 异步获取关联记录的完整数据承诺 | function | no | iOS/Android | yes | -| then | 支持 Promise 链式调用,处理异步获取的关联数据 | function | no | iOS/Android | yes | +| then<U> | 支持 Promise 链式调用,处理异步获取的关联数据 | function | no | iOS/Android | yes | | set | 建立与另一个模型实例的关联关系 | function | no | iOS/Android | yes | | observe | 监听关联模型数据的实时变化 | function | no | iOS/Android | yes | @@ -777,9 +777,9 @@ ohpm install | get database | 获取数据库实例 | function | no | iOS/Android | yes | | get db | 获取数据库实例 | function | no | iOS/Android | yes | | get asModel | 返回自身实例 | function | no | iOS/Android | yes | -| callWriter | 在写入线程执行操作 | function | no | iOS/Android | yes | -| callReader | 在读取线程执行查询 | function | no | iOS/Android | yes | -| subAction | 在事务中执行子操作 | function | no | iOS/Android | yes | +| callWriter<T> | 在写入线程执行操作 | function | no | iOS/Android | yes | +| callReader<T> | 在读取线程执行查询 | function | no | iOS/Android | yes | +| subAction<T> | 在事务中执行子操作 | function | no | iOS/Android | yes | | get table | 获取对应数据表名 | function | no | iOS/Android | yes | | prepareCreate | 准备创建模型 | function | no | iOS/Android | yes | | \_prepareCreateFromDirtyRaw | 从脏数据创建模型 | function | no | iOS/Android | yes | @@ -796,9 +796,9 @@ ohpm install **Query 类** | Name | Description | Type | Required | Platform | HarmonyOS Support | | --------------------------------------------- | --------------------------------------------- | -------- | -------- | ----------- | ----------------- | -| pipe | 对查询结果进行链式转换操作 | function | no | iOS/Android | yes | +| pipe<T> | 对查询结果进行链式转换操作 | function | no | iOS/Android | yes | | fetch | 立即执行查询获取结果 | function | no | iOS/Android | yes | -| then | 支持 Promise 链式调用查询结果 | function | no | iOS/Android | yes | +| then<U> | 支持 Promise 链式调用查询结果 | function | no | iOS/Android | yes | | observe | 监听查询结果的实时变化 | function | no | iOS/Android | yes | | observeWithColumns | 监听特定字段变化的查询结果 | function | no | iOS/Android | yes | | fetchCount | 获取查询结果的数量统计 | function | no | iOS/Android | yes | @@ -821,7 +821,7 @@ ohpm install **Schema 类** | Name | Description | Type | Required | Platform | HarmonyOS Support | | --------------------------------------------- | --------------------------------------------- | -------- | -------- | ----------- | ----------------- | -| tableName | 定义数据库表的名称 | function | no | iOS/Android | yes | +| tableName<T extends Model> | 定义数据库表的名称 | function | no | iOS/Android | yes | | columnName | 定义数据库字段的名称 | function | no | iOS/Android | yes | | appSchema | 定义整个应用数据库的结构模式 | function | no | iOS/Android | yes | | validateColumnsSchema | 验证字段模式定义的合法性 | function | no | iOS/Android | yes | -- Gitee From 0f781fd1fb2789a6c19d035caccf44f2b46f1734 Mon Sep 17 00:00:00 2001 From: zhouyong <550468167@qq.com> Date: Tue, 23 Dec 2025 13:53:10 +0800 Subject: [PATCH 14/22] =?UTF-8?q?fix:=20[Issues:=20#IDE3MY]=20=E4=BF=AE?= =?UTF-8?q?=E6=94=B9watermelondb=E7=9A=84=E6=8C=87=E5=AF=BC=E6=96=87?= =?UTF-8?q?=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- en/watermelondb.md | 713 +++++++++++++++-------------------------- zh-cn/watermelondb.md | 717 +++++++++++++++--------------------------- 2 files changed, 520 insertions(+), 910 deletions(-) diff --git a/en/watermelondb.md b/en/watermelondb.md index e82239a35..e8c4a0861 100644 --- a/en/watermelondb.md +++ b/en/watermelondb.md @@ -6,7 +6,7 @@ This project is developed based on [watermelondb@[v0.28.1-0](https://github.com/Nozbe/WatermelonDB)]. -Please go to the Releases page of the third-party library to check the matching version information: [@react-native-ohos/watermelondb Releases](Nozbe/WatermelonDB: 🍉 Reactive & asynchronous database for powerful React and React Native apps ⚡️(https://github.com/Nozbe/WatermelonDB)). For older versions that have not been released to npm, please refer to the Installation [Install Guide](/zh-cn/tgz-usage.md)。 +Please go to the Releases page of the third-party library to check the matching version information: ([@react-native-ohos/watermelondb Releases](https://gitcode.com/OpenHarmony-RN/rntpc_watermelondb)). For older versions that have not been released to npm, please refer to the Installation [Install Guide](/zh-cn/tgz-usage.md)。 information: | Third-party library version | Release information | Supported RN version | @@ -34,487 +34,305 @@ The following code demonstrates the basic usage scenarios of this library: > [!WARNING] The library name imported remains unchanged during usage. ### watermelondb example ```js -import React, { useCallback, useEffect, useMemo, useState } from 'react'; +import React, { useState } from 'react'; +import { View, Text, Button, StyleSheet, ScrollView } from 'react-native'; import { - Alert, - FlatList, - Pressable, - SafeAreaView, - ScrollView, - StyleSheet, - Text, - View, -} from 'react-native'; -import { Database } from '@react-native-ohos/watermelondb'; -import SQLiteAdapter from '@react-native-ohos/watermelondb/adapters/sqlite'; -import { DatabaseProvider, useDatabase } from '@react-native-ohos/watermelondb/react'; -import { mySchema } from './models/schema'; -import { dbModels } from './models/index.js'; - -const adapter = new SQLiteAdapter({ - dbName: 'WatermelonRelationDemo', - schema: mySchema, - jsi: false, - log: (sql) => console.log('SQL执行:', sql), - onSetUpError: (error) => { - console.error('[WatermelonDemo*] 初始化数据库失败', error); - }, -}); + appSchema, + tableSchema, + tableName, + columnName, + validateColumnSchema, +} from '@nozbe/watermelondb/Schema'; +import { Database } from '@nozbe/watermelondb'; +import SQLiteAdapter from '@nozbe/watermelondb/adapters/sqlite'; +import { schemaMigrations } from '@nozbe/watermelondb/Schema/migrations'; + +const userTable = tableName('users'); + +const userColumns = { + name: columnName('name'), + email: columnName('email'), + age: columnName('age'), +}; -const database = new Database({ - adapter, - modelClasses: dbModels, +const userTableSchema = tableSchema({ + name: userTable, + columns: [ + { name: userColumns.name, type: 'string' }, + { name: userColumns.email, type: 'string', isIndexed: true }, + { name: userColumns.age, type: 'number', isOptional: true }, + ], }); -const SAMPLE_MOVIES = [ - { - title: '星际穿越', - posterImage: - 'https://image.tmdb.org/t/p/original/rAiYTfKGqDCRIIqo664sY9XZIvQ.jpg', - genre: '科幻', - description: '一段跨越宇宙与时间、寻找新家园的旅程。', - }, - { - title: '疯狂动物城', - posterImage: - 'https://image.tmdb.org/t/p/original/hlK0e0wAQ3VLuJcsfIYPvb4JVud.jpg', - genre: '动画', - description: '一只兔子警官与狐狸搭档,揭开阴谋、守护城市。', - }, - { - title: '速度与激情 10', - posterImage: - 'https://image.tmdb.org/t/p/original/qDRGPAcQoW8Wuig9bvoLpHwf1gU.jpg', - genre: '动作', - description: '家人永远是第一位,飙车、爆炸、热血永不停歇。', - }, -]; - -const SAMPLE_REVIEWS = [ - '剧情紧凑,完全停不下来!', - '配乐太棒了,影院体验绝佳。', - '主角魅力满分,期待续作。', - '画面惊艳,值得二刷三刷。', - '故事内核很温暖,看完心情很好。', -]; - -const randomItem = (items) => items[Math.floor(Math.random() * items.length)]; - -const formatDate = (value) => { - if (!value) { - return '未知'; - } - try { - const date = value instanceof Date ? value : new Date(value); - return date.toISOString().slice(0, 10); - } catch (error) { - return '未知'; - } +const userInfoTable = tableName('user_info'); +const userInfoColumns = { + userId: columnName('user_id'), + address: columnName('address'), + phone: columnName('phone'), }; +const userInfoTableSchema = tableSchema({ + name: userInfoTable, + columns: [ + { name: userInfoColumns.userId, type: 'string', isIndexed: true }, + { name: userInfoColumns.address, type: 'string' }, + { name: userInfoColumns.phone, type: 'string', isOptional: true }, + ], +}); -const ActionButton = ({ label, onPress, type = 'default' }) => { - const background = - type === 'danger' - ? styles.dangerButton - : type === 'secondary' - ? styles.secondaryButton - : styles.primaryButton; - return ( - - {label} - - ); -}; +const orderTable = tableName('order'); +const orderTableSchema = tableSchema({ + name: orderTable, + columns: [ + { name: columnName('order_no'), type: 'string', isIndexed: true }, + { name: columnName('user_id'), type: 'string', isIndexed: true }, + { name: columnName('amount'), type: 'number' }, + { name: columnName('status'), type: 'string', defaultValue: 'pending' }, + ], +}); -const MovieCard = ({ movie, onAddReview, onRename, onDelete }) => { - const [reviews, setReviews] = useState([]); - const [movieInfo, setMovieInfo] = useState(movie.getMovie()); - - useEffect(() => { - const subscription = movie.reviews.observe().subscribe({ - next: (list) => setReviews(list), - error: (error) => console.warn('订阅评论失败', error), - }); - return () => subscription.unsubscribe(); - }, [movie]); - - // 订阅 movie 对象的变化,以便在更新时刷新 UI - useEffect(() => { - const subscription = movie.observe().subscribe({ - next: (updatedMovie) => { - const newInfo = updatedMovie.getMovie(); - console.log('%c watermelondbConsoleLogger movie card updated:', 'color: #0e93e0;background: #aaefe5;', { - id: updatedMovie.id, - newTitle: newInfo.title, - }); - setMovieInfo(newInfo); - }, - error: (error) => console.warn('订阅电影变化失败', error), - }); - return () => subscription.unsubscribe(); - }, [movie]); - - const info = movieInfo; +const productTable = tableName('product'); +const productTableSchema = tableSchema({ + name: productTable, + columns: [ + { name: columnName('product_name'), type: 'string' }, + { name: columnName('price'), type: 'number' }, + { name: columnName('stock'), type: 'number', defaultValue: 0 }, + ], +}); - return ( - - {info.title} - {info.genre} - {info.description} - 上映日期:{formatDate(info.releaseDateAt)} - 短评:{reviews.length} 条 - {reviews.slice(0, 2).map((review) => ( - - · {review.body} - - ))} - {reviews.length > 2 ? ( - · …… - ) : null} - - onAddReview(movie)} /> - onRename(movie)} /> - onDelete(movie)} /> - - - ); +const tableSchemaMap = { + tableName: userTableSchema, + columnName: userInfoTableSchema, + tableSchema: orderTableSchema, + appSchema: productTableSchema, }; -const MovieScreen = () => { - const databaseInstance = useDatabase(); - const moviesCollection = useMemo( - () => databaseInstance.collections.get('movies'), - [databaseInstance], - ); - const [movies, setMovies] = useState([]); - const [loading, setLoading] = useState(true); +const getAppDatabaseSchema = (tableSchema) => { + return appSchema({ + version: 1, + tables: [tableSchema], + }); +}; - useEffect(() => { - const subscription = moviesCollection.query().observe().subscribe({ - next: (list) => { - console.log('%c watermelondbConsoleLogger movies list updated:', 'color: #0e93e0;background: #aaefe5;', { - count: list.length, - titles: list.map(m => m.title), - }); - setMovies(list); - setLoading(false); - }, - error: (error) => console.error('订阅电影列表失败', error), - }); - return () => subscription.unsubscribe(); - }, [moviesCollection]); - - const seedDemoData = useCallback(async () => { - console.log( - '%c watermelondbConsoleLogger moviesCollection:', - 'color: #0e93e0;background: #aaefe5;', - moviesCollection, - ); - const current = await moviesCollection.query().fetch(); - console.log( - '%c watermelondbConsoleLogger current:', - 'color: #0e93e0;background: #aaefe5;', - current, - ); - if (current.length > 0) { - Alert.alert('提示', '数据库中已经有电影数据,无需重复导入。'); - return; +const validateUserColumns = () => { + const results = []; + userTableSchema.columnArray.forEach(column => { + try { + validateColumnSchema(column); + results.push({ column: column.name, valid: true }); + } catch (error) { + results.push({ column: column.name, valid: false, error: error.message }); } - await databaseInstance.write(async () => { - await Promise.all( - SAMPLE_MOVIES.map((payload, index) => - moviesCollection.create((movie) => { - movie.title = payload.title; - movie.genre = payload.genre; - movie.posterImage = payload.posterImage; - movie.description = payload.description; - movie.releaseDateAt = new Date(Date.now() - index * 24 * 60 * 60 * 1000); - }), - ), - ); - }); - }, [databaseInstance, moviesCollection]); - - const addRandomMovie = useCallback(async () => { - const payload = randomItem(SAMPLE_MOVIES); - await databaseInstance.write(async () => { - await moviesCollection.create((movie) => { - movie.title = `${payload.title} · ${Math.floor(Math.random() * 100)}`; - movie.genre = payload.genre; - movie.posterImage = payload.posterImage; - movie.description = payload.description; - movie.releaseDateAt = new Date(); - }); - }); - }, [databaseInstance, moviesCollection]); + }); + console.log(results); + return results; +}; - const addReview = useCallback( - async (movie) => { - await databaseInstance.write(async () => { - await movie.addReview(randomItem(SAMPLE_REVIEWS)); - }); - }, - [databaseInstance], - ); +// ===================== 初始化数据库改为接收表类型参数(核心修改)===================== +// 初始化数据库(新增tableType参数) +const initializeDatabase = async (tableType) => { + // 根据表类型获取对应表结构 + const targetTableSchema = tableSchemaMap[tableType] || userTableSchema; + const adapter = new SQLiteAdapter({ + dbName: `watermelon_${tableType}_db`, // 不同表使用不同数据库名 + schema: getAppDatabaseSchema(targetTableSchema), + jsi: false, + migrations: schemaMigrations({ migrations: [] }), + }); + + await adapter.initializingPromise; // 等待适配器初始化 + return new Database({ adapter, modelClasses: [] }); +}; - const renameMovie = useCallback( - async (movie) => { - try { - console.log('%c watermelondbConsoleLogger renameMovie before:', 'color: #0e93e0;background: #aaefe5;', movie.title); - await databaseInstance.write(async () => { - await movie.update((record) => { - const baseTitle = record.title.split(' · ')[0]; - const newTitle = `${baseTitle} · v${Math.floor(Math.random() * 10 + 1)}`; - console.log('%c watermelondbConsoleLogger renameMovie updating:', 'color: #0e93e0;background: #aaefe5;', { - oldTitle: record.title, - baseTitle, - newTitle, - }); - record.title = newTitle; - }); - }); - console.log('%c watermelondbConsoleLogger renameMovie after:', 'color: #0e93e0;background: #aaefe5;', movie.title); - } catch (error) { - console.error('[WatermelonDemo] 随机改名失败', error); - Alert.alert('错误', `随机改名失败: ${error.message}`); - } - }, - [databaseInstance], - ); +const SchemaExample = () => { + const [validationResult, setValidationResult] = useState(null); + const [createResult, setCreateResult] = useState(null); + const [database, setDatabase] = useState({}); // 修改为对象存储不同表的数据库实例 - const deleteMovie = useCallback( - async (movie) => { - await databaseInstance.write(async () => movie.deleteMovie()); - }, - [databaseInstance], - ); + // 验证字段结构(保留原有) + const handleValidateSchema = () => { + const results = validateUserColumns(); + setValidationResult(results); + setCreateResult(null); + }; - const clearAll = useCallback(async () => { - await databaseInstance.write(async () => { - const allMovies = await moviesCollection.query().fetch(); - await Promise.all(allMovies.map((movie) => movie.deleteMovie())); - }); - }, [databaseInstance, moviesCollection]); - - // 1. 测试关联查询:查询指定电影的所有评论 - const testRelationQuery = useCallback(async () => { - if (movies.length === 0) { - Alert.alert("提示", "请先添加电影数据"); - return; - } + // ===================== 修改创建方法为接收表类型参数(核心修改)===================== + // 创建数据库表(新增tableType参数) + const handleCreateTables = async (tableType) => { try { - const targetMovie = movies[0]; // 取第一个电影 - const reviews = await targetMovie.reviews.fetch(); // 测试 Relation.fetch() - const reviewTexts = reviews.map(r => r.body).join("\n- "); - Alert.alert( - `电影《${targetMovie.title}》的评论`, - reviewTexts || "暂无评论" - ); - } catch (error) { - console.error("关联查询测试失败", error); - Alert.alert("错误", "关联查询测试失败:" + error.message); - } - }, [movies]); + // 检查该表是否已创建 + if (database[tableType]) { + const tableName = tableSchemaMap[tableType]?.name || userTable; + setCreateResult({ + success: true, + message: `数据库(${tableType})已存在`, + tables: [tableName], + }); + return; + } - // 2. 测试批量添加评论(测试 has_many 关联批量操作) - const testBulkAddReviews = useCallback(async () => { - if (movies.length === 0) { - Alert.alert("提示", "请先添加电影数据"); - return; - } - try { - const targetMovie = movies[0]; - await databaseInstance.write(async () => { - // 批量添加3条评论 - for (let i = 0; i < 3; i++) { - await targetMovie.addReview(`批量测试评论 ${i + 1}:${randomItem(SAMPLE_REVIEWS)}`); - } + const db = await initializeDatabase(tableType); + // 存储对应表类型的数据库实例 + setDatabase({ ...database, [tableType]: db }); + const tableName = tableSchemaMap[tableType]?.name || userTable; + setCreateResult({ + success: true, + message: `数据库表(${tableType})创建成功`, + tables: [tableName], }); - Alert.alert("成功", `已为《${targetMovie.title}》批量添加3条评论`); } catch (error) { - console.error("批量添加评论测试失败", error); - Alert.alert("错误", "批量添加评论失败:" + error.message); - } - }, [movies, databaseInstance]); - - // 3. 测试事务回滚(故意制造错误,验证事务原子性) - const testTransactionRollback = useCallback(async () => { - if (movies.length === 0) { - Alert.alert("提示", "请先添加电影数据"); - return; - } - try { - const targetMovie = movies[0]; - await databaseInstance.write(async () => { - // 第一步:正常添加评论 - await targetMovie.addReview("事务测试:这是一条正常评论"); - // 第二步:故意抛出错误,验证事务回滚(上面的评论应被撤销) - throw new Error("故意触发事务回滚测试"); + setCreateResult({ + success: false, + message: `数据库表(${tableType})创建失败`, + error: error.message, }); - } catch (error) { - console.warn("事务回滚测试触发", error.message); - // 验证评论是否被回滚 - const reviews = await targetMovie.reviews.fetch(); - const hasTestReview = reviews.some(r => r.body.includes("事务测试")); - Alert.alert( - "事务回滚测试结果", - hasTestReview - ? "测试失败:事务未回滚" - : "测试成功:事务已回滚(错误前的操作被撤销)" - ); } - }, [movies, databaseInstance]); - - // 在 MovieScreen 组件的 useCallback 方法区域添加 - const deleteReview = useCallback( - async (review) => { - try { - await databaseInstance.write(async () => { - await review.deleteReview(); // 调用 Review 模型的 deleteReview 方法 - }); - Alert.alert("成功", "评论已删除"); - } catch (error) { - console.error("删除评论失败", error); - Alert.alert("错误", `删除评论失败: ${error.message}`); - } - }, - [databaseInstance] - ); + }; + + // 清空页面状态(保留原有) + const handleClear = () => { + setValidationResult(null); + setCreateResult(null); + setDatabase({}); // 重置为空对象 + }; return ( - - - WatermelonDB 示例 - - - - - - - + + WatermelonDB 表创建示例 + + + {/* 为每个按钮绑定不同的tableType参数 */} +