From bcf5fa868cd12e6121ee00c54677250609c811e8 Mon Sep 17 00:00:00 2001 From: dzc <2388224167@qq.com> Date: Mon, 31 Aug 2026 15:04:30 +0800 Subject: [PATCH] =?UTF-8?q?feat:=E9=80=9A=E7=9F=A5=E6=B6=88=E6=81=AF?= =?UTF-8?q?=E6=8E=88=E6=9D=83=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env | 8 +-- AGENTS.md | 82 ++++++++++++++++++++++++++ src/App.vue | 20 +++++-- src/pages/order/daily-menu-confirm.vue | 7 ++- src/sheep/utils/permission.ts | 13 ++-- 5 files changed, 114 insertions(+), 16 deletions(-) create mode 100644 AGENTS.md diff --git a/.env b/.env index c14ef26..3d3dc0b 100644 --- a/.env +++ b/.env @@ -33,15 +33,15 @@ VITE_OAUTH2_SOCIAL_CLIENT='social:social' # 是否开启websocket 消息接受, VITE_WEBSOCKET_ENABLE = true # --------------------------- start 开发环境 start ---------------------------------- -VITE_APP_DEV_SERVICE = https://www.zianxin.com/api +# VITE_APP_DEV_SERVICE = https://www.zianxin.com/api # VITE_APP_DEV_SERVICE = https://dzc-test-1.fy.takin.cc # VITE_APP_DEV_SERVICE = http://127.0.0.1:9999 # VITE_APP_DEV_SERVICE = https://www.zianxin.com/api -# VITE_APP_DEV_SERVICE = https://dzc-test-1.fy.takin.cc +VITE_APP_DEV_SERVICE = https://dzc-test-1.fy.takin.cc #VITE_APP_DEV_SERVICE = http://127.0.0.1:9999 -VITE_IMAGE_DEV_PATH = https://www.zianxin.com/api -# VITE_IMAGE_DEV_PATH = https://dzc-test-1.fy.takin.cc +# VITE_IMAGE_DEV_PATH = https://www.zianxin.com/api +VITE_IMAGE_DEV_PATH = https://dzc-test-1.fy.takin.cc # VITE_IMAGE_DEV_PATH = http://127.0.0.1:9999 # VITE_IMAGE_DEV_PATH = https://www.zianxin.com/api # VITE_IMAGE_DEV_PATH = https://dzc-test-1.fy.takin.cc diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..68b3801 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,82 @@ +# `biz_zax_app_cloud` 开发规则 + +> 适用于 uni-app 移动端项目。执行任务前必须先完整阅读 [HQ Platform 通用规则](../agents/common.md),并继续检查目标目录内更具体的说明和现有同类实现。 + +## 技术栈基线与平台范围 + +以下版本是当前项目扫描结果,后续以 `package.json`、`src/uni_modules/tm-ui/package.json` 和锁文件为准。 + +- 应用框架:uni-app Vue 3,DCloud 依赖版本为 `3.0.0-5000720260410001`,应用由 `createSSRApp` 创建。 +- 基础运行时:Vue `3.4.x`、TypeScript `4.9.x`、Vite `5.2.8`。 +- UI 框架:项目内置 uni_modules 版 tm-ui `3.2.0`,Vue 3 + TypeScript,支持 App Vue 与 H5;本地组件描述明确不支持 App NVUE,因此使用 NVUE 前必须单独核对组件兼容性。 +- 样式体系:UnoCSS `0.58.9`、`unocss-preset-weapp`、Sass。UnoCSS 已启用 `whRpx`、Attributify 和 class transformer。 +- 状态管理:Pinia `3.0.x` 与 `pinia-plugin-persistedstate`,持久化适配器使用 `uni.getStorageSync` / `uni.setStorageSync`,不得直接替换为浏览器 `localStorage`。 +- 网络层:`luch-request`,统一封装位于 `src/sheep/utils/request.ts`,负责 H5/App Base URL、Token、Token 刷新、租户头、TOC 头、请求/响应加解密、登录过期跳转和统一提示。 +- 路由:以 `src/pages.json` 为页面与分包真源,通过 Vite 路由插件读取 `meta`,业务跳转统一使用 `src/sheep/router` 对 `uni.navigateTo`、`switchTab`、`redirectTo`、`reLaunch` 的封装。 +- 列表与数据展示:`z-paging` 用于分页列表;`@qiun/uni-ucharts` 和本地 qiun/uCharts 组件用于跨端图表。 +- 图形能力:用户关系树使用 `@antv/hierarchy` 的 `compactBox` 布局;不要把 PC 端 `@vue-flow` 直接搬到移动端。 +- 动效与工具:Lottie Web、Day.js、Radash、CryptoJS、Base64 等已经存在,使用前先检查跨端封装。 +- 目标平台:**iOS App、Android App、H5**。项目虽保留多个小程序构建依赖和脚本,但当前需求默认不以小程序为验收目标,除非用户另行指定。 + +## 关键目录与入口 + +- `src/main.ts`:创建应用,加载 `uno.css`,注册 Pinia 和 tm-ui。 +- `src/App.vue`:应用生命周期、启动逻辑、App 平台推送/权限初始化、全局状态联动等。 +- `src/pages`:实际页面,包含登录、用户、食堂、商城、订单、钱包、配送、门禁/人脸、商户和管理等业务。 +- `src/pages.json`:页面、分包、导航栏、TabBar 和 Easycom 配置。新增页面必须同步登记,优先放入对应业务分包。 +- `src/sheep/api`:移动端接口层;页面不得直接散落 `uni.request` 或新建请求实例。 +- `src/sheep/components`:基础组件、CRUD 组件和业务公共组件;Easycom 已将 `base-*`、`crud-*` 自动映射到此处。 +- `src/sheep/hooks`:页面与列表公共逻辑;新页面应先搜索分页、状态和业务 Hook。 +- `src/sheep/stores`:用户、TabBar、购物车、支付、推送等 Pinia Store。 +- `src/sheep/router`:统一路由跳转、TabBar 判断、防重复跳转和返回处理。 +- `src/sheep/utils`:请求、环境、加解密、文件、支付、推送、平台跳转等工具。 +- `src/uni_modules/tm-ui`:项目实际使用的 TMUI 源码与组件,不得直接修改第三方组件来解决单页问题,除非明确要求进行全局修复。 +- `src/static/app-plus` 与 `src/static/h5`:平台专属资源;共用资源放在通用 `src/static` 下。 +- `vite.config.ts`:`@` 指向 `src`,`@@` 指向 `src/sheep`;接入 uni、UnoCSS、路由插件和 IIFE 构建兼容处理,开发服务端口为 `8889`。 + +## UI 与 UnoCSS 约定 + +- `src/pages.json` 已通过 Easycom 自动映射 `tm-*`、`base-*`、`crud-*` 和 `z-paging*`,优先直接使用这些既有组件命名。 +- 修改前必须检查 `unocss.config.ts`。当前已有 `flex-col-center`、`flex-row-center`、`flex-col`、`flex-row`、`border-base` shortcuts,以及 `theme-text-*`、`main-text-*`、`sub-text-*`、`second-text-*`、`show-line-*`、`card`、`shadow-main` 等规则。 +- `whRpx: true` 会把宽高相关原子值按 rpx 体系处理。新增尺寸前先理解现有类的实际输出,避免把 px、rpx 和设计稿数值混用。 +- 普通布局、间距、字号、圆角、颜色、截断优先用 UnoCSS;只有平台差异、动态计算、第三方组件深层覆盖或复杂动画才使用局部 SCSS/CSS。 +- UI 组件优先使用项目内 tm-ui 与 `sheep/components` 封装。需要核对 API 时参考 与本地组件源码,不能凭其他 UI 框架经验猜测属性和事件。 +- 页面视觉应延续同业务现有风格,尤其是顶栏、安全区、卡片密度、列表行高、按钮尺寸、表单校验、弹层和加载/空状态。 + +## 请求、状态与路由约定 + +- 所有业务请求通过 `src/sheep/utils/request.ts` 和 `src/sheep/api` 发起;不得绕过现有 Token 刷新、请求加密、租户和错误处理。 +- H5 的 Base URL 从 `VITE_API_URL` 读取,非 H5 由 `getBaseApiUrl()` 根据开发/生产环境读取服务地址;修改环境逻辑前必须同时核对两条路径。 +- Token 与用户信息使用项目 storage 常量和 Pinia 持久化,不得另建同义 Key。 +- 页面跳转优先使用 `@@/router`,保持 TabBar、节流、防重复跳转和无历史返回逻辑一致。 +- 新增页面时先判断属于主包还是现有分包,避免扩大主包;页面路径、TabBar 路径和路由辅助逻辑必须保持一致。 + +## 原生与跨平台能力 + +- `src/manifest.json` 已配置 Push、Payment、OAuth、Share、VideoPlayer、Maps、Barcode、Camera 等 App 模块,以及 Android/iOS 权限、Universal Links、支付、地图和推送配置。 +- 不得随意修改 AppID、包名、Bundle ID、证书、Universal Links、推送 AppKey、支付参数、地图 Key、权限或隐私描述。需求涉及这些配置时先说明影响并取得确认。 +- App 生命周期与推送初始化已有 `APP-PLUS` 条件编译;新增平台代码必须沿用 `#ifdef` / `#ifndef`,并提供 H5 或另一移动平台的合理降级。 +- H5 专属 DOM、Lottie 或浏览器 API 必须隔离;App 原生插件、`plus` API 和权限请求也必须隔离。共享模块不能在导入阶段直接访问单平台全局对象。 +- 必须检查安全区域、状态栏、底部手势区、软键盘、页面滚动、返回行为、触摸目标和不同屏宽。 + +## 常用命令与验证 + +- H5 开发:`pnpm dev:h5`。 +- H5 构建:`pnpm build:h5`。 +- 类型检查:`pnpm type-check`。 +- App 真机与发行包按项目现有 HBuilderX / uni-app 工作流执行;不得把 H5 构建成功等同于 iOS、Android 已验证。 +- 涉及条件编译、原生权限、支付、推送、地图、相机或扫码时,必须分别核对 Android、iOS 与 H5 路径,并如实说明哪些平台只做了静态检查。 + +## 开发规则 + +- 修改前必须查看 `unocss.config.ts`、`vite.config.ts`、`src/pages.json`、`src/manifest.json`、全局样式和同类页面,确认已有快捷类、主题、页面配置与平台配置。 +- UI 组件优先使用项目内现有 tm-ui 组件和封装,并遵循当前项目的组件调用方式;需要确认组件能力时参考 TMUI 文档,不凭印象编造属性或事件。 +- 常规样式优先使用 UnoCSS 原子类和项目已有 shortcuts/rules,不重复写可被原子类表达的 scoped CSS。 +- 所有页面和功能必须同时考虑 iOS、Android、H5,不得只按单一平台实现。涉及平台能力差异时,使用 uni-app 官方能力或规范的条件编译,并为不支持的平台提供合理降级。 +- 注意安全区域、状态栏、导航栏、底部操作区、键盘顶起、滚动容器、触摸区域、单位选择和横竖屏/不同屏宽下的表现。 +- 禁止直接使用仅浏览器可用的 DOM/BOM API 或仅 App 可用的原生能力而不做平台隔离。 +- 日常修改不自动执行 H5 或 App 构建;收到用户明确的集中验证指令后,再检查 H5 构建,并按改动范围核对 iOS、Android 分支及相关配置,明确说明实际验证范围。 + +## 完成前项目检查 + +- [ ] 移动端改动已考虑 iOS、Android、H5 适配。 diff --git a/src/App.vue b/src/App.vue index ebc1e05..08d15b4 100644 --- a/src/App.vue +++ b/src/App.vue @@ -10,6 +10,9 @@ import jgPush from "@/sheep/utils/jg-push" import { consumePendingPushRoute } from "@/sheep/utils/push-route" import { showPermissionUsage } from "@/sheep/utils/permission" +/** 通知权限用途说明已由用户处理过的本地标记,避免未授权时每次冷启动重复弹窗。 */ +const PUSH_PERMISSION_USAGE_HANDLED_STORAGE_KEY = "push_permission_usage_handled_v1" + export default { data() { return { @@ -36,11 +39,18 @@ export default { pushStatus.notificationPermission === "granted" || pushStatus.notificationPermission === "provisional" || pushStatus.notificationEnabled === true - if (!pushPermissionGranted) { - const shouldRequestPushPermission = await showPermissionUsage("通知", { - feature: "消息通知", - purpose: "用于接收订单、配送、核销和系统服务状态等重要消息提醒。", - }) + const pushPermissionUsageHandled = Boolean(uni.getStorageSync(PUSH_PERMISSION_USAGE_HANDLED_STORAGE_KEY)) + if (!pushPermissionGranted && !pushPermissionUsageHandled) { + const shouldRequestPushPermission = await showPermissionUsage( + "通知", + { + feature: "消息通知", + purpose: "用于接收订单、配送、核销和系统服务状态等重要消息提醒。", + }, + "去授权", + ) + // 用户确认或取消都视为已完成一次选择,后续由用户在系统设置中自行调整通知权限。 + uni.setStorageSync(PUSH_PERMISSION_USAGE_HANDLED_STORAGE_KEY, true) if (shouldRequestPushPermission) { if (this.system.platform === "android") { await jgPush.requestAndroidPermission() diff --git a/src/pages/order/daily-menu-confirm.vue b/src/pages/order/daily-menu-confirm.vue index 3b38830..cfca7a4 100644 --- a/src/pages/order/daily-menu-confirm.vue +++ b/src/pages/order/daily-menu-confirm.vue @@ -12,8 +12,7 @@ + :allowed-identity-types="allowedIdentityTypes" @open="openDrawer" /> @@ -112,7 +111,9 @@ {{ formatReservationDate(reservation.apptUseDate) }} - {{ reservation.apptTitle || "预约餐段" }} + + {{ hooks.useEnum("hardHourTypeEnum", reservation.hourType) || "预约餐段" }} + {{ reservation.username || "用餐人" }} · {{ bookedFoodSummary(reservation) }} diff --git a/src/sheep/utils/permission.ts b/src/sheep/utils/permission.ts index 3edcc74..ee5ec14 100644 --- a/src/sheep/utils/permission.ts +++ b/src/sheep/utils/permission.ts @@ -44,14 +44,19 @@ export type ImagePermissionUsage = { * * @param permissionName 面向用户展示的权限名称。 * @param usage 当前业务功能及其权限用途。 - * @returns 用户点击“继续”时返回 true,取消或弹窗失败时返回 false。 + * @param confirmText 确认按钮文案;未传入时沿用通用权限流程的“继续”。 + * @returns 用户点击确认按钮时返回 true,取消或弹窗失败时返回 false。 */ -export const showPermissionUsage = (permissionName: string, usage: PermissionUsage): Promise => { +export const showPermissionUsage = ( + permissionName: string, + usage: PermissionUsage, + confirmText = "继续", +): Promise => { return new Promise((resolve) => { uni.showModal({ title: `${permissionName}权限使用说明`, content: `权限名称:${permissionName}权限\n服务功能:${usage.feature}\n使用目的:${usage.purpose}`, - confirmText: "继续", + confirmText, cancelText: "取消", success: (res) => resolve(Boolean(res.confirm)), fail: () => resolve(false), @@ -422,7 +427,7 @@ const getPermissionDisplayName = (permissionId: PermissionId): string => { * * @param permissionId 应用内部权限类型。 * @param usage 当前业务功能及其权限用途。 - * @returns 用户点击“继续”时返回 true,取消或弹窗失败时返回 false。 + * @returns 用户点击确认按钮时返回 true,取消或弹窗失败时返回 false。 */ const showPermissionUsageModal = (permissionId: PermissionId, usage: PermissionUsage): Promise => { const permissionName = getPermissionDisplayName(permissionId)