diff --git a/AGENTS.md b/AGENTS.md index 4a24a30..6ba59ca 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -27,7 +27,7 @@ - 模块之间不得通过相对路径或内部目录穿透;确需协作时只能从目标模块的 `public.ts` 导入。模块内稳定契约放在 `contracts.ts`,稳定的跨应用模型仍应上移到 `@chongchong/shared`。 - `VITE_API_BASE_URL` 控制浏览器请求前缀,`API_PROXY_TARGET` 控制 Vite 开发代理上游;后者只由开发服务器读取,不进入客户端构建。新增或变更环境变量时同步更新 `.env.example`,不得提交真实密钥或环境专属凭据。 - `src/styles/`:全局设计令牌和布局;优先复用现有 `--primary`、`--navy`、`--border`、`--muted`。 -- 跨应用稳定模型放到 `packages/shared`,客户工作台专属类型保留在本项目。 +- 本应用多个模块稳定复用的前端模型放到应用内 `shared/`,客户工作台专属类型保留在对应业务模块。不要链接应用目录之外的 TypeScript 包,服务端契约以 `baiyue-firstleg` Controller/OpenAPI 为准。 ## 租户、认证与数据安全 diff --git a/shared/AGENTS.md b/shared/AGENTS.md index e25fc86..d0032b9 100644 --- a/shared/AGENTS.md +++ b/shared/AGENTS.md @@ -1,28 +1,21 @@ -# Shared Package Instructions +# 应用内 Shared Package Instructions ## 项目定位 -- 本包提供跨应用共享的 TypeScript 类型、常量、轻量工具和可复用资源。 -- 当前公共能力包括产品名称、用户模型、导航模型、API 响应模型和头程订单状态常量。 -- 只有具有明确跨项目语义,或已经被两个或以上项目稳定复用的能力才进入本包;单一应用的页面状态、UI 组件和临时业务逻辑留在对应项目。尚无消费者的契约型导出必须标记为暂定,并有对应 API 或产品决策依据。 +- 本包只服务当前前端应用,提供多个业务模块共同使用的 TypeScript 类型、常量和轻量工具。 +- 依赖保持为 `@chongchong/shared: link:./shared`,确保当前应用目录可作为独立容器构建上下文。 +- 不承担 Admin、Client 之间的源码复用;跨应用字段需要分别映射同一份 Java Controller/OpenAPI 契约,不能通过目录外链接耦合两个部署单元。 ## 公共 API 与依赖边界 -- `src/index.ts` 是当前唯一公共入口。新增导出应有清晰命名和稳定语义,不让消费者依赖未导出的内部实现。 -- 公共类型保持平台无关,不依赖 DOM、Vue、微信、uni-app 或 Node 专属运行时,除非任务明确改变本包定位。 -- 不在共享类型中编码客户端鉴权结论;租户隔离、权限和数据合法性仍由 `services/api` 最终校验。 -- 避免以“复用”为由建立宽泛的万能类型。优先使用小而明确的模型,并区分请求、响应、领域状态和展示模型。 -- 新增依赖前先确认原生 TypeScript 能力或现有依赖无法满足需求;本包不应成为所有应用依赖的重量级入口。 +- `src/index.ts` 是当前唯一公共入口;模块专属页面状态、UI 组件和临时业务逻辑留在对应模块。 +- 共享类型保持前端平台无关,不编码鉴权结论;租户隔离、权限、状态机和数据合法性由 `baiyue-firstleg` Java 服务端最终保证。 +- 服务端字段变化先核对 Controller、DTO 和 OpenAPI,再更新本应用的类型、API 适配层和测试;本包不反向定义服务端行为。 +- 避免建立宽泛的万能类型或引入重量级依赖。 -## 兼容性与变更原则 +## 兼容性与验证 -- 修改导出名称、字段类型、枚举值或常量语义前,搜索所有消费者并评估兼容性。 -- 优先采用向后兼容的新增;删除或破坏性修改必须同步更新全部消费者,并在交付说明中明确迁移影响。 -- API 契约文档是服务端落地后的唯一权威来源;共享包只映射经确认的客户端模型,不反向定义服务端行为。API 尚未落地前,`ApiResponse` 和订单状态等无消费者导出视为暂定,不得当作已发布契约。 -- 状态码、权限码和业务常量应有唯一来源,不在多个应用中复制维护相同列表。 +- 修改导出名称、字段类型、枚举值或常量语义前,搜索当前应用内全部消费者。 +- 优先采用兼容性新增;破坏性修改必须同步更新当前应用消费者。 +- 本包没有独立质量脚本;变更后运行父应用声明的类型检查、测试或构建命令。 -## 质量门禁 - -- 当前包未声明独立的安装、测试、类型检查或构建脚本,不得声称这些检查已在本包内通过。 -- 变更公共导出后,至少运行现有消费者的类型检查;当前直接消费者包括 `apps/admin-web` 和 `apps/client-web`。 -- 若新增本包自己的质量脚本,应同步更新 `package.json`、`README.md` 和本文件,并为公共工具及兼容性边界增加测试。 diff --git a/shared/README.md b/shared/README.md index ec46228..5b5cb76 100644 --- a/shared/README.md +++ b/shared/README.md @@ -1,7 +1,6 @@ -# 共享模块 +# 应用内共享模块 -用于存放跨应用共享的类型定义、常量、工具和可复用资源。 +本目录存放当前前端应用多个业务模块共同使用的 TypeScript 类型、常量和轻量工具,通过 `@chongchong/shared: link:./shared` 引用。 -当前已提供产品名称、用户模型、导航模型、API 响应模型,以及经一期产品决策确认的头程订单状态与运输方式常量。 +该目录必须保留在应用内部,以支持 Admin、Client 分别使用自身目录作为容器构建上下文。它不作为 Java 服务端契约来源;接口字段、状态机、权限和业务规则以 `baiyue-firstleg` Controller、DTO 和 OpenAPI 为准。 -当前还提供平台收货仓模型、订单仓库快照模型和供 Admin、Client 静态原型共用的演示仓库数据。订单仓库快照在下单时复制仓库 ID、编码、名称及国内地址,避免仓库主数据后续改名或停用影响历史订单展示;真实仓库列表、状态、下单可用性和快照持久化最终由服务端 API 保证。 diff --git a/src/assets/brand/auth-ocean.jpg b/src/assets/brand/auth-ocean.jpg new file mode 100644 index 0000000..f47f055 Binary files /dev/null and b/src/assets/brand/auth-ocean.jpg differ diff --git a/src/modules/auth/components/AuthShell.vue b/src/modules/auth/components/AuthShell.vue new file mode 100644 index 0000000..2a317a2 --- /dev/null +++ b/src/modules/auth/components/AuthShell.vue @@ -0,0 +1,54 @@ + + + diff --git a/src/modules/auth/views/AuthViews.test.mjs b/src/modules/auth/views/AuthViews.test.mjs index 4b5f4d3..4c60c46 100644 --- a/src/modules/auth/views/AuthViews.test.mjs +++ b/src/modules/auth/views/AuthViews.test.mjs @@ -4,6 +4,7 @@ import test from 'node:test' const loginSource = readFileSync(new URL('./LoginView.vue', import.meta.url), 'utf8') const registerSource = readFileSync(new URL('./RegisterView.vue', import.meta.url), 'utf8') +const authShellSource = readFileSync(new URL('../components/AuthShell.vue', import.meta.url), 'utf8') const legalSource = readFileSync(new URL('./LegalDocumentView.vue', import.meta.url), 'utf8') const legalDocumentsSource = readFileSync(new URL('../legalDocuments.ts', import.meta.url), 'utf8') const authRoutesSource = readFileSync(new URL('../routes.ts', import.meta.url), 'utf8') @@ -12,6 +13,22 @@ const httpSource = readFileSync(new URL('../../../api/http.ts', import.meta.url) const sessionSource = readFileSync(new URL('../../../api/session.ts', import.meta.url), 'utf8') const authApiSource = readFileSync(new URL('../api/auth.ts', import.meta.url), 'utf8') const mainSource = readFileSync(new URL('../../../main.ts', import.meta.url), 'utf8') +const stylesSource = readFileSync(new URL('../../../styles/index.css', import.meta.url), 'utf8') + +test('uses one prototype-inspired responsive shell for login and registration', () => { + assert.match(loginSource, / { + for (const section of ['账号信息', '企业信息', '联系信息']) assert.match(registerSource, new RegExp(section)) + assert.doesNotMatch(loginSource + registerSource, /微信登录|企业微信登录|短信验证码|邮箱验证码|lang-switcher/) +}) test('matches the backend username login contract', () => { assert.match(loginSource, /label="用户名"/) diff --git a/src/modules/auth/views/LoginView.vue b/src/modules/auth/views/LoginView.vue index a63a068..6d25987 100644 --- a/src/modules/auth/views/LoginView.vue +++ b/src/modules/auth/views/LoginView.vue @@ -1,10 +1,10 @@ diff --git a/src/modules/auth/views/RegisterView.vue b/src/modules/auth/views/RegisterView.vue index 1d63fc0..723ccc5 100644 --- a/src/modules/auth/views/RegisterView.vue +++ b/src/modules/auth/views/RegisterView.vue @@ -1,10 +1,10 @@ diff --git a/src/modules/dashboard/views/DashboardView.test.mjs b/src/modules/dashboard/views/DashboardView.test.mjs index 589cdeb..4de34c7 100644 --- a/src/modules/dashboard/views/DashboardView.test.mjs +++ b/src/modules/dashboard/views/DashboardView.test.mjs @@ -24,3 +24,14 @@ test('isolates recoverable dashboard failures without hiding successful sections assert.match(source, /@click="loadDashboardData"/) assert.doesNotMatch(source, /