159 lines
5.5 KiB
JavaScript
159 lines
5.5 KiB
JavaScript
/**
|
|
* 将标准双 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('<el-table')) return null
|
|
|
|
let out = content
|
|
|
|
// 根容器 class 清理
|
|
out = out.replace(
|
|
/<div class="[^"]*">\s*\n\s*<el-card class="!border-none" shadow="never">/,
|
|
'<div>\n <admin-page-filter-panel>'
|
|
)
|
|
if (!out.includes('admin-page-filter-panel')) {
|
|
out = out.replace(
|
|
/<el-card class="!border-none" shadow="never">/,
|
|
'<admin-page-filter-panel>'
|
|
)
|
|
}
|
|
|
|
// 第一个 filter card 结束
|
|
out = out.replace(
|
|
/<\/el-form>\s*\n\s*<\/el-card>/,
|
|
'</el-form>\n </admin-page-filter-panel>'
|
|
)
|
|
|
|
// 第二个 data card 开始 - 提取 v-loading
|
|
const loadingMatch = out.match(
|
|
/<el-card([^>]*?)class="[^"]*mt-4[^"]*"[^>]*shadow="never"[^>]*>/
|
|
)
|
|
const altLoadingMatch = out.match(
|
|
/<el-card v-loading="([^"]+)"([^>]*)class="[^"]*mt-4[^"]*"([^>]*)shadow="never"([^>]*)>/
|
|
)
|
|
|
|
if (altLoadingMatch) {
|
|
out = out.replace(
|
|
altLoadingMatch[0],
|
|
`<admin-page-data-panel v-loading="${altLoadingMatch[1]}">`
|
|
)
|
|
} else if (loadingMatch) {
|
|
out = out.replace(loadingMatch[0], '<admin-page-data-panel>')
|
|
} else {
|
|
out = out.replace(
|
|
/<el-card class="!border-none mt-4" shadow="never">/,
|
|
'<admin-page-data-panel>'
|
|
)
|
|
out = out.replace(
|
|
/<el-card v-loading="([^"]+)" class="mt-4 !border-none" shadow="never">/,
|
|
'<admin-page-data-panel v-loading="$1">'
|
|
)
|
|
out = out.replace(
|
|
/<el-card v-loading="([^"]+)" class="!border-none mt-4" shadow="never">/,
|
|
'<admin-page-data-panel v-loading="$1">'
|
|
)
|
|
}
|
|
|
|
// toolbar: 紧跟 data panel 后的首个 div 包裹按钮
|
|
out = out.replace(
|
|
/(<admin-page-data-panel[^>]*>\s*)<div>\s*\n(\s*<el-button[\s\S]*?<\/div>\s*\n)/,
|
|
'$1<template #toolbar>\n$2</template>\n'
|
|
)
|
|
out = out.replace(
|
|
/(<admin-page-data-panel[^>]*>\s*)<div>\s*\n(\s*<router-link[\s\S]*?<\/div>\s*\n)/,
|
|
'$1<template #toolbar>\n$2</template>\n'
|
|
)
|
|
|
|
// 移除 table 前的 mt-4 class
|
|
out = out.replace(/<el-table class="mt-4"/g, '<el-table')
|
|
out = out.replace(/<el-table\s+class="mt-4"\s+/g, '<el-table ')
|
|
|
|
// pagination footer
|
|
out = out.replace(
|
|
/<div class="flex(?: mt-4)? justify-end(?: mt-4)?">\s*\n\s*<pagination([^/]*)\/>\s*\n\s*<\/div>\s*\n\s*<\/el-card>/,
|
|
'<template #footer>\n <pagination$1/>\n </template>\n </admin-page-data-panel>'
|
|
)
|
|
out = out.replace(
|
|
/<div class="flex mt-4 justify-end">\s*\n\s*<pagination([^/]*)\/>\s*\n\s*<\/div>\s*\n\s*<\/el-card>/,
|
|
'<template #footer>\n <pagination$1/>\n </template>\n </admin-page-data-panel>'
|
|
)
|
|
|
|
// 无 pagination 的 data card 闭合
|
|
if (out.includes('<admin-page-data-panel') && out.includes('</el-card>')) {
|
|
out = out.replace(/<\/el-table>\s*\n\s*<\/el-card>/, '</el-table>\n </admin-page-data-panel>')
|
|
}
|
|
|
|
// table 上的 v-loading 移到 panel(若 panel 还没有)
|
|
out = out.replace(
|
|
/<admin-page-data-panel>\s*\n(\s*)<el-table([^>]*?)v-loading="([^"]+)"([^>]*)>/,
|
|
'<admin-page-data-panel v-loading="$3">\n$1<el-table$2$4>'
|
|
)
|
|
out = out.replace(/<el-table([^>]*?)v-loading="([^"]+)"([^>]*)>/, '<el-table$1$3>')
|
|
|
|
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)' : ''}`)
|