You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

713 lines
21 KiB
TypeScript

This file contains invisible Unicode characters!

This file contains invisible Unicode characters that may be processed differently from what appears below. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to reveal hidden characters.

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

// 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<number> {
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<number> {
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<UpgradeLog[]>([])
`;
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<boolean> {
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);
});