# CLAUDE.md This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. ## 项目概述 Aimill 人力资源移动端 —— 基于 **uni-app + Vue 3** 的静态原型,覆盖考勤、申请、审批、通讯录、HR 管理等模块。设计参考企业微信的卡片式界面,需求文档为《Aimill工业互联网平台-人力资源管理子系统设计文档 V2.1》。 项目当前是 **演示版**:数据来自 `src/data/mock.js`,用户操作通过 `uni.setStorageSync` 持久化在本地。后续接入真实接口时,应新增 `src/api/` 请求层并逐步替换页面中的 mock 数据(见 [README.md](README.md))。 ## 常用命令 通过 pnpm 运行: ```bash pnpm install # 安装依赖(启用 pnpm core-js/esbuild 等构建) pnpm dev:h5 # 启动 H5 开发服务器(默认 hash 路由) pnpm build:h5 # 构建 H5 产物到 dist/build/h5 pnpm dev:mp-weixin # 微信小程序开发模式 pnpm build:mp-weixin # 构建微信小程序,产物在 dist/build/mp-weixin ``` 构建微信小程序后将 `dist/build/mp-weixin` 导入微信开发者工具即可预览。正式发布前在 [src/manifest.json](src/manifest.json) 中填写小程序 AppID(当前为空字符串)。 仓库同时存在 `yarn.lock`,但 [package.json](package.json) 与 [pnpm-workspace.yaml](pnpm-workspace.yaml) 指向 pnpm 为主包管理器,建议统一使用 pnpm。 ## 架构总览 ``` src/ ├── App.vue # 应用入口:onLaunch/onShow 中调用 guardLogin() ├── main.js # createSSRApp 工厂 ├── manifest.json # uni-app 多端配置(h5 / mp-weixin / app-plus) ├── pages.json # 路由表 + globalStyle 导航栏 ├── pages/ # 所有页面,按业务模块划分目录 │ ├── login/ # 登录页(含演示/服务器模式切换) │ ├── home/ # 首页(考勤 + 快捷入口 + 待办 + 公告) │ ├── work/ # 工作台(模块九宫格 + 搜索) │ ├── contacts/ # 通讯录列表 │ ├── profile/ # 我的(含 profile/edit 子页) │ ├── apply/ # 申请表单 form.vue + 我的申请 list.vue │ ├── approval/ # 审批中心 index + 详情 detail │ ├── attendance/ # 考勤日历 │ ├── salary/ # 工资条 │ ├── leave/ # 假期余额 │ ├── training/ # 培训学习 │ ├── performance/ # 绩效记录 │ ├── employee/ # 员工详情 │ ├── notice/ # 通知公告 │ └── manage/ # 人事管理(招聘/合同/入职/异动/数据看板,通过 ?type= 复用) ├── components/ │ ├── AppTabBar.vue # 自定义底部 TabBar(首页/工作台/通讯录/我的) │ └── ModuleIcon.vue # 通用图标块(icon + color + soft 模式) ├── data/ │ └── mock.js # 全部 mock 数据与枚举(currentUser, applicationTypes, approvals 等) ├── utils/ │ ├── storage.js # uni.storage 封装:登录态、申请、草稿、审批结果等 │ ├── auth.js # 登录与服务器请求:演示/服务器双模式 │ └── router.js # go(url) / switchMain(url) 薄封装 ├── styles/ │ └── global.scss # 全局样式、原子类(.card/.section/.press/.status-tag…) └── uni.scss # SCSS 变量($theme-color=#1677ff 等) ``` ## 关键设计约定 ### 路由与 TabBar - **没有使用 uni-app 自带的 tabBar 配置**。4 个 Tab 页面(`home/work/contacts/profile`)通过 [`AppTabBar.vue`](src/components/AppTabBar.vue) 组件自行渲染,切换时统一调用 `uni.reLaunch`。 - 子页面(`apply/*`、`approval/*` 等)使用 `uni.navigateTo` 跳转,包装在 [`src/utils/router.js`](src/utils/router.js) 中:`go()` 跳转、`switchMain()` 重启到首页。 - 路由表全部声明在 [src/pages.json](src/pages.json),新增页面必须同时在此处注册。 ### 登录与权限 - [src/App.vue](src/App.vue) 的 `guardLogin()` 在 `onLaunch` / `onShow` 中根据 `isLoggedIn()` 跳转 `/pages/login/index` 或 `/pages/home/index`。 - 登录页支持两种模式([src/utils/auth.js](src/utils/auth.js)): - **演示模式**(默认):账号 `ZY20230128` / 密码 `123456`,常量见 [src/utils/storage.js](src/utils/storage.js) 的 `DEMO_ACCOUNT` / `DEMO_PASSWORD`。 - **服务器模式**:用户通过登录页右上角 ⚙ 配置 `protocol + hostname + port`,写入 `apiInfo` storage。请求路径前缀为 `${baseUrl}/api`,登录接口 `/main/user/login`,权限树 `/system/resources/getResourcesTreePDA`。 - 登录后会调用 [`saveLoginSession()`](src/utils/storage.js) 写入 `token` / `userInfo` / `auth`,并按需加载权限树到 `treeList` 与 `authorities`。 ### 数据持久化 所有本地状态集中在 [src/utils/storage.js](src/utils/storage.js),key 前缀 `aimill_hr_`: | Key | 内容 | 写入函数 | | --- | --- | --- | | `aimill_hr_applications` | 我提交的申请列表 | `addApplication` | | `aimill_hr_application_drafts` | 各类型申请草稿(按 type 索引) | `saveDraft` / `getDraft` / `removeDraft` | | `aimill_hr_approval_results` | 我审批过的结果(按申请 id 索引) | `setApprovalResult` | | `aimill_hr_training_progress` | 课程学习进度 | `setTrainingProgress` | | `aimill_hr_profile_changes` | 个人信息变更 | `saveProfileChange` | | `aimill_hr_auth` / `token` / `userInfo` | 登录态 | `saveLoginSession` / `logout` | | `passwordMemo` / `passInfo` / `aimill_hr_remembered_account` | 记住密码 | `saveRememberedCredentials` | | `apiInfo` | 服务器模式配置 | `saveServerConfig` | ### 状态管理 - **未引入 Pinia / Vuex**。跨页面共享状态通过 `uni.storage` + 页面 `onShow` 钩子重新读取(如 [src/pages/home/index.vue](src/pages/home/index.vue) 的 `pendingCount` 计算)。 - 模拟器待办数硬编码为 3:`Math.max(0, 3 - Object.keys(getApprovalResults()).length)`,存在于首页 / 工作台 / AppTabBar 三处,新增审批场景时需要同步修改。 ### 申请表单(核心交互模式) [src/pages/apply/form.vue](src/pages/apply/form.vue) 是多类型申请的统一入口,所有类型(请假/加班/外勤/出差/调班/补卡/证明等)共用一个文件: - URL 参数 `?type=xxx` 决定渲染哪种字段与校验(参考 [src/data/mock.js](src/data/mock.js) 的 `applicationTypes`)。 - `onLoad` / `onShow` / `hashchange` 三处监听 URL,确保返回页面或 hash 变化时刷新表单。 - 表单数据写入 `aimill_hr_application_drafts`,提交成功后调用 `addApplication()` 并 `removeDraft()`,再 `redirectTo` 到我的申请。 ### 样式规范 - 单位统一使用 **rpx**(uni-app 默认 750rpx = 屏幕宽),不要混用 px/rem。 - 主题色 `#1677ff` 在 [src/uni.scss](src/uni.scss)、[src/styles/global.scss](src/styles/global.scss) 与 CSS 变量 `--theme-color` 三处定义;新增颜色变量请保持一致。 - 常用原子类见 [src/styles/global.scss](src/styles/global.scss): - `.page`(带 TabBar,底部留 120rpx)/ `.page-no-tab`(无 TabBar,底部留 40rpx) - `.card`、`.section`、`.divider` - `.status-tag` 配合 `.status-pending` / `.status-approved` / `.status-rejected` - `.press:active { opacity: .7 }`(点击反馈) - `.safe-bottom`(适配安全区) - `.avatar`(渐变蓝色块) - 媒体查询 `@media (min-width: 800px)` 将 H5 内容居中并限宽 520px,模拟移动端体验。 - 微信小程序构建时需在 [src/manifest.json](src/manifest.json) 中填写 AppID,目前为空。 ## 现有数据与 mock 资源 [src/data/mock.js](src/data/mock.js) 是接入真实 API 前唯一的真实数据来源,包含: - `currentUser`:当前登录用户 - `quickActions`:首页 8 个快捷入口 - `applicationTypes`:申请类型元数据(标题、副标题、图标、配色) - `approvals` / `contacts` / `notices` / `workModules`:列表页数据 ## 数据对接路径 接入真实接口时的推荐顺序(按 README 建议): 1. 新建 `src/api/` 目录,按模块封装 `uni.request`,参考 [src/utils/auth.js](src/utils/auth.js) 的 `request()` 封装(已处理 401、`zoomwin-token` 头、`code !== 0` 等)。 2. 优先替换列表类页面(`approval/index`、`contacts`、`notice`、`work`),这些页面只读 mock,无副作用。 3. 最后处理带本地状态的页面(`apply/form`、`approval/detail`、`profile/edit`),注意保留现有的 storage key 兼容旧数据或新增迁移逻辑。 4. 服务端字段映射需保留 mock 数据中的 `id`、`type`、`color`、`status` 等键名以避免大量 UI 改动。 ## 子智能体委派 跨多个文件的复杂任务应通过 `Agent` 工具委派子智能体。**通用的委派原则、prompt 模板、并行注意事项与禁止事项**见 [agent.md](agent.md)。 本项目的特定约束(子智能体必须遵守的封装边界): - **本地持久化** 一律经 [src/utils/storage.js](src/utils/storage.js),禁止在页面中直接调用 `uni.setStorageSync` 并自定义 key。 - **页面跳转** 一律经 [src/utils/router.js](src/utils/router.js) 的 `go()` / `switchMain()`,禁止直接调用 `uni.navigateTo` / `uni.reLaunch`。 - **登录与请求** 一律经 [src/utils/auth.js](src/utils/auth.js) 的 `performLogin()` / `request()`,演示/服务器模式由 storage key `apiInfo` 决定,不要硬编码请求地址。 - **mock 数据** 一律从 [src/data/mock.js](src/data/mock.js) 导入,不要在 `.vue` 文件中内联常量列表。 - **不要引入 Pinia / Vuex**,不要切换包管理器,不要替换 `AppTabBar` 为 uni-app 原生 tabBar,不要改动 `src/manifest.json` 的 `appid`。 派发子智能体时,在 prompt 中显式声明以上封装边界,并附上本文件路径以便它读取完整上下文。