149 lines
8.0 KiB
Markdown
149 lines
8.0 KiB
Markdown
# CardHarmonyOS
|
||
|
||
CardHarmonyOS 是期货首页卡片的 HarmonyOS ArkTS 示例工程,使用 ArkUI 声明式 UI
|
||
实现 AI 观点、市场排名和 AI 选期三张业务卡片。首页入口为
|
||
`entry/src/main/ets/pages/Index.ets`。
|
||
|
||
## 环境要求
|
||
|
||
- DevEco Studio(支持 HarmonyOS API 20 及以上)
|
||
- HarmonyOS SDK 6.0.0 或更高版本
|
||
- ohpm
|
||
- HarmonyOS 手机或模拟器
|
||
|
||
项目目标 SDK 为 HarmonyOS 6.1.0(API 23),兼容 SDK 为 6.0.0(API 20)。
|
||
|
||
## 快速开始
|
||
|
||
1. 使用 DevEco Studio 打开仓库根目录。
|
||
2. 执行 `ohpm install` 安装依赖。
|
||
3. 在 **File > Project Structure > Signing Configs** 中配置本机调试签名。
|
||
4. 选择 `entry` 模块和 `default` product。
|
||
5. 连接设备或启动模拟器,点击 **Run** 构建并安装应用。
|
||
|
||
单元测试位于 `entry/src/test/`,设备测试位于 `entry/src/ohosTest/`,可在
|
||
DevEco Studio 中右键测试文件执行。
|
||
|
||
## 目录结构
|
||
|
||
```text
|
||
AppScope/ 应用级配置与资源
|
||
entry/src/main/ets/
|
||
├── pages/ 首页及详情页
|
||
├── ai-view/ AI 观点卡片
|
||
├── market-ranking/ 市场排名卡片
|
||
├── ai-pick/ AI 选期卡片
|
||
└── common/ 卡片容器、数据模型及行情字段常量
|
||
entry/src/main/resources/ 文案、颜色、图片和页面配置
|
||
doc/ HTTP 与 4106 行情接口文档
|
||
```
|
||
|
||
业务卡片通常由 `Models`、`DataFetcher`、`NodeComponent` 和 Mock 数据组成。
|
||
市场排名与 AI 选期分别通过独立的 `HqRequestClient` 构造行情订阅参数,共享
|
||
`common/HqFieldId.ets` 中的 4106 字段 ID。
|
||
|
||
## MarketRanking 生命周期
|
||
|
||
`MarketRankingNodeComponent` 出现时读取卡片配置、注册 `tcpReconnect` 事件监听,并在
|
||
`firstPageVisible` 为 `true` 时获取当前 Tab 的 contracts。contracts 返回后先生成占位列表,
|
||
再由 `MarketRankingHqRequestClient` 发起行情订阅,行情回调会整体替换 `CardData`。
|
||
|
||
```mermaid
|
||
flowchart TD
|
||
appear["aboutToAppear"] --> config["读取卡片配置"]
|
||
config --> listen["监听 tcpReconnect"]
|
||
listen --> visible{"firstPageVisible?"}
|
||
visible -- 否 --> idle["等待页面恢复可见"]
|
||
visible -- 是 --> fetch["获取当前 Tab 的 contracts"]
|
||
fetch --> placeholder["填充合约占位列表"]
|
||
placeholder --> stillVisible{"页面仍然可见?"}
|
||
stillVisible -- 否 --> idle
|
||
stillVisible -- 是 --> subscribe["订阅行情"]
|
||
subscribe --> update["行情回调更新 CardData"]
|
||
update --> waiting["等待下一次行情推送"]
|
||
waiting --> update
|
||
|
||
refresh["refreshTrigger 变化"] --> stop["停止旧行情订阅"]
|
||
tab["Tab 切换"] --> stop
|
||
stop --> clear["清空列表"]
|
||
clear --> fetch
|
||
|
||
hidden["firstPageVisible = false"] --> stopOnly["停止行情订阅"]
|
||
shown["firstPageVisible = true"] --> hasContracts{"已有 contracts?"}
|
||
hasContracts -- 是 --> subscribe
|
||
hasContracts -- 否 --> fetch
|
||
|
||
reconnect["tcpReconnect"] --> reconnectVisible{"页面可见?"}
|
||
reconnectVisible -- 否 --> idle
|
||
reconnectVisible -- 是 --> reconnectStop["停止旧行情订阅"]
|
||
reconnectStop --> hasContracts
|
||
|
||
disappear["aboutToDisappear"] --> unlisten["解绑 tcpReconnect"]
|
||
unlisten --> stopOnly
|
||
```
|
||
|
||
运行期间按以下规则更新:
|
||
|
||
- `refreshTrigger`:由父组件通过 `@Link` 传入;变化后停止旧行情、重新获取 contracts,
|
||
并在可见时重新订阅。
|
||
- `firstPageVisible`:由父组件通过 `@Provide` 提供,组件通过 `@Consume` 监听,默认
|
||
为 `true`。变为不可见时立即停止行情;恢复可见时优先使用已有 contracts 重新订阅,
|
||
没有 contracts 才重新请求。
|
||
- `tcpReconnect`:组件通过公共 `emitter` 监听。重连且页面可见时停止旧行情并重新订阅;
|
||
页面不可见时忽略,恢复可见后再订阅。
|
||
- Tab 切换:停止旧行情、清空列表、获取新 Tab 的 contracts,再订阅对应行情。
|
||
|
||
`aboutToDisappear` 会解绑 TCP 重连监听并停止行情订阅。HTTP 请求等待期间如果页面变为
|
||
不可见,contracts 仍可更新,但不会创建行情订阅。
|
||
|
||
### 当前异常与恢复处理
|
||
|
||
| 异常或状态 | 处理方式 |
|
||
| --- | --- |
|
||
| HTTP 获取 contracts 失败 | `updateCardData` 捕获异常并清空列表 |
|
||
| TCP 连接恢复 | 收到 `tcpReconnect` 后,仅在页面可见时停止旧行情并重新订阅 |
|
||
| TCP 重连时没有 contracts | 重新请求 contracts,请求成功后再订阅行情 |
|
||
| TCP 重连时页面不可见 | 忽略重连事件,页面恢复可见后再订阅 |
|
||
| 页面变为不可见 | 立即停止行情订阅 |
|
||
| 页面恢复可见 | 优先使用已有 contracts 恢复订阅;没有数据时重新请求 |
|
||
| 手动刷新 | 停止旧行情、重新请求 contracts,并在可见时重新订阅 |
|
||
| Tab 切换 | 停止旧行情、清空旧列表、请求新 Tab 数据并重新订阅 |
|
||
| HTTP 返回时页面已隐藏 | 更新 contracts,但不建立行情订阅 |
|
||
| 组件消失 | 解绑 TCP 重连监听并停止行情订阅 |
|
||
|
||
当前异常处理仍属于基础兜底:尚未实现 HTTP 自动重试、错误提示、请求取消、请求序号校验,
|
||
以及行情订阅失败后的延迟重试。快速切换 Tab 时,较早发出的请求仍可能晚于新请求返回;
|
||
首次加载使用的延迟任务也尚未在组件消失时取消。
|
||
|
||
## 卡片跳转链接
|
||
|
||
下表只记录页面跳转,不包含图片、HTTP 数据接口或行情接口。`{...}` 表示运行时参数。
|
||
|
||
| 卡片 | 触发入口 | 目标链接模板 | 当前本地路由 | 当前状态 |
|
||
| --- | --- | --- | --- | --- |
|
||
| MarketRanking | 点击合约 | `client://client.html?action=ymtz^webid=2205^stockcode={code}^marketid={market}` | `pages/Detail` | `clientUrl` 仅作为参数传入本地 Mock 页 |
|
||
| MarketRanking | 点击标题 | `cardConfig.card_url.android`,为空时使用 `card_url.ios` | `pages/StrategyDetail` | 当前 Mock 配置为空,不跳转 |
|
||
| AiPick | 点击合约 | `client://client.html?action=ymtz^webid=2205^stockcode={contract}^marketid={market}` | `pages/Detail` | `clientUrl` 仅作为参数传入本地 Mock 页 |
|
||
| AiPick | 点击标题 | `cardConfig.card_url.android`,为空时使用 `card_url.ios` | `pages/StrategyDetail` | 当前 Mock 配置为空,不跳转 |
|
||
| AiPick | 一句话定制策略 | `client://client.html?action=ymtz^ishiddenbar=1^mode=new^webid=2804^url=https://fupage.10jqka.com.cn/ai-web/ai-diagnosis-home.html?sync=1^title=AI选期` | `pages/StrategyDetail` | 尚未调用客户端能力 |
|
||
| AiPick | 点击策略卡片 | `client://client.html?action=ymtz^ishiddenbar=1^mode=new^webid=2804^url=https://fupage.10jqka.com.cn/ai-web/ai-diagnosis-detail.html?id={id}&type={periodType}&name={name}^title=AI选期` | `pages/StrategyDetail` | `name` 使用 URL 编码;尚未调用客户端能力 |
|
||
| AiPick | 点击策略标签 | `client://client.html?action=ymtz^ishiddenbar=1^mode=new^webid=2804^url=https://fupage.10jqka.com.cn/ai-web/ai-diagnosis-label.html?id={id}&type={periodType}&label={label}^title=AI选期` | `pages/StrategyDetail` | `label` 使用 URL 编码;尚未调用客户端能力 |
|
||
|
||
## 数据与接口
|
||
|
||
当前 HTTP Fetcher 仅构造请求参数,行情客户端也尚未连接真实客户端桥接层;页面使用 Mock
|
||
数据并以 2.5 秒间隔模拟行情更新。接入真实服务前请阅读:
|
||
|
||
- [市场排名接口](doc/market-ranking-interfaces.md)
|
||
- [AI 选期接口](doc/ai-pick-interfaces.md)
|
||
|
||
不要在源码或提交中新增 Cookie、Token、证书、私钥或本机签名密码。签名材料应通过
|
||
DevEco Studio 在本机配置。
|
||
|
||
## 开发约定
|
||
|
||
- 使用 ArkTS 显式类型,组件采用 ArkUI 声明式写法。
|
||
- 类型和类使用 PascalCase,变量和方法使用 camelCase,常量使用 UPPER_SNAKE_CASE。
|
||
- 公共行情字段统一维护在 `HqFieldId`,避免在业务卡片中硬编码字段 ID。
|
||
- 新增接口或修改 4106 参数时,同步更新 `doc/` 下对应文档。
|