# 前端技术规范

## 运行环境
- 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 请求代理至后端服务
