# FlashLink Home Web

基于 React + TypeScript + Vite 的 H5 前端项目。

## 环境要求

- Node.js >= 22.12.0（React Router v7 framework 模式 + Vite 7 要求）
- pnpm >= 10

> 依赖统一用 pnpm 命令管理（`pnpm add` / `pnpm add -D`），勿手改 package.json 版本号，否则会与 lockfile 不一致导致 `frozen-lockfile` 安装失败。

## 快速开始

```bash
pnpm install --frozen-lockfile
pnpm dev
```

浏览器访问控制台输出的本地地址即可。

## 渲染模式（CSR / SSG）

项目支持两种渲染模式，**同一份代码、构建时切换**（互斥，一次只出一种产物）。两种模式与 `--mode`（环境：localdev/development/test/production）**正交**，通过不同的 npm script 选择（脚本内部用 `APP_RENDER_MODE` 环境变量传给 `vite.config.ts`）。

| 模式 | 入口 | 路由 | i18n | 产物 | 适用 |
| --- | --- | --- | --- | --- | --- |
| **SSG**（默认） | `root.tsx` / `entry.client.tsx` | `/:lang/home`（路径前缀多语言） | `frameworkI18n.ts`（同步 init） | `dist/client/`（预渲染静态 HTML） | 需爬虫/SEO 可见内容，官网/落地页 |
| **CSR** | `index.html` / `main.tsx` | `/home`（无语言前缀） | `i18n/index.ts`（LanguageDetector 异步） | `dist/`（空壳 SPA） | 纯单页应用，无预渲染需求 |

> 两套入口、路由（`src/routes.ts` vs `src/router/`）、i18n 并存且**完全独立**，由 `APP_RENDER_MODE` 决定实际加载哪套。详见 [SSG 架构文档](./docs/SSG.md)。

## 脚本说明

| 命令 | 说明 |
| --- | --- |
| `pnpm dev` | 本地开发（默认 = `dev:ssg`） |
| `pnpm dev:ssg` / `pnpm dev:csr` | 指定模式本地开发（`.env.localdev`） |
| `pnpm build` | 生产构建（默认 = `build:ssg:prod`） |
| `pnpm build:ssg:{dev,test,prod}` | SSG 模式各环境构建 → `dist/client/` |
| `pnpm build:csr:{dev,test,prod}` | CSR 模式各环境构建（先 tsc，再 vite build） → `dist/` |
| `pnpm build:dev` / `build:test` / `build:prod` | 默认模式（SSG）各环境构建别名 |
| `pnpm typecheck` | 生成路由类型并做 TS 类型检查（`react-router typegen && tsc`） |
| `pnpm preview` | 预览 SSG 产物（= `preview:ssg`） |
| `pnpm preview:ssg` | 用 sirv 静态伺服 `dist/client/`（端口 8848） |
| `pnpm preview:csr` | 用 sirv 静态伺服 `dist/`（端口 8848） |
| `pnpm lint` / `pnpm lint:fix` | ESLint 检查 / 自动修复 |
| `pnpm stylelint` / `pnpm stylelint:fix` | 样式检查 / 自动修复 |

## 环境配置

通过不同 `.env` 文件区分环境，Vite 按 **mode** 加载：

| 文件 | 何时加载 |
| --- | --- |
| `.env` | 始终加载（公共默认） |
| `.env.localdev` | `pnpm dev`（`--mode localdev`） |
| `.env.development` | `pnpm build:dev` |
| `.env.production` | `pnpm build` / `pnpm build:prod` |
| `.env.test` | `pnpm build:test` |
| `.env.local` | 本地覆盖，除 test 外都会加载（一般不提交） |

需在环境文件中配置的变量见 `.env` 内注释或项目文档。

## 技术栈

- **框架**：React 18、TypeScript
- **构建**：Vite 7、pnpm
- **渲染**：双模式（构建时切换）——SSG（React Router v7 framework 模式 + 静态预渲染，默认）/ CSR（传统单页应用）。详见 [渲染模式](#渲染模式csr--ssg) 与 [SSG 架构文档](./docs/SSG.md)
- **UI**：Ant Design 5、Tailwind CSS、SCSS/Less
- **路由**：React Router 7（SSG 用 `/:lang/...` 路径前缀多语言；CSR 用 `/home` 无前缀）
- **状态**：基于 Context + Hooks 的 Model 方案（类 Umi max）
- **国际化**：i18next（SSG 用 `src/i18n/frameworkI18n.ts` 同步 init；CSR 用 `src/i18n/index.ts` LanguageDetector 异步）
- **请求**：Axios 封装（`src/utils/request`），请求体 brotli 压缩（brotli-wasm）
- **图标**：@iconify/react + 自定义 SVG 转 Iconify 插件
- **规范**：ESLint、Prettier、Stylelint、Commitlint、Husky、lint-staged

## 项目结构

```
├── build/              # Vite 构建相关（插件、工具、分包/压缩/服务配置）
│   └── config/         # 细粒度 Vite 配置模块（chunks、build、plugins、server、optimize）
├── public/             # 静态资源
├── src/
│   ├── assets/         # 资源
│   │   └── iconify/    # 单色 single-color、多色 multi-color 图标
│   ├── components/     # 公共组件
│   ├── config/         # 应用与请求配置（含 request 拦截器、错误处理）
│   ├── defines/        # 枚举与常量
│   ├── hooks/          # 公共 Hooks
│   ├── i18n/           # 国际化配置
│   ├── layouts/        # 布局
│   ├── locales/        # 多语言文案（zh-CN、en-US、fa-IR）
│   ├── models/         # 全局状态 Model
│   ├── pages/          # 页面（按页面分目录，直接作为 route module）
│   ├── routes/         # framework 路由用的包装组件（langLayout、rootRedirect 等）
│   ├── services/       # API 接口（按模块分目录）
│   ├── styles/         # 全局样式
│   ├── utils/          # 工具与 request 封装
│   ├── root.tsx        # HTML 外壳 + Provider 层（取代 index.html/App.tsx）
│   ├── routes.ts       # framework 路由配置（含 /:lang 前缀）
│   ├── entry.client.tsx # 客户端入口（hydrateRoot，取代 main.tsx）
│   └── entry.server.tsx # 构建期预渲染入口
├── types/              # 全局类型与 env 类型
├── docs/SSG.md         # SSG / framework 模式架构文档
├── react-router.config.ts # framework 模式配置（ssr:false + prerender）
├── vite.config.ts
└── package.json
```

## 开发规范摘要

- 严格 TypeScript，遵循项目 ESLint/Prettier 配置
- 提交信息使用 Conventional Commits
- 组件：函数组件 + Hooks；样式优先 Tailwind，必要时 SCSS/Less
- 用户可见文案全部走 `locales`，禁止在代码中硬编码
- 常量与枚举放在 `src/defines/`，请求在 `src/services/` 按模块组织
- 新页面：在 `src/pages/` 下建目录、在 `src/routes.ts` 中注册路由、在 `locales` 补文案；若页面需预渲染，在 `react-router.config.ts` 的 `prerender()` 中加路径（详见 [SSG 架构文档](./docs/SSG.md)）

## 状态管理（Model）

- Model 定义在 `src/models/`，通过 `createModel(hook, name)` 创建。
- 在组件中：`const user = userModel.useModel()`，使用返回的状态与方法。
- 根组件已通过 `autoImportModels` 注入所有 Model 的 Provider。

详见 `src/utils/model/` 与现有 `userModel`、`dialogModel`、`userConfigModel` 实现。

## 图标

- 单色图标：放入 `src/assets/iconify/single-color/`，会转为 `currentColor`，可用 `className` 控制颜色。
- 多色图标：放入 `src/assets/iconify/multi-color/`，保留原色。
- 使用：`import icon from '@/assets/iconify/single-color/xxx.svg'`，再 `<Icon icon={icon} />`（@iconify/react）。

## 多语言

- 文案在 `src/locales/` 下按语言分目录（zh-CN、en-US、fa-IR），每语言含 common、components、menus、pages 等模块。
- 组件内：`const { t } = useTranslation();`，`t('key')` 或带命名空间的 key。
- 禁止在页面/组件中写死中文或英文字符串。

## 请求与接口

- 请求封装：`src/utils/request`，基础配置与拦截器在 `src/config/request/`（认证、加解密、错误处理）。
- 接口按模块放在 `src/services/`，每模块可有 `typings.d.ts` + `index.ts`。
- 调用：`import { fetchXxx } from '@/services/xxx'`，错误由统一错误处理兜底，可选 `skipErrorHandler` 自行处理。

## 本地存储

- `src/utils/localUtils`、`src/utils/sessionUtils` 提供 `createLocalTools` / `createSessionTools`，支持按 key/value 加密与命名空间。
- 通过环境变量配置加密密钥与存储命名空间，敏感数据建议开启加密选项。

## 部署

两种渲染模式的产物目录与 nginx 配置**不同**，按实际构建的模式选择对应配置。多环境通过 `build:dev` / `build:test` / `build:prod` 与对应 `.env.*` 区分。

### SSG 模式（默认）

- 产物在 **`dist/client/`**（framework 模式产物结构）。`ssr:false` 为纯静态预渲染，**无需常驻 Node 服务**，对 CDN 加速零影响。
- 预渲染路径在 `react-router.config.ts` 的 `prerender()` 中配置；未预渲染的路径由 SPA fallback（`dist/client/index.html`）承载。
- nginx 需先尝试命中预渲染静态页，再回落 SPA fallback（详见 [SSG 架构文档](./docs/SSG.md)）：
  ```nginx
  server {
      root /var/www/home-web/dist/client;

      location / {
          # 依次尝试：精确文件 → 目录 index（命中预渲染页） → flat 后缀 → 回落 SPA fallback
          try_files $uri $uri/ $uri.html /index.html;
      }

      # 带 hash 的静态资源长缓存
      location /assets/ {
          expires 1y;
          add_header Cache-Control "public, immutable";
      }
  }
  ```

### CSR 模式

- 产物在 **`dist/`**，仅有一个空壳 `index.html`，无预渲染文件，即标准单页应用部署。
- nginx 只需最基础的 SPA fallback（无需 `$uri/`、`$uri.html`，产物里没有对应文件）：
  ```nginx
  server {
      root /var/www/home-web/dist;

      location / {
          try_files $uri /index.html;
      }

      # 带 hash 的静态资源长缓存
      location /assets/ {
          expires 1y;
          add_header Cache-Control "public, immutable";
      }
  }
  ```
