[Huawei] 添加前端设计约定文档
This commit is contained in:
@@ -0,0 +1,322 @@
|
|||||||
|
# 🏗️ 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. 后续如有不合理之处,更新此文档(不改代码,改规矩)
|
||||||
Reference in New Issue
Block a user