提交
This commit is contained in:
1 parent
594e4ce38e
commit
e233269abf
144 files changed
+228178
-111974
No files matched your search
@@ -0,0 +1,188 @@
|
||||
/**
|
||||
* 将带 class="list-table" 的 <el-table> 批量替换为 <ListTable table-key="...">。
|
||||
* 同文件多表自动追加 -2/-3;不会误匹配 el-table-column。
|
||||
*
|
||||
* Usage: node scripts/migrate-list-table.mjs
|
||||
*/
|
||||
import fs from 'node:fs'
|
||||
import path from 'node:path'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url))
|
||||
const srcRoot = path.resolve(__dirname, '../src')
|
||||
const viewsRoot = path.resolve(srcRoot, 'views')
|
||||
const componentsRoot = path.resolve(srcRoot, 'components')
|
||||
|
||||
/** 匹配真正的 el-table 开标签,排除 el-table-column / el-table-column-group 等 */
|
||||
const OPEN_RE = /<el-table(?=[\s/>])/g
|
||||
const CLOSE_TAG = '</el-table>'
|
||||
|
||||
function walk(dir, out = []) {
|
||||
if (!fs.existsSync(dir)) return out
|
||||
for (const name of fs.readdirSync(dir)) {
|
||||
const full = path.join(dir, name)
|
||||
const st = fs.statSync(full)
|
||||
if (st.isDirectory()) {
|
||||
// 跳过 ListTable 自身,避免把内部 el-table 替换掉
|
||||
if (full.endsWith(`${path.sep}list-table`)) continue
|
||||
walk(full, out)
|
||||
} else if (name.endsWith('.vue')) out.push(full)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
function toKebab(s) {
|
||||
return String(s)
|
||||
.replace(/([a-z0-9])([A-Z])/g, '$1-$2')
|
||||
.replace(/[_\s]+/g, '-')
|
||||
.replace(/-+/g, '-')
|
||||
.toLowerCase()
|
||||
}
|
||||
|
||||
function baseKeyFromFile(file) {
|
||||
let rel
|
||||
if (file.startsWith(viewsRoot + path.sep) || file.startsWith(viewsRoot + '/')) {
|
||||
rel = path.relative(viewsRoot, file)
|
||||
} else {
|
||||
rel = path.relative(componentsRoot, file)
|
||||
}
|
||||
const parts = rel.split(path.sep)
|
||||
const stem = path.basename(file, '.vue')
|
||||
const dirs = parts.slice(0, -1).map(toKebab)
|
||||
if (!['index', 'Index'].includes(stem)) dirs.push(toKebab(stem))
|
||||
return dirs.filter(Boolean).join('-') || toKebab(stem)
|
||||
}
|
||||
|
||||
function hasListTableClass(attrText) {
|
||||
return /class\s*=\s*(['"`])([^'"`]*\blist-table\b[^'"`]*)\1/.test(attrText)
|
||||
}
|
||||
|
||||
function findOpenTagEnd(content, nameEnd) {
|
||||
let j = nameEnd
|
||||
let quote = null
|
||||
while (j < content.length) {
|
||||
const ch = content[j]
|
||||
if (quote) {
|
||||
if (ch === quote) quote = null
|
||||
} else if (ch === '"' || ch === "'" || ch === '`') {
|
||||
quote = ch
|
||||
} else if (ch === '>') {
|
||||
return j
|
||||
}
|
||||
j++
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
function transform(content, file) {
|
||||
const baseKey = baseKeyFromFile(file)
|
||||
let tableIndex = 0
|
||||
let i = 0
|
||||
let out = ''
|
||||
|
||||
while (i < content.length) {
|
||||
OPEN_RE.lastIndex = i
|
||||
const m = OPEN_RE.exec(content)
|
||||
if (!m) {
|
||||
out += content.slice(i)
|
||||
break
|
||||
}
|
||||
|
||||
out += content.slice(i, m.index)
|
||||
const start = m.index
|
||||
const nameEnd = start + '<el-table'.length
|
||||
const openEnd = findOpenTagEnd(content, nameEnd)
|
||||
if (openEnd < 0) {
|
||||
out += content.slice(start)
|
||||
break
|
||||
}
|
||||
|
||||
const attrText = content.slice(nameEnd, openEnd)
|
||||
const selfClosing = content[openEnd - 1] === '/'
|
||||
const isList = hasListTableClass(attrText)
|
||||
|
||||
if (!isList) {
|
||||
out += content.slice(start, openEnd + 1)
|
||||
i = openEnd + 1
|
||||
continue
|
||||
}
|
||||
|
||||
tableIndex += 1
|
||||
const key = tableIndex === 1 ? baseKey : `${baseKey}-${tableIndex}`
|
||||
let newAttrs = attrText
|
||||
if (!/\btable-key\s*=/.test(newAttrs) && !/\btableKey\s*=/.test(newAttrs)) {
|
||||
newAttrs = ` table-key="${key}"` + newAttrs
|
||||
}
|
||||
|
||||
if (selfClosing) {
|
||||
out += `<ListTable${newAttrs.slice(0, -1)} />`
|
||||
i = openEnd + 1
|
||||
continue
|
||||
}
|
||||
|
||||
out += `<ListTable${newAttrs}>`
|
||||
i = openEnd + 1
|
||||
|
||||
let depth = 1
|
||||
while (i < content.length && depth > 0) {
|
||||
OPEN_RE.lastIndex = i
|
||||
const nextOpen = OPEN_RE.exec(content)
|
||||
const nextCloseIdx = content.indexOf(CLOSE_TAG, i)
|
||||
|
||||
if (nextCloseIdx < 0) {
|
||||
out += content.slice(i)
|
||||
i = content.length
|
||||
depth = 0
|
||||
break
|
||||
}
|
||||
|
||||
const openIdx = nextOpen ? nextOpen.index : -1
|
||||
if (openIdx >= 0 && openIdx < nextCloseIdx) {
|
||||
// 嵌套 el-table:原样拷贝开标签,depth++
|
||||
out += content.slice(i, openIdx)
|
||||
const nestedNameEnd = openIdx + '<el-table'.length
|
||||
const nestedOpenEnd = findOpenTagEnd(content, nestedNameEnd)
|
||||
if (nestedOpenEnd < 0) {
|
||||
out += content.slice(openIdx)
|
||||
i = content.length
|
||||
depth = 0
|
||||
break
|
||||
}
|
||||
out += content.slice(openIdx, nestedOpenEnd + 1)
|
||||
i = nestedOpenEnd + 1
|
||||
if (content[nestedOpenEnd - 1] !== '/') depth += 1
|
||||
continue
|
||||
}
|
||||
|
||||
// 关闭标签
|
||||
out += content.slice(i, nextCloseIdx)
|
||||
depth -= 1
|
||||
if (depth === 0) {
|
||||
out += '</ListTable>'
|
||||
} else {
|
||||
out += CLOSE_TAG
|
||||
}
|
||||
i = nextCloseIdx + CLOSE_TAG.length
|
||||
}
|
||||
}
|
||||
|
||||
return { content: out, count: tableIndex }
|
||||
}
|
||||
|
||||
const files = [...walk(viewsRoot), ...walk(path.join(componentsRoot, 'business'))]
|
||||
let total = 0
|
||||
const changed = []
|
||||
|
||||
for (const file of files) {
|
||||
const raw = fs.readFileSync(file, 'utf8')
|
||||
if (!raw.includes('list-table') || !raw.includes('<el-table')) continue
|
||||
const { content, count } = transform(raw, file)
|
||||
if (count > 0 && content !== raw) {
|
||||
fs.writeFileSync(file, content)
|
||||
total += count
|
||||
changed.push(`${path.relative(path.resolve(__dirname, '..'), file)} (${count})`)
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`Migrated ${total} tables in ${changed.length} files:`)
|
||||
for (const line of changed) console.log(' -', line)
|
||||
Reference in new issue
Block a user