docs: 补充 README 使用说明与 agents.md 维护扩展指南
This commit is contained in:
parent
e3c1534263
commit
d53c5e8870
118
README.md
Normal file
118
README.md
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
# 在线工具箱(tool.xpcool.com)
|
||||||
|
|
||||||
|
纯前端在线工具站:**Vite + Vue3 + Pinia + TypeScript + UnoCSS + TDesign**。14 个常用工具,所有文件处理与计算均在浏览器本地完成,**不上传任何用户数据到服务器**。
|
||||||
|
|
||||||
|
## 功能特性
|
||||||
|
|
||||||
|
- 14 个实用工具:图片压缩/裁剪、二维码生成解析、JSON 格式化、时间戳转换、Base64 编解码、JWT 解析、Cron 表达式、进制转换、哈希计算、身份证解析、地址解析、URL 解析、图片 OCR 识别
|
||||||
|
- 左侧固定导航 + 中间内容区;PC Web 与移动端 H5 响应式适配(移动端为抽屉导航)
|
||||||
|
- 暗黑 / 浅色主题切换,选择持久化,刷新不丢失
|
||||||
|
- 每个工具独立路由、独立本地历史记录(localStorage)
|
||||||
|
- 通用组件:复制按钮、下载按钮、错误提示、历史记录面板
|
||||||
|
- 全部能力由浏览器原生 API、WebAssembly 与 npm 前端库实现,无任何 mock 后端接口、无 CDN 脚本引入
|
||||||
|
|
||||||
|
## 技术栈
|
||||||
|
|
||||||
|
| 类别 | 选型 |
|
||||||
|
| --- | --- |
|
||||||
|
| 构建 | Vite 6 + pnpm |
|
||||||
|
| 框架 | Vue 3(`<script setup>` 组合式 API) |
|
||||||
|
| 语言 | TypeScript(strict) |
|
||||||
|
| 状态 | Pinia |
|
||||||
|
| UI | TDesign Vue Next + tdesign-icons-vue-next |
|
||||||
|
| 样式 | UnoCSS 原子类 + SCSS(使用 TDesign 设计令牌变量) |
|
||||||
|
| 路由 | Vue Router 4(History 模式) |
|
||||||
|
|
||||||
|
工具依赖:`qrcode`、`jsqr`、`cropperjs`、`browser-image-compression`、`tesseract.js`、`cron-parser`、`spark-md5`(全部通过 npm 安装)。
|
||||||
|
|
||||||
|
## 快速开始
|
||||||
|
|
||||||
|
要求:Node.js ≥ 18,pnpm ≥ 8。
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. 安装依赖
|
||||||
|
pnpm install
|
||||||
|
|
||||||
|
# 2. 启动开发服务器(默认 http://localhost:5173)
|
||||||
|
pnpm dev
|
||||||
|
|
||||||
|
# 3. 类型检查
|
||||||
|
pnpm type-check
|
||||||
|
|
||||||
|
# 4. 生产构建(输出到 dist/)
|
||||||
|
pnpm build
|
||||||
|
|
||||||
|
# 5. 本地预览构建产物
|
||||||
|
pnpm preview
|
||||||
|
```
|
||||||
|
|
||||||
|
## 页面路由
|
||||||
|
|
||||||
|
| 路由 | 工具 |
|
||||||
|
| --- | --- |
|
||||||
|
| `/` | 首页(工具卡片导航) |
|
||||||
|
| `/image-compress` | 图片压缩 |
|
||||||
|
| `/image-cropper` | 图片裁剪 |
|
||||||
|
| `/qrcode` | 二维码生成 / 解析 |
|
||||||
|
| `/json-format` | JSON 格式化 |
|
||||||
|
| `/timestamp` | 时间戳转换 |
|
||||||
|
| `/base64` | Base64 编解码 |
|
||||||
|
| `/jwt-parse` | JWT 解析 |
|
||||||
|
| `/cron` | Cron 表达式工具 |
|
||||||
|
| `/radix-convert` | 进制转换 |
|
||||||
|
| `/hash` | 哈希计算 |
|
||||||
|
| `/idcard` | 身份证解析 |
|
||||||
|
| `/address-parse` | 地址解析 |
|
||||||
|
| `/url-parse` | URL 解析 |
|
||||||
|
| `/ocr` | 图片 OCR 识别 |
|
||||||
|
|
||||||
|
## 目录结构
|
||||||
|
|
||||||
|
```
|
||||||
|
├── index.html # 入口 HTML(含防闪烁主题脚本)
|
||||||
|
├── package.json
|
||||||
|
├── vite.config.ts # Vite 配置(别名、分包)
|
||||||
|
├── unocss.config.ts # UnoCSS 配置(快捷类)
|
||||||
|
├── tsconfig.json / tsconfig.node.json
|
||||||
|
└── src/
|
||||||
|
├── main.ts # 应用入口(TDesign 全量注册、主题初始化)
|
||||||
|
├── App.vue
|
||||||
|
├── env.d.ts
|
||||||
|
├── assets/styles/ # 全局样式(TDesign 令牌变量)
|
||||||
|
├── router/
|
||||||
|
│ ├── index.ts # 路由表(由注册表生成)
|
||||||
|
│ └── tools.ts # ★ 工具注册表(唯一数据源)
|
||||||
|
├── stores/theme.ts # 主题状态(Pinia)
|
||||||
|
├── composables/
|
||||||
|
│ ├── useHistory.ts # 本地历史记录
|
||||||
|
│ ├── useClipboard.ts # 剪贴板复制
|
||||||
|
│ └── useDownload.ts # 本地文件下载
|
||||||
|
├── components/common/ # CopyButton / DownloadButton / ErrorAlert / HistoryPanel
|
||||||
|
├── layout/ # MainLayout(响应式布局)+ SideNav(侧边导航)
|
||||||
|
├── utils/index.ts # 通用工具函数
|
||||||
|
└── views/ # 首页 + 14 个工具页面(每个工具一个目录)
|
||||||
|
```
|
||||||
|
|
||||||
|
## 部署
|
||||||
|
|
||||||
|
构建产物为纯静态文件,托管到任意静态服务器或 OSS/CDN 即可。使用 History 路由需配置回退(以 Nginx 为例):
|
||||||
|
|
||||||
|
```nginx
|
||||||
|
location / {
|
||||||
|
root /usr/share/nginx/html;
|
||||||
|
index index.html;
|
||||||
|
try_files $uri $uri/ /index.html;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 隐私说明
|
||||||
|
|
||||||
|
所有计算均在浏览器本地完成:图片压缩/裁剪/OCR 使用本地 Canvas/WASM,哈希使用本地 Web Crypto 与 spark-md5,历史记录仅存于本机 localStorage。项目不包含任何后端接口调用,用户文件永远不会离开浏览器。
|
||||||
|
|
||||||
|
## OCR 语言包说明
|
||||||
|
|
||||||
|
OCR 的引擎代码(worker + wasm)已随 npm 依赖打包进产物;语言数据属于运行时资源,默认从官方语言包源加载(首次识别约 20MB,浏览器会缓存)。如需完全离线部署:下载 `chi_sim.traineddata.gz` 与 `eng.traineddata.gz` 放入 `public/traineddata/`,并将 `src/views/ocr/OcrView.vue` 中的 `LANG_PATH` 改为 `${window.location.origin}/traineddata`。
|
||||||
|
|
||||||
|
## 维护与扩展
|
||||||
|
|
||||||
|
新增工具、修改架构、排查问题的完整说明见 [agents.md](./agents.md)。
|
||||||
161
agents.md
Normal file
161
agents.md
Normal file
@ -0,0 +1,161 @@
|
|||||||
|
# agents.md — 项目维护与扩展指南
|
||||||
|
|
||||||
|
本文档面向后续接手维护/扩展本项目的 AI 编码代理(Agent)与开发者,说明架构约定、关键机制、开发流程与常见坑。动手改代码前请先读完本文。
|
||||||
|
|
||||||
|
## 1. 项目定位
|
||||||
|
|
||||||
|
- 名称:tool.xpcool.com(在线工具箱)
|
||||||
|
- 形态:**纯前端 SPA**,无后端、无 mock 接口。所有计算与文件处理在浏览器本地完成,用户文件不出浏览器。
|
||||||
|
- 技术栈:Vite 6 + Vue 3(`<script setup>`)+ Pinia + TypeScript(strict)+ UnoCSS + TDesign Vue Next + pnpm。
|
||||||
|
- 客户端:PC Web + 移动端 H5 响应式(`<768px` 时侧边栏变为抽屉导航)。
|
||||||
|
- 包管理:**pnpm**(存在 `pnpm-lock.yaml`,禁止换成 npm/yarn 安装)。
|
||||||
|
|
||||||
|
## 2. 目录结构与职责
|
||||||
|
|
||||||
|
```
|
||||||
|
src/
|
||||||
|
├── main.ts # 入口:注册 Pinia/Router/TDesign 全量组件,初始化主题
|
||||||
|
├── App.vue # 仅承载 <router-view>
|
||||||
|
├── assets/styles/ # 全局 SCSS,颜色只用 TDesign 令牌变量(--td-*)
|
||||||
|
├── router/
|
||||||
|
│ ├── tools.ts # ★ 工具注册表:全站工具唯一数据源(菜单/首页/路由都从这里生成)
|
||||||
|
│ └── index.ts # 路由表:MainLayout 作为父路由,子路由由 tools.ts 映射生成
|
||||||
|
├── stores/theme.ts # 暗黑/浅色主题(Pinia + localStorage 持久化)
|
||||||
|
├── composables/
|
||||||
|
│ ├── useHistory.ts # 本地历史(每个工具独立 key,localStorage 持久化)
|
||||||
|
│ ├── useClipboard.ts # 复制(Clipboard API + execCommand 回退)
|
||||||
|
│ └── useDownload.ts # 下载(Blob/文本/DataURL)
|
||||||
|
├── components/common/ # CopyButton / DownloadButton / ErrorAlert / HistoryPanel
|
||||||
|
├── layout/
|
||||||
|
│ ├── MainLayout.vue # 顶栏 + 左侧固定导航 + 内容区 + 移动端抽屉 + 底部
|
||||||
|
│ └── SideNav.vue # 导航列表(含首页项 + 工具列表,数据来自 tools.ts)
|
||||||
|
├── utils/index.ts # 通用函数(文件读取、字节格式化、北京时间格式化等)
|
||||||
|
└── views/
|
||||||
|
├── HomeView.vue # 首页:工具卡片栅格(数据来自 tools.ts)
|
||||||
|
└── <tool-name>/XxxView.vue # 每个工具一个目录一个页面
|
||||||
|
```
|
||||||
|
|
||||||
|
## 3. 核心机制
|
||||||
|
|
||||||
|
### 3.1 工具注册表(最重要的扩展点)
|
||||||
|
|
||||||
|
`src/router/tools.ts` 是全站唯一数据源:侧边导航、首页卡片、路由表均由此自动生成。**新增工具不要手动改路由文件、首页或侧边栏。**
|
||||||
|
|
||||||
|
```ts
|
||||||
|
export interface ToolItem {
|
||||||
|
path: string // 路由路径(相对根路径,不含前导 /)
|
||||||
|
name: string // 工具名(菜单/卡片/页面标题)
|
||||||
|
desc: string // 一句话描述
|
||||||
|
icon: Component // TDesign 图标组件
|
||||||
|
component: () => Promise<Component> // 懒加载组件
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**新增一个工具的完整步骤:**
|
||||||
|
|
||||||
|
1. 新建 `src/views/<tool-name>/<ToolName>View.vue`,参考现有页面结构(标题 → 表单卡片 → 错误提示 → 结果卡片 → 历史面板)。
|
||||||
|
2. 在 `tools.ts` 的 `tools` 数组末尾追加一条配置,`component` 使用 `() => import('@/views/<tool-name>/<ToolName>View.vue')`。
|
||||||
|
3. 完成——菜单、首页卡片、路由、文档标题自动生效。可运行 `pnpm type-check` 验证。
|
||||||
|
|
||||||
|
**注意:** `path` 必须与页面内 `useHistory('xxx')` 的 key 一致(历史记录约定),并避免与现有 14 个 path 重复。
|
||||||
|
|
||||||
|
### 3.2 历史记录约定
|
||||||
|
|
||||||
|
- 统一使用 `useHistory(key)`,key 建议等于路由 path,例如 `useHistory('json-format')`。
|
||||||
|
- 数据结构 `HistoryItem { text: string; time: number }`,上限 10 条,去重,最新在前,localStorage 持久化(key 形如 `tool-history:<key>`)。
|
||||||
|
- 文本类工具:`text` 存输入原文,点击历史回填输入框。
|
||||||
|
- 文件类工具(图片压缩/裁剪等):文件本体**不要**存入 localStorage(会撑爆配额),改为存「文件名 | 关键参数」形式的摘要,点击历史只恢复参数。
|
||||||
|
|
||||||
|
### 3.3 主题机制
|
||||||
|
|
||||||
|
- TDesign 通过 `document.documentElement.setAttribute('theme-mode', 'dark')` 切换暗黑模式。
|
||||||
|
- `stores/theme.ts` 负责切换 + 持久化(key:`tool-theme`);`index.html` 内联脚本在挂载前预置属性防闪烁;`main.ts` 再同步一次。
|
||||||
|
- **自定义样式一律使用 `var(--td-*)` 令牌**(如 `--td-bg-color-page`、`--td-text-color-primary`、`--td-brand-color`),禁止硬编码颜色,否则暗黑模式会破版。
|
||||||
|
|
||||||
|
### 3.4 公共组件
|
||||||
|
|
||||||
|
| 组件 | 用途 | 关键 Props |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `CopyButton` | 复制结果 | `text`(必填)、`label` |
|
||||||
|
| `DownloadButton` | 下载文件 | `filename`、`getBlob: () => Blob \| null` |
|
||||||
|
| `ErrorAlert` | 错误提示 | `message`(为空不渲染),`@close` |
|
||||||
|
| `HistoryPanel` | 历史面板 | `items`、`@select`、`@remove`、`@clear` |
|
||||||
|
|
||||||
|
新建工具页必须复用这些组件,保持全站交互一致。
|
||||||
|
|
||||||
|
## 4. 各工具实现要点与依赖库
|
||||||
|
|
||||||
|
| 工具 | 目录 | 核心依赖 / 实现 | 备注 |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| 图片压缩 | `views/image-compress` | `browser-image-compression` | `alwaysKeepResolution: true` + `initialQuality` 实现纯质量压缩;WebP 编码部分 Safari 不支持,已做 try/catch 降级提示 |
|
||||||
|
| 图片裁剪 | `views/image-cropper` | `cropperjs`(含 `cropperjs/dist/cropper.css`) | 实例在 `onBeforeUnmount` 必须 `destroy()`;换图时先 destroy 再重建 |
|
||||||
|
| 二维码 | `views/qrcode` | `qrcode`(生成)+ `jsqr`(解析) | 解析需先把图片绘制到 canvas 取 `ImageData` |
|
||||||
|
| JSON 格式化 | `views/json-format` | 原生 `JSON.parse/stringify` | 缩进支持 2/4 空格/Tab |
|
||||||
|
| 时间戳 | `views/timestamp` | 原生 Date + `Intl.DateTimeFormat('Asia/Shanghai')` | 时间显示固定北京时间(与用户时区无关);日期→时间戳按 UTC+8 换算 |
|
||||||
|
| Base64 | `views/base64` | 原生 `btoa/atob` + `TextEncoder/TextDecoder` | 中文必须走 UTF-8 字节转换,直接 `btoa(中文)` 会抛异常 |
|
||||||
|
| JWT | `views/jwt-parse` | 原生 atob + Base64Url 补位 | **只解码不验签**,页面已声明;exp/iat/nbf 按秒转北京时间 |
|
||||||
|
| Cron | `views/cron` | `cron-parser`(`parseExpression`) | 支持 5/6 段;`interval.next().toDate()` 迭代下次执行时间;可视化生成器用 `parseExpression` 做合法性校验 |
|
||||||
|
| 进制转换 | `views/radix-convert` | **BigInt 手动逐位解析** | 禁止用 `parseInt/Number`(大数会丢精度);支持负号、0x/0o/0b 前缀、下划线分隔 |
|
||||||
|
| 哈希 | `views/hash` | `spark-md5`(MD5)+ Web Crypto `crypto.subtle.digest`(SHA1/256/512) | 大文件按 4MB 分片喂给 `SparkMD5.ArrayBuffer`,避免内存峰值 |
|
||||||
|
| 身份证 | `views/idcard` | 纯 TS 实现 GB 11643-1999 校验 | 内置全部省级行政区代码表;校验位权重表勿改动 |
|
||||||
|
| 地址解析 | `views/address-parse` | 内置省份表 + 正则规则 | 纯规则近似解析,非地理数据库;直辖市无地级市层级需特殊处理 |
|
||||||
|
| URL 解析 | `views/url-parse` | 原生 `URL` / `URLSearchParams` | 无协议输入自动补 `https://`;构造器用 `searchParams.append` 自动编码 |
|
||||||
|
| OCR | `views/ocr` | `tesseract.js`(WASM) | 见第 5 节离线说明 |
|
||||||
|
|
||||||
|
## 5. OCR 特殊说明(重要)
|
||||||
|
|
||||||
|
`views/ocr/OcrView.vue` 中:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
import TesseractWorker from 'tesseract.js/dist/worker.min.js?url'
|
||||||
|
import TesseractCore from 'tesseract.js-core/tesseract-core.wasm.js?url'
|
||||||
|
```
|
||||||
|
|
||||||
|
- worker 与 wasm 核心已通过 Vite `?url` 资源导入打进构建产物,代码层面无 CDN 依赖。
|
||||||
|
- 语言数据是运行时资源,`LANG_PATH` 常量默认指向官方语言包源(首次识别下载约 20MB 后浏览器缓存)。**完全离线部署**:将 `chi_sim.traineddata.gz`、`eng.traineddata.gz` 放入 `public/traineddata/`,把 `LANG_PATH` 改为 `${window.location.origin}/traineddata`。
|
||||||
|
- worker 实例做了懒加载 + 复用 + `onBeforeUnmount` 时 `terminate()`,新增 OCR 相关功能时请沿用该模式,避免重复初始化。
|
||||||
|
|
||||||
|
## 6. 编码规范约定
|
||||||
|
|
||||||
|
- Vue 3 组合式 API `<script setup lang="ts">`;页面逻辑保持“状态 → 校验 → 处理 → 历史”结构。
|
||||||
|
- TypeScript strict 模式(`vue-tsc` 参与构建),**不要**用 `any` 掩盖类型错误;`pnpm build` 必须通过。
|
||||||
|
- 注释用中文,说明“为什么”而非复述代码;每个工具页顶部有用途说明。
|
||||||
|
- 上传使用 TDesign `t-upload`(`:auto-upload="false"`,从 `UploadFile[].raw` 取 `File`)。
|
||||||
|
- 所有对象 URL(`URL.createObjectURL`)在用完后 `revokeObjectURL`,组件卸载时清理。
|
||||||
|
- 样式:布局类用 UnoCSS 原子类或 `unocss.config.ts` 中已定义的快捷类(`page-wrap`、`form-row`、`code-block`);颜色类一律用 TDesign 令牌变量。
|
||||||
|
- 移动端适配:栅格用 `grid-template-columns: repeat(auto-fill, ...)` + `@media (max-width: 767px)` 降为单列,参考 `HomeView.vue`。
|
||||||
|
|
||||||
|
## 7. 已知坑位速查
|
||||||
|
|
||||||
|
1. **TDesign 图标名**:`tdesign-icons-vue-next` 不存在 `CropIcon`、`NumbersIcon`(本项目已用 `FrameIcon`、`CalculatorIcon` 替代)。新增图标前可用 Node 脚本验证导出名,避免构建失败:
|
||||||
|
```bash
|
||||||
|
node --input-type=module -e "import('tdesign-icons-vue-next').then(m=>console.log('XxxIcon' in m))"
|
||||||
|
```
|
||||||
|
2. **暗黑模式**:自定义样式忘用 `--td-*` 变量会在暗黑下出现白块/黑字,是最常见的回归点。
|
||||||
|
3. **时间显示**:全站用户可见时间一律北京时间(`utils/formatBeijing`),不要直接 `toLocaleString()`。
|
||||||
|
4. **大整数**:进制转换用 BigInt;任何“数字输入”都要考虑超出 `Number.MAX_SAFE_INTEGER` 的场景。
|
||||||
|
5. **cron-parser API**:v4 使用 `parseExpression(expr)` 返回可迭代表达式,异常即非法表达式;不支持秒的表达式是标准 5 段。
|
||||||
|
6. **移动端复制**:非 HTTPS 环境 `navigator.clipboard` 不可用,`useClipboard` 已内置 `execCommand` 回退,不要重写复制逻辑。
|
||||||
|
7. **JSON.parse 的输入**:Vue `v-model` 绑定 textarea 的是字符串;解析前先 `trim()` 判空,错误信息取 `e.message` 展示给用户。
|
||||||
|
|
||||||
|
## 8. 构建、验证与提交
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pnpm install # 安装依赖
|
||||||
|
pnpm dev # 开发调试
|
||||||
|
pnpm type-check # 仅类型检查
|
||||||
|
pnpm build # 类型检查 + 生产构建(vue-tsc && vite build)
|
||||||
|
pnpm preview # 预览 dist
|
||||||
|
```
|
||||||
|
|
||||||
|
- 任何改动提交前必须 `pnpm build` 通过;产物 `dist/` 与 `node_modules/` 已被 `.gitignore` 排除。
|
||||||
|
- 提交信息使用 Conventional Commits(`feat:` / `fix:` / `docs:` / `refactor:`)。
|
||||||
|
- 冒烟验证:`pnpm dev` 后访问 `/` 与改动路由,确认 200 且页面正常渲染(纯前端站点可直接 curl 验证 HTML 骨架)。
|
||||||
|
|
||||||
|
## 9. 后续扩展建议
|
||||||
|
|
||||||
|
- **新增工具**:按 3.1 的步骤即可,零框架改动。
|
||||||
|
- **PWA / 离线**:可接入 `vite-plugin-pwa`,配合第 5 节 OCR 离线方案实现完全离线工具站。
|
||||||
|
- **国际化**:文案目前硬编码中文,若需 i18n 建议 vue-i18n,工具注册表的 `name/desc` 改为 key。
|
||||||
|
- **体积优化**:TDesign 目前全量注册(约 1.4MB gzip 373KB),若追求极致可改按需引入(unplugin-vue-components + TDesignResolver)。
|
||||||
|
- **更多工具**:色值转换、UUID 生成、正则测试、YAML 转换(js-yaml)、图片格式互转(canvas)等均可按现有模式快速复制。
|
||||||
Loading…
Reference in New Issue
Block a user