834 lines
28 KiB
Markdown
834 lines
28 KiB
Markdown
# 前端技术规范
|
||
|
||
## 运行环境
|
||
- Node.js >= 20.9
|
||
- npm >= 10.x
|
||
|
||
## 项目结构
|
||
|
||
### Monorepo 架构
|
||
- 包管理:npm workspaces + Turborepo 2.0
|
||
- 项目根目录:`geo_bsp/`
|
||
- 共享包命名空间:`@geomative/*`
|
||
|
||
### 应用划分
|
||
```
|
||
apps/
|
||
├── web/ # 公开网站 — Next.js 16 (Turbopack),端口 3000(国际站) / 3002(中文站)
|
||
├── admin/ # 后台管理系统 — Vite 6 + React 19 + React Router DOM 7,端口 3003
|
||
└── api/ # 后端 API 服务 — Hono,端口 3001
|
||
|
||
packages/
|
||
├── types/ # 共享 TypeScript 类型定义 (@geomative/types)
|
||
├── ui/ # 共享 UI 组件库 (@geomative/ui)
|
||
├── utils/ # 共享工具函数 (@geomative/utils)
|
||
└── config/ # 共享配置 — Tailwind、ESLint、Prettier、TSConfig (@geomative/config)
|
||
```
|
||
|
||
### 共享包依赖方向
|
||
```
|
||
config -> types -> utils -> ui -> apps/*
|
||
```
|
||
- 禁止循环依赖
|
||
- `types` 不依赖 `ui`
|
||
- `utils` 不依赖 `ui`
|
||
- `ui` 可依赖 `types` 和 `utils`
|
||
|
||
## 技术栈
|
||
|
||
| 类别 | 技术 | 版本 |
|
||
|------|------|------|
|
||
| 前端框架 | Next.js (App Router) | 16 |
|
||
| 后台管理 | Vite + React Router DOM | 6 / 7 |
|
||
| UI 库 | React | 19 |
|
||
| 样式方案 | Tailwind CSS | 3.4 |
|
||
| 类型系统 | TypeScript | 5.0+ |
|
||
| 服务端状态 | TanStack React Query | API 数据获取、缓存、同步 |
|
||
| 客户端状态 | Zustand | 全局 UI 状态、认证状态、权限缓存(仅 admin) |
|
||
| 表单处理 | React Hook Form + Zod | - |
|
||
| 富文本编辑 | Tiptap | - |
|
||
| 国际化 | next-intl | 4.9 |
|
||
| 认证 | JWT + RBAC | - |
|
||
|
||
## apps/web — 公开网站
|
||
|
||
### 路由结构(App Router)
|
||
```
|
||
app/
|
||
├── page.tsx # 首页
|
||
├── layout.tsx # 根布局(国际化)
|
||
├── globals.css # 全局样式
|
||
│
|
||
├── about/ # 关于我们
|
||
│ ├── company/ # 公司介绍
|
||
│ ├── history/ # 发展历程
|
||
│ ├── certifications/ # 资质认证
|
||
│ ├── partners/ # 合作伙伴
|
||
│ ├── dealers/ # 经销商
|
||
│ └── contact/ # 联系我们
|
||
│
|
||
├── products/ # 产品中心
|
||
│ ├── [category]/ # 产品分类
|
||
│ └── [category]/[slug]/ # 产品详情
|
||
│
|
||
├── solutions/ # 解决方案
|
||
│ ├── page.tsx # 方案总览
|
||
│ └── [slug]/ # 方案详情
|
||
│
|
||
├── academy/ # 技术学院
|
||
│ ├── product-operation/ # 产品操作
|
||
│ ├── case-library/ # 案例库
|
||
│ ├── geophysical-knowledge/ # 物探知识
|
||
│ └── certification/ # 认证培训
|
||
│
|
||
├── support/ # 技术支持
|
||
│ ├── knowledge-base/ # 知识库
|
||
│ ├── faq/ # FAQ
|
||
│ ├── downloads/ # 下载中心
|
||
│ ├── software-updates/ # 软件更新
|
||
│ ├── after-sales/ # 售后服务
|
||
│ └── warranty/ # 保修政策
|
||
│
|
||
├── community/ # 社区动态
|
||
├── geometa/ # GeoMeta 平台
|
||
├── configurator/ # 报价配置器
|
||
├── cases/[slug]/ # 案例详情
|
||
├── pages/[slug]/ # 动态页面
|
||
├── privacy-policy/ # 隐私政策
|
||
└── terms-of-use/ # 使用条款
|
||
```
|
||
|
||
### 关键配置
|
||
- ISR 策略:首页 30 分钟,产品页 1 小时
|
||
- 动态路由参数为 `Promise` 类型:`params: Promise<{ slug: string }>`
|
||
- 开发模式使用 `--turbopack` 标志
|
||
- 多语言:中文站无前缀,国际站 `/en` 前缀
|
||
|
||
## apps/admin — 后台管理系统
|
||
|
||
### 当前架构(Vite + React Router)
|
||
|
||
Admin 使用 Vite 6 + React 19 + React Router DOM 7,非 Next.js。路由通过 `createBrowserRouter` 配置,所有页面使用 `lazy()` 懒加载实现代码分割。
|
||
|
||
```
|
||
apps/admin/src/
|
||
├── components/
|
||
│ ├── editor/ # Tiptap 富文本编辑器
|
||
│ ├── help/ # 帮助系统组件
|
||
│ ├── navigation/ # 导航管理组件
|
||
│ ├── seo/ # SEO 管理组件
|
||
│ └── structured-editors/ # 结构化页面编辑器
|
||
├── data/help/ # 帮助文档数据
|
||
├── hooks/ # 自定义 Hooks
|
||
├── lib/
|
||
│ ├── auth.ts # JWT Token 管理
|
||
│ ├── api-client.ts # API 客户端(自动刷新 Token)
|
||
│ ├── help-utils.ts # 帮助工具
|
||
│ └── utils.ts # 通用工具
|
||
├── pages/ # 页面组件(全部懒加载)
|
||
├── providers/
|
||
│ ├── auth-provider.tsx # 认证上下文(将迁移至 useAuthStore)
|
||
│ └── query-provider.tsx # React Query Provider
|
||
├── stores/ # Zustand 全局状态管理
|
||
│ ├── useAuthStore.ts # 认证状态(替代 AuthProvider Context)
|
||
│ ├── useUIStore.ts # 全局 UI 状态(侧边栏折叠、全局 loading,持久化)
|
||
│ ├── useLocaleStore.ts # 编辑器语言偏好(持久化)
|
||
│ └── usePermissionStore.ts # 权限数据缓存(为细粒度权限矩阵预留)
|
||
└── router/
|
||
├── index.tsx # 路由配置(createBrowserRouter)
|
||
├── AppRouter.tsx # 路由容器
|
||
├── DashboardLayout.tsx # 仪表盘布局(侧边栏 + 内容区)
|
||
├── AuthLayout.tsx # 登录页布局
|
||
└── AuthGuard.tsx # 路由守卫(JWT 认证检查)
|
||
```
|
||
|
||
### 当前路由结构
|
||
```
|
||
/login # 登录页(AuthLayout)
|
||
/ # 仪表盘首页(AuthGuard 保护)
|
||
/navigation # 导航管理
|
||
/navigation/:menuId # 菜单分支列表
|
||
/navigation/:menuId/:branchId # 子分支列表
|
||
/navigation/:menuId/:branchId/sub/:subBranchId # 子分支详情
|
||
/pages # 页面列表
|
||
/pages/:pageId # 页面编辑器
|
||
/pages/fixed/:slug # 固定页面编辑器
|
||
/content # 内容管理首页
|
||
/content/events # 活动管理
|
||
/content/news # 新闻管理
|
||
/content/reveals # 发布管理
|
||
/downloads # 下载资源列表
|
||
/downloads/categories # 下载分类管理
|
||
/downloads/:id # 下载资源编辑器
|
||
/analytics # 数据分析
|
||
/permissions # 权限管理
|
||
/settings # 系统设置
|
||
/about-me # 个人信息
|
||
/manual # 使用手册
|
||
/help/:pageId # 帮助详情
|
||
```
|
||
|
||
### 当前侧边栏菜单
|
||
```
|
||
导航管理 /navigation module: navigation
|
||
页面管理 /pages module: pages
|
||
内容管理 /content module: content
|
||
权限管理 /permissions module: permissions
|
||
系统设置 /settings module: settings
|
||
使用手册 /manual module: settings
|
||
About Me /about-me module: settings
|
||
```
|
||
|
||
### 多模块演进计划
|
||
|
||
Admin 将从当前单一 CMS 后台演进为统一企业管理平台,新增设备管理、报价管理、运输管理等模块。详见 `docNew/12-Admin多模块架构设计.md`。
|
||
|
||
**目标目录结构(演进后):**
|
||
```
|
||
app/(dashboard)/
|
||
├── layout.tsx # 统一布局,侧边栏按权限动态渲染
|
||
├── (cms)/ # 官网管理模块
|
||
│ ├── navigation/ # 导航管理
|
||
│ ├── pages/ # 页面管理
|
||
│ ├── content/ # 内容管理
|
||
│ ├── downloads/ # 下载资源
|
||
│ └── seo/ # SEO 管理
|
||
├── (devices)/ # 设备管理模块(新增)
|
||
│ ├── list/ # 设备列表
|
||
│ ├── [id]/ # 设备详情
|
||
│ ├── maintenance/ # 维护记录
|
||
│ └── categories/ # 设备分类
|
||
├── (quotations)/ # 报价系统模块(新增)
|
||
│ ├── list/ # 报价列表
|
||
│ ├── create/ # 新建报价
|
||
│ ├── [id]/ # 报价详情
|
||
│ └── templates/ # 报价模板
|
||
├── (shipping)/ # 运输管理模块(新增)
|
||
│ ├── orders/ # 运输工单
|
||
│ ├── tracking/ # 物流追踪
|
||
│ └── logistics/ # 物流商管理
|
||
└── (system)/ # 系统管理(跨模块)
|
||
├── users/ # 用户管理
|
||
├── roles/ # 角色管理 + 权限矩阵配置
|
||
├── locales/ # 语言设置
|
||
└── settings/ # 系统配置
|
||
```
|
||
|
||
**目标侧边栏分组(按权限动态渲染):**
|
||
```
|
||
官网管理
|
||
├── 导航管理
|
||
├── 页面管理
|
||
├── 内容管理
|
||
└── 下载资源
|
||
|
||
设备管理(新增)
|
||
├── 设备列表
|
||
├── 维护记录
|
||
└── 设备分类
|
||
|
||
报价管理(新增)
|
||
├── 报价列表
|
||
├── 新建报价
|
||
└── 报价模板
|
||
|
||
运输管理(新增)
|
||
├── 运输工单
|
||
├── 物流追踪
|
||
└── 物流商管理
|
||
|
||
系统设置
|
||
├── 用户管理
|
||
├── 角色权限
|
||
├── 语言设置
|
||
└── 系统配置
|
||
```
|
||
|
||
**模块间隔离原则:**
|
||
- 路由隔离:每个模块独立路由分组 `(module-name)/`
|
||
- 组件隔离:模块专用组件放模块目录内,跨模块复用的提取到 `packages/ui`
|
||
- API 隔离:每个模块独立 API namespace(如 `/admin/devices/*`)
|
||
- 类型隔离:每个模块独立类型文件,通过 `packages/types` 统一导出
|
||
- 状态隔离:React Query key 使用模块前缀(如 `['devices', 'list']`),Zustand store 按职责拆分不按模块拆分
|
||
- 禁止跨模块直接引用,必须通过共享包通信
|
||
|
||
### API 路由规划
|
||
```
|
||
/admin/auth/* # 认证(现有)
|
||
/admin/navigation/* # 导航管理(现有)
|
||
/admin/pages/* # 页面管理(现有)
|
||
/admin/content/* # 内容管理(现有)
|
||
/admin/analytics/* # 数据分析(现有)
|
||
/admin/downloads/* # 下载资源(现有)
|
||
/admin/users/* # 用户管理(现有)
|
||
|
||
/admin/devices/* # 设备管理(新增)
|
||
/admin/quotations/* # 报价管理(新增)
|
||
/admin/shipping/* # 运输管理(新增)
|
||
/admin/permissions/* # 权限矩阵管理(新增)
|
||
```
|
||
|
||
## 编码规范
|
||
|
||
### 命名规范
|
||
|
||
#### 文件命名
|
||
- 组件文件:PascalCase,如 `Button.tsx`、`HeroCarousel.tsx`
|
||
- 工具/钩子文件:camelCase,如 `useAuth.ts`、`fetchNavigation.ts`
|
||
- 类型文件:camelCase,如 `models.ts`、`cms.ts`
|
||
- 样式文件:kebab-case,如 `globals.css`
|
||
- 配置文件:kebab-case,如 `tailwind.config.ts`、`next.config.ts`
|
||
- 页面目录:kebab-case,如 `products/`、`solutions/`、`tech-academy/`
|
||
- 测试文件:被测文件名 + `.test.ts(x)`,如 `Button.test.tsx`
|
||
|
||
#### 代码命名
|
||
- 组件名:PascalCase,如 `LayoutShell`、`HomeHero`
|
||
- 函数/变量名:camelCase,如 `fetchNavigation`、`activeLocale`
|
||
- 常量:UPPER_SNAKE_CASE,如 `MAX_RETRIES`、`DEFAULT_LOCALE`
|
||
- 类型/接口名:PascalCase,如 `NavigationItem`、`PageContent`
|
||
- 枚举值:PascalCase,如 `ButtonVariant.Primary`
|
||
- 自定义 Hook:`use` 前缀,如 `useAuth`、`useTranslations`
|
||
- 事件处理函数:`handle` 前缀,如 `handleSubmit`、`handleClick`
|
||
- 布尔变量/属性:`is`/`has`/`should` 前缀,如 `isLoading`、`hasError`
|
||
|
||
#### 类型命名后缀约定
|
||
- API 响应类型:`XxxResponse`,如 `ProductsResponse`
|
||
- API 请求参数:`XxxRequest`,如 `CreatePageRequest`
|
||
- Props 类型:`XxxProps`,如 `ButtonProps`
|
||
- 状态类型:`XxxState`,如 `FormState`
|
||
- 枚举类型:无后缀,如 `Locale`、`ButtonVariant`
|
||
|
||
### 导入规范
|
||
|
||
#### 导入顺序
|
||
```typescript
|
||
// 1. React / Next.js 核心模块
|
||
import { useState, useEffect } from 'react';
|
||
import { NextRequest } from 'next/server';
|
||
|
||
// 2. 第三方库
|
||
import { useTranslations } from 'next-intl';
|
||
import { useForm } from 'react-hook-form';
|
||
|
||
// 3. 共享包 (@geomative/*)
|
||
import { NavigationItem, Product } from '@geomative/types';
|
||
import { Button, Card } from '@geomative/ui';
|
||
|
||
// 4. 应用内部模块(使用路径别名)
|
||
import { apiClient } from '@/lib/api';
|
||
import { AuthProvider } from '@/providers/auth-provider';
|
||
|
||
// 5. 相对路径导入
|
||
import { LayoutClientShell } from './components/LayoutClientShell';
|
||
import { fetchNavigation } from '../lib/navigation';
|
||
|
||
// 6. 类型导入(使用 type 关键字)
|
||
import type { Metadata } from 'next';
|
||
```
|
||
|
||
#### 路径别名
|
||
- `apps/web`:使用 `@/` 前缀映射到 `src/` 或根目录
|
||
- `apps/admin`:使用 `@/` 前缀映射到 `src/`
|
||
- 共享包:使用 `@geomative/*` 命名空间
|
||
|
||
#### 导出规范
|
||
- 优先使用命名导出(named export)
|
||
- 每个组件文件一个主组件导出
|
||
- 使用 barrel export(`index.ts`)整理模块导出
|
||
- 类型导出使用 `export type` 语法
|
||
|
||
```typescript
|
||
// 组件导出示例
|
||
export { Button } from './Button';
|
||
export type { ButtonProps, ButtonVariant } from './Button';
|
||
```
|
||
|
||
### 组件规范
|
||
|
||
#### Server Components vs Client Components(apps/web)
|
||
- 默认使用 Server Components(Next.js App Router 约定)
|
||
- 仅在需要交互(state、effects、event handlers)时使用 Client Components
|
||
- Client Components 必须在文件顶部添加 `"use client"` 指令
|
||
- 布局组件(Layout)默认为 Server Component
|
||
- 数据获取优先在 Server Component 中完成
|
||
|
||
```typescript
|
||
// Server Component — 默认,无需声明
|
||
export default async function ProductPage({ params }: { params: Promise<{ slug: string }> }) {
|
||
const { slug } = await params;
|
||
const product = await fetchProduct(slug);
|
||
return <ProductDetail product={product} />;
|
||
}
|
||
|
||
// Client Component — 需要交互时使用
|
||
"use client"
|
||
import { useState } from 'react';
|
||
export function ProductFilter({ categories }: ProductFilterProps) {
|
||
const [active, setActive] = useState<string>('all');
|
||
// ...
|
||
}
|
||
```
|
||
|
||
#### 组件结构顺序
|
||
```typescript
|
||
// 1. "use client" 指令(如需要)
|
||
"use client"
|
||
|
||
// 2. 导入
|
||
import { useState } from 'react';
|
||
|
||
// 3. 类型定义
|
||
interface ExampleProps {
|
||
title: string;
|
||
children: React.ReactNode;
|
||
}
|
||
|
||
// 4. 组件定义
|
||
export function Example({ title, children }: ExampleProps) {
|
||
// 4a. hooks
|
||
const t = useTranslations('common');
|
||
|
||
// 4b. 派生状态
|
||
const formattedTitle = title.toUpperCase();
|
||
|
||
// 4c. 事件处理
|
||
const handleClick = () => { /* ... */ };
|
||
|
||
// 4d. 渲染
|
||
return (
|
||
<div onClick={handleClick}>
|
||
<h2>{formattedTitle}</h2>
|
||
{children}
|
||
</div>
|
||
);
|
||
}
|
||
```
|
||
|
||
#### 组件设计原则
|
||
- 单一职责:每个组件只做一件事
|
||
- 组合优于继承:使用 children 和 props 组合
|
||
- Props 解构:始终解构 props,不要使用 `props.xxx`
|
||
- Forward Ref:可复用组件需要支持 ref 转发
|
||
- 有意义的默认值:为可选 props 提供合理的默认值
|
||
|
||
### 样式规范
|
||
|
||
#### Tailwind CSS 使用约定
|
||
- 使用 Tailwind 工具类,禁止手写 CSS(除非 CSS 变量或动画关键帧)
|
||
- 类名顺序遵循一致性(建议:布局 -> 尺寸 -> 间距 -> 排版 -> 颜色 -> 其他)
|
||
- 超过 5 个类名时考虑提取为组件或使用 `@apply`(谨慎使用)
|
||
- 响应式设计使用 Mobile-first 方式:`md:` -> `lg:`
|
||
- 条件样式使用模板字符串或 `clsx`/`cn` 工具函数
|
||
|
||
#### 设计令牌(Design Tokens)
|
||
|
||
| 令牌 | 值 | 用途 |
|
||
|------|-----|------|
|
||
| 主色 | `#3a6b35` | 品牌绿,CTA 按钮、强调元素 |
|
||
| 辅色 | 大地色系 | 深绿、岩石灰、矿物绿 |
|
||
| 字体 | Inter | 英文主字体 |
|
||
| 中文回退 | Source Han Sans SC, PingFang SC | 中文排版 |
|
||
| 断点 | `md: 769px`, `lg: 1025px` | 响应式分界 |
|
||
| 触摸目标 | 最小 44px | 移动端可访问性 |
|
||
| 对比度 | >= 4.5:1 | WCAG 2.1 AA |
|
||
|
||
#### 响应式断点
|
||
```css
|
||
/* Mobile-first */
|
||
/* 默认:< 769px (Mobile) */
|
||
/* md: >= 769px (Tablet) */
|
||
/* lg: >= 1025px (Desktop) */
|
||
```
|
||
|
||
### TypeScript 规范
|
||
|
||
- 启用 strict 模式
|
||
- 禁止使用 `any`,如必须使用需添加注释说明原因
|
||
- 函数参数和返回值必须有明确的类型注解
|
||
- 优先使用 `interface` 定义对象类型,`type` 用于联合类型、工具类型
|
||
- 使用 `as const` 断言定义常量对象
|
||
- 异步函数返回类型明确标注 `Promise<T>`
|
||
- 使用 TypeScript 枚举或联合字面量类型替代魔法字符串
|
||
|
||
```typescript
|
||
// 推荐
|
||
interface NavigationItem {
|
||
id: string;
|
||
title: Record<Locale, string>;
|
||
path: string;
|
||
}
|
||
|
||
type Locale = 'zh-CN' | 'en';
|
||
type ButtonVariant = 'primary' | 'secondary' | 'outline';
|
||
|
||
// 避免
|
||
const config: any = {}; // 禁止
|
||
function handleClick(e) { /* ... */ } // 缺少类型
|
||
```
|
||
|
||
### 国际化规范(next-intl,apps/web)
|
||
|
||
#### 翻译文件
|
||
- 翻译文件位于 `apps/web/messages/` 目录
|
||
- 文件格式:JSON(`zh-CN.json`、`en.json`)
|
||
- 使用命名空间分隔:`home`、`products`、`common`、`nav` 等
|
||
|
||
#### 使用方式
|
||
```typescript
|
||
// Server Component
|
||
import { useTranslations } from 'next-intl';
|
||
export default function Page() {
|
||
const t = useTranslations('home');
|
||
return <h1>{t('title')}</h1>;
|
||
}
|
||
|
||
// Client Component
|
||
"use client"
|
||
import { useTranslations } from 'next-intl';
|
||
export function ProductCard() {
|
||
const t = useTranslations('products');
|
||
return <span>{t('addToCart')}</span>;
|
||
}
|
||
```
|
||
|
||
#### 内容回退机制
|
||
- 回退链:请求语言 -> 基准语言 -> `en`
|
||
- 多语言内容使用 `Record<Locale, string>` 类型存储
|
||
- 导航项通过 `visible_locales` 控制语言可见性
|
||
|
||
#### URL 策略
|
||
- 中文站:无语言前缀(如 `/products`)
|
||
- 国际站:`/en` 前缀(如 `/en/products`)
|
||
- SEO 自动生成 hreflang 替代链接
|
||
|
||
### API 调用规范
|
||
|
||
#### 数据获取(apps/web)
|
||
- Server Components 中直接使用 `fetch` 或 API 客户端
|
||
- Client Components 中使用 TanStack React Query
|
||
- API 调用超时设置:5 秒
|
||
- API 不可用时使用本地回退内容
|
||
|
||
```typescript
|
||
// Server Component 数据获取
|
||
async function getProduct(slug: string) {
|
||
try {
|
||
const res = await fetch(`${API_URL}/api/products/${slug}`, { next: { revalidate: 3600 } });
|
||
if (!res.ok) throw new Error('Failed to fetch');
|
||
return await res.json();
|
||
} catch {
|
||
return getFallbackProduct(slug); // 回退机制
|
||
}
|
||
}
|
||
```
|
||
|
||
#### API 客户端(apps/admin)
|
||
- 使用 `src/lib/api-client.ts` 统一管理 API 请求
|
||
- JWT Token 存储在 localStorage
|
||
- 自动刷新过期 Token(Access Token + Refresh Token 双令牌)
|
||
- API 请求前缀:`/admin/*`(需认证)
|
||
|
||
#### React Query 约定
|
||
- 使用 Query Key 工厂模式
|
||
- 避免在组件中直接调用 `fetch`,通过 hooks 封装
|
||
- 乐观更新用于即时反馈操作
|
||
|
||
### 路由规范
|
||
|
||
#### Next.js App Router(apps/web)
|
||
- 使用文件系统路由:`app/` 目录
|
||
- 动态路由参数为 `Promise` 类型:`params: Promise<{ slug: string }>`
|
||
- 布局使用 `layout.tsx`,页面使用 `page.tsx`
|
||
- 加载状态使用 `loading.tsx`
|
||
- 错误处理使用 `error.tsx`
|
||
- ISR 策略:首页 30 分钟,产品页 1 小时
|
||
|
||
#### React Router(apps/admin)
|
||
- 使用 `createBrowserRouter` 配置路由
|
||
- 所有页面使用 `lazy()` 懒加载实现代码分割
|
||
- `AuthGuard` 组件保护需要认证的路由
|
||
- 路由守卫在 `src/router/AuthGuard.tsx`
|
||
- 布局结构:`DashboardLayout`(侧边栏 + 内容区)和 `AuthLayout`(登录页)
|
||
|
||
### 表单规范
|
||
|
||
- 使用 React Hook Form 管理表单状态
|
||
- 使用 Zod 定义验证 schema
|
||
- 通过 `zodResolver` 连接 React Hook Form 和 Zod
|
||
|
||
```typescript
|
||
import { useForm } from 'react-hook-form';
|
||
import { z } from 'zod';
|
||
import { zodResolver } from '@hookform/resolvers/zod';
|
||
|
||
const schema = z.object({
|
||
email: z.string().email(),
|
||
password: z.string().min(8),
|
||
});
|
||
|
||
type LoginForm = z.infer<typeof schema>;
|
||
|
||
export function LoginForm() {
|
||
const { register, handleSubmit, formState: { errors } } = useForm<LoginForm>({
|
||
resolver: zodResolver(schema),
|
||
});
|
||
// ...
|
||
}
|
||
```
|
||
|
||
### 注释规范
|
||
- 公共组件和 Hook 必须有 JSDoc 注释
|
||
- 复杂业务逻辑处需有行内注释说明意图
|
||
- 禁止保留无意义的注释和注释掉的废弃代码
|
||
- `// TODO:` 标注待办事项,需包含上下文说明
|
||
- `// NOTE:` 标注重要设计决策或注意事项
|
||
|
||
### 错误处理规范
|
||
- 页面级错误使用 `error.tsx` 捕获
|
||
- API 调用失败必须提供回退方案
|
||
- 禁止捕获错误后不做任何处理(空 catch)
|
||
- 用户操作失败时展示友好的错误提示
|
||
- 使用 TypeScript 的 discriminated union 处理不同状态
|
||
|
||
### 性能规范
|
||
|
||
- Server Components:默认使用,减少客户端 JS 体积
|
||
- 代码分割:路由级自动分割,大型组件使用 `dynamic()` 懒加载
|
||
- 图片优化:使用 `OptimizedImage` 组件或 Next.js `<Image>`
|
||
- ISR:合理设置页面重新验证时间
|
||
- Bundle 分析:定期检查包体积
|
||
- Standalone 输出:生产构建使用 `output: 'standalone'`
|
||
|
||
### 无障碍规范(A11y)
|
||
- 语义化 HTML:使用正确的标签(`<nav>`、`<main>`、`<article>`)
|
||
- 图片必须提供 `alt` 属性
|
||
- 表单控件必须关联 `<label>`
|
||
- 交互元素必须有键盘操作支持
|
||
- 焦点管理:模态框打开时捕获焦点,关闭时恢复焦点
|
||
- 颜色对比度 >= 4.5:1(WCAG 2.1 AA)
|
||
- 触摸目标最小 44px
|
||
|
||
## 共享类型定义(packages/types)
|
||
|
||
### 文件结构
|
||
```
|
||
packages/types/src/
|
||
├── models.ts # 核心数据模型(User, Product, Quotation 等)
|
||
├── cms.ts # CMS 内容类型(Navigation, Pages, SEO 等)
|
||
├── api.ts # API 请求/响应类型
|
||
├── admin.ts # 后台管理相关类型(RBAC 等)
|
||
├── configurator.ts # 报价配置器类型
|
||
├── device.ts # 设备管理相关类型(规划中)
|
||
├── quotation.ts # 报价系统相关类型(规划中)
|
||
├── shipping.ts # 运输管理相关类型(规划中)
|
||
├── permission.ts # 细粒度权限矩阵类型(规划中)
|
||
└── index.ts # 统一导出
|
||
```
|
||
|
||
### 多语言内容类型模式
|
||
```typescript
|
||
type Locale = 'zh-CN' | 'en';
|
||
|
||
// 多语言内容统一使用 Record 类型
|
||
interface Product {
|
||
id: string;
|
||
name: Record<Locale, string>;
|
||
description: Record<Locale, string>;
|
||
}
|
||
```
|
||
|
||
### API 响应类型模式
|
||
```typescript
|
||
interface ApiResponse<T> {
|
||
data: T;
|
||
message?: string;
|
||
meta?: {
|
||
total: number;
|
||
page: number;
|
||
pageSize: number;
|
||
};
|
||
}
|
||
```
|
||
|
||
## 共享组件库(packages/ui)
|
||
|
||
### 组件层级(Atomic Design)
|
||
```
|
||
packages/ui/src/
|
||
├── atoms/ # 原子组件:Button, Card, Typography, Icon, Input, Select, Textarea
|
||
├── navigation/ # 导航组件:Navbar, MobileNav, LanguageSwitcher, StickySubNav, Breadcrumb
|
||
├── layout/ # 布局组件:Footer, MapEmbed
|
||
├── content/ # 内容组件:HeroCarousel, ImageGallery, Accordion, VideoEmbed, ScrollAnimation
|
||
├── forms/ # 表单组件:ContactForm, ContactFormValidation
|
||
├── auth/ # 认证组件:AuthProvider, useAuth, NavbarAvatar, LoginPrompt
|
||
├── analytics/ # 分析组件:GoogleAnalytics, BaiduAnalytics, trackEvent
|
||
├── performance/ # 性能组件:OptimizedImage
|
||
└── index.ts # Barrel export
|
||
```
|
||
|
||
### 计划新增组件(多模块演进)
|
||
```
|
||
packages/ui/src/
|
||
├── data-table/ # 通用数据表格(设备列表、报价列表等复用)
|
||
│ ├── DataTable.tsx
|
||
│ ├── DataTablePagination.tsx
|
||
│ ├── DataTableFilter.tsx
|
||
│ └── DataTableExport.tsx
|
||
└── module-shell/ # 模块通用外壳(列表页、详情页模板)
|
||
├── ListPageShell.tsx
|
||
└── DetailPageShell.tsx
|
||
```
|
||
|
||
### 组件开发规范
|
||
- 每个组件独立文件,文件名与组件名一致
|
||
- 组件必须包含 TypeScript 类型定义
|
||
- Props 接口必须导出
|
||
- 可复用组件支持 `className` prop 以允许样式扩展
|
||
- 可交互组件支持 `ref` 转发
|
||
|
||
## 共享工具函数(packages/utils)
|
||
|
||
```
|
||
packages/utils/src/
|
||
├── admin.ts # 管理后台工具函数(权限判断、数据转换等)
|
||
├── api.ts # API 相关工具
|
||
├── cms.ts # CMS 内容处理工具
|
||
├── configurator.ts # 报价配置器工具
|
||
├── models.ts # 数据模型工具
|
||
└── index.ts # 统一导出
|
||
```
|
||
|
||
## 认证与权限
|
||
|
||
### 管理员认证(apps/admin)
|
||
- JWT 双令牌:Access Token(短期)+ Refresh Token(长期)
|
||
- Token 存储在 localStorage
|
||
- API 客户端自动刷新过期 Token
|
||
- `AuthGuard` 保护路由
|
||
|
||
### 状态管理分层(apps/admin)
|
||
|
||
详见 `docNew/13-Admin状态管理设计.md`。
|
||
|
||
```
|
||
服务端状态 → TanStack React Query(API 数据、缓存、失效)
|
||
全局客户端状态 → Zustand(认证、UI 偏好、语言偏好、权限缓存)
|
||
表单状态 → React Hook Form + Zod(表单字段、验证)
|
||
组件内临时状态 → React useState / useReducer(弹窗开关等局部状态)
|
||
```
|
||
|
||
**关键原则**:API 返回的数据绝不放进 Zustand,Zustand 只放客户端独有状态。
|
||
|
||
#### Zustand Store 结构
|
||
- `useAuthStore` — 认证状态(user、login、logout),替代现有 AuthProvider Context
|
||
- `useUIStore` — 全局 UI 状态(sidebarCollapsed 持久化、globalLoading)
|
||
- `useLocaleStore` — 编辑器语言偏好(activeLocale 持久化,统一各编辑器组件的语言切换)
|
||
- `usePermissionStore` — 权限缓存(permissions 列表、hasPermission 判断,为细粒度权限矩阵预留)
|
||
|
||
#### Store 开发规范
|
||
- 文件名:`use` + 名称 + `Store.ts`,如 `useAuthStore.ts`
|
||
- 使用 selector 精确选择避免不必要重渲染:`const user = useAuthStore((s) => s.user)`
|
||
- 持久化使用 `zustand/middleware` 的 `persist` 中间件,`partialize` 只持久化需要的字段
|
||
- 敏感数据不持久化,每次登录重新获取
|
||
|
||
### 用户认证(apps/web)
|
||
- GeoPlatform OAuth 统一登录
|
||
- 环境变量配置 OAuth 参数
|
||
|
||
### 当前 RBAC 角色
|
||
|
||
| 角色 | 标识 | 说明 |
|
||
|------|------|------|
|
||
| 超级管理员 | `super_admin` | 全部权限 |
|
||
| 内容编辑 | `content_editor` | 内容相关权限 |
|
||
| 市场营销 | `marketing` | 内容 + 分析权限 |
|
||
| 查看者 | `viewer` | 只读权限 |
|
||
|
||
**权限模块**:navigation, pages, analytics, permissions, settings, content
|
||
|
||
### 权限矩阵演进(规划中)
|
||
|
||
从固定四级角色升级为 **角色 + 细粒度权限矩阵**:
|
||
|
||
```
|
||
角色(可自定义创建)
|
||
└── 绑定权限项
|
||
|
||
权限项 = 模块 + 资源 + 操作
|
||
模块: cms | devices | quotations | shipping | system
|
||
资源: 各模块下的具体资源
|
||
操作: view | create | edit | delete | export
|
||
```
|
||
|
||
**权限标识命名规范**:`{module}.{resource}.{action}`
|
||
- `cms.pages.view` — 查看页面
|
||
- `devices.list.export` — 导出设备数据
|
||
- `quotations.create.view` — 查看新建报价页面
|
||
- `system.roles.edit` — 编辑角色权限
|
||
|
||
**前端权限控制层级**:
|
||
- 侧边栏:根据用户权限动态渲染
|
||
- 页面级:路由守卫检查模块访问权限
|
||
- 操作级:`PermissionGate` 组件包裹按钮
|
||
- API 级:后端中间件校验,前端仅做 UI 层控制
|
||
|
||
```tsx
|
||
// 按钮级权限控制示例
|
||
<PermissionGate permission="devices.list.export">
|
||
<Button onClick={handleExport}>导出设备列表</Button>
|
||
</PermissionGate>
|
||
```
|
||
|
||
## 环境变量
|
||
|
||
### apps/web
|
||
```env
|
||
NEXT_PUBLIC_DEFAULT_LOCALE=zh-CN
|
||
NEXT_PUBLIC_SITE_URL=http://localhost:3002
|
||
NEXT_PUBLIC_API_URL=http://localhost:3001
|
||
```
|
||
|
||
### apps/admin
|
||
```env
|
||
VITE_API_URL=http://localhost:3001
|
||
PORT=3003
|
||
```
|
||
|
||
## 常用命令
|
||
|
||
```bash
|
||
# 安装依赖
|
||
npm install
|
||
|
||
# 启动开发(全部)
|
||
npm run dev
|
||
|
||
# 启动开发(指定应用)
|
||
npm run dev --filter=web # 前端网站
|
||
npm run dev --filter=admin # 后台管理
|
||
|
||
# 构建
|
||
npm run build
|
||
npm run build --filter=web
|
||
npm run build:cn # 中文站
|
||
npm run build:en # 国际站
|
||
|
||
# 代码检查
|
||
npm run lint
|
||
|
||
# 测试
|
||
npm run test
|
||
npm run test --filter=admin
|
||
```
|
||
|
||
## 部署
|
||
|
||
### Web 应用(Next.js)
|
||
- 生产构建使用 `output: 'standalone'` 优化
|
||
- ISR 增量静态再生平衡性能与内容时效性
|
||
- Nginx 反向代理,中英文站点独立部署
|
||
|
||
### Admin 应用(Vite)
|
||
- 静态资源构建,部署至 CDN 或 Nginx
|
||
- API 请求代理至后端服务
|