死代码清理: - 删除未使用的 UI 组件 Card.vue / Tag.vue - 移除 app.config.js 死字段 healthTimeoutMs(×2)与 isDev() - 移除 PageShell.vue 死 prop padded(恒为默认值) - 移除 useLocale.js 死导出 isZh/pick/locale - 移除 router/index.js 不可达 try/catch,简化为直接 .map() - 移除 package.json 重复依赖 ical.js(子模块自带) - 移除 i18n 死键 common.notAvailable / common.serviceUnavailable - 移除 vite.config.js 陈旧的 /448 /449 dev proxy(z449 为构建期组件,不走该 URL) 提前失败/日志: - useI18nLazy.js switchLocale 加载语言包失败时 console.error 记录(原静默吞错) - modules/index.js 字段不全的模块跳过时 console.warn 告警(原与文档承诺不符、静默跳过) - deploy.sh 补齐 zWhiteBoard / zPDF_package 的 npm install 与子模块初始化 (均为构建期组件 import,全新检出时缺少依赖会构建失败) 文档对齐现状(无 iframe、无外部域名): - README 改写为简洁版,修正 iframe/ical.js/子模块数量过时描述 - architecture.md 修正 requiresAlive 示例(同源 /api/health,非外部域名) 与「仅白板使用」断言(PDF 也用);移除已删 padded prop - add-module.md 同步移除 padded、修正 requiresAlive 示例 - submodule-timeTableFix.md 修正 ical.js 归属(子模块自带,非父项目共享) - submodule-zPDF_package.md 子项目数量 3 -> 4 - 新增 submodule-zWhiteBoard.md - readme-fixes.md 记录本次勘误
6.5 KiB
架构
mainPage 是一个 Vue 3 模块化多项目展示站。核心设计目标:高内聚低耦合 -- 每个项目页签是一个独立「模块」,由注册表自动生成页签与路由,新增/删除页签只增删一个文件夹,互不影响。
模块注册表(扩展点核心)
src/modules/index.js 用 import.meta.glob('./*/index.js', { eager: true }) 约定式收集每个子模块的 index.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: healthUrl('whiteboard'), // 可选:存活探针 URL(同源 /api/health),服务失活则隐藏页签并卸载组件
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) - 标题:
titleKeyprop -> 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,日程整理页签):自带独立vue-i18n实例与中英 locale 文案。经:localeprop 桥接父项目语言。详见 submodule-timeTableFix.md。 - z449(
third_party/z449,移动游戏页签):自带独立vue-i18n实例与 locale 文案。mainPage 包装器把当前语言经:localeprop 透传,子项目App.vuewatch该 prop 同步到自己的 i18n 实例,从而跟随父项目语言开关响应式切换(不共享 messages,仅同步 locale 值)。详见 submodule-z449.md。 - zPDF_package(
third_party/zPDF_package,PDF 转换页签):经同源 fetch 调用 zTools2 的/api/pdf/*REST API。详见 submodule-zPDF_package.md。 - zWhiteBoard(
third_party/zWhiteBoard,共享记事本页签):经同源 fetch/WS 调用 zTools2 的/api/wb/*与/api/ws/wb/*。详见 submodule-zWhiteBoard.md。
i18n 桥接关键点:子项目组件用各自
useLocalecomposable 直接读自身模块级 i18n 实例(i18n.global),而非useI18n({ useScope: 'global' })。后者在嵌入时会解析到宿主已安装的 i18n 实例(不含子项目文案),导致文案回退成 i18n key(显示 ID 而非文字)。
存活校验(requiresAlive)
src/composables/useLiveness.js 提供跨域存活探测:
- 探测方式:
fetch(url, { mode: 'no-cors', cache: 'no-store' }),resolve 即存活,4s 超时。 - 时机:应用加载即探测一次,之后每 30s 周期重探(
startPolling)。 - 结果在全应用共享(模块级
cacheMap),useProjects(页签可见性)与App.vue(KeepAlive 卸载)共用同一 ref。 - 乐观显示:探测中(pending)/存活(up)都显示页签,仅明确不可达(down)才隐藏并卸载;恢复后自动重新加载。
- 探针 URL 由
healthUrl(service)拼出同源相对路径(/api/health),经 vite dev proxy / Apache 反代转发到 zTools2,与环境无关。
目前白板与 PDF 页签使用(requiresAlive: healthUrl('whiteboard'|'pdf')),二者探针均指向 zTools2 的 /api/health。
路由
src/router/index.js 遍历注册表生成路由:每个模块一条路由,path = /<id> + 可选 /:<param>?。history 模式,需站点根 .htaccess 做 SPA 回退(见 README 安装步骤)。
国际化
src/i18n/ -- 中文同步注入主 chunk(默认语言),英文通过动态 import() 懒加载成独立 chunk,只看中文的访客不会下载英文资源。各模块 tabKey 指向 tabs.* 下的文案。
集中配置
src/config/app.config.js -- 各服务的同源探针路径(/api/health)与默认参数集中管理,模块按需 import。改路径只改这里(需重新 build)。