/** * 将标准双 el-card 列表页迁移为 admin-page 架构组件 * 用法: node scripts/migrate-admin-pages.mjs [--dry-run] [glob...] */ import fs from 'node:fs' import path from 'node:path' import { fileURLToPath } from 'node:url' const __dirname = path.dirname(fileURLToPath(import.meta.url)) const viewsRoot = path.join(__dirname, '../src/views') const dryRun = process.argv.includes('--dry-run') const extraPaths = process.argv.slice(2).filter((a) => !a.startsWith('--')) const defaultTargets = [ 'article/lists/index.vue', 'article/column/index.vue', 'permission/role/index.vue', 'permission/menu/index.vue', 'dev_tools/code/index.vue', 'setting/dict/type/index.vue', 'setting/dict/data/index.vue', 'message/notice/index.vue', 'message/short_letter/index.vue', 'fans/index.vue', 'order/index.vue', 'asset/user/index.vue', 'asset/resource/index.vue', 'finance/balance_details.vue', 'finance/recharge_record.vue', 'finance/refund_record.vue' ] function migrate(content) { if (content.includes('admin-page-filter-panel')) return null if (!content.includes('el-card class="!border-none"')) return null if (!content.includes('\s*\n\s*/, '
\n ' ) if (!out.includes('admin-page-filter-panel')) { out = out.replace( //, '' ) } // 第一个 filter card 结束 out = out.replace( /<\/el-form>\s*\n\s*<\/el-card>/, '\n ' ) // 第二个 data card 开始 - 提取 v-loading const loadingMatch = out.match( /]*?)class="[^"]*mt-4[^"]*"[^>]*shadow="never"[^>]*>/ ) const altLoadingMatch = out.match( /]*)class="[^"]*mt-4[^"]*"([^>]*)shadow="never"([^>]*)>/ ) if (altLoadingMatch) { out = out.replace( altLoadingMatch[0], `` ) } else if (loadingMatch) { out = out.replace(loadingMatch[0], '') } else { out = out.replace( //, '' ) out = out.replace( //, '' ) out = out.replace( //, '' ) } // toolbar: 紧跟 data panel 后的首个 div 包裹按钮 out = out.replace( /(]*>\s*)
\s*\n(\s*\s*\n)/, '$1\n' ) out = out.replace( /(]*>\s*)
\s*\n(\s*\s*\n)/, '$1\n' ) // 移除 table 前的 mt-4 class out = out.replace(/\s*\n\s*\s*\n\s*<\/div>\s*\n\s*<\/el-card>/, '\n ' ) out = out.replace( /
\s*\n\s*\s*\n\s*<\/div>\s*\n\s*<\/el-card>/, '\n ' ) // 无 pagination 的 data card 闭合 if (out.includes('')) { out = out.replace(/<\/el-table>\s*\n\s*<\/el-card>/, '\n ') } // table 上的 v-loading 移到 panel(若 panel 还没有) out = out.replace( /\s*\n(\s*)]*?)v-loading="([^"]+)"([^>]*)>/, '\n$1' ) out = out.replace(/]*?)v-loading="([^"]+)"([^>]*)>/, '') if (out === content || !out.includes('admin-page-data-panel')) return null return out } const targets = extraPaths.length ? extraPaths.map((p) => path.resolve(p)) : defaultTargets.map((p) => path.join(viewsRoot, p)) let migrated = 0 let skipped = 0 for (const file of targets) { if (!fs.existsSync(file)) { console.warn('skip (missing):', path.relative(viewsRoot, file)) skipped++ continue } const original = fs.readFileSync(file, 'utf8') const result = migrate(original) if (!result) { console.log('skip (no match):', path.relative(viewsRoot, file)) skipped++ continue } if (dryRun) { console.log('would migrate:', path.relative(viewsRoot, file)) } else { fs.writeFileSync(file, result, 'utf8') console.log('migrated:', path.relative(viewsRoot, file)) } migrated++ } console.log(`\nDone. migrated=${migrated} skipped=${skipped}${dryRun ? ' (dry-run)' : ''}`)