docs: 精简 README,细节拆分到 docs/

README 仅保留概览/结构/基础设施/安装/开发命令;
架构、新增页签、子模块管理细节移至 docs/ 下三个 md:
- docs/architecture.md:模块注册/PageShell/KeepAlive/存活校验
- docs/add-module.md:新增页签步骤 + 元数据字段速查 + 常见变体
- docs/submodule-timeTableFix.md:submodule 升级/clone/移除/独立访问
This commit is contained in:
2026-07-23 04:09:42 +00:00
parent a844f364dc
commit 4334dec00d
4 changed files with 265 additions and 56 deletions

View File

@@ -3,6 +3,8 @@
Zikai 的作品集主页 -- Vue 3 模块化多项目展示站。浅色极简、中英双语(英文按需懒加载)。 Zikai 的作品集主页 -- Vue 3 模块化多项目展示站。浅色极简、中英双语(英文按需懒加载)。
每个项目页签是一个独立「模块」,由注册表自动生成页签与路由 -- 新增/删除页签只增删一个文件夹,互不影响。 每个项目页签是一个独立「模块」,由注册表自动生成页签与路由 -- 新增/删除页签只增删一个文件夹,互不影响。
> 架构细节、新增页签指南、子项目管理见 [`docs/`](./docs/)。
--- ---
## 项目结构 ## 项目结构
@@ -14,18 +16,16 @@ mainPage/
├── vite.config.js # 构建输出到 /root/htmlemptyOutDir=false不清空既有文件 ├── vite.config.js # 构建输出到 /root/htmlemptyOutDir=false不清空既有文件
├── .gitmodules # git submodulethird_party/timeTableFix ├── .gitmodules # git submodulethird_party/timeTableFix
├── README.md ├── README.md
├── docs/ # 架构 / 新增页签 / 子模块管理文档
├── public/favicon.svg ├── public/favicon.svg
├── third_party/ ├── third_party/
│ └── timeTableFix/ # ★ git submoduleICS 日程整理器,独立仓库 │ └── timeTableFix/ # ★ git submoduleICS 日程整理器,独立仓库
│ # src/App.vue 被 timetable 模块直接 import 为路由组件
└── src/ └── src/
├── main.js # 应用入口(挂载 i18n / router / 全局样式) ├── main.js # 应用入口(挂载 i18n / router / 全局样式)
├── App.vue # 根布局:顶栏 + 标签导航 + <KeepAlive router-view> + 页脚 ├── App.vue # 根布局:顶栏 + 标签导航 + <KeepAlive router-view> + 页脚
├── config/ ├── config/
│ └── app.config.js # 集中配置:外部服务 URL白板地址 / 存活探针等) │ └── app.config.js # 集中配置:外部服务 URL白板地址 / 存活探针等)
├── i18n/ # 国际化(中文同步注入;英文动态 import() 懒加载) ├── i18n/ # 国际化(中文同步注入;英文动态 import() 懒加载)
│ ├── index.js
│ └── locales/{zh-CN,en}.js
├── router/index.js # 路由表由模块注册表自动生成 ├── router/index.js # 路由表由模块注册表自动生成
├── modules/ # ★ 扩展点:每个页签一个文件夹 ├── modules/ # ★ 扩展点:每个页签一个文件夹
│ ├── index.js # 注册表import.meta.glob 约定式收集,带容错) │ ├── index.js # 注册表import.meta.glob 约定式收集,带容错)
@@ -33,32 +33,11 @@ mainPage/
│ ├── whiteboard/ # 共享记事本页签iframe 嵌入 + 存活校验) │ ├── whiteboard/ # 共享记事本页签iframe 嵌入 + 存活校验)
│ ├── timetable/ # 日程整理页签(构建期 import 子模块 App.vue │ ├── timetable/ # 日程整理页签(构建期 import 子模块 App.vue
│ └── calendar/ # 隐藏页(仅手动访问 /cqy │ └── calendar/ # 隐藏页(仅手动访问 /cqy
├── components/ # 共享组件TabNav / LanguageSwitcher / ui 基元) ├── components/ # 共享组件TabNav / LanguageSwitcher / ui 基元含 PageShell
├── composables/ # useProjects(页签+存活联动)/ useLiveness / useI18nLazy / useLocale ├── composables/ # useProjects / useLiveness / useI18nLazy / useLocale
└── styles/ # variables.css主题令牌+ base.css └── styles/ # variables.css主题令牌+ base.css
``` ```
## 模块机制(高内聚低耦合)
- `src/modules/index.js``import.meta.glob('./*/index.js', { eager: true })` 收集所有模块,单个模块导出异常会跳过并告警,不影响其余页签。
- 每个模块 `index.js` 默认导出元数据:
```js
export default {
id: 'whiteboard', // 唯一标识 + 路由 path
tabKey: 'tabs.whiteboard', // i18n 键
order: 2, // 页签排序
component: () => import('./Whiteboard.vue'), // 懒加载组件(独立 chunk
requiresAlive: 'https://f.zikai.wang/health' // 可选:存活探针 URL挂掉则隐藏页签
}
```
- `TabNav` 遍历注册表渲染页签;`router` 遍历注册表生成路由。
- 删除一个页签 = 删 `src/modules/<id>/` 文件夹并重新 build其余页签不受影响。
### 存活校验
声明了 `requiresAlive` 的模块,应用加载即探测探针 URL`no-cors` fetch4s 超时),之后每 30s 周期重探。
乐观显示:探测中/存活都显示页签,仅明确不可达时才隐藏并从 KeepAlive 移出(卸载组件);恢复后自动重新加载。目前仅白板页签使用。
## 依赖的基础设施 ## 依赖的基础设施
| 项 | 要求 | | 项 | 要求 |
@@ -67,9 +46,8 @@ mainPage/
| Web 服务器 | Apache 2.4(启用 `mod_rewrite`),对外静态服务 `/root/html` | | Web 服务器 | Apache 2.4(启用 `mod_rewrite`),对外静态服务 `/root/html` |
| SPA 回退 | history 模式需站点根 `.htaccess` 回退到 `index.html`(见下) | | SPA 回退 | history 模式需站点根 `.htaccess` 回退到 `index.html`(见下) |
> 共享记事本页签通过 iframe 嵌入外部服务(默认 `https://f.zikai.wang/wb/share`URL 在 `src/config/app.config.js` 集中配置。该服务需公开且允许被 iframe 嵌入。 > 共享记事本页签 iframe 嵌入外部服务(`https://f.zikai.wang/wb/share`URL 在 `src/config/app.config.js` 配置。
> > 日程整理页签timeTableFix经 git submodule 构建期集成(非 iframe详见 [`docs/submodule-timeTableFix.md`](./docs/submodule-timeTableFix.md)。
> 日程整理页签timeTableFix通过 **git submodule** 作为子项目,构建期直接 import 其 `App.vue` 为路由组件(非 iframe同时其独立 app 可经 Apache `/ttf/` 反代单独访问。
## Ubuntu 从 0 安装 ## Ubuntu 从 0 安装
@@ -102,32 +80,7 @@ RewriteRule ^ - [L]
RewriteRule ^ index.html [L] RewriteRule ^ index.html [L]
``` ```
把 `/root/html`(或 `/var/www/html`)设为 `DocumentRoot`,访问站点即可。开发预览用 `npm run dev`http://localhost:5173 `/root/html`(或 `/var/www/html`)设为 `DocumentRoot`,访问站点即可。
## 如何新增一个项目页签
1. 复制 `src/modules/whiteboard/`(或 `mobile-game/`)为 `src/modules/<新项目>/`。
2. 改 `<新项目>/index.js` 的 `id` / `tabKey` / `order` / `component`。
3. 在 `src/i18n/locales/zh-CN.js` 与 `en.js` 的 `tabs` 下加对应文案。
4. `npm run build` -- 页签与路由自动出现,无需改导航或路由代码。
## 子项目 timeTableFix 的升级
timeTableFix 作为 git submodule 独立维护,升级流程:
```bash
cd mainPage
git submodule update --remote third_party/timeTableFix # 拉取最新
cd third_party/timeTableFix && git checkout master # 固定到目标分支/commit
cd ../..
git add third_party/timeTableFix # 更新 gitlink 指针
git commit -m "chore: 升级 timeTableFix 子模块"
npm run build # 重新构建 mainPage
```
timeTableFix 本身完全独立,可单独 `npm run build` / `npm test`,也可经 Apache `/ttf/` 反代单独访问。
---
## 开发命令 ## 开发命令
@@ -137,3 +90,9 @@ npm run dev # 开发服务器 http://localhost:5173
npm run build # 构建到 /root/html npm run build # 构建到 /root/html
npm run preview # 本地预览构建产物 npm run preview # 本地预览构建产物
``` ```
## 了解更多
- [架构(模块注册 / PageShell / KeepAlive / 存活校验)](./docs/architecture.md)
- [如何新增一个项目页签](./docs/add-module.md)
- [子项目 timeTableFix 管理submodule 升级 / 移除)](./docs/submodule-timeTableFix.md)

112
docs/add-module.md Normal file
View File

@@ -0,0 +1,112 @@
# 新增一个项目页签
新增页签 = 新建 `src/modules/<新项目>/` 文件夹,无需改动导航或路由代码。注册表自动收集并生成页签与路由。
## 步骤
### 1. 建模块文件夹与元数据
复制 `src/modules/timetable/`(最简范式)或 `whiteboard/`(带存活校验)为 `src/modules/<新项目>/`
`index.js` 默认导出元数据(字段含义见 [architecture.md](./architecture.md#模块注册表扩展点核心)
```js
export default {
id: 'my-project', // 必填:唯一标识,同时作路由 path / KeepAlive 匹配名
tabKey: 'tabs.myProject', // 必填i18n 键
order: 4, // 可选:页签排序,缺省 999
component: () => import('./MyProject.vue') // 必填:懒加载组件
}
```
### 2. 实现组件,用 PageShell 包裹
组件内 `defineOptions({ name: <id> })`KeepAlive 匹配用),并用 `<PageShell>` 统一容器宽度与标题:
```vue
<script setup>
defineOptions({ name: 'my-project' })
import PageShell from '../../components/ui/PageShell.vue'
import { useLocale } from '../../composables/useLocale.js'
const { t } = useLocale()
</script>
<template>
<PageShell title-key="tabs.myProject">
<!-- 页面内容 -->
</PageShell>
</template>
```
PageShell props
- `titleKey`i18n 键,渲染页面标题;不传则无标题
- `fluid`true 突破 1080px 全宽(如日历页)
- `padded`false 去掉默认上下间距(页面自管布局时用)
- `#actions` 具名插槽:标题旁的操作区(如白板切换表单)
### 3. 加 i18n 文案
`src/i18n/locales/zh-CN.js``en.js``tabs` 下加对应文案:
```js
tabs: {
// ...
myProject: '我的项目', // zh-CN
myProject: 'My Project', // en
}
```
### 4. 构建
```bash
npm run build
```
页签与路由自动出现。
## 常见变体
### 隐藏页签(仅 URL 访问)
```js
export default { id: 'secret', tabKey: 'tabs.secret', order: 99, hidden: true, component: () => import('./Secret.vue') }
```
`hidden: true` 的页签不在导航显示,但 URL 访问后保持可见(访问过的隐藏页签不会因切走而消失)。
### 依赖外部服务存活(挂掉则隐藏并卸载)
```js
import { healthUrl } from '../../config/app.config.js'
export default {
id: 'my-service', tabKey: 'tabs.myService', order: 5,
requiresAlive: 'https://example.com/health', // 存活探针 URL
component: () => import('./MyService.vue')
}
```
服务失活时页签隐藏、组件卸载;恢复后自动重新加载。详见 [architecture.md](./architecture.md#存活校验requiresalive)。
### 路由参数(如 /my-project/:id
```js
export default { id: 'my-project', tabKey: 'tabs.myProject', order: 4, param: 'itemId', component: () => import('./MyProject.vue') }
```
生成路由 `/<id>/:<param>?`,组件内用 `useRoute().params.<param>` 读取。
### 集成独立子项目git submodule
参照 `timetable` 模块:建 mainPage 侧包装器组件 import 子项目 `App.vue`,套 `PageShell`,包装器持有 `defineOptions({ name })`。子项目保持纯净。详见 [submodule-timeTableFix.md](./submodule-timeTableFix.md)。
## 元数据字段速查
| 字段 | 必填 | 说明 |
|------|------|------|
| `id` | 是 | 唯一标识,路由 path / KeepAlive 匹配名 |
| `tabKey` | 是 | i18n 键,页签与页面标题 |
| `component` | 是 | 懒加载组件函数 |
| `order` | 否 | 排序,缺省 999 |
| `hidden` | 否 | 隐藏页签URL 访问后保持可见 |
| `requiresAlive` | 否 | 存活探针 URL失活则隐藏并卸载 |
| `param` | 否 | 路由参数名,生成 `/:<param>?` |

79
docs/architecture.md Normal file
View File

@@ -0,0 +1,79 @@
# 架构
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> })`
- 内置模块mobile-game/whiteboard/calendar直接在组件内声明。
- 子模块集成timetable由 mainPage 侧包装器 `Timetable.vue` 持有 name子项目自身不持 name保持纯净
## 存活校验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

View File

@@ -0,0 +1,59 @@
# 子项目 timeTableFixgit submodule
timeTableFixICS 日程整理器)作为 **git submodule** 被 mainPage 引入,位于 `third_party/timeTableFix`,独立仓库为 https://git.zikai.wang/zikai/timeTableFix.git 。
## 集成方式
mainPage 侧的 `src/modules/timetable/Timetable.vue` 包装器 import 子模块的 `src/App.vue` 作为组件,套 `PageShell` 统一容器与标题,并持有 KeepAlive 匹配名 `defineOptions({ name: 'timetable' })`
- 子模块 `App.vue` 保持纯净(不持 name、不依赖宿主可独立 `npm run build` / `npm test` / 部署。
- 构建期集成(非 iframe共享 mainPage 的构建、依赖(`ical.js`)与 KeepAlive 缓存。
- 子模块样式已隔离scoped + `ttf-` 前缀类名),全局重置仅在独立 app 的 `main.js` 加载,不污染宿主。
## 克隆(含子模块)
```bash
git clone --recursive https://git.zikai.wang/zikai/zMainPage.git mainPage
```
若已 clone 但未带子模块:
```bash
cd mainPage
git submodule update --init --recursive
```
## 升级子模块到最新版本
```bash
cd mainPage
git submodule update --remote third_party/timeTableFix # 拉取最新
cd third_party/timeTableFix && git checkout master # 固定到目标分支/commit
cd ../..
git add third_party/timeTableFix # 更新 gitlink 指针
git commit -m "chore: 升级 timeTableFix 子模块"
npm run build # 重新构建 mainPage
git push # 推送 mainPagegitlink
```
> 推送顺序:先确保子模块的 commit 已推到 timeTableFix.git再推 mainPage 的 gitlink否则别人 clone mainPage 时拉不到子模块对应 commit。
## 移除子模块集成(仅移除 mainPage 页签,不影响 timeTableFix 本身)
```bash
cd mainPage
git submodule deinit -f third_party/timeTableFix
git rm third_party/timeTableFix
git commit -m "chore: 移除 timeTableFix 子模块集成"
```
同时删 `src/modules/timetable/` 文件夹即移除页签。timeTableFix 独立仓库与部署不受影响。
## 独立访问
timeTableFix 独立 app 可经 Apache `/ttf/` 反代单独访问(`https://zikai.wang/ttf/`),与 mainPage 组件集成互不冲突。反代配置:
```apache
ProxyPass /ttf/ http://127.0.0.1:8888/
ProxyPassReverse /ttf/ http://127.0.0.1:8888/
```