# 小程序及快应用前端源码 ## 1. 📋 项目说明 该项目基于 **uni-app** 开发构建,请使用 [HBuilderX](https://www.dcloud.io/hbuilderx.html) 进行代码开发及构建发布。 ## 2. 🛠️ 开发说明 - 源码基于 **Vue2** 版本 - SCSS 采用 **dart-sass** 进行编译输出 ## 3. 🔧 开发调试说明 ### 3.1 注意事项 - 应用访问等关键参数的配置来源于 `./common/js/config.js` ### 3.2 本地调试 `.local.config.js.example` 是本地调试配置示例。 拷贝 `.local.config.js.example` 为 `.local.config.js` 文件,默认开发调试模式会自动加载该文件 --- ## 4. 📦 发布说明 ### 4.1 小程序发布 #### 4.1.1 基本操作步骤(通用版/定制化版) ##### ##### ##### ##### 【前置准备】 1. 在项目根目录打开终端安装依赖: ```bash npm install ``` *已有依赖可跳过此步骤* ##### ##### ##### ##### 【发布构建】 1. 使用 HBuilderX 打开项目(不用改manifest.json的web,只有H5要改 ) 2. 选择菜单栏「发行」 → 「小程序-微信」→ 「发行」,等待构建完成 ⚠️ **注意**:底部控制台弹出"请在微信小程序开发者工具中点击上传"后再执行下一步 3. 构建完成后,**自动执行**补丁脚本(拷贝 site.js / config 文件 + 生成 ZIP 包),无需手动操作 4. 找到项目根目录 `/unpackage/dist/build` 下生成的 mp-weixin 压缩包 💡 **示例**:`mp-weixin-2026-05-11-3bff91e.zip` *压缩包文件名末尾为 Git 提交 ID* *同时该目录下会生成未压缩的 `mp-weixin` 目录* ##### ##### ##### ##### 【验证与重命名】 1. 打开未压缩的 `mp-weixin` 目录,找到 `site.js` 文件,将文件内的 `uniacid` 值改为当前客户编号(如:2812)并保存 2. 打开微信开发者工具,导入「mp-weixin」目录,点击「编译」,在开发者工具控制台验证有无报错,以及能否返回对应客户的业务数据 3. 确保无误后将生成的 mp-weixin 压缩包重命名 - **定制化版**格式:`定制化-客户编号-域名-mp-weixin-当前日期-Git提交ID.zip` - **通用版**无需重命名 💡 **示例**:`custom-2812-xcx.aigc-quickapp.com-mp-weixin-2026-05-11-3bff91e.zip` 🚫 **禁止**:压缩包命名禁止包含 `/ \ : * ? " < > |` 等特殊字符 ##### ##### ##### ##### 【交付与最终发布】🔍 1. 将重命名后的文件发送给技术人员 *📌通用版直接将生成的 mp-weixin 压缩包发送给技术人员* 2. **技术人员操作流程**: - 解压压缩包 - 确认 `site.js` 中的 `uniacid` 为客户编号 - 用微信开发者工具导入解压后的代码目录 - 编译验证无误后,上传发布 ### 4.2 快应用发布 #### 4.2.1 基本操作(通用版/定制化版) ##### 4.2.1.1 【发布构建】 1. 使用 HBuilderX 打开项目 2. 打开项目根目录的 `manifest.json` 文件,切换至可视化配置界面: 1. 依次点击「Web 配置」→「运行的基础配置」 2. 根据情况查看是否修改路径中/hwappx/????/的客户编号 📌 **通用版**保留原有 `/hwappx/common/` 📌 **测试版**保留原有 `/hwappx/test/` 💡 **个性化定制示例**:`/hwappx/2811/`,其中 2811 为定制化版客户编号 3. 选择菜单栏「发行」 → 「自定义发行」 → 选择对应的站点脚本 💡 例如:网址是 `5g.aigc-quickapp.com` 选 `H5-5g.aigc-quickapp.com` 4. 修改以下配置: 1. **网站标题**:快应用 2. **网站域名**:当前客户域名 3. 确认后点击「发行」等待构建完成 ⚠️ **注意**:构建完成后自动输出到 `unpackage/dist/build/h5/`,格式为 `{域名}-{base路径}.zip` 💡 **示例**:`xcx.aigc-quickapp.com-hwappx.common-3bff91e-20260511_1058.zip` ##### 4.2.1.2 【交付与最终发布】🔍 1. 将生成的 `.zip` 文件发送给运维人员 2. **运维人员操作流程**: 1. 解压压缩包 2. 打开快应用开发者工具,导入解压后的代码目录 3. 验证代码无报错后,执行上传发布操作 ##### 4.2.1.3 【测试流程】 H5:打开自己的浏览器输入网址(https://5g.aigc-quickapp.com/hwappx/common/?uniacid=75)改站点 如果是test就把hwappx/common/改为hwappx/test/----------改站点 --- ### 4.3 鸿蒙元服务发布(mp-harmony) #### 4.3.1 【发布构建】 1. 使用 HBuilderX 打开项目 2. 选择菜单栏「发行」 → 「HarmonyOS」→ 「发行」,等待构建完成 3. 构建完成后,**自动执行** `harmony.patch.js` 补丁脚本(配置拷贝 + ZIP 打包) 4. 找到项目根目录 `/unpackage/dist/build` 下生成的 mp-harmony 压缩包 #### 4.3.2 【DevEco Studio 编译与产物】 在 DevEco Studio 中导入 `mp-harmony` 工程后,点击「Build」→「Build App(s)/Project(s)」,实际执行的任务是 `assembleApp`: ``` # 由 DevEco Studio 自动执行,无需手动敲 hvigorw --mode project -p product=default assembleApp ``` 这个任务是**两步串联**的 —— **先逐个 module 编译出包,再把这些包打成 APP**: | 产物 | 路径 | 说明 | |------|------|------| | 分包(中间产物) | `{module}/build/default/outputs/default/{module}-default-signed.{hap\|hsp}` | 每个 module 一个,编译+签名的最小单位 | | APP(最终产物) | `build/outputs/default/mp-harmony-default-signed.app` | 把全部分包收拢成的分发单元 | 📌 **元服务上架 AppGallery Connect 上传的是 `.app`**,不是 `.hap`。`pack.info` 中 `"bundleType": "atomicService"` 即元服务包标记。 📌 **本工程共 1 个 HAP + 5 个 HSP**(不是 6 个 HAP)—— `module.json5` 里 `type: "entry"` 编成 **HAP**,`type: "shared"` 编成 **HSP**(共享包,动态下发)。详见 `build/outputs/default/pack.info`: | 包名 | 扩展名 | moduleType | 说明 | |------|--------|------------|------| | `entry-default` | `.hap` | `entry` | 主包,EntryAbility + 服务卡片 + uniapp 主 bundle | | `components_diy-default` | `.hsp` | `shared` | ascf 动态化分包 | | `pages_goods-default` | `.hsp` | `shared` | ascf 动态化分包 | | `pages_order-default` | `.hsp` | `shared` | ascf 动态化分包 | | `pages_promotion-default` | `.hsp` | `shared` | ascf 动态化分包 | | `pages_tool-default` | `.hsp` | `shared` | ascf 动态化分包 | ⚠️ **主包体积**:元服务官方要求**单个包 ≤ 2MB**(按压缩后算,含其直接依赖的 HSP),同一设备类型下全部分包总和 ≤ 10MB。实测本工程主包 `entry-default.hap` 约 **2.37 MiB**,已超单包 2MB 限制;总包约 **6.39 MiB**,在 10MB 以内。 > 若只想单模块调试,可在 DevEco 中用「Build Hap(s)/Module(s)」(即 `assembleHap`),只产出分包、不打 APP。 #### 4.3.3 【工程包导出(换机编译)】 需要在一台**没有本仓库 / 没有 HBuilderX** 的电脑上编译鸿蒙工程时,用脚本把 `stub-app-projects/mp-harmony` 里编译所需的文件导成一个 zip: ```bash npm run pack:harmony ``` 输出到 `unpackage/dist/harmony-export/mp-harmony-project-v<日期>-<提交ID>.zip`,解压后即得 `mp-harmony/` 目录。 | 参数 | 用途 | |------|------| | (无参数) | 默认行为:含签名证书,不含 `node_modules` | | `--no-certs` | 不含 `cert-profile/`(见下方安全提示) | | `--with-node-modules` | 额外含 `hvigor/node_modules`(约 320 MB),供**离线**目标机 | | `--out <目录>` | 自定义输出目录 | **脚本全程只读源目录**,产物先复制到暂存目录再压缩,不修改 `stub-app-projects` 下任何文件。 **导出内容** | 类别 | 说明 | |------|------| | 工程源码 | `AppScope/`、`entry/src/`、`ascf/ascf_build/*/src/`、各类 `*.json5` / `hvigorfile.ts` | | `ascf/ascf_src/` | uniapp 编译产物(页面/组件代码),**带上它目标机就不需要本仓库和 HBuilderX** | | `cert-profile/` | 签名证书(`--no-certs` 可排除) | **已排除**(可由目标机 DevEco 重新生成) `node_modules/`(约 320 MB)、`oh_modules/`、`**/build/`、`.preview/`(约 22 MB)、`.idea/`、`.hvigor/`、`local.properties`、`.cxx/`、`.test/` > ⚠️ **证书安全**:默认导出**包含签名私钥** `cert-profile/qq/qq.p12`,且 p12 密码在 `build-profile.json5` 中本就是明文。请仅在可信渠道传递,用完及时销毁或归档。对外发给不可信对象时务必加 `--no-certs`。 > > 📌 缺证书 hvigor 无法签名,产不出可上传的 `.app`。用 `--no-certs` 时需自行把 `cert-profile/` 放到目标工程对应位置。 **目标机操作** 1. 安装 DevEco Studio 5.0.x(本工程 `compatibleSdkVersion` 5.0.0(12)) 2. 解压 zip,得到 `mp-harmony/` 3. DevEco Studio「打开」`mp-harmony` 目录本身 4. 首次打开自动执行依赖同步(`ohpm install` 拉取 `hvigor/node_modules`),**需联网**;离线机请用 `--with-node-modules` 导出 5. 「Build」→「Build App(s)/Project(s)」,产物 `build/outputs/default/mp-harmony-default-signed.app` > ⚠️ **`ascf/ascf_src/` 是导出时刻的快照**。若前端代码有更新,必须回源机器重新执行导出,否则编译出的包内容是旧的。 📌 zip 由脚本内纯 Node 实现(`zlib` + 手写 ZIP 头)生成,条目名统一正斜杠并置 UTF-8 标志位,可跨 Windows/macOS/Linux 使用。 **产出后自检(每次导出都会跑)** 脚本写完 zip 后会把文件**重新解析一遍**,任何一项不符直接判失败、不输出「完成」: | 校验项 | 内容 | |---|---| | EOCD / 中央目录 | 签名、条目数、中央目录偏移与长度边界一致 | | `localOffset` | 每条都必须真的指向本地文件头,且文件名与中央目录一致 | | CRC | **逐条**解压后重算 CRC32 比对 | | 条目名 | 必须正斜杠、必须置 UTF-8 标志位、必须可逆解码为合法 UTF-8 | | 内容断言 | 构建必需文件齐全;`node_modules`/`build`/`.preview` 等排除项不得混入 | ```bash npm test # 跑全量测试, 含 pack-harmony-project.test.js 的 16 条用例 ``` > 📌 测试分**正向**(写出来能还原)与**反向**(故意篡改 zip,断言自检能抓到)两类。反向用例锁定了开发期真实踩过的三个 bug:中央目录偏移漏算文件数据长度、`localOffset` 记成数据区起点、`.test` 子串匹配误伤 `entry/src/ohosTest/ets/test/` 源码。**反向用例如果失效,自检就形同虚设**,改动脚本时务必保持它们通过。 #### 4.3.4 【常见错误】 **❌ `hvigor ERROR: 00303218 Configuration Error`** ``` Error Message: The reason and usedScene attributes are mandatory for user_grant permissions. at entry\src\main\module.json5 ``` **原因**:`user_grant` 类型权限必须**同时**声明 `reason` 和 `usedScene` 两个字段,只写 `usedScene` 会导致 `:entry:default@PreBuild` 失败。 **解决**:给对应权限补上 `reason` 字段,并在 `entry/src/main/resources/base/element/string.json` 中新增对应的理由文案。 ```json5 // entry/src/main/module.json5 { "name": "ohos.permission.MICROPHONE", "reason": "$string:permission_microphone_reason", "usedScene": { "abilities": ["EntryAbility"], "when": "always" } } ``` ```json // entry/src/main/resources/base/element/string.json { "name": "permission_microphone_reason", "value": "用于语音搜索与语音输入功能,录制语音以识别用户需求" } ``` 📌 **权限类型速查** —— 只有 `user_grant` 才需要 `reason`: | 权限 | 类型 | 需要 reason | |------|------|-------------| | `ohos.permission.INTERNET` | system_grant | 否 | | `ohos.permission.GET_NETWORK_INFO` | system_grant | 否 | | `ohos.permission.VIBRATE` | system_grant | 否 | | `ohos.permission.MICROPHONE` | **user_grant** | **是** | 📌 `reason` 文案必须是**用户可读的中文说明**,不能为空或写 `xxx description` 之类的占位文本,否则上架审核会被驳回。 📌 **改完 `reason` 还不够** —— `user_grant` 权限编译通过后,仍需在运行时弹窗申请,否则录音等功能不可用。本项目已在 [EntryAbility.ets](stub-app-projects/mp-harmony/entry/src/main/ets/entryability/EntryAbility.ets) 的 `onCreate` 中接入: ```ts onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void { // 必须先调父类: ascf runtime 的初始化在 AscfUIAbility.onCreate 里, 漏掉会导致元服务白屏 super.onCreate(want, launchParam); this.requestMicrophonePermission(); } ``` > ⚠️ **坑**:`AscfUIAbility` 父类**已定义** `onCreate`,重写时必须调 `super.onCreate(want, launchParam)`,否则 ascf runtime 初始化被跳过,元服务白屏。 > ⚠️ **坑**:`Permissions` 在 `@ohos.permissions` 里是**字符串联合类型**([permissions.d.ts](https://gitee.com/openharmony/docs/blob/master/zh-cn/application-dev/reference/apis-ability-kit/js-apis-permissions.md)),不是枚举,**只能用在类型位置**,当值引用会报: > ``` > 'Permissions' only refers to a type, but is being used as a value here. > ``` > 所以正确写法是标注 + 字面量,且权限名必须与 `module.json5` 中声明的一致: > ```ts > import { abilityAccessCtrl, AbilityConstant, Permissions, Want } from '@kit.AbilityKit'; > > const PERMISSION_MICROPHONE: Permissions = 'ohos.permission.MICROPHONE'; > // ❌ Permissions.MICROPHONE —— 编译不过 > // ⚠️ abilityAccessCtrl.SwitchType.MICROPHONE(=1) 是"开关类型",不是权限名,别搞混 > ``` 📌 为什么在 `onCreate` 申请而不是"用户点录音时才申请": - 权限状态在 JS 侧(`uni.getRecorderManager`)读不到,项目里也没有现成的 JS ↔ ArkTS 通道,做不了按需触发 - `requestPermissionsFromUser` 对已授权的应用直接 resolve 且**不弹窗**,所以实际只有首次启动打扰用户一次 📌 用户拒绝后的降级:不影响浏览/下单/卡片等其他功能,仅语音输入不可用;[ai-chat-voice-panel.vue](pages_tool/ai-chat/components/ai-chat-voice-panel.vue) 会 catch 到 recorder 错误并 `showToast` 提示用户。 #### 4.3.5 【待完善】⚠️ - **release 签名仍指向 debug 证书**:`build-profile.json5` 中 `release` 签名的 `profile` 仍是 `./cert-profile/qq/qqDebug.p7b`,与 `default`(debug)共用同一份证书。本地调试无碍,但提交 AppGallery 前必须换成**发布证书 + release 类型 provisioning profile**,否则 AGC 校验会直接拒绝。 #### 4.3.6 【交付与最终发布】🔍 1. 将生成的 `.zip` 文件发送给运维人员 2. **运维人员操作流程**: - 解压压缩包 - 打开鸿蒙开发者工具,导入解压后的代码目录 - 验证代码无报错后,执行上传发布操作 #### 4.3.7 【本地设置】 ![](readme_files/1.jpg) --- ## 5. 📝 构建与版本信息 ### 5.1 自动注入机制 生产构建时,`vue.config.js` 中的 `VersionInjectPlugin` 会自动执行以下操作: | 操作 | 适用平台 | 说明 | |------|---------|------| | `__app_version` 全局变量注入 | 所有平台 | 构建产物的 `vendor.js`(小程序)或 `index.html`(H5)中自动内联版本数据 | | 构建版本信息查看 | 所有平台 | 首页右下角连击 5 次弹出模态框显示 `commitId` 和 `buildTime` | | 极限压缩优化 | 小程序(mp-weixin / mp-harmony) | 生产构建自动应用多层次压缩配置,显著减小产物包体积 | 版本数据包含: - **commitId**:当前构建的 Git 短提交 ID - **buildTime**:构建时间 > **开发模式**:HMR 热更新时由 webpack emit 钩子自动处理,不影响开发体验。 ### 5.2 自动 ZIP 打包 **H5 自定义发行**构建完成后自动执行: | 步骤 | 说明 | |------|------| | 重命名输出目录 | `{域名}-{base路径用.连接}`(如 `xcx.aigc-quickapp.com-hwappx.common`) | | 创建 ZIP 文件 | 文件名格式:`{目录名}-{Git提交ID}-{上海时区时间戳}.zip` | | 清理 | ZIP 创建完成后自动删除源目录 | 💡 **示例 ZIP**:`xcx.aigc-quickapp.com-hwappx.common-3bff91e-20260511_1058.zip` **微信小程序**生产构建时自动执行: - 拷贝 `site.js` / `project.config.json` / `project.private.config.json` 文件到构建目录 - 创建 ZIP 包(文件名包含 Git 提交 ID) **鸿蒙小程序**生产构建时自动执行: - 拷贝必要配置文件到构建目录 - 创建 ZIP 包(文件名包含 Git 提交 ID) ### 5.3 环境配置 域名配置统一通过 `package.json` 中各脚本的 `env.UNI_APP_DOMAIN` 传入: ```json "h5-xcx.aigc-quickapp.com": { "title": "...", "env": { "UNI_PLATFORM": "h5", "UNI_APP_DOMAIN": "https://xcx.aigc-quickapp.com/" }, "define": { "PRODUCTION": true } } ``` `config.js` 和 `vue.config.js` 均通过 `process.env.UNI_APP_DOMAIN` 读取,**新增/删除脚本无需改动代码**。 --- ## 6. 🔄 版本历史 ### 6.1 v1.5.1 - `VersionInjectPlugin`:webpack emit 钩子自动注入 `__app_version` 到所有平台产物 - 首页右下角连击 5 次显示构建版本信息(commitId + buildTime) - `mp-weixin.patch.js` 精简为仅文件拷贝和 ZIP 打包 - 新增 `harmony.patch.js`:鸿蒙小程序生产构建补丁脚本(配置拷贝 + ZIP 打包) - `open-directory.js`:构建完成后自动打开输出目录 - 极限压缩优化:集成 `zion-uniapp-mp-load-package` 插件 + TerserWebpackPlugin 多层次压缩配置 - 修复 `openWxWorkServiceChat` 方法中 fail 回调语法错误 - ZIP 文件名末尾使用 Git 提交 ID 替换时间戳 - 构建脚本幂等性修复,支持多次运行不产生重复注入 ### 6.2 v1.3 - 修复微信小程序构建脚本,支持复制 `project.config.json` 和 `project.private.config.json` 文件 - 增强构建脚本功能,添加命令行参数支持 - 在 `package.json` 中添加相关 npm 脚本命令 - 优化脚本执行逻辑,提高可靠性和灵活性