# 个人主页 — 需求文档 ## 1. 项目范围 **只做 Web 应用本身。** AstrBot 插件、Gitea/Seafile API 接入属于独立项目。 主页是一个**公开的静态站点**,通过 markdown/JSON 文件驱动内容(这些文件可由 AstrBot 等其他系统写入,但主页本身不关心来源)。 - 域名:`sepcomet.xyz`(备案中) - 部署:自有服务器 Docker 环境 - 访问:完全公开,求职时可直接给链接 - 用户背景:Unity 客户端开发,Web 经验不多,怎么方便怎么来 --- ## 2. 页面与功能 ### 2.1 首页 - **Hero 区**:头像、名字、一句话简介、社交链接(GitHub/Gitea/Blog 等) - **开发日志摘要**:最新 3-5 条日志的卡片列表 - **精选项目**:featured 项目的卡片展示 - **Gitea 活动概览**:贡献热力图(可选) ### 2.2 开发日志(/logs) - 按时间倒序的日志列表 - 每条日志卡片:标题、日期、标签、摘要 - 点击进入日志详情页(/logs/[slug]) - 日志详情:Markdown 全文渲染,关联的仓库名和 commit 链接 - 内容来自 `src/content/logs/` 下的 `.md` 文件(文件来源不在本范围内,由 AstrBot 或其他系统写入) ### 2.3 项目展示(/projects) - 项目卡片网格布局 - 每个项目卡片:名称、描述、封面图、标签 - 点击可展开/跳转详情:演示视频(如有)、Gitea 仓库链接、Seafile 下载链接(如有) - 数据来源:`src/content/projects/index.json`(手动维护) ### 2.4 Gitea 统计(可嵌入首页或独立页) - 贡献热力图(客户端 JS 调 Gitea API 渲染) - 最近活动列表 - Gitea 地址通过站点配置注入 ### 2.5 Seafile 分享(/shares 或首页侧栏) - 文件分享链接列表,每条含:文件名、描述、链接、时间 - 数据来源:`src/content/shares/index.json`(手动维护) --- ## 3. 功能优先级 **P0(必须有):** - 首页 Hero 区(个人简介 + 链接) - 日志列表 + 详情页(Markdown 渲染) - 项目展示卡片 **P1(很重要):** - Gitea 贡献热力图(客户端 API 调用) - 响应式布局 **P2(锦上添花):** - Seafile 分享链接列表 - 日志按标签/时间筛选 - 项目演示视频嵌入 - 深色模式 --- ## 4. 技术选型 | 层 | 选型 | 理由 | |----|------|------| | **框架** | [Astro](https://astro.build) | 静态站点生成,原生 `.md` 支持,零 JS 默认 + 可选交互岛 | | **样式** | Tailwind CSS | 原子化 CSS,快速出效果,不需要手写大量样式 | | **交互组件** | React(按需引入) | 仅 Gitea 热力图等需交互处使用,Astro Islands | | **内容存储** | 文件系统(`.md` + `.json`) | 无需数据库,日志/项目数据直接文件 | | **部署** | Nginx + Docker | 静态文件服务,单容器 | | **构建触发** | 内容文件变更后手动/脚本 rebuild | 日志写入 → `npm run build` → nginx reload | --- ## 5. 数据模型 ### 日志 frontmatter(src/content/logs/*.md) ```yaml --- title: "personal-homepage 项目初始化" date: 2026-05-03 repo: "sepcomet/personal-homepage" tags: [astro, init, setup] summary: "搭建 Astro 项目骨架,确定目录结构与部署方案" --- 正文内容(Markdown) — 客观 diff 分析 + 主观动机,由 AstrBot 生成后写入 ``` ### 项目数据(src/content/projects/index.json) ```json [ { "name": "personal-homepage", "description": "个人主页 Dashboard", "gitea_repo": "sepcomet/personal-homepage", "cover_image": "/images/projects/homepage.png", "demo_video": "https://...", "download_link": "https://seafile.sepcomet.xyz/...", "tags": ["astro", "web"], "featured": true } ] ``` ### 站点配置(src/config.ts) ```ts export const site = { name: "SepComet", title: "个人主页", tagline: "Unity 开发者", avatar: "/images/avatar.png", gitea: { url: "https://gitea.sepcomet.xyz", username: "sepcomet" }, links: [ { name: "Gitea", url: "https://gitea.sepcomet.xyz" }, { name: "GitHub", url: "https://github.com/sepcomet" } ] } ``` --- ## 6. 目录结构 ``` personal-homepage/ ├── src/ │ ├── pages/ │ │ ├── index.astro # 首页 │ │ ├── logs/ │ │ │ ├── index.astro # 日志列表 │ │ │ └── [slug].astro # 日志详情 │ │ ├── projects/ │ │ │ └── index.astro # 项目展示 │ │ └── shares.astro # Seafile 分享 │ ├── components/ │ │ ├── Header.astro │ │ ├── Footer.astro │ │ ├── Hero.astro │ │ ├── LogCard.astro │ │ ├── ProjectCard.astro │ │ └── GiteaHeatmap.jsx # React Island │ ├── content/ │ │ ├── logs/ # *.md(外部系统写入) │ │ ├── projects/ │ │ │ └── index.json │ │ └── shares/ │ │ └── index.json │ ├── config.ts │ └── styles/ │ └── global.css ├── public/ │ ├── images/ │ │ └── avatar.png │ └── favicon.svg ├── astro.config.mjs ├── tailwind.config.mjs ├── Dockerfile ├── docker-compose.yml └── package.json ``` --- ## 7. 部署 ```yaml # docker-compose.yml services: homepage: build: . ports: - "8080:80" volumes: - ./src/content:/app/src/content # 日志/项目内容热更新 - ./public/images:/app/public/images restart: unless-stopped ``` - Docker 构建阶段:`npm run build` 生成静态文件 - 运行阶段:Nginx 托管 `dist/` 目录 - 更新日志:AstrBot 写入新的 `.md` → 触发 rebuild → nginx 热加载 --- ## 8. 实施计划 | Phase | 内容 | 产出 | |-------|------|------| | **1** | Astro 项目初始化 + Tailwind + Header/Footer/Hero + Docker | 可访问的骨架页面 | | **2** | 日志系统:列表页 + 详情页 + frontmatter 解析 + 示例内容 | 日志模块 | | **3** | 项目展示:卡片组件 + JSON 数据 + 布局 | 项目模块 | | **4** | Gitea 热力图 React Island + 站点配置 | 统计模块 | | **5** | Seafile 分享 + 响应式打磨 + 深色模式 + SEO | 收尾 | --- ## 9. 不在此项目范围内 - AstrBot 插件开发(独立项目) - Gitea/Seafile API 的调用与数据拉取(独立脚本或 AstrBot 插件负责) - 日志 markdown 文件的生成逻辑(AstrBot 负责) - 自动化部署流程(CI/CD,可后续补充) 主页只负责:**读取已有数据文件 → 渲染为 HTML → 静态托管**。