# kjTool **Repository Path**: gozaozhe_admin/kj-tool ## Basic Information - **Project Name**: kjTool - **Description**: 这是一个 基于 Vibe Coding 思想模式 通过AI 开发的 nodejs 桌面软件。 能够自定义追加快捷方式和阅读照片册 使用 QClaw 生成了基础的 技术选型 功能需求文档,初期代码由 Trae CN 完成,花费 四五个小时完成初步且可运行。 第二天 通过 QClaw 对话进行了二次修改。 - **Primary Language**: Unknown - **License**: MIT - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-05-16 - **Last Updated**: 2026-06-01 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # KJ 快捷启动器 基于 Electron + Vue 3 的桌面快捷启动工具,支持文件、文件夹、链接和漫画的分类管理及快捷访问。 > 免费AI 以及降智 到简单 的 cmd 脚本代码都不会写了。 > 前几 写个 python 脚本 生成 c 盘 大文件目录分析 报告 html 都还不错 > > 本项目暂停更新了 ![Electron](https://img.shields.io/badge/Electron-28.0.0-47848F) ![Vue](https://img.shields.io/badge/Vue-3.4.0-4FC08D) ![TypeScript](https://img.shields.io/badge/TypeScript-5.3.0-3178C6) ![NaiveUI](https://img.shields.io/badge/NaiveUI-2.44.1-20C997) ![Tailwind](https://img.shields.io/badge/TailwindCSS-3.4.0-06B6D4) --- ## 图片展示 ![屏幕截图 2026-06-01 220020](img/屏幕截图 2026-06-01 220020.png) ## 功能特性 ### 分类管理 - **8 大默认分类**:开发(💻)、工具(🔧)、文档(📄)、漫画(📚)、视频(🎬)、游戏(🎮)、音乐(🎵)、其他(📁) - 每个分类拥有独立的颜色主题和 emoji 图标 - 支持添加自定义分类(支持自定义名称、颜色、图标) - 支持删除自定义分类(默认分类不可删除) - 分类排序由 `sortOrder` 字段控制 ### 标签系统 - 每个分类下可创建多个标签(Tab)对项目进行二级细分 - 默认每个分类都有一个名为"默认"的标签,不可删除 - 标签支持增删操作,双击标签可删除 - 项目可以同时属于一个或多个标签(`tabId` 为数组,支持多选) ### 快捷项目(QuickBlock) 支持 4 种类型: | 类型 | 说明 | 打开方式 | |------|------|----------| | `file` | 文件快捷方式 | 通过 Electron `shell.openPath` 打开 | | `folder` | 文件夹快捷方式 | 通过 Electron `shell.openPath` 打开 | | `url` | 网址链接 | 通过 Electron `shell.openExternal` 在浏览器打开 | | `comic` | 漫画阅读器 | 打开内置漫画阅读器,支持多章节管理 | - 每个项目有封面图片(coverPath),支持手动上传或自动从目录提取 - 支持收藏(isFavorite)和置顶(pinned)标记 - 记录打开次数(openCount)和最后打开时间(lastOpenTime) - 按名称字母顺序自动排序 - 支持批量导入(漫画分类下可将目录下的所有子文件夹作为独立漫画添加) ### 漫画阅读器 - **自动扫描**:自动识别漫画目录结构(支持单话和多话模式) - **章节管理**:`comicMeta.chapters` 数组存储所有章节信息 - **阅读进度**:自动保存当前阅读到的图片位置,下次打开可选择继续 - **横竖屏适配**:横屏模式自动切换为三列网格布局 - **阅读记录**:通过 `comic-history.json` 持久化,精确到每个漫画每个章节 ### 界面与交互 - **侧边栏**:可折叠的分类导航栏(120px 宽度),选中分类带左侧彩色边框 - **标签栏**:水平滚动的标签切换栏,支持多标签筛选 - **网格布局**:响应式网格(3~9 列自适应),根据侧边栏展开状态自动调整列数 - **悬浮操作**:鼠标悬浮卡片时显示打开、编辑、修改封面、删除按钮 - **同名检测**:同一分类下同名项目会标红边框提示重复 - **防重点击**:非漫画分类有点击锁机制(700ms),防止重复触发打开操作 - **深色/浅色主题**:支持跟随系统(system)、深色(dark)、浅色(light)三种模式 --- ## 技术架构 ### 技术栈 | 层级 | 技术 | 版本 | |------|------|------| | 桌面框架 | Electron | 28.0.0 | | 前端框架 | Vue 3 (Composition API) | 3.4.0 | | 类型系统 | TypeScript | 5.3.0 | | 构建工具 | Vite | 5.0.0 | | UI 组件库 | Naive UI | 2.44.1 | | 状态管理 | Pinia | 2.1.7 | | 样式方案 | Tailwind CSS (CSS 变量主题) | 3.4.0 | | 原子图标 | @vicons/carbon, @vicons/ionicons5 | 0.12.0 | | 打包工具 | electron-builder | 24.9.0 | ### 项目结构 ``` kjTool/ ├── electron/ │ └── main.js # Electron 主进程(窗口管理、IPC、数据持久化) ├── src/ │ ├── main.ts # 渲染进程入口(Vue App 挂载) │ ├── App.vue # 根组件(主题注入、布局结构) │ ├── components/ │ │ ├── Sidebar.vue # 左侧分类导航栏 │ │ ├── TitleBar.vue # 顶部标题栏(含窗口控制按钮) │ │ ├── TabBar.vue # 标签切换栏 │ │ ├── QuickBlockGrid.vue # 快捷项目网格容器 │ │ ├── QuickBlockCard.vue # 单个快捷项目卡片 │ │ ├── AddBlockModal.vue # 添加项目模态框 │ │ ├── SettingsModal.vue # 设置模态框 │ │ ├── ComicChapterSelect.vue # 漫画章节选择弹窗 │ │ └── ComicViewer.vue # 漫画阅读器 │ ├── stores/ │ │ └── app.ts # Pinia Store(所有业务状态和操作) │ ├── types/ │ │ └── index.ts # TypeScript 类型定义 │ └── styles/ │ └── main.css # 全局样式(CSS 变量定义、滚动条样式) ├── index.html # HTML 入口 ├── package.json # 依赖配置与 NPM 脚本 ├── vite.config.ts # Vite 配置(路径别名、Dev Server) ├── tailwind.config.js # Tailwind 配置(CSS 变量颜色映射) ├── tsconfig.json # TypeScript 配置(路径别名 @ -> src) └── tsconfig.node.json # Node 环境 TS 配置(Vite 构建用) ``` --- ## 数据模型 ### 类型定义 (`src/types/index.ts`) #### Category(分类) ```typescript interface Category { id: string // 唯一标识(如 'code', 'comic') name: string // 显示名称(如 '开发') icon: string // 图标标识(如 'code', 'comic') color: string // 主题色(如 '#06b6d4') sortOrder: number // 排序顺序 isDefault: boolean // 是否默认分类(默认分类不可删除) isVisible: boolean // 是否显示 } ``` #### Tab(标签) ```typescript interface Tab { id: string // 标签ID(如 'default', 'web-dev') categoryId: string // 所属分类ID name: string // 标签名称 icon?: string // 可选图标 sortOrder: number // 排序顺序 filter?: FilterConfig // 过滤配置(暂未完全使用) } ``` #### QuickBlock(快捷项目) ```typescript interface QuickBlock { id: string // 唯一ID(时间戳 + 随机字符串) categoryId: string // 所属分类ID tabId: string[] // 所属标签ID数组(支持多标签) type: 'file' | 'folder' | 'comic' | 'url' // 项目类型 name: string // 显示名称 description?: string // 描述文字 coverPath?: string // 封面图片(Base64 DataURL 或 kjfile:// 协议 URL) targetPath: string // 目标路径(文件路径/文件夹路径/URL) isFavorite: boolean // 是否收藏 lastOpenTime?: number // 上次打开时间戳 openCount: number // 累计打开次数 comicMeta?: { // 漫画元数据(仅 type='comic' 有) totalChapters: number chapters?: ComicChapter[] } fileTypeIcon?: string // 文件类型 emoji 图标 pinned: boolean // 是否置顶 addedTime: number // 添加时间戳 modifiedTime: number // 修改时间戳 } ``` #### ComicChapter(漫画章节) ```typescript interface ComicChapter { id: string // 章节ID(如 'ch1', 'ch2') name: string // 章节名称(如 '第1话'、目录名) folderPath: string // 章节图片所在文件夹路径 images: string[] // 图片文件名数组(已排序) totalPages: number // 图片总页数 addedTime: number // 添加时间戳 } ``` --- ## 数据持久化 ### 存储位置 数据统一保存在用户目录 `~/.config/kjxapp/` 下(`os.homedir()` + `.config/kjxapp`)。 ### 存储文件结构(新版) | 文件 | 说明 | |------|------| | `categories.json` | 全部分类列表(所有分类的元数据) | | `category-{id}.json` | 每个分类的独立数据文件(包含该分类的 tabs 和 blocks) | | `settings.json` | 应用全局设置(主题、DevTools 等) | | `comic-history.json` | 漫画阅读进度记录 | ### 存储文件结构(兼容旧版) 旧版使用单一 `app-data.json` 存储所有数据。初次保存时自动迁移到新版分文件格式。 ### 数据加载流程 1. 读取 `settings.json` 加载应用设置 2. 尝试读取 `categories.json`(新版格式) 3. 若无则读取 `app-data.json`(旧版格式),并触发一次保存以迁移到新版 4. 遍历每个分类,读取对应的 `category-{id}.json` 获取该分类的 tabs 和 blocks ### 防抖保存机制 使用 500ms 防抖定时器,合并同一分类的多次变更,只写有变更的分类文件,避免频繁 IO。 --- ## Electron 主进程 API(IPC) 主进程通过 `ipcMain.handle` 暴露以下 API,渲染进程通过 `ipcRenderer.invoke` 调用: ### 窗口控制 | IPC 通道 | 参数 | 返回值 | 说明 | |----------|------|--------|------| | `window-minimize` | - | - | 最小化窗口 | | `window-close` | - | - | 关闭窗口 | | `window-maximize` | - | - | 最大化/还原切换 | | `toggle-devtools` | `open: boolean` | - | 切换开发者工具 | ### 文件选择 | IPC 通道 | 参数 | 返回值 | 说明 | |----------|------|--------|------| | `select-folder` | - | `string \| undefined` | 打开系统目录选择对话框 | | `select-file` | - | `string \| undefined` | 打开系统文件选择对话框 | ### 文件系统操作 | IPC 通道 | 参数 | 返回值 | 说明 | |----------|------|--------|------| | `get-subdirectories` | `folderPath: string` | `string[]` | 获取目录下所有一级子目录(排序) | | `get-images-from-folder` | `folderPath: string` | `Array<{name, path, url}>` | 获取目录下所有图片文件(支持 jpg/png/gif/bmp/webp/tif/tiff) | | `get-folder-cover` | `folderPath: string` | `string \| null` | 获取目录第一张图片的 Base64 DataURL | | `read-file-as-dataurl` | `filePath: string` | `string \| null` | 读取文件为 DataURL(用于封面压缩) | | `scan-comic-folder` | `folderPath: string` | `{totalChapters, chapters[]}` | 扫描漫画目录,返回章节信息(自动识别单话/多话) | ### 数据读写 | IPC 通道 | 参数 | 返回值 | 说明 | |----------|------|--------|------| | `read-categories` | - | `Category[]` | 读取分类列表 | | `write-categories` | `data: Category[]` | - | 写入分类列表 | | `read-category-data` | `categoryId: string` | `{tabs, blocks}` | 读取指定分类的数据 | | `write-category-data` | `categoryId, data` | - | 写入指定分类的数据 | | `read-app-settings` | - | `object` | 读取应用设置 | | `write-app-settings` | `data: object` | - | 写入应用设置 | | `read-comic-history` | - | `object` | 读取漫画阅读历史 | | `write-comic-history` | `data: object` | - | 写入漫画阅读历史 | ### 自定义协议 注册 `kjfile://` 协议解决 Electron 中本地文件路径的访问问题: - 接收 `kjfile://{URL编码的文件路径}` 格式的 URL - 解码后通过 `protocol.registerFileProtocol` 映射到本地文件系统 --- ## Pinia Store 详解(`src/stores/app.ts`) Store 是整个应用的状态中枢,负责所有业务逻辑。 ### 状态树 ``` app Store ├── UI 状态 │ ├── isSidebarExpanded # 侧边栏是否展开 │ ├── isSettingsOpen # 设置弹窗是否打开 │ ├── isAddBlockOpen # 添加项目弹窗是否打开 │ ├── isComicViewerOpen # 漫画阅读器是否打开 │ ├── isComicChapterSelectOpen # 漫画章节选择是否打开 │ ├── currentCategoryId # 当前选中的分类ID │ ├── currentTabId # 当前选中的标签ID │ └── currentComicBlock/Chapter # 当前打开的漫画和章节 ├── 数据 │ ├── categories: Category[] # 全部分类列表 │ ├── tabs: Tab[] # 全部标签列表 │ ├── quickBlocks: QuickBlock[] # 全部快捷项目列表 │ └── settings # 应用设置 └── 计算属性 ├── currentCategory # 当前分类对象 ├── currentTabs # 当前分类下的标签 └── currentBlocks # 当前分类+标签下的项目(按名称排序) ``` ### 核心方法 | 方法 | 说明 | |------|------| | `loadFromStorage()` | 从 Electron 主进程加载所有数据(防重复初始化) | | `saveToStorage(categoryId?)` | 防抖保存数据到主进程(可选指定分类) | | `selectCategory(id)` | 切换分类,同时切换到该分类的第一个标签 | | `selectTab(id)` | 切换标签 | | `addBlock(block)` | 添加项目(添加后自动按名称排序) | | `deleteBlock(id)` | 删除项目 | | `updateBlock(id, updates)` | 部分更新项目 | | `sortBlocksByName()` | 手动触发按名称重新排序 | | `openComicViewerWithChapter(block, chapter)` | 打开漫画阅读器 | | `addCategory(category)` | 添加分类(同时添加默认标签) | | `deleteCategory(id)` | 删除分类(同时删除该分类下所有标签和项目) | | `addTab(tab)` | 添加标签 | | `deleteTab(tabId, categoryId)` | 删除标签(同时删除该标签下的所有项目) | | `saveSettings(settings)` | 保存应用设置 | --- ## 组件层次结构 ``` App.vue ├── Sidebar.vue # 左侧分类导航 │ └── 分类列表 + 添加分类弹窗 + 删除确认弹窗 ├── TitleBar.vue # 顶部工具栏 │ ├── 侧边栏折叠按钮 │ ├── 排序按钮(确认对话框) │ ├── 设置按钮 │ └── 窗口控制(最小化、关闭) ├── TabBar.vue # 标签切换栏 │ ├── 标签列表 + 新增标签按钮 │ └── 添加/删除标签弹窗 ├── QuickBlockGrid.vue # 内容区网格容器 │ ├── QuickBlockCard[] # 项目卡片(按 currentBlocks 渲染) │ ├── 添加按钮(漫画分类直接添加,其他分类打开模态框) │ └── 批量导入按钮(仅漫画分类显示) │ └── QuickBlockCard.vue # 单个项目卡片 │ ├── 封面区(图片/渐变色/emoji) │ ├── 悬浮操作层(打开/编辑/改封面/删除) │ ├── 名称(未悬浮时) │ ├── 操作按钮行(悬浮时,显示话数+编辑/改封面) │ ├── 编辑弹窗(名称/描述/标签多选/路径) │ └── 删除确认弹窗 ├── AddBlockModal.vue # 添加项目模态框 │ ├── 类型选择(文件/文件夹/链接,仅非漫画分类) │ ├── 名称输入 │ ├── 路径选择 + 文件夹/漫画目录选择 │ ├── 封面上传(压缩后 Base64 存储) │ └── 描述输入 ├── SettingsModal.vue # 设置弹窗 │ ├── 主题切换(深色/浅色/跟随系统) │ └── 开发者工具开关 ├── ComicChapterSelect.vue # 漫画章节选择弹窗 │ └── 章节列表(点击打开阅读器) └── ComicViewer.vue # 漫画阅读器(全屏) ├── 顶部工具栏(标题、章节名、关闭) ├── 滚动内容区(单页/三列网格自适应) ├── 加载中状态 └── 恢复阅读位置确认弹窗 ``` --- ## 主题系统 ### CSS 变量(`src/styles/main.css`) 通过 Tailwind CSS 变量注入 + `darkMode: 'class'` 实现: **深色主题(默认)** - `--color-bg-primary`: `#0f172a` - `--color-bg-secondary`: `#1e293b` - `--color-bg-tertiary`: `#334155` - `--color-text-primary`: `#f1f5f9` - `--color-text-secondary`: `#94a3b8` - `--color-text-tertiary`: `#64748b` - `--color-border`: `#334155` - `--color-primary`: `#6366f1` **浅色主题** - `--color-bg-primary`: `#f8fafc` - `--color-bg-secondary`: `#f1f5f9` - `--color-bg-tertiary`: `#e2e8f0` - `--color-text-primary`: `#1e293b` - `--color-text-secondary`: `#475569` - `--color-text-tertiary`: `#94a3b8` - `--color-border`: `#e2e8f0` - `--color-primary`: `#6366f1` ### 主题切换 在 `App.vue` 和 `SettingsModal.vue` 中通过 `document.documentElement.classList.remove/ add('dark'/'light')` 切换,Watch `store.settings.theme` 响应变化。 --- ## 窗口配置 - **尺寸**:默认 560px × 全屏高度,定位在屏幕右侧(`x: width - 560`) - **最小尺寸**:400px × 600px - **无边框**:使用自定义标题栏(`-webkit-app-region: drag` 控制拖拽区) - **背景色**:`#0f172a`(与深色主题背景一致) - **Electron 版本**:28.0.0,`nodeIntegration: true`,`contextIsolation: false` --- ## 安装与运行 ### 环境要求 - Node.js >= 18(建议使用项目配套版本) - Windows 10/11 x64 ### 安装依赖 ```bash npm install ``` ### 开发模式 ```bash # 仅启动 Vite 前端开发服务器(http://localhost:5173) npm run dev # 同时启动 Vite + Electron 开发模式(推荐) npm run electron:dev ``` > `electron:dev` 使用 `concurrently` 同时启动两个进程,`wait-on` 确保 Vite 服务器就绪后再启动 Electron。 ### 构建应用 ```bash # 构建前端(TypeScript 编译 + Vite build) npm run build # 构建完整 Electron 安装包(依赖 npm run build) npm run electron:build ``` 构建产物位于 `dist-electron/` 目录: - `KJ 快捷启动器 Setup 1.0.0.exe` — NSIS 安装包(支持自定义安装路径) - `win-unpacked/` — 便携版(解压即用) --- ## 开发命令一览 | 命令 | 说明 | |------|------| | `npm run dev` | 启动 Vite 开发服务器(端口 5173) | | `npm run build` | TypeScript 类型检查 + Vite 构建 | | `npm run preview` | 预览 Vite 构建产物 | | `npm run electron:dev` | Vite + Electron 并行开发 | | `npm run electron:build` | 构建 Windows 安装包 | --- ## 许可证 MIT License