enterprise-saa-s-dashboard-.../.kiro/steering/frontend.md

834 lines
28 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.

# 前端技术规范
## 运行环境
- 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 Componentsapps/web
- 默认使用 Server ComponentsNext.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-intlapps/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
- 自动刷新过期 TokenAccess Token + Refresh Token 双令牌)
- API 请求前缀:`/admin/*`(需认证)
#### React Query 约定
- 使用 Query Key 工厂模式
- 避免在组件中直接调用 `fetch`,通过 hooks 封装
- 乐观更新用于即时反馈操作
### 路由规范
#### Next.js App Routerapps/web
- 使用文件系统路由:`app/` 目录
- 动态路由参数为 `Promise` 类型:`params: Promise<{ slug: string }>`
- 布局使用 `layout.tsx`,页面使用 `page.tsx`
- 加载状态使用 `loading.tsx`
- 错误处理使用 `error.tsx`
- ISR 策略:首页 30 分钟,产品页 1 小时
#### React Routerapps/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:1WCAG 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 QueryAPI 数据、缓存、失效)
全局客户端状态 → Zustand认证、UI 偏好、语言偏好、权限缓存)
表单状态 → React Hook Form + Zod表单字段、验证
组件内临时状态 → React useState / useReducer弹窗开关等局部状态
```
**关键原则**API 返回的数据绝不放进 ZustandZustand 只放客户端独有状态。
#### 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 请求代理至后端服务