Commit 688b07fddfe114e1e603750d3a3137bb0cba3bf8

Authored by weixin_45529955
1 parent 82579a79

新建分支 语音播报 功能设置

{
"mcpServers": {
"figma-developer-mcp": {
"command": "/Users/wangdongni/nodejs/v20/bin/npx",
"args": [
"-y",
"figma-developer-mcp",
"--stdio"
],
"env": {
"FIGMA_API_KEY": "figd_MhWE2nNs0P2wrczrUcPeFByg-M_1lME4AnH1XpQWs"
}
}
}
}
... ...
[mcp_servers.figma-developer-mcp]
command = "/Users/wangdongni/nodejs/v20/bin/npx"
args = [
"-y",
"figma-developer-mcp",
"--stdio",
]
[mcp_servers.figma-developer-mcp.env]
FIGMA_API_KEY = "figd_MhWE2nNs0P2wrczrUcPeFByg-M_1lME4AnH1XpQWs"
... ...
{
"mcpServers": {
"figma-developer-mcp": {
"command": "/Users/wangdongni/nodejs/v20/bin/npx",
"args": [
"-y",
"figma-developer-mcp",
"--stdio"
],
"env": {
"FIGMA_API_KEY": "figd_MhWE2nNs0P2wrczrUcPeFByg-M_1lME4AnH1XpQWs",
"PATH": "/Users/wangdongni/nodejs/v20/bin:/usr/bin:/bin"
}
}
}
}
... ...
# 周到安排商家(one_key)开发技术文档
> 本文件供后续协助开发时快速理解项目结构、代码习惯与开发约定。
> 最后更新:2026-08-06
## 0. 开发约定(重要,优先遵守)
以下是后续开发必须沿用的约定:
1. **屏幕适配**
- 新建页面一律使用 `w()` / `font()`(来自 `lib/utils/screen_util.dart`),不要直接写 `ScreenUtil().setWidth()` / `ScreenUtil().setSp()`。
- `w()` / `font()` 的入参就是 **375 设计稿的数值**:`w(375)` = 整屏宽,`font(14)` = 14 号字。
- **同一个文件内绝不混用**两种写法:新文件用 `w()/font()`;修改老文件时跟随该文件已有风格(老文件多为 `ScreenUtil().setWidth(x)`,其数值是 750 口径,不用改)。
2. **间距**:一律用 `VEmptyView(高度)` / `HEmptyView(宽度)`。
- 注意口径:这两个组件内部直接 `setWidth(数值)`(未 ×2),**传 750 口径数值 = 375 设计稿数值 ×2**。详见 §6.2。
3. **分割线**:一律用 `MyDivider(宽, 高, {color})`,默认色 `MyColours.color_dbe3e3`。
4. **颜色**:一律从 `MyColours`(`lib/res/colors.dart`)取,不要写裸 `Color(0x...)`。
5. **页面导航**:新页面三步注册(Routes 常量 → route_handles Handler → NavigatorUtil 方法),见 §7。
6. **状态管理**:`ChangeNotifier + provider`,页面数据放 Provider,路由级用 `ChangeNotifierProvider` 包裹,见 §8。
7. **网络请求**:接口地址加在 `HttpUrl`,请求统一走 `DioUtil().request<T>()`,返回 `BaseResp`,成功码 `code == 200`(`Constant.status_success`),见 §9。
---
## 1. 项目概览
- 项目名:`one_key`,App 显示名「周到安排商家」,面向餐厅/商家的预订与经营管理系统。
- Android 包名:`com.naji.one_key`;iOS Bundle ID:`com.naji.reserveLine`。
- Flutter:`3.27.3`(通过 fvm 管理,见 `.fvmrc`);Dart SDK `>=3.0.0`。
- 服务端地址:`http://xt04.colorcun.com/`(`lib/net/http_url.dart`),请求统一 `Method.post`,参数作为 query 拼在 URL 上,接口名形如 `?service=Android.X.Y` / `?service=App.X.Y`。
- 双版本:正常版 + 极速版(`Constant.normalApp` 控制,当前为 `false` 即双版本共存;极速版页面在 `lib/pages_sim/`,路由名带 `_sim`)。
- 微信:AppId `wx4d105e36a36a6139`,UniversalLink `https://xt04.colorcun.com/link/OneKey/`。
- 文案:UI 文案基本硬编码中文,`lib/generated/l10n` 实际使用很少。
## 2. 环境与常用命令
```bash
fvm flutter run # 运行
fvm flutter pub run build_runner build --delete-conflicting-outputs # 生成 model 的 .g.dart
fvm flutter analyze # 静态检查
```
## 3. 目录结构
| 目录 | 说明 |
| --- | --- |
| `lib/main.dart` | 入口:初始化路由、SharedPreferences、全局 Provider、ScreenUtilInit、MaterialApp |
| `lib/app.dart` | `App` 全局静态类:`App.sp`(SharedPreferences)、`App.router`、`App.navigatorKey`、屏幕/状态栏信息 |
| `lib/pages/` | 业务页面,按模块分子目录(home、login、message、notebook、table、feast、my、detail、coupon、order_advance、create_store 等) |
| `lib/pages_sim/` | 极速版页面副本,与正常版同名页面需同步维护 |
| `lib/provider/` | 状态管理:`ChangeNotifier` 子类,命名 `XxxProvider` / `XxxModel` |
| `lib/models/` | 数据模型:`json_serializable` + `.g.dart`,常见命名 `XxxSunModel` |
| `lib/net/` | 网络层:`dio_util.dart`(DioUtil 单例)、`base_resp.dart`(BaseResp)、`http_url.dart`(接口地址常量) |
| `lib/route/` | fluro 路由:`routes.dart`(路径常量 + 注册)、`route_handles.dart`(Handler)、`navigator_util.dart`(跳转方法) |
| `lib/res/` | `colors.dart`(MyColours 色板)、`dimens.dart`(Dimens 字号/间距常量)、`styles.dart`、`strings.dart` |
| `lib/utils/` | `screen_util.dart`(w/h/font 等适配函数)、`utils.dart`(Utils 工具大集合)、`dialog_util.dart`、`event_bus.dart`、`misc.dart`(权限)等 |
| `lib/widgets/` | 公共组件:SystemAppBar、RoundButton、CommonButton、VEmptyView、HEmptyView、MyDivider、ExceptionPage 等 |
| `lib/constant/` | `constant.dart`(全局常量、权限配置)、`state.dart`(PageState 等枚举)、`error.dart`(MyError) |
| `assets/` | 图片与音频资源,已在 pubspec 全量声明 |
| `library/` | 本地依赖插件(webview_flutter 修改版、sendsms、table_calendar 等) |
## 4. 启动流程(main.dart)
1. `FluroRouter` 创建并调用 `Routes.configureRoutes(router)` 注册全部路由,赋给 `App.router`。
2. `App.initSp()` 初始化 `SharedPreferences`(全局通过 `App.sp` 访问)。
3. `runApp(MultiProvider(...))` 注册全局 Provider(MessageModel、UserModel、OrderFoodProvider、CouponProvider、CreateStoreModel 等)。
4. `ScreenUtilInit(designSize: Size(750, 1334), minTextAdapt: true, splitScreenMode: true)` 初始化屏幕适配,注意 **设计尺寸是 750×1334**。
5. `MaterialApp`:主题 `useMaterial3: false`,`primaryColor: Colors.white`,iOS 转场用 `PopScopePageTransitionsBuilder`,`home` 由本地状态决定:
- 首次打开(`isFirst`)→ `FirstPage`(初始化微信/网络等后跳登录);
- 已登录 → `HomePage(initialIndex: 1)`;
- 未登录 → `LoginPage`。
## 5. 整体架构与数据流
```
页面 (pages/) ── Provider.of / Consumer ──> Provider (provider/)
Provider ── DioUtil().request() ──> HttpUrl 接口 ──> 服务端 (xt04.colorcun.com)
页面状态:PageState 枚举 → ExceptionPage 统一渲染 加载中/网络错误/超时/空页面
```
关键点:
- 页面通过 `Provider.of<XxxProvider>(context, listen: true/false)` 或 `Consumer<XxxProvider>` 拿数据;Provider 内部 `notifyListeners()` 驱动刷新。
- 每个 Provider 通常维护若干 `PageState` 字段(`Loading / ConnectivityError / ConnectivityTimeOut / Empty / Success` 等,见 `lib/constant/state.dart`),页面据此渲染 `ExceptionPage` 或列表内容。
- 用户信息:`UserModel`(全局注册),登录后用户对象 JSON 存 `App.sp`(key:`user`),通过 `Provider.of<UserModel>(context, listen: false).user!` 获取 `sellerid` / `openid` / `token` 等。
- 权限控制:`Constant.authorityConfig`(模块 → 角色数组)+ `Utils.getAuthority(context, '模块名')` 判断当前角色是否有权。
## 6. UI 规范(重点)
### 6.1 屏幕适配:w() / font() 与 375 设计稿
`lib/utils/screen_util.dart` 提供顶层函数(新页面直接 import 使用):
```dart
double w(num width) => ScreenUtil().setWidth(width * 2); // 宽(宽度等比缩放)
double h(num height) => ScreenUtil().setWidth(height * 2); // 高(也是 setWidth!按宽度缩放)
double h2(num height) => ScreenUtil().setHeight(height * 2); // 高(按高度缩放,少用)
double r(num radius) => ScreenUtil().radius(radius * 2); // 圆角
double font(num size) => ScreenUtil().setSp(size * 2); // 字号
```
由于 `ScreenUtilInit` 的设计尺寸是 **750×1334**,而上述函数内部把入参 ×2,所以:
- **`w()` / `font()` 的入参即 375 设计稿数值**:`w(375)` = 整屏宽,`w(20)` = 20 逻辑像素,`font(16)` = 16 号字,`r(8)` = 8 圆角。
- 后续设计稿按 375 宽,新页面直接把稿子上的数值填进 `w()/font()/r()` 即可,**不需要自己换算**。
- `h()` 实际按宽度等比缩放(与 `w()` 一致),保证横向适配时高度不溢出;`h2()` 才是按高度缩放。
> 老文件大量使用 `ScreenUtil().setWidth(40)`、`ScreenUtil().setSp(28)` 这种 750 口径写法(数值 ≈ 375 值 ×2)。**修改老文件时保持该文件原有写法,不要混入 `w()/font()`;新建文件则统一用 `w()/font()`。**
### 6.2 间距:VEmptyView / HEmptyView
- `VEmptyView(高度)` → `SizedBox(height: ScreenUtil().setWidth(高度))`
- `HEmptyView(宽度)` → `SizedBox(width: ScreenUtil().setWidth(宽度))`
⚠️ **口径注意**:这两个组件内部直接 `setWidth(数值)`,**没有 ×2**,所以传的是 750 口径数值。换算关系:
| 375 设计稿间距 | 代码写法 |
| --- | --- |
| 6px | `VEmptyView(12)` / `HEmptyView(12)` |
| 12px | `VEmptyView(24)` |
| 24px | `VEmptyView(48)` |
也就是说:**375 设计稿上的间距数值 ×2 后传给 VEmptyView/HEmptyView**。已有代码都是这个口径(例如 `w()` 风格文件中 `VEmptyView(24)` 与 `w(12)` 实际渲染尺寸一致)。
### 6.3 分割线:MyDivider
```dart
MyDivider(宽, 高, {color});
// 例如:MyDivider(w(375), w(1)) 默认颜色 MyColours.color_dbe3e3
// 自定义颜色:MyDivider(w(375), w(1), color: MyColours.color_DBDBDB)
```
新页面分割线一律用 `MyDivider`,不要用 `Divider()` / `Container(color:...)` 代替。
### 6.4 颜色:MyColours
色板集中在 `lib/res/colors.dart`(`MyColours` 类),命名 `color_<十六进制>`。常用色:
| 用途 | 常量 | 值 |
| --- | --- | --- |
| 主色(App 主题青绿色) | `MyColours.app_main` | `0xFF24A4A8` |
| 主操作色(按钮/选中/链接) | `MyColours.color_199095` | `0xFF199095` |
| 辅助青绿(强调/标签) | `MyColours.color_438E93` | `0xFF438E93` |
| 正文深色 | `MyColours.color_333333` / `color_353535` / `color_515151` | `0xFF333333` 等 |
| 次要文字 | `MyColours.color_666666` / `color_777777` | `0xFF666666` 等 |
| 弱化文字/占位 | `MyColours.color_999999` / `color_bfbfbf` | `0xFF999999` 等 |
| 页面背景 | `MyColours.color_f5f5f5` / `color_f7f7f7` / `color_f8f9fd` | 浅灰 |
| 白色 | `MyColours.color_ffffff` | `0xFFFFFFFF` |
| 红色(错误/删除/未读角标) | `MyColours.color_E34D59` / `color_fa5151` / `color_E95750` | 红系 |
| 分割线默认 | `MyColours.color_dbe3e3` | `0xFFdbe3e3` |
### 6.5 字号与文字
- 新页面统一 `font(数值)`:常用 `font(10) / font(12) / font(14) / font(16) / font(18) / font(22)`。
- `lib/res/dimens.dart` 中有 `Dimens.font_sp10~24` 等常量,但页面里使用较少,新代码不强制使用。
- 文字样式一般直接 `TextStyle(fontSize: font(14), color: MyColours.color_333333)`;不需要 `ThemeData.textTheme`。
### 6.6 图片资源
- 图片在 `assets/`,pubspec 已声明 `- assets/`,新图直接放入即可。
- 加载方式:`Image.asset(Utils.getImgPath('xxx'))`(自动补 `assets/xxx.png`),或网络图 `CachedNetworkImage`。
### 6.7 常用公共组件
| 组件 | 文件 | 说明 |
| --- | --- | --- |
| `SystemAppBar` | `lib/widgets/system_app_bar.dart` | 标准导航栏(AppBar 实现),标题居中,返回键 `assets/icon_back.png`,可配右侧文字/图片按钮 |
| `MyAppBar` | `lib/widgets/app_bar.dart` | 自绘导航栏,可自定义背景/字体色/右侧按钮 |
| `RoundButton` | `lib/widgets/round_button.dart` | 圆角渐变按钮,常用参数 `text/width/height/radius/fontSize/onPressed`;w 风格文件里 `width: w(48), height: h(18), radius: r(7)` |
| `CommonButton` | `lib/widgets/common_button.dart` | 状态按钮,`state`:-1 灰禁、0 描边、1 实心主色、-3 红色 |
| `GradientButton` | `lib/widgets/gradient_button.dart` | 纯渐变容器按钮 |
| `ExceptionPage` | `lib/widgets/page_exception.dart` | 按 `PageState` 渲染 加载中/网络错误/超时/空页面,带重试回调 |
| `LoadingPage / EmptyPage / ErrorPage` | `lib/widgets/` | 状态页基础组件 |
| `Loading` | `lib/widgets/loading.dart` | 全局 loading 弹层:`Loading.showLoading(context)` / `hideLoading` |
| `MyItem` | `lib/widgets/my_item.dart` | 「标题 + 内容 + 右箭头」列表行 |
| `RowMultiText` | `lib/widgets/row_multi_text.dart` | 左右两段文字行 |
| `TextInputFieldWidget` | `lib/widgets/text_input_field_widget.dart` | 无边框输入框 |
| `FixedText` | `lib/widgets/fixed_text.dart` | 固定字号文本(`textScaleFactor: 1.0`) |
| `CustomTabIndicator` | `lib/widgets/custom_tab_indicator.dart` | 自定义 Tab 指示器 |
### 6.8 页面骨架模板(新页面按此写)
```dart
import 'package:flutter/material.dart';
import 'package:one_key/res/colors.dart';
import 'package:one_key/utils/screen_util.dart'; // w()/font()
import 'package:one_key/widgets/MyDivider.dart'; // 分割线
import 'package:one_key/widgets/h_empty_view.dart'; // 水平间距
import 'package:one_key/widgets/v_empty_view.dart'; // 垂直间距
import 'package:one_key/widgets/system_app_bar.dart'; // 导航栏
class DemoPage extends StatefulWidget {
const DemoPage({super.key});
@override
State<DemoPage> createState() => _DemoPageState();
}
class _DemoPageState extends State<DemoPage> {
@override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) {
// 初始化请求
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: MyColours.color_f5f5f5,
appBar: SystemAppBar('页面标题'),
body: Column(
children: [
Container(
width: w(375), // 375 设计稿直接填
height: w(48),
padding: EdgeInsets.symmetric(horizontal: w(20)),
alignment: Alignment.centerLeft,
child: Text('内容',
style: TextStyle(
fontSize: font(16), color: MyColours.color_333333)),
),
MyDivider(w(375), w(1)), // 分割线
VEmptyView(24), // 间距 = 375稿 12px
Row(children: [
HEmptyView(20), // 水平间距 = 375稿 10px
const Text('另一段'),
]),
],
),
);
}
}
```
### 6.9 首页 Tab 结构
`HomePage`(`lib/pages/home/home_page.dart`):`PageView`(`NeverScrollableScrollPhysics`)+ `BottomNavigationBar`,共 5 个 Tab:
| index | 页面 | 底部图标 |
| --- | --- | --- |
| 0 | 消息 `MessagePage` | `message_default/selected.png` |
| 1 | 预订本 `NoteBookPage`(默认页) | `notebook_default/selected.png` |
| 2 | 桌位 `TableManagerPage` | `tablemanage_default/selected.png` |
| 3 | 宴会 `FeastPage` | `y_feast_un/sel.png` |
| 4 | 我的 `MyPage` | `my_default/selected.png` |
## 7. 路由规范(fluro)
新增页面三步:
1. `lib/route/routes.dart`:定义路径常量,如 `static String demo_page = "/demo_page";`。
2. `lib/route/route_handles.dart`:定义 `Handler`,构造页面并包上需要的 `ChangeNotifierProvider`;然后在 `Routes.configureRoutes` 里 `router.define(Routes.demo_page, handler: xxxHandler);`。
3. `lib/route/navigator_util.dart`:写 `static void goDemoPage(BuildContext context, {参数})`,内部 `App.router.navigateTo(context, "...")`。
传参规则:
- 简单参数拼在路径 query 上,如 `${Routes.demo_page}?id=$id`,Handler 里 `params["id"]!.first` 取。
- 复杂对象先 `json.encode` + `FluroConvertUtils.fluroCnParamsDecode` 编码,Handler 里 `FluroConvertUtils.fluroCnParamsDecode(...)` 解码后 `XxxModel.fromJson`。
- 跳转前 `NavigatorUtil` 内部会先 `Utils.changeStatusColor(color: MyColours.color_ffffff)`。
## 8. 状态管理规范(provider)
- 全局状态:`main.dart` 的 `MultiProvider` 注册(UserModel、MessageModel、OrderFoodProvider 等)。
- 页面级状态:路由 Handler 里用 `ChangeNotifierProvider<XxxProvider>(create: (_) => XxxProvider(), child: XxxPage())` 包裹(可多层嵌套)。
- 页面取数据:`Provider.of<XxxProvider>(context, listen: true)`(监听)或 `listen: false`(不监听,如请求方法内);列表用 `Consumer<XxxProvider>` 更清晰。
- Provider 内请求完成后 `notifyListeners()`;页面 `setState` 一般只在页面自身状态变化时调用。
- 页面初始化请求放在 `initState` + `WidgetsBinding.instance.addPostFrameCallback` 中(避免 build 前请求)。
## 9. 网络请求规范
### 9.1 接口定义
`lib/net/http_url.dart`:`static const String xxx = "?service=App.X.Y"; // 注释`。
基础地址 `HttpUrl.server_address = "http://xt04.colorcun.com/"`,请求统一 `Method.post`,`data` 参数会作为 query 拼在 URL 上(`DioUtil` 内部 `queryParameters: data`)。
### 9.2 发起请求
```dart
BaseResp<dynamic> baseResp = await DioUtil().request<dynamic>(
context, Method.post, HttpUrl.xxx,
data: params, // Map<String, String?>,会作为 queryParameters
showLoading: true, // 是否显示全局 loading
);
if (baseResp.code == Constant.status_success) { // 200
// baseResp.data / baseResp.responseData
} else {
Utils.showToast(baseResp.msg ?? "");
}
```
要点:
- `BaseResp` 字段:`status / code / msg / data / responseData`;响应结构 `{ret, msg, data}`,`ret == 200` 为成功(`HttpConfig` 在 `main.dart` 的 `initDio()` 里配置:`status: "ret", code: "ret", msg: "msg", data: "data"`)。
- 常见参数:`sellerid` / `openid` 从 `Provider.of<UserModel>(context, listen: false).user!` 取;部分接口要 `sid`、`uid`、`token`。
- 错误处理:请求抛 `MyError`(`lib/constant/error.dart`),`type: 1` = 网络错误,`type: 2` = 超时;Provider 里通常据此把 `pageState` 置为 `PageState.ConnectivityError / ConnectivityTimeOut`,再 `notifyListeners()`。
- 断网时 `DioUtil` 内部会 `Utils.showToast("网络错误,请稍后重试")`。
- 上传文件(FormData)可参考 `FinanceProvider.upLoadApplyments` / `UploadImageProvider`(七牛上传)。
## 10. 数据模型规范
- 文件在 `lib/models/`,类名多为 `Xxx` / `XxxSunModel`。
- 使用 `json_serializable`:类上加 `@JsonSerializable()`,文件末尾 `part 'xxx.g.dart';`,提供 `factory Xxx.fromJson(...)` 与 `toJson()`。
- 修改模型字段后运行 build_runner 重新生成 `.g.dart`:
`fvm flutter pub run build_runner build --delete-conflicting-outputs`
## 11. 常用工具速查
- `Utils`(`lib/utils/utils.dart`):
- `Utils.showToast(msg)`:Toast 提示;
- `Utils.changeStatusColor({color})`:状态栏颜色;
- `Utils.getImgPath(name)`:拼 `assets/name.png`;
- `Utils.getAuthority(context, key)`:角色权限判断;
- `Utils.keySort(map)`:参数签名排序(部分接口需要);
- `Utils.compressImage / compressImageToFile`:图片压缩;
- 时间/农历/正则/手机号脱敏等大量工具方法。
- `DialogUtil`(`lib/utils/dialog_util.dart`):标签选择、滚轮选择器、消息弹窗、日历选择等。
- `EventBus`(`lib/utils/event_bus.dart`):全局 `bus.on(事件名, cb)` / `bus.emit(事件名, arg)`。
- 权限:`lib/utils/misc.dart` 的 `requestPermission(Permission.xxx)`(带使用目的说明弹窗)。
- 全局存储:`App.sp!.getString / setString / getBool ...`(SharedPreferences 单例)。
## 12. 注意事项与常见坑
1. **适配写法不要混用**:`w()` 是 375 口径、`ScreenUtil().setWidth()` 是 750 口径,混用会直接导致尺寸差一倍。同一文件保持一种风格。
2. **VEmptyView/HEmptyView 是 750 口径**:按 375 稿子写间距要 ×2(§6.2 有对照表)。
3. **`h()` 是宽度等比缩放**:不要用它做需要按屏幕高度伸缩的布局;需要高度适配用 `h2()`。
4. **极速版同步**:`lib/pages_sim/` 是极速版副本,修改正常版核心页面时注意是否需要在 sim 版同步;路由常量带 `_sim` 后缀。
5. **老页面是状态机**:列表页先判 `PageState` 再渲染 `ExceptionPage` / `ListView`,不要绕过直接写列表。
6. **接口地址都在 HttpUrl**:不要在新代码里硬编码 URL。
7. **模型改了记得跑 build_runner**,否则 `fromJson` 不会更新。
8. **打包注意事项**(`lib/constant/constant.dart` 注释):`Constant.normalApp` 控制极速版是否打包;测试账号已由 `0001` 换成 `0019`;微信小程序类型 `Constant.wxMiniProgramType` 分正式/体验/开发。
9. **新建图片记得放 `assets/`**(目录已全量声明,无需改 pubspec,除非新增音频 mp3 需单独声明)。
10. **请求里传 `sellerid/openid` 前判空**,UserModel 未登录时 `user` 可能为 null。
## 13. 新页面开发 Checklist
- [ ] 页面文件放对应业务目录(`lib/pages/模块/xxx_page.dart`),类名 `XxxPage`;
- [ ] import `utils/screen_util.dart`,全程用 `w()/font()/r()`,不写 `ScreenUtil().setWidth`;
- [ ] 间距用 `VEmptyView/HEmptyView`(375 稿数值 ×2),分割线用 `MyDivider`;
- [ ] 颜色只从 `MyColours` 取;
- [ ] 导航栏用 `SystemAppBar`(普通页)/ `MyAppBar`(自定义样式);
- [ ] 请求方法放对应 Provider,`HttpUrl` 加接口,成功判断 `code == Constant.status_success`;
- [ ] 页面状态用 `PageState + ExceptionPage`;
- [ ] 在 `routes.dart`、`route_handles.dart`、`navigator_util.dart` 完成三步注册;
- [ ] 需要权限的入口用 `Utils.getAuthority(context, key)` 过滤;
- [ ] 涉及正常版/极速版双端时,检查 `pages_sim` 是否需要同步。
... ...
../../../../ohos/oh_modules/.ohpm/bugly@0.6.1/oh_modules/bugly
\ No newline at end of file
... ...