/** * 切换 pages.json 到不同端的专属编译模式。 * * 用法: * npm run pages:consumer → 仅编译 consumer 相关页面 * npm run pages:merchant → 仅编译 merchant 相关页面 * npm run pages:admin → 仅编译 admin 相关页面 * npm run pages:full → 恢复完整 pages.json */ const fs = require("fs"); const path = require("path"); const root = path.resolve(__dirname, ".."); const pagesJson = path.join(root, "pages.json"); const pagesFull = path.join(root, "pages.full.json"); const backupDir = path.join(root, ".pages-backup"); const pagesModeFile = path.join(root, "utils", "pagesMode.uts"); const currentClientFile = path.join(root, "config", "current-client.uts"); const pageExtensions = [".uvue", ".vue", ".nvue"]; const consumerUserPages = [ "pages/user/boot", "pages/user/login", "pages/user/register", "pages/user/forgot-password", "pages/user/terms", "pages/user/center", "pages/user/profile", "pages/user/change-password", "pages/user/bind-phone", "pages/user/bind-email", ]; const merchantUserPages = [ "pages/user/login", "pages/user/boot", "pages/user/register", "pages/user/forgot-password", "pages/user/terms", "pages/user/change-password", ]; const adminUserPages = [ "pages/user/login", "pages/user/boot", "pages/user/forgot-password", "pages/user/terms", "pages/user/change-password", ]; const targetConfigs = { consumer: { fileName: "pages.consumer.json", label: "消费者端", entryPath: "pages/main/index", homeRedirect: "/pages/main/index", tabBarPath: "/pages/main/index", orderedTopLevelPaths: [ "pages/main/index", ...consumerUserPages, "pages/main/messages", "pages/main/cart", "pages/main/profile", ], topLevelMatcher(pagePath) { return ( pagePath.startsWith("pages/main/") || consumerUserPages.includes(pagePath) ); }, subPackageMatcher(rootPath) { return rootPath === "pages/mall/consumer"; }, tabBarMatcher(pagePath) { return pagePath.startsWith("pages/main/"); }, defaultCondition() { return { current: 0, list: [ { name: "consumer端", path: "pages/main/index", query: "role=consumer", }, ], }; }, }, merchant: { fileName: "pages.merchant.json", label: "商家端", entryPath: "pages/user/login", homeRedirect: "/pages/mall/merchant/index", tabBarPath: "", orderedTopLevelPaths: [ "pages/user/login", "pages/user/boot", "pages/user/register", "pages/user/forgot-password", "pages/user/terms", "pages/user/change-password", "pages/mall/merchant/index", "pages/mall/merchant/messages", "pages/mall/merchant/orders", "pages/mall/merchant/growth", "pages/mall/merchant/profile", ], topLevelMatcher(pagePath) { return ( merchantUserPages.includes(pagePath) || pagePath.startsWith("pages/mall/merchant/") ); }, subPackageMatcher() { return false; }, defaultCondition() { return { current: 0, list: [ { name: "merchant端", path: "pages/mall/merchant/index", query: "role=merchant", }, ], }; }, }, admin: { fileName: "pages.admin.json", label: "管理后台", entryPath: "pages/user/login", homeRedirect: "/pages/mall/admin/homePage/index", tabBarPath: "", orderedTopLevelPaths: [ "pages/user/login", "pages/user/boot", "pages/user/forgot-password", "pages/user/terms", "pages/user/change-password", "pages/mall/admin/homePage/index", "pages/mall/admin/userCenter/index", ], topLevelMatcher(pagePath) { return ( adminUserPages.includes(pagePath) || pagePath === "pages/mall/admin/homePage/index" || pagePath === "pages/mall/admin/userCenter/index" ); }, subPackageMatcher(rootPath) { return rootPath.startsWith("pages/mall/admin/"); }, defaultCondition() { return { current: 0, list: [ { name: "admin端", path: "pages/mall/admin/homePage/index", query: "role=admin", }, ], }; }, }, delivery: { fileName: "pages.delivery.json", label: "服务人员端/上门服务端", entryPath: "pages/user/login", homeRedirect: "/pages/mall/delivery/home/index", tabBarPath: "/pages/mall/delivery/home/index", standalone: true, }, }; const fullModeConfig = { homeRedirect: "/pages/main/index", tabBarPath: "/pages/main/index", }; const mode = process.argv[2]; // 'consumer' | 'merchant' | 'admin' | 'full' function readJson(filePath) { const rawContent = fs.readFileSync(filePath, "utf8").replace(/^\uFEFF/, ""); return JSON.parse(rawContent); } function writeJson(filePath, content) { fs.writeFileSync(filePath, `${JSON.stringify(content, null, 2)}\n`, "utf8"); } function writePagesMode(modeName) { fs.writeFileSync( pagesModeFile, `export const CURRENT_PAGES_MODE = '${modeName}'\n`, "utf8", ); } function writeCurrentClient(modeName) { fs.mkdirSync(path.dirname(currentClientFile), { recursive: true }); fs.writeFileSync( currentClientFile, `export const CURRENT_CLIENT: string = '${modeName}'\n`, "utf8", ); console.log(`🔁 已同步当前端标识 → ${modeName}`); } function ensureFullBackup() { if (fs.existsSync(pagesFull)) { return; } fs.copyFileSync(pagesJson, pagesFull); console.log("📦 已备份 pages.json → pages.full.json"); } function backupCurrentPages(targetName) { if (!fs.existsSync(pagesJson)) { return; } fs.mkdirSync(backupDir, { recursive: true }); const timestamp = new Date().toISOString().replace(/[.:]/g, "-"); const backupPath = path.join( backupDir, `pages.${targetName}.${timestamp}.json`, ); fs.copyFileSync(pagesJson, backupPath); console.log(`📦 已备份当前 pages.json → ${path.relative(root, backupPath)}`); } function getPageMap(fullConfig) { return new Map(fullConfig.pages.map((page) => [page.path, page])); } function findMatchingPageFile(routePath) { for (const extension of pageExtensions) { const candidate = path.join(root, `${routePath}${extension}`); if (fs.existsSync(candidate)) { return candidate; } } return null; } function clonePage(page) { return JSON.parse(JSON.stringify(page)); } function flattenSubPackagePages(fullConfig, rootPath) { const targetPackage = fullConfig.subPackages.find( (item) => item.root === rootPath, ); if (!targetPackage) { return []; } return targetPackage.pages.map((page) => ({ path: `${rootPath}/${page.path}`, style: page.style ? JSON.parse(JSON.stringify(page.style)) : {}, })); } function getTopLevelPages(fullConfig, targetConfig) { const pageMap = getPageMap(fullConfig); const matchedFromTopLevel = fullConfig.pages .filter((page) => targetConfig.topLevelMatcher(page.path)) .map((page) => clonePage(page)); const flattenedPages = (targetConfig.flattenRoots || []).flatMap((rootPath) => flattenSubPackagePages(fullConfig, rootPath), ); const mergedMap = new Map(); for (const page of [...matchedFromTopLevel, ...flattenedPages]) { mergedMap.set(page.path, page); } const orderedPages = []; for (const pagePath of targetConfig.orderedTopLevelPaths) { if (mergedMap.has(pagePath)) { orderedPages.push(mergedMap.get(pagePath)); mergedMap.delete(pagePath); } else if (pageMap.has(pagePath)) { orderedPages.push(clonePage(pageMap.get(pagePath))); } } for (const page of [...matchedFromTopLevel, ...flattenedPages]) { if (mergedMap.has(page.path)) { orderedPages.push(mergedMap.get(page.path)); mergedMap.delete(page.path); } } return dedupePages(orderedPages); } function getSubPackages(fullConfig, targetConfig) { return fullConfig.subPackages .filter((item) => targetConfig.subPackageMatcher(item.root)) .map((item) => JSON.parse(JSON.stringify(item))); } function dedupePages(pages) { const seen = new Set(); return pages.filter((page) => { if (seen.has(page.path)) { return false; } seen.add(page.path); return true; }); } function getCondition(targetConfig, sourceCondition) { if (sourceCondition && Array.isArray(sourceCondition.list)) { const filteredList = sourceCondition.list.filter( (item) => item.path === targetConfig.entryPath, ); if (filteredList.length > 0) { return { current: 0, list: filteredList, }; } } return targetConfig.defaultCondition(); } function validateGeneratedConfig(targetName, config) { const missingRoutes = []; const routeSet = new Set(); for (const page of config.pages || []) { routeSet.add(page.path); if (!findMatchingPageFile(page.path)) { missingRoutes.push(page.path); } } for (const subPackage of config.subPackages || []) { for (const page of subPackage.pages || []) { const fullPath = `${subPackage.root}/${page.path}`; routeSet.add(fullPath); if (!findMatchingPageFile(fullPath)) { missingRoutes.push(fullPath); } } } for (const tabItem of config.tabBar?.list || []) { if (!routeSet.has(tabItem.pagePath)) { missingRoutes.push(`tabBar -> ${tabItem.pagePath}`); } } for (const conditionItem of config.condition?.list || []) { if (!routeSet.has(conditionItem.path)) { missingRoutes.push(`condition -> ${conditionItem.path}`); } } if (missingRoutes.length > 0) { console.error(`❌ ${targetName} 配置存在无效页面引用:`); for (const routePath of missingRoutes) { console.error(` - ${routePath}`); } process.exit(1); } } function buildTargetConfig(fullConfig, targetName, sourceCondition) { const targetConfig = targetConfigs[targetName]; const nextConfig = {}; nextConfig.pages = getTopLevelPages(fullConfig, targetConfig); nextConfig.subPackages = getSubPackages(fullConfig, targetConfig); if (targetConfig.tabBarMatcher) { const filteredTabBarList = (fullConfig.tabBar?.list || []).filter((item) => targetConfig.tabBarMatcher(item.pagePath), ); if (filteredTabBarList.length > 0 && fullConfig.tabBar) { nextConfig.tabBar = { ...JSON.parse(JSON.stringify(fullConfig.tabBar)), list: filteredTabBarList, }; } } for (const key of Object.keys(fullConfig)) { if (["pages", "subPackages", "tabBar", "condition"].includes(key)) { continue; } nextConfig[key] = JSON.parse(JSON.stringify(fullConfig[key])); } nextConfig.condition = getCondition(targetConfig, sourceCondition); validateGeneratedConfig(targetName, nextConfig); return nextConfig; } function writeTargetConfig(targetName, config) { const targetPath = path.join(root, targetConfigs[targetName].fileName); writeJson(targetPath, config); fs.copyFileSync(targetPath, pagesJson); writePagesMode(targetName); writeCurrentClient(targetName); const pageCount = config.pages.length; const subPackageCount = config.subPackages.length; const tabBarCount = config.tabBar?.list?.length || 0; console.log(`✅ 已切换为【${targetConfigs[targetName].label}专属编译模式】`); console.log(` 目标配置:${targetConfigs[targetName].fileName}`); console.log(` pages:${pageCount} 个`); console.log(` subPackages:${subPackageCount} 个`); console.log(` tabBar:${tabBarCount} 个`); console.log(" 恢复完整版本:npm run pages:full"); } function switchStandaloneConfig(targetName) { const targetPath = path.join(root, targetConfigs[targetName].fileName); if (!fs.existsSync(targetPath)) { console.error(`❌ ${targetConfigs[targetName].fileName} 不存在。`); process.exit(1); } const config = readJson(targetPath); validateGeneratedConfig(targetName, config); fs.copyFileSync(targetPath, pagesJson); writePagesMode(targetName); writeCurrentClient(targetName); const pageCount = config.pages != null ? config.pages.length : 0; const subPackageCount = config.subPackages != null ? config.subPackages.length : 0; const tabBarCount = config.tabBar != null && Array.isArray(config.tabBar.list) ? config.tabBar.list.length : 0; console.log(`✅ 已切换为【${targetConfigs[targetName].label}专属编译模式】`); console.log(` 目标配置:${targetConfigs[targetName].fileName}`); console.log(` pages:${pageCount} 个`); console.log(` subPackages:${subPackageCount} 个`); console.log(` tabBar:${tabBarCount} 个`); console.log(" 恢复完整版本:npm run pages:full"); } function restoreFullPages() { if (!fs.existsSync(pagesFull)) { console.error("❌ pages.full.json 不存在。"); console.error(" 可能当前已经是完整模式,或备份文件已被删除。"); process.exit(1); } backupCurrentPages("full"); fs.copyFileSync(pagesFull, pagesJson); writePagesMode("full"); writeCurrentClient("full"); console.log("✅ 已恢复【完整 pages.json】"); console.log(" 包含 consumer + merchant + admin 全部页面。"); } if (mode === "full") { restoreFullPages(); } else if (Object.prototype.hasOwnProperty.call(targetConfigs, mode)) { ensureFullBackup(); backupCurrentPages(mode); if (targetConfigs[mode].standalone === true) { switchStandaloneConfig(mode); process.exit(0); } const fullConfig = readJson(pagesFull); const sourceCondition = fs.existsSync(pagesJson) ? (() => { try { return readJson(pagesJson).condition; } catch (error) { return null; } })() : null; const nextConfig = buildTargetConfig(fullConfig, mode, sourceCondition); writeTargetConfig(mode, nextConfig); } else { console.log("用法:"); console.log(" npm run pages:consumer 切换到消费者端专属编译模式"); console.log(" npm run pages:delivery 切换到服务人员端专属编译模式"); console.log(" npm run pages:merchant 切换到商家端专属编译模式"); console.log(" npm run pages:admin 切换到管理后台专属编译模式"); console.log(" npm run pages:full 恢复完整编译模式"); }