Files
zMainPage/docs/architecture.md
Zikai 6ad6c15e9d feat: 拆分移动游戏项目为独立 z449 子模块(git submodule + i18n prop 桥接)
- mobile-game 模块源码迁出至独立仓库 z449(third_party/z449),独立可渲染/维护
- z449 App.vue 为便携式组件:自带独立 vue-i18n 实例与 locale 文案
- mainPage 侧 MobileGameWrapper 透传 :locale prop,子模块 watch 同步语言
- mainPage i18n 精简:仅保留 tabs.mobileGame,删除 mobileGame.* 大块文案
- 文档:新增 docs/submodule-z449.md,更新 README/architecture/add-module
2026-07-23 07:04:08 +00:00

87 lines
5.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 架构
mainPage 是一个 Vue 3 模块化多项目展示站。核心设计目标:**高内聚低耦合** -- 每个项目页签是一个独立「模块」,由注册表自动生成页签与路由,新增/删除页签只增删一个文件夹,互不影响。
## 模块注册表(扩展点核心)
`src/modules/index.js``import.meta.glob('./*/index.js', { eager: true })` 约定式收集每个子模块的 `index.js`。每个模块默认导出元数据:
```js
export default {
id: 'whiteboard', // 必填:唯一标识,同时作路由 path / KeepAlive 匹配名
tabKey: 'tabs.whiteboard', // 必填i18n 键,页签标题与 PageShell 标题均用此
component: () => import('./Whiteboard.vue'), // 必填:懒加载组件(函数形式,独立 chunk
order: 2, // 可选:页签排序,缺省 999排在显式页签之后
hidden: true, // 可选:不在导航显示,但 URL 访问后保持可见(如 cqy
requiresAlive: 'https://f.zikai.wang/health', // 可选:存活探针 URL服务失活则隐藏页签并卸载组件
param: 'boardId', // 可选:路由参数名,生成 /<id>/:<param>? 子路径
}
```
### 元数据字段 -> 消费方矩阵
| 字段 | 注册表 | router | useProjects | App.vue | TabNav |
|------|--------|--------|-------------|---------|--------|
| `id` | 必填校验 | path+name+meta.moduleId | visited 集合 / 路由关联 | keepInclude 名单 | `:to` + `:key` |
| `tabKey` | 必填校验 | - | - | - | 标题文案 |
| `component` | 必填校验 | 路由 component | - | RouterView 渲染 | - |
| `order` | 排序 | 决定 `/` 重定向目标 | 继承顺序 | - | 继承顺序 |
| `hidden` | 透传 | - | 可见性门控 | - | - |
| `requiresAlive` | 透传 | - | 可见性门控 | keepInclude + startPolling | - |
| `param` | 透传 | `/:<param>?` 后缀 | - | - | - |
### 容错
注册表对每个模块做字段完整性校验(缺 `id`/`tabKey`/`component` 的模块跳过),单个模块异常不影响其余页签加载。
## PageShell统一页面外壳
`src/components/ui/PageShell.vue` 是可复用的页面外壳组件,统一所有页面的:
- **容器宽度**:默认复用全局 `.container``max-width: 1080px`,居中,左右 `padding``fluid` 变体突破限制全宽(如日历页)
- **垂直节奏**`padding: var(--space-xl) 0 var(--space-2xl)``padded: false` 可关
- **标题**`titleKey` prop -> i18n 文案,渲染为 `<h1>`
- **操作区**`#actions` 具名插槽,放在标题旁(如白板的切换白板表单)
各模块根组件用 `<PageShell>` 包裹内容,不再各写一套 `.container`/标题/间距。新增页签照此复用。
## KeepAlive 缓存与卸载
`App.vue``<KeepAlive :include="keepInclude">` 包裹路由组件,切换页签不卸载(状态保留)。
- `keepInclude` 是模块 id 列表,默认包含所有模块。
- 声明了 `requiresAlive` 的模块,当存活探测为 `down` 时移出名单 -> 组件卸载;恢复 `up` 后重新纳入。
- **匹配约定**KeepAlive 按组件 `name` 匹配,因此每个模块组件需 `defineOptions({ name: <模块 id> })`
- 内置模块whiteboard/calendar直接在组件内声明。
- 子模块集成timetable/mobile-game由 mainPage 侧包装器持有 name子项目自身不持 name保持纯净
## 子模块集成git submodule + 构建期组件)
部分页签由独立仓库的子项目经 git submodule 集成mainPage 侧用包装器 import 子项目 `App.vue` 作为路由组件(非 iframe共享构建与 KeepAlive 缓存:
- **timeTableFix**`third_party/timeTableFix`,日程整理页签):无 i18n子项目硬编码中文。详见 [submodule-timeTableFix.md](./submodule-timeTableFix.md)。
- **z449**`third_party/z449`,移动游戏页签):子项目自带独立 `vue-i18n` 实例与 locale 文案。mainPage 包装器把当前语言经 `:locale` prop 透传,子项目 `App.vue` `watch` 该 prop 同步到自己的 i18n 实例,从而跟随父项目语言开关响应式切换(不共享 messages仅同步 locale 值)。详见 [submodule-z449.md](./submodule-z449.md)。
## 存活校验requiresAlive
`src/composables/useLiveness.js` 提供跨域存活探测:
- 探测方式:`fetch(url, { mode: 'no-cors', cache: 'no-store' })`resolve 即存活4s 超时。
- 时机:应用加载即探测一次,之后每 30s 周期重探(`startPolling`)。
- 结果在全应用共享(模块级 `cache` Map`useProjects`(页签可见性)与 `App.vue`KeepAlive 卸载)共用同一 ref。
- **乐观显示**探测中pending/存活up都显示页签仅明确不可达down才隐藏并卸载恢复后自动重新加载。
目前仅白板页签使用(`requiresAlive: 'https://f.zikai.wang/health'`)。
## 路由
`src/router/index.js` 遍历注册表生成路由每个模块一条路由path = `/<id>` + 可选 `/:<param>?`。history 模式,需站点根 `.htaccess` 做 SPA 回退(见 README 安装步骤)。
## 国际化
`src/i18n/` -- 中文同步注入主 chunk默认语言英文通过动态 `import()` 懒加载成独立 chunk只看中文的访客不会下载英文资源。各模块 `tabKey` 指向 `tabs.*` 下的文案。
## 集中配置
`src/config/app.config.js` -- 外部服务 URL白板地址、存活探针路径等集中管理模块按需 import。改 URL 只改这里(需重新 build