diff --git a/.kiro/steering/frontend.md b/.kiro/steering/frontend.md new file mode 100644 index 0000000..7b01a31 --- /dev/null +++ b/.kiro/steering/frontend.md @@ -0,0 +1,833 @@ +# 前端技术规范 + +## 运行环境 +- 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 ; +} + +// Client Component — 需要交互时使用 +"use client" +import { useState } from 'react'; +export function ProductFilter({ categories }: ProductFilterProps) { + const [active, setActive] = useState('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 ( +
+

{formattedTitle}

+ {children} +
+ ); +} +``` + +#### 组件设计原则 +- 单一职责:每个组件只做一件事 +- 组合优于继承:使用 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` +- 使用 TypeScript 枚举或联合字面量类型替代魔法字符串 + +```typescript +// 推荐 +interface NavigationItem { + id: string; + title: Record; + 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

{t('title')}

; +} + +// Client Component +"use client" +import { useTranslations } from 'next-intl'; +export function ProductCard() { + const t = useTranslations('products'); + return {t('addToCart')}; +} +``` + +#### 内容回退机制 +- 回退链:请求语言 -> 基准语言 -> `en` +- 多语言内容使用 `Record` 类型存储 +- 导航项通过 `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; + +export function LoginForm() { + const { register, handleSubmit, formState: { errors } } = useForm({ + resolver: zodResolver(schema), + }); + // ... +} +``` + +### 注释规范 +- 公共组件和 Hook 必须有 JSDoc 注释 +- 复杂业务逻辑处需有行内注释说明意图 +- 禁止保留无意义的注释和注释掉的废弃代码 +- `// TODO:` 标注待办事项,需包含上下文说明 +- `// NOTE:` 标注重要设计决策或注意事项 + +### 错误处理规范 +- 页面级错误使用 `error.tsx` 捕获 +- API 调用失败必须提供回退方案 +- 禁止捕获错误后不做任何处理(空 catch) +- 用户操作失败时展示友好的错误提示 +- 使用 TypeScript 的 discriminated union 处理不同状态 + +### 性能规范 + +- Server Components:默认使用,减少客户端 JS 体积 +- 代码分割:路由级自动分割,大型组件使用 `dynamic()` 懒加载 +- 图片优化:使用 `OptimizedImage` 组件或 Next.js `` +- ISR:合理设置页面重新验证时间 +- Bundle 分析:定期检查包体积 +- Standalone 输出:生产构建使用 `output: 'standalone'` + +### 无障碍规范(A11y) +- 语义化 HTML:使用正确的标签(`