# 鸿蒙基础仓库 **Repository Path**: laobanpi/hongmeng-basic-warehouse ## Basic Information - **Project Name**: 鸿蒙基础仓库 - **Description**: No description available - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-05-08 - **Last Updated**: 2026-05-18 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # taxBase —— 鸿蒙应用基础模板 一个开箱即用的 HarmonyOS 工程模板,封装了启动隐私 / 用户协议流程、全屏沉浸式默认行为、通用 UI 组件、工具类,以及多主体上架(分支 + 签名)的接入约定。 ## 一、概述 clone 本仓库即获得: - 符合规范的隐私政策 / 用户协议启动流程,**协议 URL 跟随 bundleName 动态生成** - 默认开启**全屏沉浸式**,状态栏与底部导航条高度通过 AppStorage 自动注入 - 跨项目复用的通用组件(标题栏、加载态、空态、隐私弹窗)和工具(Preference / Storage / Logger / Http) - 多主体上架的「分支 + 签名」接入约定(与安卓侧一致) - SDK:HarmonyOS 6.0.2 (API 22) ## 二、核心特性 | 能力 | 说明 | |------|------| | 默认全屏沉浸式 | EntryAbility 启动即 setWindowLayoutFullScreen,业务页面零成本 | | 动态状态栏 / 导航条高度 | AppStorage 注入 `statusBarHeight` / `naviIndicatorHeight`,自动跟随旋转 / 折叠态 | | 动态 bundleName 协议 URL | EntryAbility 注入 → AppProfile 拼接 `{base}/{bundleName}/{ystk\|yhxy}.html`,特殊应用支持 if 切换到 `*2.html` | | 动态 app_name AppStorage 注入 | EntryAbility 启动时从 `string.json` 注入 `app_name`,`@StorageProp('app_name')` 直接消费,无需各页 import 资源 | | 设置页应用图标动态显示 | SettingsPage 用 `DrawableDescriptor` 读 `module.json5.icon`,分层 / 普通图标自适应,换图标无需改代码 | | 通用 UI 组件 | CommonTitleBar / CommonHeader / EmptyView / LoadingView / PrivacyDialog | | 弹窗管理组件 | ConfirmDialog / LoadingDialog(含 LoadingManager 单例)/ BottomSheetDialog / CustomContainerDialog | | 动画与过渡 | `AnimUtil` 提供标准点击缩放(`.clickEffect`)、进出场过渡(`.transition`)及弹性曲线,统一交互视觉 | | Toast 工具 | `ToastUtil.show/showLong/showSuccess/showWarn/showError` 静态调用,零样板 | | 样式扩展库 | `CommonStyles.ets` 集中管理 `@Extend`,Button / Text / Column / Row / TextInput | | ViewModel 约定 | `viewmodels/` 目录存放 `*DataStore.ets`,AppStorage 桥接状态,Key 集中声明 | | 完整启动流程 | SplashPage → PrivacyDialog → Index,含 router + NavPathStack 双版本协议页 | | 标准工具类 | PreferenceUtil / StorageUtil / Logger / HttpService | | 默认 4-Tab 主页 | Index.ets 内置「首页 / 发现 / 工具 / 我的」TabBar,图标已就位 | ## 三、目录结构 ``` taxBase/ ├── AppScope/ │ ├── app.json5 # bundleName / vendor / versionCode(克隆后改) │ └── resources/base/ # 应用级图标资源 ├── build-profile.json5 # 工程级构建配置 + signingConfigs ├── entry/ │ ├── build-profile.json5 │ └── src/main/ │ ├── module.json5 # 已加 INTERNET 权限 │ ├── ets/ │ │ ├── entryability/ │ │ │ └── EntryAbility.ets # 全屏沉浸式 + 动态高度 + bundleName 注入 │ │ ├── entrybackupability/ │ │ │ └── EntryBackupAbility.ets │ │ ├── common/ │ │ │ ├── AppProfile.ets # 应用元数据 + 协议 URL 构造 │ │ │ ├── TabRoutes.ets # NavPathStack 路由常量 │ │ │ └── AppTheme.ets # 颜色 / 尺寸 Token │ │ ├── components/ │ │ │ ├── CommonTitleBar.ets # 浅色标题栏(自适应状态栏) │ │ │ ├── CommonHeader.ets # 主色背景标题栏 │ │ │ ├── EmptyView.ets # 通用空态 │ │ │ ├── LoadingView.ets # 通用加载态(行内) │ │ │ ├── ConfirmDialog.ets # ⭐ 通用二次确认弹窗 │ │ │ ├── LoadingDialog.ets # ⭐ 全局加载遮罩 + LoadingManager 单例 │ │ │ ├── BottomSheetDialog.ets # ⭐ 底部弹出面板(@BuilderParam 插槽) │ │ │ ├── CustomContainerDialog.ets # ⭐ 通用自定义内容弹窗容器 │ │ │ └── CommonStyles.ets # ⭐ @Extend 样式扩展库 │ │ ├── view/ │ │ │ └── PrivacyDialog.ets # 启动隐私弹窗 │ │ ├── pages/ │ │ │ ├── Index.ets # ⭐ 4-Tab 容器(Navigation + Tabs) │ │ │ ├── SplashPage.ets # 启动页(弹隐私) │ │ │ ├── PrivacyPage.ets # 隐私协议(router 版) │ │ │ └── UserAgreementPage.ets # 用户协议(router 版) │ │ ├── tabs/ │ │ │ ├── home/HomePage.ets # 首页(占位,业务方替换) │ │ │ ├── discover/DiscoverPage.ets # 发现(占位) │ │ │ ├── tools/ToolsPage.ets # 工具(占位) │ │ │ └── settings/ │ │ │ ├── SettingsPage.ets # 我的(菜单:反馈/协议/隐私/版本) │ │ │ └── pages/ │ │ │ ├── FeedbackPage.ets # 意见反馈(NavPathStack 版) │ │ │ ├── PrivacyPage.ets # 隐私协议(NavPathStack 版) │ │ │ └── UserAgreementPage.ets │ │ ├── utils/ │ │ │ ├── AnimUtil.ets # ⭐ 通用动画、点击反馈、过渡效果 │ │ │ ├── PreferenceUtil.ets # 持久化首选项 │ │ │ ├── StorageUtil.ets # AppStorage 薄封装 │ │ │ ├── Logger.ets # hilog 薄封装 │ │ │ └── ToastUtil.ets # ⭐ Toast 轻提示(show/showLong/showSuccess/showWarn/showError) │ │ ├── viewmodels/ │ │ │ └── SampleDataStore.ets # ⭐ ViewModel 示例(拷贝新建业务 DataStore) │ │ └── service/ │ │ └── HttpService.ets # GET / POST JSON 通用封装 │ └── resources/ │ ├── base/element/ # color / string / float │ ├── base/profile/ # main_pages / backup_config │ └── dark/element/ # 暗色色板 └── README.md ``` ## 四、快速使用 新工程基于本模板初始化的 3 步: 1. **改包名 / 主体**:`AppScope/app.json5` 的 `bundleName` / `vendor` 改为目标值(vendor 即发布主体英文标识) 2. **改应用名**:`AppScope/resources/base/element/string.json` 与 `entry/src/main/resources/base/element/string.json` 的 `app_name`(两侧保持一致) 3. **上传协议 HTML**:在 `http://ys.niukebang.cn/{bundleName}/` 目录上传 `ystk.html`(隐私)和 `yhxy.html`(用户协议) > **AppStorage 自动注入**:`EntryAbility.onWindowStageCreate` 启动时自动把 > - `app_bundleName`(来自 `abilityInfo.bundleName`) > - `app_name`(来自 `resourceManager.getStringByNameSync('app_name')`) > > 写入 AppStorage。业务页面只需 `@StorageProp('app_name')` 即可拿到,**不需要在每页 import string 资源**。SplashPage、PrivacyDialog、AppProfile.buildPolicyWebUrl 都已经在用这两个键。 > **协议文件命名冲突**:服务端如果已有同名 `ystk.html` / `yhxy.html` 占用,可在 `AppProfile.ets` 的 `buildPolicyWebUrl` 里把当前应用名追加到 `useAlt` 条件(如 `appName === '社保医保通'`),自动切到 `ystk2.html` / `yhxy2.html`,不需要改 URL 模板。 替换图标: - 应用图标:放一张 1024×1024 PNG 到 `AppScope/resources/base/media/app_icon.png`,运行分层脚本拆分(详见下) - 启动图:`entry/src/main/resources/base/media/splash_bg.jpeg` - Tab 图标:`entry/src/main/resources/base/media/{main,two,three,set}_{normal,selected}.png`(每对各两张:未选中 + 选中) **分层图标脚本(v2 — 双线性渐变模型)**: ```bash python3 MySkill/harmonyos-layered-icon/scripts/process_icon.py \ AppScope/resources/base/media/app_icon.png ./icon_output ``` 一次生成 **4 件产物**到 `./icon_output/`: | 产物 | 用途 | |------|------| | `foreground.png` | 拷到 `AppScope/` + `entry/.../media/` | | `background.png` | 同上 | | `preview_composite.png` | 人工肉眼核对合成效果 | | `app_icon_store.png` | **直接上传应用市场**,1024×1024 PNG(<3MB),无需二次合成 | v2 用 2×2 BILINEAR resize 构建期望背景模型,能正确保留渐变图标里的深色 banner / 双色拼接区,不会出现 v1 「深色被擦淡」的问题。完整规范见 [MySkill 分层图标 Skill](./MySkill/harmonyos-layered-icon/SKILL.md)。 > 模板已迁入一套来自 WeiZhang4 的可用图标作为默认值,直接编译就能跑;按品牌替换即可。 ## 五、全屏沉浸式默认行为 - EntryAbility 启动时自动 `setWindowLayoutFullScreen(true)` - 注入两个 AppStorage 键(单位 vp): - `statusBarHeight` —— 顶部安全区高度 - `naviIndicatorHeight` —— 底部导航条高度 - 监听 `avoidAreaChange`,旋转 / 折叠态切换时自动更新 ### 5.1 底部 28vp 兜底(上架硬性要求) AppGallery 审核会扫描底部控件距屏幕底边的距离,**< 28vp 即触发**「应用未适配导航条」警告(参考 [华为底部导航条适配规范](https://developer.huawei.com/consumer/cn/forum/topic/0214200865840885234?fid=0102104600515103427))。 直接用 `padding({ bottom: this.naviIndicatorHeight })` 在 navi 高度为 0 的机型(手势导航关闭 / 部分折叠态)会失去 28vp 安全距离 → 审核挂。模板统一用 `utils/SafeArea.ets` 的 `bottomSafeArea` 函数避免重复造轮子: ```ts import { bottomSafeArea } from '../utils/SafeArea' @StorageProp('statusBarHeight') statusBarHeight: number = 0 @StorageProp('naviIndicatorHeight') naviIndicatorHeight: number = 0 build() { Column() { // 自定义标题栏顶部 padding Row().padding({ top: this.statusBarHeight }) // ... } .padding({ bottom: bottomSafeArea(this.naviIndicatorHeight) }) // = max(navi, 28) } ``` `bottomSafeArea(navi)` 等价于 `Math.max(navi, 28)`,保证任何机型至少留 28vp 底部避让。 ### 5.2 模板已内置的避让 | 位置 | 说明 | |------|------| | `pages/Index.ets` | 4-Tab 主容器统一在外层 Column 给 `Tabs` 加底部 padding,**子 Tab 页(HomePage 等)不要重复加 padding** | | `pages/SplashPage.ets` | 启动页 progress + welcome text 区域已加 | | `pages/PrivacyPage / UserAgreementPage`(router 与 NavPathStack 两版) | 协议页 WebView 容器已加 | | `tabs/settings/pages/FeedbackPage.ets` | 反馈页提交按钮区已加 | | `components/BottomSheetDialog.ets` | 底部弹出面板内部已加 | | `CommonTitleBar / CommonHeader` | 顶部 statusBarHeight 已内置 | 新增业务页面默认走 `bottomSafeArea`;4-Tab 体系内的 Tab 子页不需要自己加底部 padding(Index 统一处理)。 ## 六、默认 4-Tab 主页结构 `pages/Index.ets` 已搭好 `Navigation + Tabs` 主框架,4 个 Tab 默认接入: | 序号 | 标题 | 内容 | 图标资源 | |------|------|------|---------| | 0 | 首页 | `tabs/home/HomePage.ets` | `main_normal/selected.png` | | 1 | 发现 | `tabs/discover/DiscoverPage.ets` | `two_normal/selected.png` | | 2 | 工具 | `tabs/tools/ToolsPage.ets` | `three_normal/selected.png` | | 3 | 我的 | `tabs/settings/SettingsPage.ets` | `set_normal/selected.png` | 设计要点: - `Index` 用 `@Provide('pageStack')` 下发 NavPathStack,子页面 `@Consume` 获取并 `pushPathByName` - `navDestination` Builder 集中映射 4 条设置路由(隐私 / 协议 / 关于 / 反馈) - 图标 + 启动图均迁移自 WeiZhang4,可直接替换 `entry/src/main/resources/base/media/{main,two,three,set}_{normal,selected}.png` 与 `splash_bg.jpeg` 来换风格 业务接入:把对应 Tab 文件(如 `HomePage.ets`)的内容替换为业务模块即可,不必动 `Index.ets`。 「我的」Tab 内置 4 个菜单项: | # | 菜单 | 行为 | |---|------|------| | 1 | 意见反馈 | 跳 `FeedbackPage`(含问题类型选择 + 内容 + 联系方式) | | 2 | 用户协议 | 跳 `tabs/settings/pages/UserAgreementPage`(WebView) | | 3 | 隐私政策 | 跳 `tabs/settings/pages/PrivacyPage`(WebView) | | 4 | 版本号 | 仅显示 `APP_METADATA.versionName`,不可点击 | 版本号自动从 `common/AppProfile.ets` 的 `APP_METADATA.versionName` 读取,与 `AppScope/app.json5` 的 `versionName` 保持一致即可。 ## 七、组件架构 | 模块 | 说明 | |------|------| | `common/` | 应用元数据、路由常量、主题 Token | | `components/` | 通用 UI(标题栏、空态、加载态、弹窗三件套、样式扩展库) | | `view/` | 业务弹窗(隐私同意) | | `pages/` | 启动 + 协议页(router 版) | | `tabs/settings/` | 设置页 + NavPathStack 协议页 | | `utils/` | Preference / Storage / Logger / ToastUtil | | `service/` | HttpService | | `viewmodels/` | 业务 DataStore,通过 AppStorage 管理全局状态(见 7.2 节) | ### 7.1 路由协议页两个版本 - **router 版** `pages/PrivacyPage.ets` `pages/UserAgreementPage.ets` - 启动流程内(SplashPage → 协议)使用,`@Entry`,独立路由 - **NavPathStack 版** `tabs/settings/pages/*.ets` - Tab 内(设置页 → 协议)使用,`@Component` + `NavDestination`,统一栈管理 接入新版协议页只需在 SettingsPage 的 NavPathStack 注册路由(已预设常量),并把对应组件挂到 navDestination Builder 即可。 ### 7.2 ViewModel / DataStore 约定 业务数据状态通过 AppStorage 桥接,约定在 `viewmodels/` 目录存放: - **文件命名**:`XxxDataStore.ets`(如 `UserDataStore.ets`、`OrderDataStore.ets`) - **类型**:纯静态类,无需实例化 - **Key 集中声明**:以 `static readonly KEY_*` 常量形式写在类内,避免魔法字符串散落各处 ```ts export class UserDataStore { static readonly KEY_NICKNAME = 'user_nickname' static setNickname(val: string) { AppStorage.setOrCreate(UserDataStore.KEY_NICKNAME, val) } static getNickname(): string { return AppStorage.get(UserDataStore.KEY_NICKNAME) ?? '' } } ``` 页面中响应式消费: ```ts @StorageProp(UserDataStore.KEY_NICKNAME) nickname: string = '' ``` 拷贝 `viewmodels/SampleDataStore.ets` 并按注释替换类名与字段即可新建 DataStore。 ### 7.3 通用弹窗系列 | 组件 | 文件 | 用途 | |------|------|------| | `ConfirmDialog` | `components/ConfirmDialog.ets` | 二次确认(可配标题/内容/按钮文字/颜色/回调) | | `LoadingDialog` + `LoadingManager` | `components/LoadingDialog.ets` | 全屏加载遮罩,Service 层调用 `LoadingManager.show()/hide()` | | `BottomSheetDialog` | `components/BottomSheetDialog.ets` | 底部面板,内容区通过 `@BuilderParam` 完全自定义 | | `CustomContainerDialog` | `components/CustomContainerDialog.ets` | 通用自定义弹窗,提供标准背景和圆角,内容完全由 `@BuilderParam` 自定义 | 详细用法见各文件头部注释。 ### 7.4 Toast 工具 ```ts import { ToastUtil } from '../utils/ToastUtil' ToastUtil.show('保存成功') ToastUtil.showSuccess('上传完成') ToastUtil.showWarn('网络较慢,请稍候') ToastUtil.showError('请求失败,请重试') ToastUtil.showLong('这是一条较长时间显示的提示') ``` ### 7.5 样式扩展库(@Extend) `components/CommonStyles.ets` 集中维护 `@Extend` 装饰器,消除各页面内联重复样式: | 扩展函数 | 目标组件 | 效果 | |----------|----------|------| | `primaryBtn()` | Button | 主色实心大按钮 | | `outlineBtn()` | Button | 主色描边大按钮 | | `dangerBtn()` | Button | 危险操作红色按钮 | | `titleText()` | Text | 页面一级标题 | | `bodyText()` | Text | 正文内容 | | `captionText()` | Text | 辅助说明文字 | | `linkText()` | Text | 可点击超链接 | | `cardColumn()` | Column | 标准白色卡片 | | `listItemRow()` | Row | 标准列表行 | | `standardInput()` | TextInput | 标准输入框 | ```ts import { primaryBtn, bodyText, cardColumn } from '../components/CommonStyles' Button('提交').primaryBtn() Text('内容说明').bodyText() Column().cardColumn() { ... } ``` ### 7.6 动画与过渡(AnimUtil) `utils/AnimUtil.ets` 集中管理应用内常用的动画效果、点击反馈、页面过渡,保证视觉统一: - **点击反馈**:`AnimUtil.clickLight` / `AnimUtil.clickHeavy`,搭配组件 `.clickEffect()` 属性使用。 - **页面/组件过渡**:`AnimUtil.scaleFadeTransition`(缩放淡入淡出)、`slideUpFadeTransition`(底部滑入)、`slideRightFadeTransition`(右侧滑入),搭配组件 `.transition()` 属性使用。 - **动画曲线**:内置推荐的弹性曲线 `AnimUtil.springCurve`。 ## 八、资源约定 - `resources/base/element/color.json` —— 11 个语义色(`primary_color`、`bg_page`、`text_primary` 等) - `resources/base/element/string.json` —— `app_name` 与 `EntryAbility_*` 文案 - `resources/base/element/float.json` —— 字号 / 间距 / 圆角常量 - `resources/dark/element/color.json` —— 暗色模式同名色板 - `AppScope/resources/base/media/` —— 应用图标(`layered_image.json` + `foreground.png` + `background.png`) 代码中也有 `common/AppTheme.ets` 提供同样的常量类(`AppColors` / `AppDimens`),用于不便引用资源 ID 的场景。两者保持值一致。 ## 九、多主体上架 不引入 product flavor。每个主体 = 一条分支。 ### 11.1 分支命名规则 ``` master # 主体: 三条 xxxx_blm # 主体: 班灵猫 xxxx_yl # 主体: 有俍 xxxx_qx # 主体: 趣信 xxxx_xh # 主体: 星辉 xxxx_ls # 主体: 烙熵 xxxx_ctl # 主体: 磁通量 xxxx_{主体缩写}_{App中文名} # 同主体多软著 xxxx_{主体缩写}_{App中文名}_{渠道名} # 同 App 渠道大改版 ``` ### 11.2 切分支接入清单 从 master 切出新主体分支后,依次改: 1. `AppScope/app.json5` 的 `bundleName` / `vendor` / `versionCode` / `versionName`(`vendor` 即发布主体英文标识) 2. `AppScope/resources/base/element/string.json` 的 `app_name`,以及 `entry/src/main/resources/base/element/string.json` 的 `app_name` / `EntryAbility_label` / `EntryAbility_desc` / `module_desc` 3. `AppScope/resources/base/media/{foreground,background}.png` 图标 4. `entry/src/main/module.json5` 的 `startWindowIcon`(推荐改为 `$media:foreground` 或替换对应 `startIcon.png`,使启动屏图标与应用图标视觉一致) 5. 本地准备新主体的签名材料(详见第十一章) 6. `build-profile.json5` 的 `signingConfigs`(路径与别名指向新主体) 7. 在 `http://ys.niukebang.cn/{新bundleName}/` 上传 `ystk.html` 和 `yhxy.html` 8. **运行上架前自检**:参考 [MySkill/harmonyos-prerelease-check](./MySkill/harmonyos-prerelease-check/SKILL.md),批量校验 bundleName / app_name / icon / startWindowIcon / label / 签名 / 协议 URL 是否都已切到新主体 ## 十、隐私 / 用户协议接入说明 ### 11.1 动态 bundleName 原理 ``` EntryAbility.onWindowStageCreate ├─ AppStorage.setOrCreate('app_bundleName', this.context.abilityInfo.bundleName) └─ AppStorage.setOrCreate('app_name', resourceManager.getStringByNameSync('app_name')) AppProfile.buildPolicyWebUrl(type) ├─ 读 AppStorage('app_bundleName') → 拼出 {base}/{bundleName}/{file}.html └─ 读 AppStorage('app_name') → 命中 useAlt 条件时切到 ystk2/yhxy2 ``` 模板代码不写死 bundleName 也不写死 app_name,换应用只需改 `AppScope/app.json5` + `string.json` 两处。 **特殊应用切协议文件**:服务端协议文件命名冲突时(例如同一 bundleName 下需要切到 v2 版),在 `AppProfile.ets` 的 `buildPolicyWebUrl` 里追加 `|| appName === 'XXX'`: ```ts const useAlt = appName === '社保医保通' // 按需 || appName === '其他应用' const privacyFile = useAlt ? 'ystk2.html' : 'ystk.html' const agreementFile = useAlt ? 'yhxy2.html' : 'yhxy.html' ``` ### 11.2 弹窗触发时机 - 首次启动:SplashPage 进度为 0 时弹 PrivacyDialog - 用户点「同意并继续」→ 进度条开始走 → 满 100 跳 Index - 用户点「拒绝并退出」→ `context.terminateSelf()` 如需「同意一次后不再弹」,在 PrivacyDialog 同意回调中用 `PreferenceUtil.put('privacy_agreed', true)`,并在 SplashPage `onPageShow` 加判断。 ## 十一、打包与签名配置 ### 11.1 必备本地文件 签名材料路径自定,放本地任意位置即可(建议加入 `.gitignore`,模板已做)。 | 文件 | 作用 | 来源 | 命名规范 | |------|------|------|----------| | `{主体名}.p12` | 签名密钥库 | DevEco Studio 生成或团队提供 | 例:`luoshang.p12` | | keyAlias | p12 内部别名(**不是独立文件**) | 与 .p12 同名前缀 | 例:`luoshang.keystore` | | `{主体名}.cer` | 数字证书 | 用 .csr 在华为开发者联盟申请下载 | 例:`luoshang.cer` | | `{appName}.csr` | 证书请求 | DevEco Studio 用已有 .p12 生成(详见 11.3) | 例:`MyApp.csr` | | `{appName}.p7b` | Profile | AppGallery Connect 按 bundleName 申请下载 | 例:`MyApp.p7b` | **密码**:团队内部约定值,由签名管理员保管。**不写入代码 / 注释 / README**。 ### 11.2 build-profile.json5 → signingConfigs ```json5 "signingConfigs": [{ "name": "default", "type": "HarmonyOS", "material": { "certpath": "../signing/luoshang.cer", // 路径按本地实际位置填 "storePassword": "******", // 团队约定值 "keyAlias": "luoshang", // p12 内部别名前缀 "keyPassword": "******", "profile": "../signing/MyApp.p7b", "signAlg": "SHA256withECDSA", "storeFile": "../signing/luoshang.p12" } }] ``` ### 11.3 CSR 生成步骤(DevEco Studio) 1. `Build → Generate Key and CSR` 2. 选 **Existing key store file** → 选已有 `.p12`(如 `luoshang.p12`),输入 store 密码 3. 选已有别名(如 `luoshang.keystore`),输入 key 密码 4. CSR 输出文件名规范:`{App名称英文}.csr`(每个 App 独立生成) 5. 把 .csr 上传至华为开发者联盟 → 下载 `.cer` 数字证书 ### 11.4 Profile 申请与替换 1. AppGallery Connect → 我的项目 → 选对应 App 2. HarmonyOS 应用 → 证书、APP ID 和 Profile → 申请发布 Profile(绑定第 11.3 步的 .cer + 当前 bundleName) 3. 下载 `.p7b`,重命名为 `{appName}.p7b` 4. 调试期可用 Debug Profile,**发布前必须替换为 Release Profile** ### 11.5 切主体打包流程 ```bash # 1. 切主体分支 git checkout -b xxxx_yl # 2. 改代码(按 11.2 节清单 1-2) # AppScope/app.json5 + 两侧 string.json # 3. 替换签名材料路径(11.2 节 5-6) # 本地准备好「有俍」主体的 .p12 / .cer + 当前 App 的 .csr/.p7b # 改 build-profile.json5 → signingConfigs.material 路径 # 4. DevEco Studio: Build → Build Hap(s)/APP(s) → Build APP(s) # 输出 .app 文件用于上架 ``` ### 11.6 安全提醒 - **禁止**把 `.p12` / `.cer` / `.p7b` / `.csr` 提交到 git(模板 `.gitignore` 已排除) - 密码由签名管理员保管,不要写到代码注释、README、聊天记录 - 主体 `.p12` 丢失需重新生成密钥并重新申请 .cer / .p7b,会影响线上版本升级 ## 十二、应用开发完毕:上架前自检 走完 11.2 节接入清单 + 业务功能开发后,**强烈建议运行上架前自检 Skill**,避免模板默认值流到 AppGallery 审核。 - 入口:[MySkill/harmonyos-prerelease-check/SKILL.md](./MySkill/harmonyos-prerelease-check/SKILL.md) - 流程:按 6 组共 17 项校验扫描 → 输出红绿灯报告 → 引导自动修复 - 也可命令行直接跑机械校验: ```bash python3 MySkill/harmonyos-prerelease-check/scripts/prerelease_check.py --root . ``` 重点拦截 4 类易遗漏问题: 1. `app_name / EntryAbility_label / module_desc` 残留 `"taxBase"` 模板字样 2. `module.json5.startWindowIcon` 仍指向旧 `$media:startIcon`,启动屏图标与应用图标不一致 3. `bundleName` / `vendor` 仍是模板默认 `com.santiao.tax` / `example` 4. `build-profile.json5` 仍用 DEBUG 签名(含 `debugKey` / `default_taxBase` 字样) ## 十三、上架资料清单 参考 [MySkill 分层图标规范](./MySkill/harmonyos-layered-icon/SKILL.md): - **应用包内图标**:1024×1024 前景图(透明背景)+ 1024×1024 背景图(纯色 / 渐变,无透明),通过 `layered_image.json` 关联 - **应用市场展示位图标**:直接上传脚本产出的 `app_icon_store.png`(1024×1024 PNG,<3MB),与包内图标视觉一致 - **截图**:每个市场要求不一,建议准备 1080×2340(竖屏)至少 4 张 - **应用介绍**:30 字以内一句话简介 + 200 字详细介绍 - **隐私网址**:`http://ys.niukebang.cn/{bundleName}/ystk.html` - **软著**:与发布主体匹配 ## 十四、常见问题(FAQ) **Q:协议 URL 404?** 检查 `http://ys.niukebang.cn/{bundleName}/` 目录下是否上传了 `ystk.html` 和 `yhxy.html`,bundleName 与 `AppScope/app.json5` 一致。 **Q:换主体后图标 / 应用名没变?** 检查 `AppScope/resources/base/element/string.json` 的 `app_name` 和 `AppScope/resources/base/media/` 下图标是否替换。 **Q:自定义页面顶部被状态栏遮挡?** 开了全屏沉浸式后业务页面要自己避让。加 `@StorageProp('statusBarHeight')` 并对顶部容器 `padding({ top: this.statusBarHeight })`,或直接用 `CommonTitleBar` / `CommonHeader`(内置消费)。 **Q:签名报错 "profile bundleName mismatch"?** 申请 Profile 时填的 bundleName 必须与 `AppScope/app.json5` 完全一致。换 bundleName 必须重新申请 Profile。 **Q:怎么用别人的 .p12 重新生成 CSR?** 必须有 .p12 文件 + store 密码 + key 密码(同一个值)。DevEco Studio `Build → Generate Key and CSR` 选 "Existing key store file"。 **Q:是否需要在每个项目里重复 PrivacyDialog?** 不需要。模板已内建,新工程克隆即可用。 **Q:加业务权限怎么办?** 在 `entry/src/main/module.json5` 的 `requestPermissions` 数组追加,例如: ```json5 { "name": "ohos.permission.CAMERA" } { "name": "ohos.permission.LOCATION" } ``` 危险权限还需在使用前调用 `abilityAccessCtrl` 请求用户授权。