// scripts/clean-dev.ts import fs from "fs/promises"; import path from "path"; // 现代化颜色主题 const theme = { // 基础颜色 reset: "\x1b[0m", bold: "\x1b[1m", dim: "\x1b[2m", // 前景色 primary: "\x1b[38;5;75m", // 亮蓝色 success: "\x1b[38;5;82m", // 亮绿色 warning: "\x1b[38;5;220m", // 亮黄色 error: "\x1b[38;5;196m", // 亮红色 info: "\x1b[38;5;159m", // 青色 purple: "\x1b[38;5;141m", // 紫色 orange: "\x1b[38;5;208m", // 橙色 gray: "\x1b[38;5;245m", // 灰色 white: "\x1b[38;5;255m", // 白色 // 背景色 bgDark: "\x1b[48;5;235m", // 深灰背景 bgBlue: "\x1b[48;5;24m", // 蓝色背景 bgGreen: "\x1b[48;5;22m", // 绿色背景 bgRed: "\x1b[48;5;52m", // 红色背景 }; // 现代化图标集 const icons = { rocket: "🚀", fire: "🔥", star: "⭐", gem: "💎", crown: "👑", magic: "✨", warning: "⚠️", success: "✅", error: "❌", info: "ℹ️", folder: "📁", file: "📄", image: "🖼️", code: "💻", data: "📊", globe: "🌐", map: "🗺️", chat: "💬", bolt: "⚡", shield: "🛡️", key: "🔑", link: "🔗", clean: "🧹", trash: "🗑️", check: "✓", cross: "✗", arrow: "→", loading: "⏳", }; // 格式化工具 const fmt = { title: (text: string) => `${theme.bold}${theme.primary}${text}${theme.reset}`, subtitle: (text: string) => `${theme.purple}${text}${theme.reset}`, success: (text: string) => `${theme.success}${text}${theme.reset}`, error: (text: string) => `${theme.error}${text}${theme.reset}`, warning: (text: string) => `${theme.warning}${text}${theme.reset}`, info: (text: string) => `${theme.info}${text}${theme.reset}`, highlight: (text: string) => `${theme.bold}${theme.white}${text}${theme.reset}`, dim: (text: string) => `${theme.dim}${theme.gray}${text}${theme.reset}`, orange: (text: string) => `${theme.orange}${text}${theme.reset}`, // 带背景的文本 badge: (text: string, bg: string = theme.bgBlue) => `${bg}${theme.white}${theme.bold} ${text} ${theme.reset}`, // 渐变效果模拟 gradient: (text: string) => { const colors = ["\x1b[38;5;75m", "\x1b[38;5;81m", "\x1b[38;5;87m", "\x1b[38;5;159m"]; const chars = text.split(""); return chars.map((char, i) => `${colors[i % colors.length]}${char}`).join("") + theme.reset; }, }; // 创建现代化标题横幅 function createModernBanner() { console.log(); console.log( fmt.gradient(" ╔══════════════════════════════════════════════════════════════════╗") ); console.log( fmt.gradient(" ║ ║") ); console.log( ` ║ ${icons.rocket} ${fmt.title("ART DESIGN PRO")} ${fmt.subtitle("· 代码精简程序")} ${icons.magic} ║` ); console.log( ` ║ ${fmt.dim("为项目移除演示数据,快速切换至开发模式")} ║` ); console.log( fmt.gradient(" ║ ║") ); console.log( fmt.gradient(" ╚══════════════════════════════════════════════════════════════════╝") ); console.log(); } // 创建分割线 function createDivider(char = "─", color = theme.primary) { console.log(`${color}${" " + char.repeat(66)}${theme.reset}`); } // 创建卡片样式容器 function createCard(title: string, content: string[]) { console.log(` ${fmt.badge("", theme.bgBlue)} ${fmt.title(title)}`); console.log(); content.forEach((line) => { console.log(` ${line}`); }); console.log(); } // 进度条动画 function createProgressBar(current: number, total: number, text: string, width = 40) { const percentage = Math.round((current / total) * 100); const filled = Math.round((current / total) * width); const empty = width - filled; const filledBar = "█".repeat(filled); const emptyBar = "░".repeat(empty); process.stdout.write( `\r ${fmt.info("进度")} [${theme.success}${filledBar}${theme.gray}${emptyBar}${theme.reset}] ${fmt.highlight(percentage + "%")})}` ); if (current === total) { console.log(); } } // 统计信息 const stats = { deletedFiles: 0, deletedPaths: 0, failedPaths: 0, startTime: Date.now(), totalFiles: 0, }; // 清理目标 const targets = [ "README.md", "README.zh-CN.md", "CHANGELOG.md", "CHANGELOG.zh-CN.md", "src/views/safeguard", "src/views/dashboard/analysis", "src/views/dashboard/ecommerce", "src/mock/json", "src/mock/temp/articleList.ts", "src/mock/temp/commentDetail.ts", "src/mock/temp/commentList.ts", "src/assets/images/cover", "src/assets/images/safeguard", "src/assets/images/3d", "src/components/charts/art-map-chart", "src/components/business/comment-widget", ]; // 递归统计文件数量 async function countFiles(targetPath: string): Promise { const fullPath = path.resolve(process.cwd(), targetPath); try { const stat = await fs.stat(fullPath); if (stat.isFile()) { return 1; } else if (stat.isDirectory()) { const entries = await fs.readdir(fullPath); let count = 0; for (const entry of entries) { const entryPath = path.join(targetPath, entry); count += await countFiles(entryPath); } return count; } } catch { return 0; } return 0; } // 统计所有目标的文件数量 async function countAllFiles(): Promise { let totalCount = 0; for (const target of targets) { const count = await countFiles(target); totalCount += count; } return totalCount; } // 删除文件和目录 async function remove(targetPath: string, index: number) { const fullPath = path.resolve(process.cwd(), targetPath); createProgressBar(index + 1, targets.length, targetPath); try { const fileCount = await countFiles(targetPath); await fs.rm(fullPath, { recursive: true, force: true }); stats.deletedFiles += fileCount; stats.deletedPaths++; await new Promise((resolve) => setTimeout(resolve, 50)); } catch (err) { stats.failedPaths++; console.log(); console.log(` ${icons.error} ${fmt.error("删除失败")}: ${fmt.highlight(targetPath)}`); console.log(` ${fmt.dim("错误详情: " + err)}`); } } // 重置前端内置动态路由为空(`MenuProcessor.ts` 内 `builtinFrontendRoutes`;异常页在 staticRoutes) async function cleanRouteModules() { const routesPath = path.resolve(process.cwd(), "src/router"); try { try { await fs.rm(path.join(routesPath, "dashboard.ts"), { force: true }); } catch { // 忽略 } try { await fs.rm(path.join(routesPath, "builtinFrontendRoutes.ts"), { force: true }); } catch { // 忽略(旧独立文件可能已不存在) } const menuProcessorPath = path.join(routesPath, "MenuProcessor.ts"); const mp = await fs.readFile(menuProcessorPath, "utf-8"); const reset = mp.replace( /export const builtinFrontendRoutes: AppRouteRecord\[\] = \[[\s\S]*?\];/, "export const builtinFrontendRoutes: AppRouteRecord[] = [];" ); if (reset === mp) { console.log( ` ${icons.warning} ${fmt.dim("MenuProcessor.ts 中未找到 builtinFrontendRoutes,跳过重置")}` ); } else { await fs.writeFile(menuProcessorPath, reset, "utf-8"); } console.log(` ${icons.success} ${fmt.success("清理路由演示模块完成")}`); } catch (err) { console.log(` ${icons.error} ${fmt.error("清理路由演示模块失败")}`); console.log(` ${fmt.dim("错误详情: " + err)}`); } } // 路由占位常量已写在 src/router/staticRoutes.ts 顶部,清理脚本不再单独重写 async function cleanRouterConstants() { console.log( ` ${icons.info} ${fmt.dim("路由常量(ROUTE_*)位于 staticRoutes.ts 顶部,请按需手动修改")}` ); } // 清理变更日志 async function cleanChangeLog() { const changeLogPath = path.resolve(process.cwd(), "src/mock/upgrade/changeLog.ts"); try { const cleanedChangeLog = `import { ref } from 'vue' interface UpgradeLog { version: string // 版本号 title: string // 更新标题 date: string // 更新日期 detail?: string[] // 更新内容 requireReLogin?: boolean // 是否需要重新登录 remark?: string // 备注 } export const upgradeLogList = ref([]) `; await fs.writeFile(changeLogPath, cleanedChangeLog, "utf-8"); console.log(` ${icons.success} ${fmt.success("清空变更日志数据完成")}`); } catch (err) { console.log(` ${icons.error} ${fmt.error("清理变更日志失败")}`); console.log(` ${fmt.dim("错误详情: " + err)}`); } } // 清理语言文件 async function cleanLanguageFiles() { const languageFiles = [ { path: "src/locales/langs/zh.json", name: "中文语言文件" }, { path: "src/locales/langs/en.json", name: "英文语言文件" }, ]; for (const { path: langPath, name } of languageFiles) { try { const fullPath = path.resolve(process.cwd(), langPath); const content = await fs.readFile(fullPath, "utf-8"); const langData = JSON.parse(content); const menusToRemove = [ "widgets", "template", "article", "examples", "safeguard", "plan", "help", ]; if (langData.menus) { menusToRemove.forEach((menuKey) => { if (langData.menus[menuKey]) { delete langData.menus[menuKey]; } }); if (langData.menus.dashboard) { if (langData.menus.dashboard.analysis) { delete langData.menus.dashboard.analysis; } if (langData.menus.dashboard.ecommerce) { delete langData.menus.dashboard.ecommerce; } } if (langData.menus.system) { const systemKeysToRemove = [ "nested", "menu1", "menu2", "menu21", "menu3", "menu31", "menu32", "menu321", ]; systemKeysToRemove.forEach((key) => { if (langData.menus.system[key]) { delete langData.menus.system[key]; } }); } } await fs.writeFile(fullPath, JSON.stringify(langData, null, 2), "utf-8"); console.log(` ${icons.success} ${fmt.success(`清理${name}完成`)}`); } catch (err) { console.log(` ${icons.error} ${fmt.error(`清理${name}失败`)}`); console.log(` ${fmt.dim("错误详情: " + err)}`); } } } // 清理快速入口组件 async function cleanFastEnterComponent() { const fastEnterPath = path.resolve(process.cwd(), "src/config/fastEnter.ts"); try { const cleanedFastEnter = `/** * 快速入口配置 * 包含:应用列表、快速链接等配置 */ import { WEB_LINKS } from "@utils/constants"; import type { FastEnterConfig } from '@/types/config' const fastEnterConfig: FastEnterConfig = { // 显示条件(屏幕宽度) minWidth: 1200, // 应用列表 applications: [ { name: '工作台', description: '系统概览与数据统计', icon: 'ri:pie-chart-line', iconColor: '#377dff', enabled: true, order: 1, routeName: 'Console' }, { name: '官方文档', description: '使用指南与开发文档', icon: 'ri:bill-line', iconColor: '#ffb100', enabled: true, order: 2, link: WEB_LINKS.DOCS }, { name: '技术支持', description: '技术支持与问题反馈', icon: 'ri:user-location-line', iconColor: '#ff6b6b', enabled: true, order: 3, link: WEB_LINKS.COMMUNITY }, { name: '哔哩哔哩', description: '技术分享与交流', icon: 'ri:bilibili-line', iconColor: '#FB7299', enabled: true, order: 4, link: WEB_LINKS.BILIBILI } ], // 快速链接 quickLinks: [ { name: '登录', enabled: true, order: 1, routeName: 'Login' }, { name: '注册', enabled: true, order: 2, routeName: 'Login' }, { name: '忘记密码', enabled: true, order: 3, routeName: 'Login' }, { name: '个人中心', enabled: true, order: 4, routeName: 'Profile' } ] } export default Object.freeze(fastEnterConfig) `; await fs.writeFile(fastEnterPath, cleanedFastEnter, "utf-8"); console.log(` ${icons.success} ${fmt.success("清理快速入口配置完成")}`); } catch (err) { console.log(` ${icons.error} ${fmt.error("清理快速入口配置失败")}`); console.log(` ${fmt.dim("错误详情: " + err)}`); } } // 更新菜单接口 async function updateMenuApi() { const apiPath = path.resolve(process.cwd(), "src/api/system-manage.ts"); try { const content = await fs.readFile(apiPath, "utf-8"); const updatedContent = content.replace( "url: '/api/v3/system/menus'", "url: '/api/v3/system/menus/simple'" ); await fs.writeFile(apiPath, updatedContent, "utf-8"); console.log(` ${icons.success} ${fmt.success("更新菜单接口完成")}`); } catch (err) { console.log(` ${icons.error} ${fmt.error("更新菜单接口失败")}`); console.log(` ${fmt.dim("错误详情: " + err)}`); } } // 用户确认函数 async function getUserConfirmation(): Promise { const { createInterface } = await import("readline"); return new Promise((resolve) => { const rl = createInterface({ input: process.stdin, output: process.stdout, }); console.log( ` ${fmt.highlight("请输入")} ${fmt.success("yes")} ${fmt.highlight("确认执行清理操作,或按 Enter 取消")}` ); console.log(); process.stdout.write(` ${icons.arrow} `); rl.question("", (answer: string) => { rl.close(); resolve(answer.toLowerCase().trim() === "yes"); }); }); } // 显示清理警告 async function showCleanupWarning() { createCard("安全警告", [ `${fmt.warning("此操作将永久删除以下演示内容,且无法恢复!")}`, `${fmt.dim("请仔细阅读清理列表,确认后再继续操作")}`, ]); const cleanupItems = [ { icon: icons.image, name: "图片资源", desc: "演示用的封面图片、3D图片、运维图片等", color: theme.orange, }, { icon: icons.file, name: "演示页面", desc: "widgets、article、examples、safeguard 等页面", color: theme.purple, }, { icon: icons.code, name: "路由模块文件", desc: "删除演示路由模块,只保留核心模块(dashboard、system、result、exception)", color: theme.primary, }, { icon: icons.link, name: "路由常量", desc: "占位路径见 staticRoutes.ts 顶部(脚本不自动覆盖)", color: theme.info, }, { icon: icons.data, name: "Mock数据", desc: "演示用的JSON数据、文章列表、评论数据等", color: theme.success, }, { icon: icons.globe, name: "多语言文件", desc: "清理中英文语言包中的演示菜单项", color: theme.warning, }, { icon: icons.map, name: "地图组件", desc: "移除art-map-chart地图组件", color: theme.error }, { icon: icons.chat, name: "评论组件", desc: "移除comment-widget评论组件", color: theme.orange }, { icon: icons.bolt, name: "快速入口", desc: "移除分析页、礼花效果、聊天、更新日志、定价、留言管理等无效项目", color: theme.purple, }, ]; console.log(` ${fmt.badge("", theme.bgRed)} ${fmt.title("将要清理的内容")}`); console.log(); cleanupItems.forEach((item, index) => { console.log(` ${item.color}${theme.reset} ${fmt.highlight(`${index + 1}. ${item.name}`)}`); console.log(` ${fmt.dim(item.desc)}`); }); console.log(); console.log(` ${fmt.badge("", theme.bgGreen)} ${fmt.title("保留的功能模块")}`); console.log(); const preservedModules = [ { name: "Dashboard", desc: "工作台页面" }, { name: "System", desc: "系统管理模块" }, { name: "Result", desc: "结果页面" }, { name: "Exception", desc: "异常页面" }, { name: "Auth", desc: "登录注册功能" }, { name: "Core Components", desc: "核心组件库" }, ]; preservedModules.forEach((module) => { console.log(` ${icons.check} ${fmt.success(module.name)} ${fmt.dim(`- ${module.desc}`)}`); }); console.log(); createDivider(); console.log(); } // 显示统计信息 async function showStats() { const duration = Date.now() - stats.startTime; const seconds = (duration / 1000).toFixed(2); console.log(); createCard("清理统计", [ `${fmt.success("成功删除")}: ${fmt.highlight(stats.deletedFiles.toString())} 个文件`, `${fmt.info("涉及路径")}: ${fmt.highlight(stats.deletedPaths.toString())} 个目录/文件`, ...(stats.failedPaths > 0 ? [ `${icons.error} ${fmt.error("删除失败")}: ${fmt.highlight(stats.failedPaths.toString())} 个路径`, ] : []), `${fmt.info("耗时")}: ${fmt.highlight(seconds)} 秒`, ]); } // 创建成功横幅 function createSuccessBanner() { console.log(); console.log( fmt.gradient(" ╔══════════════════════════════════════════════════════════════════╗") ); console.log( fmt.gradient(" ║ ║") ); console.log( ` ║ ${icons.star} ${fmt.success("清理完成!项目已准备就绪")} ${icons.rocket} ║` ); console.log( ` ║ ${fmt.dim("现在可以开始您的开发之旅了!")} ║` ); console.log( fmt.gradient(" ║ ║") ); console.log( fmt.gradient(" ╚══════════════════════════════════════════════════════════════════╝") ); console.log(); } // 主函数 async function main() { // 清屏并显示横幅 console.clear(); createModernBanner(); // 显示清理警告 await showCleanupWarning(); // 统计文件数量 console.log(` ${fmt.info("正在统计文件数量...")}`); stats.totalFiles = await countAllFiles(); console.log(` ${fmt.info("即将清理")}: ${fmt.highlight(stats.totalFiles.toString())} 个文件`); console.log(` ${fmt.dim(`涉及 ${targets.length} 个目录/文件路径`)}`); console.log(); // 用户确认 const confirmed = await getUserConfirmation(); if (!confirmed) { console.log(` ${fmt.warning("操作已取消,清理中止")}`); console.log(); return; } console.log(); console.log(` ${icons.check} ${fmt.success("确认成功,开始清理...")}`); console.log(); // 开始清理过程 console.log(` ${fmt.badge("步骤 1/6", theme.bgBlue)} ${fmt.title("删除演示文件")}`); console.log(); for (let i = 0; i < targets.length; i++) { await remove(targets[i], i); } console.log(); console.log(` ${fmt.badge("步骤 2/6", theme.bgBlue)} ${fmt.title("清理路由模块")}`); console.log(); await cleanRouteModules(); console.log(); console.log(` ${fmt.badge("步骤 3/6", theme.bgBlue)} ${fmt.title("重写路由常量")}`); console.log(); await cleanRouterConstants(); console.log(); console.log(` ${fmt.badge("步骤 4/6", theme.bgBlue)} ${fmt.title("清空变更日志")}`); console.log(); await cleanChangeLog(); console.log(); console.log(` ${fmt.badge("步骤 5/6", theme.bgBlue)} ${fmt.title("清理语言文件")}`); console.log(); await cleanLanguageFiles(); console.log(); console.log(` ${fmt.badge("步骤 6/7", theme.bgBlue)} ${fmt.title("清理快速入口")}`); console.log(); await cleanFastEnterComponent(); console.log(); console.log(` ${fmt.badge("步骤 7/7", theme.bgBlue)} ${fmt.title("更新菜单接口")}`); console.log(); await updateMenuApi(); // 显示统计信息 await showStats(); // 显示成功横幅 createSuccessBanner(); } main().catch((err) => { console.log(); console.log(` ${icons.error} ${fmt.error("清理脚本执行出错")}`); console.log(` ${fmt.dim("错误详情: " + err)}`); console.log(); process.exit(1); });