From bfbadface44865e4b0344ccdf671ccf18bf51d0a Mon Sep 17 00:00:00 2001 From: Huawei Date: Tue, 21 Jul 2026 07:59:47 +0000 Subject: [PATCH] =?UTF-8?q?[Huawei]=20=E6=B7=BB=E5=8A=A0=E5=89=8D=E7=AB=AF?= =?UTF-8?q?=E8=AE=BE=E8=AE=A1=E7=BA=A6=E5=AE=9A=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- suites-help/前端设计约定.md | 322 ++++++++++++++++++++++++++++++++++++ 1 file changed, 322 insertions(+) create mode 100644 suites-help/前端设计约定.md diff --git a/suites-help/前端设计约定.md b/suites-help/前端设计约定.md new file mode 100644 index 0000000..5cf6248 --- /dev/null +++ b/suites-help/前端设计约定.md @@ -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 +实现方式:`/layout.tsx` + +### 无侧边导航(全宽) + +``` +┌────────────────────────────────────────────┐ +│ 顶部导航 │ +├────────────────────────────────────────────┤ +│ │ +│ 居中内容区 │ +│ │ +└────────────────────────────────────────────┘ +``` + +适用:/、/publish +实现方式:`/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. 后续如有不合理之处,更新此文档(不改代码,改规矩)