mirror of
https://github.com/jason5ng32/MyIP.git
synced 2026-11-04 08:55:47 +00:00
Remove unnecessary files
This commit is contained in:
1 parent
9e80e8027b
commit
ca4db706df
5 files changed
-458
No files matched your search
@@ -1,180 +0,0 @@
|
||||
# 01 — Bootstrap → Tailwind + shadcn-vue
|
||||
|
||||
**目标**:彻底移除 Bootstrap,UI 层完全迁移到 Tailwind v4 + shadcn-vue,bootstrap-icons 替换为 lucide-vue-next。最终 `package.json` 不再依赖 `bootstrap` / `bootstrap-icons`。
|
||||
|
||||
**状态**:✅ 完全完成
|
||||
|
||||
- 阶段 A(基建):✅ 完成
|
||||
- 阶段 B(命令式 Bootstrap JS 替换:Toast / Tooltip / Modal / Offcanvas):✅ 完成
|
||||
- 阶段 C.1(Dropdown / Collapse / Accordion / Tabs / ScrollSpy 收尾):✅ 完成
|
||||
- 阶段 C.2(模板层 Bootstrap class → Tailwind / shadcn-vue,28 次 commit 逐组件):✅ 完成
|
||||
- 阶段 D(bootstrap-icons → lucide-vue-next):✅ 完成
|
||||
- 阶段 E(卸载 bootstrap 包 + 删 bootstrap CSS import):✅ 完成
|
||||
- **结果**:`package.json` 不再依赖 `bootstrap` 或 `bootstrap-icons`;`style.css` 中相关 `@import` 全部移除
|
||||
|
||||
**影响范围**:30+ Vue 文件、router/index.js 中的 DOM 操作、main.js 中的全局 tooltip 指令、style/style.css。
|
||||
|
||||
---
|
||||
|
||||
## 阶段 A — 基础设施搭建 ✅
|
||||
|
||||
- [x] 安装 Tailwind v4 (`tailwindcss@^4.2.2`、`@tailwindcss/vite@^4.2.2`),在 `vite.config.js` 注入 `tailwindcss()` 插件
|
||||
- [x] 安装 shadcn-vue 运行时依赖 (`reka-ui`、`class-variance-authority`、`clsx`、`tailwind-merge`、`lucide-vue-next`),建立 `components.json`,约定组件落在 `frontend/components/ui/`,工具函数在 `frontend/lib/utils.js`
|
||||
- [x] 设计 design tokens(shadcn 标准 CSS 变量集 + neutral baseColor + oklch 色彩空间)写入 `style/style.css` 的 `@theme inline` 块
|
||||
- [x] 暗色模式:`store.setDarkMode()` 同步切换 `<html>` 的 `.dark` class,`@custom-variant dark` 绑定该 class,Tailwind `dark:` 变体生效
|
||||
- [x] Tailwind 与 Bootstrap 共存验证:`npm run build` 通过、`npm test` 23 全绿、Vite HMR 正常、CSS bundle 含 `--background` 等 tokens 与 `.dark { }` 规则
|
||||
- [x] `tailwindcss` / `@tailwindcss/vite` 归入 devDependencies(保持 dev 分支 "chore: move dev tools out of production deps" 的约定)
|
||||
- [x] 落地一个 shadcn `Button` 组件(`frontend/components/ui/button/`)作为后续替换的样板
|
||||
|
||||
## 阶段 B — 命令式 Bootstrap JS 替换(高 ROI 优先)
|
||||
|
||||
> 共 13 处 `import ... from 'bootstrap'`,每替一处都直接简化代码。
|
||||
|
||||
- [x] **Toast** → shadcn `sonner`(vue-sonner):`components/widgets/Toast.vue`(保持 `store.setAlert()` 外部 API 不变)
|
||||
- [x] **Tooltip** → shadcn-vue `Tooltip`(基于 reka-ui):
|
||||
- 初版保留 `v-tooltip` 指令(自实现 DOM 方案)失败:tooltip 在实际页面上完全不显示,问题与项目历史上 Bootstrap Tooltip 也需要特殊处理一致。删除。
|
||||
- 最终方案:
|
||||
- 新建 `components/ui/tooltip/`(Tooltip / TooltipTrigger / TooltipContent / TooltipProvider + 便利组件 `JnTooltip`)
|
||||
- `App.vue` 根层包 `<TooltipProvider>`,所有子 tooltip 共享 provider
|
||||
- 11 处 `v-tooltip` 调用点全部替换为 `<JnTooltip :text :side>` 包裹,`store.isMobile` 时自动跳过(与旧行为一致)
|
||||
- 删除 `main.js` 的 `import { Tooltip } from 'bootstrap'` + 指令注册
|
||||
- `frontend/directives/` 目录已删除
|
||||
- [x] **Modal** → shadcn `Dialog`:
|
||||
- [x] `components/widgets/Help.vue`(`openModal()` 对外 API 保留)
|
||||
- [x] `components/widgets/QueryIP.vue`(开启时自动 focus 输入框)
|
||||
- [x] `components/User.vue`
|
||||
- [x] `components/Additional.vue`
|
||||
- [x] 新增 `components/ui/dialog/`(Dialog / DialogContent / DialogClose)
|
||||
- [x] **Offcanvas** → shadcn `Sheet`(完整迁移,含 store 协调 + router 清理 + Patch.vue 互斥逻辑移除):
|
||||
- [x] `components/Nav.vue`(移动端 Sheet + 桌面端 inline + OpenPreferences 改用 store)
|
||||
- [x] `components/Achievements.vue`
|
||||
- [x] `components/Footer.vue`
|
||||
- [x] `components/Advanced.vue`(fullscreen 改为响应式 class)
|
||||
- [x] `components/widgets/Preferences.vue`(新增,原本只被 Nav.vue 通过 DOM 控制)
|
||||
- [x] `components/widgets/Patch.vue`(listenOffcanvas 整个移除,互斥由 `store.openSheet` 单字段天然保证)
|
||||
- [x] 新增 `components/ui/sheet/`(Sheet / SheetContent / SheetClose)
|
||||
- [x] 新增 `store.openSheet` 单字段 + `setOpenSheet` / `toggleSheet` actions
|
||||
- [x] 安装 `tw-animate-css` 支持 Tailwind v4 的 animate-in/out 工具类
|
||||
- [x] **删除 `router/index.js` 里对 `document.getElementById('offcanvasTools')` 的命令式操作**,改为 store 状态驱动 Sheet 开合
|
||||
- [x] 删除 `main.js` 里的 `import 'bootstrap'`(所有 Bootstrap JS 部件已迁完,JS 是死代码,安全删除;Bootstrap CSS 仍通过 `style.css` 加载)
|
||||
|
||||
## 阶段 C — 视觉层批量重写
|
||||
|
||||
### C.1(已完成)— 清掉 4 类剩余的 Bootstrap JS 部件 ✅
|
||||
|
||||
- [x] **Dropdown** (`data-bs-toggle="dropdown"`) → shadcn-vue `DropdownMenu`
|
||||
- [x] `components/Nav.vue`(用户菜单)
|
||||
- [x] `components/advanced-tools/DnsResolver.vue`
|
||||
- [x] **Collapse / Accordion** (`data-bs-toggle="collapse"`) → shadcn-vue `Collapsible` 或 `Accordion`(新建 `components/ui/collapsible/` + `components/ui/accordion/`)
|
||||
- [x] `components/advanced-tools/Whois.vue`(Accordion 单选,默认展开第一项)
|
||||
- [x] `components/advanced-tools/SecurityChecklist.vue`(两处:分类 intro = Collapsible;每项 info = v-show 字典,因为 table row 结构不适合 Collapsible 包裹)
|
||||
- [x] `components/advanced-tools/MtrTest.vue`(Accordion 单选)
|
||||
- [x] `components/ip-infos/IPCard.vue`(ASN info = Collapsible;简化 per-card 状态为单 ref)
|
||||
- [x] 连带修了 Bootstrap Collapse 展开后内容变空白的 bug
|
||||
- [x] **Tab** (`data-bs-toggle="tab"`) → shadcn-vue `Tabs`
|
||||
- [x] `components/Achievements.vue`(Get / NotGet 切换)
|
||||
- [x] **ScrollSpy** (`data-bs-spy="scroll"`) → 删除(Patch.vue 的 `checkSectionsAndTrack` 已经在做同样的事,scrollspy 是冗余)
|
||||
- [x] `App.vue`
|
||||
|
||||
### C.2 — 模板层 Bootstrap class → Tailwind/shadcn-vue(进行中)
|
||||
|
||||
**教训**:曾在 commit 254b081 尝试"一次性写 @apply compat shim 替代整份 bootstrap.min.css"的偷懒做法,导致大面积视觉回归(~168 个 class 的 shim 映射值对不上 Bootstrap 精确色/间距,加上 Tailwind Preflight 干扰),已于 commit 3c28014 revert。
|
||||
|
||||
**迁移约定**(C.2 每个 commit 都遵守):
|
||||
|
||||
1. **能用 shadcn-vue 现成组件就用**:`<Button>` 替 `.btn`,`<Card>` 替 `.card`,`<Alert>` 替 `.alert`,`<Badge>` 替 `.badge`,`<Input>` 替 `.form-control`,`<Select>` 替 `.form-select`,`<Checkbox>` / `<Switch>` 替 `.form-check-input`,`<Progress>` 替 `.progress`,`<Separator>` / `<Table>` / `<Skeleton>` / `<Avatar>` 等同理。用到时如果 `frontend/components/ui/` 下还没有,**先把它们从 shadcn-vue 源抄进来再用**(保持 copy-in 风格)
|
||||
2. **shadcn 没有或视觉差距太大时才自己写 Tailwind**:例如状态驱动的多色按钮(success / warning / danger 同时存在)、浮动触发器这种特殊用法
|
||||
3. **不引入任何 compat 中介层**:不写 `.btn { @apply ... }` 这种;所有样式直接在模板 class 里用 Tailwind 表达
|
||||
4. **一次一个组件**:单 commit、改完人眼对比视觉再提交
|
||||
5. **色彩映射**(粗略参考,实际以视觉对比为准):
|
||||
- `text-success` / `bg-success` → `text-green-600` / `bg-green-600`
|
||||
- `text-warning` / `bg-warning` → `text-yellow-600` / `bg-yellow-500`
|
||||
- `text-danger` / `bg-danger` → `text-red-600` / `bg-red-600`
|
||||
- `text-info` / `bg-info` → `text-sky-600` / `bg-sky-500`
|
||||
- `text-secondary` / `bg-secondary` → `text-neutral-500` / `bg-neutral-500`
|
||||
- `bg-dark` → `bg-neutral-900`, `bg-light` → `bg-neutral-100`
|
||||
- `text-muted` → `text-neutral-500`
|
||||
6. **间距 / 布局 class 大多 Tailwind 原生可用**:`mb-3` / `mt-5` / `px-3` / `py-2` / `text-center` / `flex-1` 等名字相同,直接保留即可
|
||||
7. **要改名的布局 class**:
|
||||
- `d-flex` → `flex`
|
||||
- `d-none` → `hidden`
|
||||
- `align-items-center` → `items-center`
|
||||
- `justify-content-between` → `justify-between`
|
||||
- `flex-column` → `flex-col`
|
||||
- `w-100` / `h-100` → `w-full` / `h-full`
|
||||
8. **暗色模式**:用 Tailwind 的 `dark:` 变体,不再用 `:class="{ 'dark-mode-*': isDarkMode }"` 模式(store.isDarkMode 已经通过 `setDarkMode` 同步到 `<html>.dark`)
|
||||
- 所有组件 C.2 都改完之后,`style.css` 里的 bootstrap CSS import 才能删,bootstrap 包才能卸载
|
||||
|
||||
建议顺序(按视觉复杂度从低到高):
|
||||
|
||||
- [x] `components/widgets/InfoMask.vue` — 用 shadcn `<Button size="icon">` + 状态色 :class 覆盖;浮动定位改 Tailwind;`document.querySelector` → ref;resize listener 补了 cleanup
|
||||
- [x] `components/widgets/PWA.vue` — 无工作:模板只有 `<pwa-install>` 第三方 custom element,零 Bootstrap class
|
||||
- [x] `components/widgets/Patch.vue` — 无工作:模板空
|
||||
- [x] `components/svgicons/Brand.vue` / `IPError.vue` — 无工作:纯 SVG,仅 `me-1` 一处 Tailwind v4 原生兼容
|
||||
- [x] `components/Footer.vue` — ToggleGroup(3 选一 radio 按钮组)+ Badge(changelog 条目类型)+ Separator(`<hr>`)+ 大量 link/text 颜色由 Bootstrap `link-*` / `text-*` 改为 Tailwind 直接表达;`showAbout/changelog/specialthanks` 三个 boolean 合并为单个 `content` ref + v-if;顺手修了原 `offcanvasBody.scrollTop = 0` 对 ref 对象直接操作的 bug(正确写法是 `sheetBody.value.scrollTop`);新增 `components/ui/{badge,toggle-group,separator}/`
|
||||
- [x] `components/Additional.vue` — 移除 `.container`/`.row`/`d-flex`/`.modal-*`/`.dark-mode-*` 等 Bootstrap class,改为 Tailwind;Curl dialog 内部结构用 Tailwind 表达;保留 `.jn-curl` / `.jn-comment` 自定义类(非 Bootstrap)
|
||||
- [ ] `components/svgicons/*`(纯 SVG,可能无工作)
|
||||
- [x] `components/widgets/Help.vue` — 两列快捷键表,`.row`/`.col`/`.col-8`/`.col-auto` → `flex` + `flex-1` + `shrink-0`;`<kbd>` 样式直接内联,替代 `.text-bg-light` + Bootstrap 默认 kbd 样式
|
||||
- [x] `components/widgets/QueryIP.vue` — 浮动搜索按钮换 `<Button size="icon">` + 响应式 right-position;`.form-control` → `<Input>`;结果 `.list-group` → plain `<ul>` + Tailwind;`.progress`/`.progress-bar` → `<Progress>`;新增 `components/ui/{input,progress}/`
|
||||
- [x] `components/User.vue` — Benefits Dialog:`.modal-content/header/body/footer` / `.table`/`.table-dark`/`.table-responsive` 改为 Tailwind table;dark-mode 全用 `dark:` 变体;移除 unused `isDarkMode` / `isMobile` computed
|
||||
- [x] `components/ConnectivityTest.vue` — refresh 按钮用 `<Button size="icon" variant="outline">`;`.row`/`.col-6 col-md-3` → `flex flex-wrap -mx-2` + `w-1/2 md:w-1/4 px-2`;`.card` 改为 Tailwind;`text-info/success/warning/danger` 映射到 `text-sky/green/yellow/red-600`(保留 `.jn-text-warning` 自定义色)
|
||||
- [x] `components/DnsLeaksTest.vue` — 相同模式:Button + flex/wrap grid + `.alert alert-info/success` → Tailwind 配色块 + dark: 变体
|
||||
- [x] `components/WebRtcTest.vue` — 与 Connectivity/DnsLeaks 同模式
|
||||
- [x] `components/advanced-tools/Empty.vue` — 无工作:只是空 div
|
||||
- [x] `components/advanced-tools/BrowserInfo.vue` — row/col → flex/w-_;`.alert` → Tailwind 色块;`.badge text-bg-*` → shadcn `<Badge>` + 色彩覆盖;`.form-check form-switch` → shadcn `<Switch>`;`.spinner-grow` → `animate-pulse` 圆点;新增 `components/ui/switch/`
|
||||
- [x] `components/advanced-tools/MacChecker.vue` — `.input-group` → flex + Input + Button;table 用 Tailwind;result card 结构改 Tailwind flex grid;text-success/secondary 图标色映射
|
||||
- [x] `components/advanced-tools/RuleTest.vue` — 卡片网格 + .alert 色块 + RefreshAll 按钮全部改 Tailwind + shadcn Button
|
||||
- [x] `components/advanced-tools/DnsResolver.vue` — input-group 三段 (Input + Type Dropdown + Run) 用 shadcn;table 改 Tailwind
|
||||
- [x] `components/advanced-tools/Whois.vue` — input + Button;`.alert-success` → Tailwind 色块;raw 数据预览 `.card card-body bg-light/bg-black` → Tailwind
|
||||
- [x] `components/advanced-tools/GlobalLatencyTest.vue` — `.form-select` → shadcn `<Select>`(新增 `components/ui/select/`);table 改 Tailwind
|
||||
- [x] `components/advanced-tools/CensorshipCheck.vue` — input/button、两列 table (test/control group) 用 Tailwind;`.alert alert-info/danger/success` 最终结果条 → Tailwind 色块条件分支
|
||||
- [x] `components/advanced-tools/MtrTest.vue` — IP Select + Run Button;Accordion 已在 phase C.1 迁移过,这里只清 class;AS 徽章 → shadcn `<Badge>` + 色彩覆盖
|
||||
- [x] `components/advanced-tools/SecurityChecklist.vue` — 最复杂组件:进度条、分类卡列表、filter ToggleGroup(替 btn-group radio hack)、checklist table + 优先级 Badge + Switch(替 form-check-input);`.alert alert-success` 统计条 + `.progress progress-stacked` 多段进度 → Tailwind
|
||||
- [x] `components/advanced-tools/InvisibilityTest.vue` — agreement checkbox + 运行按钮;score 总结 alert;14 行表格结果,全部改 Tailwind
|
||||
- [x] `components/Advanced.vue` — 工具入口卡片网格 + Sheet 头部 + 全屏切换按钮,全部改 Tailwind;`.jn-icon-dark` 合并到 `:global(.dark) .jn-icon` 选择器
|
||||
- [x] `components/SpeedTest.vue` — input-group(2 个 size select + 开始按钮)+ 4 列测试结果 + 4 chart canvas 网格;`.progress.progress-bar-striped.progress-bar-animated` → Tailwind bar;`.alert.alert-success` 结果条 → 绿色块;分数颜色映射。Select 两处保留 native `<select>` 因为需要 number 值而 shadcn Select 是 string(适度用规则 2)
|
||||
- [x] `components/ip-infos/DataPairBar.vue` — .progress + .progress-bar 双色条 → Tailwind flex + 两个 div;label badges → shadcn Badge;fw-light/fw-bold → font-light/bold
|
||||
- [x] `components/ip-infos/ASNInfo.vue` — .alert alert-light → Tailwind 色块;.badge text-bg-* → shadcn Badge;.placeholder col-N → inline width% 骨架
|
||||
- [x] `components/ip-infos/IPCard.vue` — 完整改写:卡片结构、IP 信息列表、质量进度条用 shadcn `<Progress>`、刷新按钮 Button;placeholder-glow 改 animate-pulse;保留 `.jn-link2` / `.jn-link2-dark` 虚线连接线
|
||||
- [x] `components/IpInfos.vue` — 把 row + `col-xl-*`/`col-md-*` 动态栅格换成 `flex flex-wrap -mx-2` + Tailwind 条件 w-_ 映射(1/2/3/4+ 张卡片分别对应 full/1-2/1-3/1-3)
|
||||
- [x] `components/Nav.vue` — `.navbar`/`.navbar-expand-lg`/`.container-xxl`/`.bg-body-tertiary`/`.dark-mode-nav`/`.navbar-brand`/`.navbar-toggler-icon`/`.nav-link`/`.btn-outline-light`/`.btn-dark`/`.dropdown-toggle`/`.badge text-bg-*` 全部替换为 Tailwind + dark: 变体;汉堡包图标保留 SVG data URI,移到 `jn-hamburger-icon` scoped class + `:global(.dark)` dark 变体
|
||||
- [x] `components/Achievements.vue` — Sheet header + Tabs;成就卡片 grid;`.alert alert-success/warning` → Tailwind 色块;成就卡片 .jn-achievements-card-dark 合并为 `:global(.dark) .jn-achievements-card`;`.text-secondary` + conditional 'text-light' 合并为 `text-neutral-500 dark:text-neutral-100`
|
||||
- [x] `components/widgets/Preferences.vue` — 全部 btn-group radio 模式 → `<ToggleGroup>`;所有 form-check-input/form-switch → shadcn `<Switch>`;`.list-group.list-group-item` → Tailwind flex 列表;移除所有 `.dark-mode-*` / `.btn-outline-dark/light` / `.form-check-input jn-check-dark/light` 条件类
|
||||
- [x] `components/widgets/Toast.vue` — 无工作:已在阶段 B 完成(sonner,仅包装 `<Sonner />`,无模板 Bootstrap class)
|
||||
- [x] `App.vue` 模板部分 — `.container mt-5` → `mx-auto w-full px-4 mt-5`(保留 .jn-container 的 max-width 自定义);`.rounded-2` → `.rounded-md`。App.vue 脚本大改部分归到 refactor/02 单独做
|
||||
|
||||
C.2 全部完成之后 → 进入阶段 E(卸载 bootstrap 和 CSS import)。
|
||||
|
||||
## 阶段 D — 图标迁移 ✅
|
||||
|
||||
- [x] 安装 `lucide-vue-next`(已在阶段 A 完成)
|
||||
- [x] 全仓库搜索 `bi bi-` 类名,逐一替换为对应的 lucide 组件:
|
||||
- 70 种不同 bi 图标 → 对应 lucide 组件(详见 commit 说明的映射表)
|
||||
- 21 个组件文件、~101 处 `<i class="bi bi-*">` 全部替换
|
||||
- 通用视觉对齐:lucide 组件统一附加 `class="inline size-[1em] align-[-0.125em]"` 复现 bootstrap-icons 的字号自适应 + 基线微调行为
|
||||
- 动态 class 场景(如 eye/eye-slash 切换)改用 `<component :is="...">`
|
||||
- SecurityChecklist / ConnectivityTest 的 i18n 数据里存储 `bi-*` 字符串,新增组件内 map 把字符串映射到 lucide 组件(保留 i18n 数据不动)
|
||||
- Whois 的编号圆圈(原本用 `bi-N-circle-fill`)改为 Tailwind 渲染的纯数字小徽章
|
||||
- RuleTest / DnsLeaksTest / IPCard 的编号数字图标同理
|
||||
- [x] 删除 `style/style.css` 里的 `bootstrap-icons` import
|
||||
- [x] `npm uninstall bootstrap-icons`
|
||||
|
||||
## 阶段 E — 卸载与清理 ✅
|
||||
|
||||
- [x] 删除 `style/style.css` 里的 `@import 'bootstrap/dist/css/bootstrap.min.css'`
|
||||
- [x] `npm uninstall bootstrap bootstrap-icons`
|
||||
- [x] `npm run build` 通过
|
||||
|
||||
---
|
||||
|
||||
## 完成前回顾清单 ✅
|
||||
|
||||
- [x] 全仓库 `grep -r "from 'bootstrap'"` 结果为 0
|
||||
- [x] 全仓库 `grep -r "bi bi-"` 结果为 0(仅剩 SecurityChecklist 内 i18n 数据映射表的字符串键,不是 DOM class)
|
||||
- [x] `package.json` 不再含 `bootstrap` / `bootstrap-icons`
|
||||
- [x] `npm run build` 通过
|
||||
- [x] `npm test` 全绿(46/46)
|
||||
- [ ] `npm run dev` 启动后所有 Modal / Sheet / Tooltip / Toast 行为正常(留给 UI polish session 验证)
|
||||
- [ ] 暗色模式在所有页面表现一致(留给 UI polish session 验证)
|
||||
- [ ] 移动端布局未破坏(`store.isMobile` 路径手动验证一次)
|
||||
- [ ] router 切换 advanced-tools 时 Sheet 正常开合
|
||||
@@ -1,42 +0,0 @@
|
||||
# 02 — App.vue 上帝组件拆分
|
||||
|
||||
**目标**:将 `frontend/App.vue`(590 行,14 个 ref,混合了模板、加载编排、刷新调度、信息遮罩、快捷键注册、滚动控制)拆分为职责清晰的多个 composables 和子组件。最终 `App.vue` 只剩模板 + 少量编排代码(目标 < 150 行)。
|
||||
|
||||
**状态**:✅ 完成
|
||||
|
||||
**前置依赖**:refactor/01 已全部完成(UI 基建 + 图标迁移都已落地),此时拆分无需二次重构。
|
||||
|
||||
---
|
||||
|
||||
## 子任务
|
||||
|
||||
### 拆分 composables(新建 `frontend/composables/` 目录)✅
|
||||
|
||||
- [x] `use-shortcuts.js` — 抽出 `ShortcutKeys()` / `registerShortcutKeys` / `loadShortcuts`;重复的"滚到 AdvancedTools + navigate + track"合并为一个 helper
|
||||
- [x] `use-info-mask.js` — 抽出 `applyMask` / `removeMask` / `toggleInfoMask` + `infoMaskLevel` / `isInfosLoaded` / `showMaskButton`;监听 `store.allHasLoaded` 内置
|
||||
- [x] `use-refresh-orchestrator.js` — 抽出 `refreshEverything` / `scheduleTimedTasks` / `loadingControl`;监听 `store.shouldRefreshEveryThing` 内置
|
||||
- [x] `use-scroll-to.js` — `scrollToElement` 小工具
|
||||
|
||||
### 模板与 ref 整理 ✅
|
||||
|
||||
- [x] App.vue 顶层 14 个 ref 收敛:删除 alert 相关 4 个本地 ref(`alertStyle` / `alertMessage` / `alertTitle` / `alertToShow`),所有 alert 直接走 `store.setAlert()`
|
||||
- [x] 删除 `main.js` 中已废弃的 `app.config.globalProperties.$trackEvent` 和 `$analytics`(全仓搜索确认无调用方)
|
||||
- [x] `loadingControl` 保留递归 setTimeout 方案(改用 watch 需要多追一个 flag,复杂度反而上升,收益有限;语义已清晰封装在 composable 里)
|
||||
|
||||
### 验收 ✅
|
||||
|
||||
- [x] App.vue 总行数 < 150(实际 130 行)
|
||||
- [x] `npm run build` 通过
|
||||
- [x] `npm test` 全绿(46/46)
|
||||
- [x] composables 文件每个都有头部注释说明输入输出
|
||||
- [ ] 运行时验证(自动刷新 / 手动刷新 / 遮罩切换 / 所有快捷键)—— 留给 UI polish session
|
||||
|
||||
---
|
||||
|
||||
## 完成前回顾清单
|
||||
|
||||
- [x] 全仓库 `grep` 确认 App.vue 中拆出的所有函数没有被其它组件直接引用过
|
||||
- [x] `frontend/composables/` 下文件均为单一职责,没有"杂物 composable"
|
||||
- [x] App.vue 不再持有任何与 alert / refresh / mask / shortcut 相关的本地状态
|
||||
- [ ] 手动跑一遍:自动刷新模式 / 手动刷新模式 / 切换到任一 advanced-tool / 触发遮罩 1 / 2 / 解除 / 触发每个快捷键
|
||||
- [ ] 03 任务里为这些新 composables 添加单测(已在 03 文档登记为阶段 C 待办)
|
||||
@@ -1,128 +0,0 @@
|
||||
# 03 — 增加单元测试
|
||||
|
||||
**目标**:扩展现有 Node 内置 test runner 测试覆盖,给后续重构(特别是 01、02、04)提供安全网。
|
||||
|
||||
**状态**:✅ 完成
|
||||
|
||||
**测试栈**:**Node 内置 `node --test`**(不引入 Vitest / Jest,避免增加依赖)。
|
||||
**位置**:根目录 `tests/`,文件命名 `xxx.test.js`,用 `node:test` + `node:assert/strict`。
|
||||
|
||||
---
|
||||
|
||||
## 已经完成(dev 分支已合入)
|
||||
|
||||
- [x] `package.json` 加 scripts:`"test": "node --test tests/*.test.js"`、`"check": "npm run test && npm run build"`
|
||||
- [x] 根目录建立 `tests/` 目录
|
||||
- [x] `common/valid-ip.js` + `frontend/utils/valid-ip.js` 测试(`tests/valid-ip.test.js`)
|
||||
- [x] API smoke 测试(`tests/api-smoke.test.js`):覆盖 referer 检查 + configs / map / maxmind / dns 早期校验路径,无网络调用
|
||||
|
||||
> 现状参考:跑 `npm test` 应全绿。新增测试请遵循同样的 ESM + `node:test` 风格,避免引入新依赖。
|
||||
|
||||
---
|
||||
|
||||
## 待办子任务
|
||||
|
||||
### 阶段 A — utils 纯函数补齐
|
||||
|
||||
- [x] `frontend/utils/transform-ip-data.js` — 各类 raw 输入到统一卡片数据的映射 / 边界
|
||||
- [x] `frontend/utils/masked-info.js` — 遮罩输出格式(mock i18n t 函数)
|
||||
- [x] `frontend/utils/country-name.js` — 国家代码 → 名称映射 + 兜底
|
||||
- [x] `frontend/utils/timestamp-to-date.js` — 时区与格式
|
||||
- [x] `frontend/utils/system-detect.js` — UA 模拟测试 OS / Mobile 判定
|
||||
- [x] `frontend/utils/speedtest-colos.js` — 数据查询函数
|
||||
- [ ] `frontend/utils/shortcut.js` — 跳过:模块加载时 `document.addEventListener('keydown', ...)` 有副作用,且 `navigateCards` 操作 DOM 高度依赖 CSS class/ref,Node 环境测成本高于收益。替代方案:02 拆分后 composables 里的 shortcut 编排行为走阶段 C 的 composable 测(更贴近真实使用)
|
||||
|
||||
### 阶段 B — API smoke 扩展 ✅
|
||||
|
||||
已覆盖所有后端 handler 的 referer / 方法 / 参数早期校验路径(tests/api-handlers.test.js):
|
||||
|
||||
- [x] `api/get-user-info.js`
|
||||
- [x] `api/get-whois.js`
|
||||
- [x] `api/cf-radar.js`
|
||||
- [x] `api/invisibility-test.js`
|
||||
- [x] `api/ip-sb.js` / `api/ipapi-com.js` / `api/ipapi-is.js` / `api/ipcheck-ing.js` / `api/ipinfo-io.js` / `api/ip2location-io.js`
|
||||
- [x] `api/mac-checker.js`
|
||||
- [x] `api/update-user-achievement.js`
|
||||
- [x] `api/dns-resolver.js` 的更多分支(hostname 非字符串 / hostname 不含 dot)
|
||||
|
||||
### 阶段 C — composables 测试 ✅
|
||||
|
||||
02 拆出的 4 个 composable 全部补齐(tests/composable-*.test.js):
|
||||
|
||||
- [x] `use-scroll-to.js`
|
||||
- [x] `use-info-mask.js` — 覆盖 level 0→1→2→0 完整流程 + allHasLoaded watch
|
||||
- [x] `use-refresh-orchestrator.js` — loadingControl 两分支 + refresh watch + 递归重试
|
||||
- [x] `use-shortcuts.js` — 注册行为 + 关键快捷键 action 逻辑(R / 数字 / ? / h 等)
|
||||
|
||||
为让 composables 能在 Node 环境下被 import,做了三处 portability 小修:
|
||||
- `frontend/composables/use-info-mask.js` / `use-shortcuts.js` 的 `@/` 别名改为相对路径
|
||||
- `frontend/utils/use-analytics.js` 的 `import.meta.env` 用可选链兜底
|
||||
|
||||
### 阶段 D — store 测试 ✅
|
||||
|
||||
- [x] `store.js` 所有关键 action 的契约测试(tests/store.test.js,27 用例覆盖
|
||||
setAlert / setLoadingStatus / setRefreshEveryThing / setCurrentPath / setDarkMode /
|
||||
toggleSheet / updateIPDBs / updateAllIPs / setPreferences / loadPreferences / trigger* 等)
|
||||
- [x] getter 测试(allHasLoaded / activeSources / curlDomainsHadSet)
|
||||
- [x] getDbUrl 委托 buildDbUrl 的间接契约
|
||||
- [x] 04 任务的成就相关数据已在 tests/achievements.test.js 里覆盖
|
||||
|
||||
为让 store 能在 Node 环境下被 import,做了几处 portability 小修:
|
||||
- `frontend/store.js` 的 `./locales/i18n` 补上 `.js` 后缀
|
||||
- `frontend/locales/i18n.js` 的 JSON import 统一加 `with { type: 'json' }`(Vite + Node 都支持)
|
||||
- `frontend/firebase-init.js` / `frontend/store.js` 的 `import.meta.env` 全部用可选链
|
||||
|
||||
### 阶段 E — 共享与后端 ✅
|
||||
|
||||
- [x] `common/referer-check.js` — 独立测试覆盖 localhost 兜底、多域配置、子域边界、畸形 URL(tests/referer-check.test.js)
|
||||
- [x] `common/maxmind-service.js` — 路径 / ready 契约 + 真实 DB 端到端查询(仓库自带 GeoLite2 数据)
|
||||
|
||||
---
|
||||
|
||||
## Vue 组件测试(不在本阶段范围)
|
||||
|
||||
Node test runner 测 Vue SFC 需要额外编译/jsdom 配置,会破坏"零依赖"原则。**本阶段不做组件测试**,需要时单独评估方案。
|
||||
|
||||
---
|
||||
|
||||
## 完成前回顾清单 ✅
|
||||
|
||||
- [x] `npm run check` 通过(test + build 全绿)
|
||||
- [x] 所有新测试文件遵循 `tests/*.test.js` 命名
|
||||
- [x] 测试不依赖网络(外部 API 测试只覆盖 referer / 参数早 reject 路径)
|
||||
- [x] 测试不引入新 npm 依赖(全部用 node:test + node:assert/strict + 既有 pinia/vue 运行时)
|
||||
- [x] 02 任务完成后回头补了 composables 测试
|
||||
- [x] 04 任务完成后回头补了成就解锁逻辑测试(achievements.test.js 已覆盖)
|
||||
- [x] 副作用 utils 的处理决定:
|
||||
- `use-analytics.js` — 不测;全部依赖浏览器 GA/gtag 注入,Node 环境下逻辑无意义。已做 SSR 可选链修正以便测试其它依赖它的模块
|
||||
- `register-service-worker.js` — 不测;依赖 `navigator.serviceWorker`,无单测价值
|
||||
- `shortcut.js` — 不直接测;顶层 `document.addEventListener` 是不可避免的模块副作用。通过 `use-shortcuts.js` 的组合测试间接覆盖了 `mappingKeys` 行为
|
||||
|
||||
---
|
||||
|
||||
## 最终产出
|
||||
|
||||
新增测试文件(14 个):
|
||||
|
||||
- tests/valid-ip.test.js(既有)
|
||||
- tests/achievements.test.js(既有)
|
||||
- tests/default-preferences.test.js(既有)
|
||||
- tests/ip-databases.test.js(既有)
|
||||
- tests/sections.test.js(既有)
|
||||
- tests/api-smoke.test.js(既有)
|
||||
- tests/country-name.test.js
|
||||
- tests/masked-info.test.js
|
||||
- tests/speedtest-colos.test.js
|
||||
- tests/system-detect.test.js
|
||||
- tests/timestamp-to-date.test.js
|
||||
- tests/transform-ip-data.test.js
|
||||
- tests/api-handlers.test.js
|
||||
- tests/composable-scroll-to.test.js
|
||||
- tests/composable-info-mask.test.js
|
||||
- tests/composable-refresh-orchestrator.test.js
|
||||
- tests/composable-shortcuts.test.js
|
||||
- tests/store.test.js
|
||||
- tests/referer-check.test.js
|
||||
- tests/maxmind-service.test.js
|
||||
|
||||
总用例数:**211 个测试、52 个 suite,全绿**。
|
||||
@@ -1,57 +0,0 @@
|
||||
# 04 — store.js 硬编码数据治理
|
||||
|
||||
**目标**:将 `frontend/store.js` 中硬编码的数据(主要是 21 条 `userAchievements`、可能还有其它常量)抽离到独立数据文件,store 只持有可变状态和 actions,便于维护、便于测试、便于未来本地化与扩展。
|
||||
|
||||
**状态**:✅ 已完成
|
||||
|
||||
---
|
||||
|
||||
## 子任务
|
||||
|
||||
### 阶段 A — 现状盘点 ✅
|
||||
|
||||
- [x] 通读 `frontend/store.js`,列出所有"硬编码常量数据"候选清单
|
||||
- [x] 在本文件下方"盘点结果"小节登记发现项(5 类,其中 4 类可抽离)
|
||||
|
||||
### 阶段 B — achievements 抽离 ✅
|
||||
|
||||
- [x] 新建 `frontend/data/achievements.js`:`ACHIEVEMENTS_DEFINITIONS` 数组(name / img,不含状态)+ `createInitialAchievementsState()` 工厂
|
||||
- [x] store 的 `state.userAchievements` 改为调用工厂初始化
|
||||
- [x] 校验所有读取 `userAchievements` 的地方(`Achievements.vue` / `App.vue` / `User.vue` / `SpeedTest.vue` / `CensorshipCheck.vue` / `Whois.vue` / `SecurityChecklist.vue` / `RuleTest.vue` / `InvisibilityTest.vue` / `Preferences.vue`)形状未改,行为不变
|
||||
- [x] `IAmHuman` 图片路径缺失的 leading `/` 修正,全部统一到 `/achievements/...`
|
||||
|
||||
### 阶段 C — 其它硬编码 ✅
|
||||
|
||||
- [x] **ipDBs** → `frontend/data/ip-databases.js`:导出 `IP_DATABASES`(定义)、`createInitialIpDBs()`(工厂)、`buildDbUrl(db, ip, lang)`(纯函数)。`store.getDbUrl()` 改为 find + buildDbUrl。
|
||||
- [x] **defaultPreferences** → `frontend/data/default-preferences.js`:导出 `DEFAULT_PREFERENCES`(Object.freeze)+ `createDefaultPreferences()`。`store.loadPreferences()` 改用工厂。
|
||||
- [x] **SECTION_IDS / mountingStatus / loadingStatus** → `frontend/data/sections.js`:导出 `SECTION_IDS`、`DEFAULT_SECTION`、`createMountingStatus()`、`createLoadingStatus()`。store 的三处初始化 + `Patch.vue` 里原先硬编码的 `sectionIds` 数组全部改用共享源。
|
||||
|
||||
### 阶段 D — 测试 ✅
|
||||
|
||||
- [x] `tests/achievements.test.js`:21 条定义、路径格式、工厂独立性、runtime shape
|
||||
- [x] `tests/ip-databases.test.js`:7 条定义、工厂独立性、`buildDbUrl` 的模板替换 / 默认 lang / 无 {{lang}} 占位符 / 空输入
|
||||
- [x] `tests/default-preferences.test.js`:freeze、shape、工厂独立性
|
||||
- [x] `tests/sections.test.js`:SECTION_IDS 顺序、工厂独立性
|
||||
|
||||
---
|
||||
|
||||
## 盘点结果
|
||||
|
||||
| # | 项 | 所在行号 | 性质 | 归宿 |
|
||||
|---|---|---|---|---|
|
||||
| 1 | `userAchievements`(21 条 name+img 对象图) | 19–41 | 静态定义 + 运行时状态混合 | 抽离定义到 `data/achievements.js`,store state 由工厂函数生成。**同时修正** `IAmHuman` 图片路径缺失的 leading `/`。 |
|
||||
| 2 | `ipDBs`(7 个 IP 数据源的 id/text/url/enabled) | 82–90 | 纯静态定义 | 抽到 `data/ip-databases.js`。URL 模板替换(`getDbUrl` action)同时抽成纯函数 `buildDbUrl(db, ip, lang)`。 |
|
||||
| 3 | `defaultPreferences`(10 个用户偏好默认值) | 173–184 (action 内) | 纯静态定义 | 抽到 `data/default-preferences.js`。 |
|
||||
| 4 | 段落 ID 列表(`mountingStatus`/`loadingStatus` 的 key + 初始 `currentSection`) | 45–52、62–67、81 | 跨文件共享常量(还被 `Patch.vue` 和 App.vue ShortcutKeys 使用) | 抽到 `data/sections.js`,导出 `SECTION_IDS` + `createMountingStatus()` + `createLoadingStatus()`;store、Patch.vue 都改用共享源。 |
|
||||
| 5 | `alert` / `curl` / `configs` / `userPreferences` 初始对象 | 74–80、53–57、72、73 | 运行时 state 容器 | **保留在 store**,不是硬编码数据,只是 state 默认值结构。 |
|
||||
|
||||
---
|
||||
|
||||
## 完成前回顾清单
|
||||
|
||||
- [ ] `frontend/store.js` 中已无大段静态对象字面量
|
||||
- [ ] `frontend/data/` 下文件均为纯数据 + 纯函数,无副作用、无 store 依赖
|
||||
- [ ] 全仓库 grep 确认所有 `userAchievements` 的读写仍走 store,没有组件直接 import 数据文件硬编码
|
||||
- [ ] 图片路径前缀问题已统一并验证渲染
|
||||
- [ ] 任何新增导出都在文件顶部注释说明用途
|
||||
- [ ] 已与 03 任务联动,补充必要的单测
|
||||
@@ -1,51 +0,0 @@
|
||||
# 重构计划索引
|
||||
|
||||
> 本目录管理 MyIP 当前阶段的重构任务。每个文件对应一个独立子项目。
|
||||
|
||||
启动日期:2026-04-15
|
||||
|
||||
---
|
||||
|
||||
## 子任务总览
|
||||
|
||||
| # | 文件 | 主题 | 状态 |
|
||||
|---|---|---|---|
|
||||
| 01 | [01-bootstrap-to-tailwind.md](./01-bootstrap-to-tailwind.md) | Bootstrap → Tailwind + shadcn-vue(彻底替换) | ✅ 完成(bootstrap 与 bootstrap-icons 均已卸载,图标迁移到 lucide-vue-next) |
|
||||
| 02 | [02-app-vue-split.md](./02-app-vue-split.md) | App.vue 上帝组件拆分 | ✅ 完成(App.vue 594 → 130 行,4 个 composables 拆出) |
|
||||
| 03 | [03-add-tests.md](./03-add-tests.md) | 增加单元测试(Node `node --test`) | ✅ 完成(A~E 全部收尾,211 测试全绿) |
|
||||
| 04 | [04-store-hardcoded.md](./04-store-hardcoded.md) | store.js 硬编码数据治理 | ✅ 已完成 |
|
||||
|
||||
状态图例:🟡 未开始 / 🟢 进行中 / ✅ 已完成 / ⚪ 已取消
|
||||
|
||||
---
|
||||
|
||||
## 推荐顺序
|
||||
|
||||
虽然顺序不强制,但建议这样推进,理由是后一项依赖前一项的成果:
|
||||
|
||||
1. **先 01(Bootstrap → Tailwind)的基础设施部分**(装 Tailwind / shadcn-vue / Sheet 替换 Offcanvas),因为 02(App.vue 拆分)会动到大量模板和命令式 Bootstrap 调用,先有新 UI 基建可以避免做两次。
|
||||
2. **同时启动 03(测试基建)**——给 utils/ 加 Vitest,这是后续重构的安全网。
|
||||
3. **04(store 硬编码)** 可以独立穿插进行,影响面小。
|
||||
4. **02(App.vue 拆分)** 放到 01 推进过半再做,拆出的 composables 会更干净。
|
||||
|
||||
---
|
||||
|
||||
## 完成纪律(重要)
|
||||
|
||||
**每次将子任务从 `[ ]` 改成 `[x]` 之前,必须执行以下检查:**
|
||||
|
||||
1. **全局搜索回扫**:用 grep / Glob 搜索该子任务相关的关键词(如某个被替换的 Bootstrap class、被移除的 import、被抽离的函数名),确认全仓库没有遗漏的引用。
|
||||
2. **同类场景扫描**:思考"这个改动是否还有类似场景没处理"。比如替换了一个 Modal,确认其它 Modal 是否同样需要替换;抽出一个 composable,确认是否还有重复逻辑可以合并。
|
||||
3. **运行时验证**:能跑就跑(`npm run dev` / `npm run build` / `npm test`),看构建/测试是否仍通过。
|
||||
4. **更新本 README 的总览表**:状态从 🟡 / 🟢 改为 ✅。
|
||||
5. **如果发现遗漏**:不要勾选,回到计划文件补充新子项后再继续。
|
||||
|
||||
每个计划文件末尾都有一个"完成前回顾清单"小节,列出该任务特定的检查点。
|
||||
|
||||
---
|
||||
|
||||
## 范围约束
|
||||
|
||||
当前阶段**只做**这 4 件事。其它建议(TypeScript 化、Biome 接入、依赖瘦身、circle-progress 替换、firebase 动态加载、vue-router 版本核对等)暂不进入计划,避免范围蔓延。
|
||||
|
||||
完成 4 项后再评估下一阶段做什么。
|
||||
Reference in new issue
Block a user