
// 极简交互式Todo待办工具 | 核心功能增删改查本地JSON持久化 // 运行 bun src/todocli.ts 输入 exit 退出 import fs from fs/promises; import path from path; import readline from readline; // 持久化文件路径 const TASK_FILE path.resolve(__dirname, ./tasks.json); // 核心类型 type Task { id: number; title: string; priority: low | medium | high; status: active | done; }; type TaskStore { list: Task[]; nextId: number; }; // ---------------------- 文件读写核心工具 ---------------------- // 加载任务文件不存在返回空模板 async function loadTasks(): PromiseTaskStore { try { const raw await fs.readFile(TASK_FILE, utf8); return JSON.parse(raw) as TaskStore; } catch { return { list: [], nextId: 1 }; } } // 保存任务到本地json全量覆盖写入 async function saveTasks(store: TaskStore) { await fs.writeFile(TASK_FILE, JSON.stringify(store, null, 2), utf8); } // 错误打印工具 function errLog(msg: string) { console.log(\x1b[31m❌ ${msg}\x1b[0m); } // 成功打印封装统一绿色输出 function sucLog(msg: string) { console.log(\x1b[32m✅ ${msg}\x1b[0m); } // ---------------------- 完整CRUD业务命令 ---------------------- const cmdMap: Recordstring, (args: string[]) Promisevoid { // 【增】add 标题 --priorityhigh --done创建即完成 add: async (args) { const priorityArg args.find(item item.startsWith(--priority)); const priority priorityArg ? (priorityArg.split()[1] as Task[priority]) : medium; // 判断是否创建直接完成 const createDone args.includes(--done); // 过滤配置参数提取任务标题 const title args.filter(i !i.startsWith(--)).join( ).trim(); if (!title) return errLog(必须输入任务标题); const store await loadTasks(); const newTask: Task { id: store.nextId, title, priority, status: createDone ? done : active }; store.list.push(newTask); store.nextId 1; await saveTasks(store); const statusText createDone ? 已完成 : 待完成; sucLog(创建任务 #${newTask.id}【${statusText}】${title}); }, // 【查】list / list --statusactive/done list: async (args) { const statusArg args.find(item item.startsWith(--status)); const filterStatus statusArg ? statusArg.split()[1] : all; const store await loadTasks(); let showList store.list; if (filterStatus ! all) showList store.list.filter(t t.status filterStatus); console.log(\x1b[32m 任务列表\x1b[0m); if (showList.length 0) return console.log( 暂无任务); showList.forEach(t { const stat t.status active ? 待完成 : 已完成; console.log( ID:${t.id} | ${t.title} | 优先级:${t.priority} | ${stat}); }); }, // 【改】标记完成 done id done: async (args) { const id Number(args[0]); if (isNaN(id) || id 0) return errLog(请输入合法数字ID); const store await loadTasks(); const target store.list.find(t t.id id); if (!target) return errLog(不存在ID${id}的任务); target.status done; await saveTasks(store); sucLog(任务#${id}已标记完成); }, // 【改】update 1 --title新标题 --prioritylow update: async (args) { const id Number(args[0]); if (isNaN(id) || id 0) return errLog(第一个参数必须是任务ID); const titleArg args.find(i i.startsWith(--title)); const prioArg args.find(i i.startsWith(--priority)); const store await loadTasks(); const target store.list.find(t t.id id); if (!target) return errLog(不存在ID${id}的任务); // 按需更新字段 if (titleArg) target.title titleArg.split()[1]; if (prioArg) target.priority prioArg.split()[1] as Task[priority]; await saveTasks(store); sucLog(任务#${id}修改成功); }, // 【删】删除单条任务 delete id delete: async (args) { const id Number(args[0]); if (isNaN(id) || id 0) return errLog(请输入合法数字ID); const store await loadTasks(); const oldLen store.list.length; store.list store.list.filter(t t.id ! id); if (store.list.length oldLen) return errLog(不存在ID${id}的任务); await saveTasks(store); sucLog(任务#${id}已删除); }, // 批量清理已完成任务 clear clear: async () { const store await loadTasks(); const before store.list.length; store.list store.list.filter(t t.status ! done); const del before - store.list.length; await saveTasks(store); sucLog(清理完成移除${del}条已完成任务); } }; // ---------------------- 交互主循环 ---------------------- // 启动交互式命令行主程序负责终端输入输出、命令循环监听 async function startCli() { // 创建readline交互实例用于读取终端键盘输入与打印输出 const rl readline.createInterface({ input: process.stdin, // 标准输入流接收用户键盘输入 output: process.stdout, // 标准输出流向控制台打印文字 prompt: \x1b[32mtodo \x1b[0m // 自定义输入提示符\x1b[32m设置绿色字体\x1b[0m重置字体颜色 }); console.clear(); console.log( Todo极简工具【增删改查】); console.log(可用命令); console.log(add 任务名 --prioritylow/medium/high --done 创建任务--done直接完成); console.log(list --statusactive/done 查看任务); console.log(done ID 标记任务完成); console.log(update ID --title新标题 --priorityhigh 修改任务); console.log(delete ID 删除单条任务); console.log(clear 清空全部已完成任务); console.log(exit 退出程序\n); // 在控制台输出自定义提示符 todo 等待用户输入命令 rl.prompt(); // 监听用户按下回车的输入事件input 为用户输入的整行文本 rl.on(line, async (input) { // 去除输入文本首尾多余空格 const line input.trim(); // 判断输入为exit执行退出逻辑 if (line exit) { sucLog(退出程序数据已保存); // 关闭readline交互实例 rl.close(); // 终止当前事件回调后续代码执行 return; } // 用户仅输入空格/直接回车无有效内容重新弹出输入提示符 if (!line) { rl.prompt(); return; } // 以任意空白符空格、Tab分割整行输入拆分为命令参数数组 const parts line.split(/\s/); // 数组第一个元素为主命令add/list/update等 const cmd parts[0]; // 截取数组剩余所有元素作为命令的参数列表 const args parts.slice(1); // 判断输入的命令不存在 if (!cmdMap[cmd]) { errLog(未知命令${cmd}); } else { // 执行对应命令的异步处理函数捕获命令内部异常并打印错误 await cmdMap[cmd](args).catch(e errLog((e as Error).message)); } // 打印空行分隔两次命令输出优化控制台阅读体验 console.log(); // 执行完一条命令后再次弹出提示符等待下一次输入形成循环交互 rl.prompt(); }); // 监听readline关闭事件彻底结束Node/Bun进程0代表正常退出码 rl.on(close, () process.exit(0)); } // 启动程序捕获全局致命异常 startCli().catch(e errLog(全局异常${(e as Error).message}));0.1