Files

323 lines
11 KiB
Markdown
Raw Permalink 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.
# 🏗️ AgentGIS 前端约定
> **目的**: 减少每个子系统在前端上的反复调整,一次定规矩,后续照做。
> **适用**: suite-market、auth-center、discussions 等所有 Next.js 前端。
> **版本**: v1.0 (2026-07-21)
---
## 一、页面骨架
所有页面使用统一的三段式骨架布局,不另起炉灶:
```
┌──────────────────────────────────────────────┐
│ 顶部导航(Navbar) │
│ 套件 | 发布 | 管理 | 文档 [登录/用户头像] │
├────────┬─────────────────────────────────────┤
│ 左侧 │ 右侧内容区 │
│ 导航 │ (各页面自定) │
│ │ │
│ (各页面 │ │
│ 自定) │ │
└────────┴─────────────────────────────────────┘
└────────────── 页脚(Footer) ──────────────────┘
```
**规则:**
- 顶部导航统一在 `Navbar.tsx` 维护,不单独定制
- 左侧导航通过页面级 `layout.tsx` 注入(Next.js App Router
- 页脚全局统一
- 不出现面包屑(用左侧导航替代)
---
## 二、未认证状态
所有需要登录的页面,未认证时**必须**做到:
```
┌─────────────────────────────────────┐
│ 🔒 请登录后浏览 │
│ │
│ 登录后可查看套件、文档、管理后台等 │
│ │
│ (不显示登录按钮 — header 右上角有) │
└─────────────────────────────────────┘
```
**规则:**
- 使用统一 `LoginPrompt.tsx` 组件,不自制
- 不显示登录按钮/链接(header 右上角已有「登录」按钮)
- API 调用返回 401 时,前端必须捕获并切换到 LoginPrompt,**不能卡在加载中**
- 公开页面(首页等)不需登录,正常渲染
---
## 三、数据加载与错误处理
所有页面按此顺序处理状态:
```
加载中 → 出错 → 空数据 → 正常渲染
```
**规则:**
- 加载中:显示统一的「加载中...」骨架屏
- 出错(含 401):捕获错误,切换 LoginPrompt 或显示错误信息
- 空数据:显示「暂无内容」+ 引导文案
- 正常渲染:展示数据
- 不允许出现「永远卡在加载中」的情况
**所有 API 调用都必须有 `.catch()`**
```typescript
// ✅ 正确
fetchData().then(setData).catch(handleError)
// ❌ 错误
fetchData().then(setData) // 无 catch = 卡在加载中
```
---
## 四、目录结构
每个 Next.js 前端项目统一按以下结构组织:
```
frontend/
├── app/ # App Router 页面
│ ├── layout.tsx # 全局布局(Navbar + Footer
│ ├── page.tsx # 首页
│ ├── suites/ # 套件
│ │ ├── layout.tsx # 套件左侧导航
│ │ ├── page.tsx # 列表
│ │ └── [id]/page.tsx # 详情
│ ├── docs/ # 文档
│ │ ├── layout.tsx # 文档树形导航
│ │ ├── page.tsx
│ │ └── [slug]/page.tsx
│ ├── admin/ # 管理后台
│ │ ├── layout.tsx # 管理侧边栏
│ │ ├── applications/
│ │ ├── suites/
│ │ └── docs/
│ └── publish/
├── components/ # 共享组件
│ ├── Navbar.tsx # 顶部导航
│ ├── LayoutShell.tsx # 骨架布局
│ ├── LoginPrompt.tsx # 未登录提示
│ └── ... # 其他通用组件
├── lib/ # 工具库
│ ├── api.ts # API 封装(含统一 401 处理)
│ ├── auth.ts # 认证
│ └── auth-context.tsx # 认证上下文
├── public/
├── package.json
└── next.config.ts
```
**规则:**
- `app/` 下只放路由文件,不放组件
- 业务组件放 `components/`
- 工具函数放 `lib/`
---
## 五、API 401 拦截器
`lib/api.ts` 中的 axios 实例统一处理 401:
```typescript
api.interceptors.response.use(
(res) => res,
async (err) => {
if (err.response?.status === 401 && typeof window !== "undefined") {
// 公开页面:静默返回错误,让页面组件自己处理
const path = window.location.pathname;
if (path === "/" || path.startsWith("/suites") || path.startsWith("/docs")) {
return Promise.reject(err);
}
// 管理类页面:尝试刷新 token
try {
const refreshRes = await axios.post(
"https://auth.mercator.cn/api/v1/public/refresh/",
{},
{ withCredentials: true }
);
if (refreshRes.data?.access_token) {
setAccessToken(refreshRes.data.access_token);
err.config.headers["Authorization"] = "Bearer " + refreshRes.data.access_token;
return axios(err.config);
}
} catch {}
// 刷新失败:跳转登录
window.location.href = "https://auth.mercator.cn/login?return_url=" + encodeURIComponent(path);
}
return Promise.reject(err);
}
);
```
**规则:**
- 公开页面(suites、docs)的 401 → 静默返回,页面组件切换到 LoginPrompt
- 管理页面(admin、publish)的 401 → 尝试刷新 token,刷新失败跳转登录
- 不拦截器里弹 alert 或直接修改 DOM
---
## 六、深色/浅色主题
- 使用 Tailwind `dark:` 变体控制深色模式
- 通过 `ThemeToggle` 组件切换(位于 `components/`
- 默认跟随系统 `prefers-color-scheme`
- 切换结果存入 localStorage,下次加载优先读取
- 所有新组件必须同时测试浅色和深色模式
---
## 七、页面宽度与容器尺寸
统一使用以下预设宽度:
| 用途 | Tailwind class | 说明 |
|------|---------------|------|
| 最宽布局 | `max-w-7xl mx-auto` | 列表页、管理后台 |
| 内容页(窄) | `max-w-3xl mx-auto` | 详情页、阅读页 |
| 内容页(中) | `max-w-5xl mx-auto` | 文档列表、套件详情 |
| 表单 | `max-w-lg mx-auto` | 登录、设置 |
| 全宽 | `max-w-none` | 极少使用 |
**不允许:** 每个页面自定义宽度,导致用户在页面间切换时视觉跳跃。
---
## 八、移动端适配
- 使用 Tailwind 响应式前缀:`sm:`640px)、`md:`768px)、`lg:`1024px)、`xl:`1280px
- 导航栏在 `lg` 断点以下折叠为汉堡菜单(已实现)
- 左侧导航在 `md` 以下默认隐藏,通过按钮切换显示
- 表格在 `md` 以下切换为卡片视图(每行一张卡片)
- 不允许仅桌面端可用的设计,所有页面必须跑通 375px 宽度
---
## 九、通知(Toast 替代 alert/confirm
- 不使用浏览器原生的 `alert()``confirm()``prompt()`
- 使用统一的 Toast 通知组件
- Toast 位置:右上角固定
- 类型:success(绿色)、error(红色)、warning(黄色)、info(蓝色)
- 自动消失:success/info 3 秒,warning/error 5 秒
- 实现:用 react-hot-toast 或自建 `ToastProvider`
---
## 十、布局模板
按是否有侧边导航分两种布局:
### 带侧边导航
```
┌──────────┬────────────────────────────────┐
│ 顶部导航 │ │
├──────────┤ 右侧内容区 │
│ 侧边栏 │ │
│ (页面 │ │
│ 自定) │ │
└──────────┴────────────────────────────────┘
```
适用:suites、docs、admin
实现方式:`<page>/layout.tsx`
### 无侧边导航(全宽)
```
┌────────────────────────────────────────────┐
│ 顶部导航 │
├────────────────────────────────────────────┤
│ │
│ 居中内容区 │
│ │
└────────────────────────────────────────────┘
```
适用:/、/publish
实现方式:`<page>/layout.tsx` 或直接在 page 中 `max-w-* mx-auto`
---
## 十一、FastAPI 端点避免冲突
suite-market 和 auth-center 后端都是 FastAPI,默认暴露 `/docs`Swagger UI)、`/redoc``/openapi.json`
如果前端有同名路由(如文档模块的 `/docs`),会与 FastAPI 默认端点冲突。
**必须:**
```python
app = FastAPI(
docs_url="/api/docs", # 改 /docs → /api/docs
redoc_url="/api/redoc", # 改 /redoc → /api/redoc
openapi_url="/api/openapi.json" # 改 /openapi.json → /api/openapi.json
)
```
---
## 十二、Header/Footer 统一
- Header:统一使用 `Navbar.tsx`components/ 下),不单独在各页面重复
- Footer:全局底部,包含 ICP 备案号、公安备案号、版权信息
- Header 和 Footer 在全局 `layout.tsx` 中加载,页面层不覆盖
---
## 十三、组件命名
| 类型 | 命名规则 | 示例 |
|------|---------|------|
| 页面组件(app/ | PascalCase + Page 后缀 | `DocsPage`, `SuiteDetailPage` |
| 布局(layout) | 文件名固定 `layout.tsx` | 不导出命名函数 |
| 通用组件 | PascalCase | `LoginPrompt`, `SuiteCard` |
| 工具函数 | camelCase | `fetchDocuments`, `formatDate` |
| API 函数 | camelCase | `fetchDocuments`, `createDocument` |
---
## 六、认证
- header 右上角只有一个「登录」按钮
- 登录跳转到 `auth.mercator.cn`
- 登录后通过 JWT cookie 回传
- API 调用由 `api.ts` 中的 axios 实例统一处理(interceptor 自动附带 token
**不允许:**
- 页面内部额外加「登录」按钮或链接
- 手动拼写 `Authorization` header
- 直接调用 `fetch`
---
## 七、样式
- 使用 Tailwind CSS
- 基础样式在 `globals.css` 中定义
- 组件样式使用 Tailwind class,不单独写 CSS 文件
- 不使用 CSS Modules 或 styled-components
---
## 八、落地方式
1. 此文档纳入 `SuiteHub/agent-profiles/suites-help/` 作为平台规范
2. 新建前端页面时,开发者对照此文档逐一检查
3. Code Review 时以此文档为标准
4. 后续如有不合理之处,更新此文档(不改代码,改规矩)