mirror of
https://gitee.com/beijing_hongye_huicheng/lilishop-ui.git
synced 2026-06-22 18:10:26 +08:00
升级Vue3,iView替换ElementPlus
- 删除babel配置、更新依赖与入口初始化 - 全量替换UI组件、样式适配,新增迁移文档与标签/过滤器自动化替换脚本
This commit is contained in:
54
buyer/scripts/fix-broken-tags.js
Normal file
54
buyer/scripts/fix-broken-tags.js
Normal file
@@ -0,0 +1,54 @@
|
||||
#!/usr/bin/env node
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
|
||||
const ROOT = path.join(__dirname, "..");
|
||||
|
||||
function walkDir(dir, files = []) {
|
||||
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
||||
const full = path.join(dir, entry.name);
|
||||
if (entry.isDirectory()) walkDir(full, files);
|
||||
else if (entry.name.endsWith(".vue")) files.push(full);
|
||||
}
|
||||
return files;
|
||||
}
|
||||
|
||||
function fix(content) {
|
||||
let c = content;
|
||||
c = c.replace(/<\/Button>/g, "</el-button>");
|
||||
c = c.replace(/<\/Button\b/g, "</el-button");
|
||||
c = c.replace(/<\/Input>/g, "</el-input>");
|
||||
c = c.replace(/<\/Input\b/g, "</el-input");
|
||||
c = c.replace(/<\/Option>/g, "</el-option>");
|
||||
c = c.replace(/<\/Option\b/g, "</el-option");
|
||||
c = c.replace(/<\/Checkbox>/g, "</el-checkbox>");
|
||||
c = c.replace(/<\/Checkbox\b/g, "</el-checkbox");
|
||||
c = c.replace(/<\/Select>/g, "</el-select>");
|
||||
c = c.replace(/<\/Select\b/g, "</el-select");
|
||||
c = c.replace(/<\/Form>/g, "</el-form>");
|
||||
c = c.replace(/<\/Modal>/g, "</el-dialog>");
|
||||
c = c.replace(/@click\.native/g, "@click");
|
||||
c = c.replace(/@on-search/g, "@keyup.enter");
|
||||
c = c.replace(/\bsearch\b(?=\s+enter-button)/g, "");
|
||||
c = c.replace(/\benter-button\b/g, "");
|
||||
c = c.replace(/<BreadcrumbItem/g, "<el-breadcrumb-item");
|
||||
c = c.replace(/<\/BreadcrumbItem>/g, "</el-breadcrumb-item>");
|
||||
c = c.replace(/<Breadcrumb/g, "<el-breadcrumb");
|
||||
c = c.replace(/<\/Breadcrumb>/g, "</el-breadcrumb>");
|
||||
c = c.replace(/type="error"/g, 'type="danger"');
|
||||
c = c.replace(/<i-col/g, "<div");
|
||||
c = c.replace(/<\/i-col>/g, "</div>");
|
||||
c = c.replace(/\{\{([^}|]+)\s*\|\s*secrecyMobile\s*\}\}/g, "{{ $filters.secrecyMobile($1) }}");
|
||||
return c;
|
||||
}
|
||||
|
||||
let count = 0;
|
||||
for (const file of walkDir(path.join(ROOT, "src"))) {
|
||||
const original = fs.readFileSync(file, "utf8");
|
||||
const fixed = fix(original);
|
||||
if (fixed !== original) {
|
||||
fs.writeFileSync(file, fixed);
|
||||
count++;
|
||||
}
|
||||
}
|
||||
console.log(`Fixed tags in ${count} files`);
|
||||
52
buyer/scripts/fix-dialog-slots.js
Normal file
52
buyer/scripts/fix-dialog-slots.js
Normal file
@@ -0,0 +1,52 @@
|
||||
#!/usr/bin/env node
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
|
||||
const ROOT = path.join(__dirname, "..");
|
||||
|
||||
function walkDir(dir, files = []) {
|
||||
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
||||
const full = path.join(dir, entry.name);
|
||||
if (entry.isDirectory()) walkDir(full, files);
|
||||
else if (entry.name.endsWith(".vue")) files.push(full);
|
||||
}
|
||||
return files;
|
||||
}
|
||||
|
||||
function fix(content) {
|
||||
let c = content;
|
||||
|
||||
c = c.replace(/<p #header>/g, "<template #header><p>");
|
||||
c = c.replace(
|
||||
/(<template #header><p>[\s\S]*?<\/p>)(\s*<div)/g,
|
||||
"$1</template>$2"
|
||||
);
|
||||
|
||||
c = c.replace(
|
||||
/<template #footer><div([^>]*)>([\s\S]*?)<\/div><\/template><\/el-dialog>/g,
|
||||
"<template #footer><div$1>$2</div></template>\n </el-dialog>"
|
||||
);
|
||||
|
||||
c = c.replace(
|
||||
/<template #default\s*>([\s\S]*?)<\/template\s*>/g,
|
||||
(match, inner) => {
|
||||
if (match.includes("el-alert")) {
|
||||
return `<template #title>${inner}</template>`;
|
||||
}
|
||||
return match;
|
||||
}
|
||||
);
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
let count = 0;
|
||||
for (const file of walkDir(path.join(ROOT, "src"))) {
|
||||
const original = fs.readFileSync(file, "utf8");
|
||||
const fixed = fix(original);
|
||||
if (fixed !== original) {
|
||||
fs.writeFileSync(file, fixed);
|
||||
count++;
|
||||
}
|
||||
}
|
||||
console.log(`Fixed dialog slots in ${count} files`);
|
||||
26
buyer/scripts/fix-duplicate-size.js
Normal file
26
buyer/scripts/fix-duplicate-size.js
Normal file
@@ -0,0 +1,26 @@
|
||||
#!/usr/bin/env node
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
|
||||
const ROOT = path.join(__dirname, "..", "src");
|
||||
|
||||
function walk(dir, files = []) {
|
||||
for (const e of fs.readdirSync(dir, { withFileTypes: true })) {
|
||||
const f = path.join(dir, e.name);
|
||||
if (e.isDirectory()) walk(f, files);
|
||||
else if (e.name.endsWith(".vue")) files.push(f);
|
||||
}
|
||||
return files;
|
||||
}
|
||||
|
||||
let n = 0;
|
||||
for (const file of walk(ROOT)) {
|
||||
let c = fs.readFileSync(file, "utf8");
|
||||
const next = c.replace(/:size="(\d+)"\s+:size="\1"/g, ':size="$1"');
|
||||
if (next !== c) {
|
||||
fs.writeFileSync(file, next);
|
||||
n++;
|
||||
console.log(path.relative(ROOT, file));
|
||||
}
|
||||
}
|
||||
console.log(`Fixed duplicate :size in ${n} files`);
|
||||
28
buyer/scripts/fix-el-icon-click.js
Normal file
28
buyer/scripts/fix-el-icon-click.js
Normal file
@@ -0,0 +1,28 @@
|
||||
#!/usr/bin/env node
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
|
||||
const ROOT = path.join(__dirname, "..", "src");
|
||||
|
||||
function walk(dir, files = []) {
|
||||
for (const e of fs.readdirSync(dir, { withFileTypes: true })) {
|
||||
const f = path.join(dir, e.name);
|
||||
if (e.isDirectory()) walk(f, files);
|
||||
else if (e.name.endsWith(".vue")) files.push(f);
|
||||
}
|
||||
return files;
|
||||
}
|
||||
|
||||
let n = 0;
|
||||
for (const file of walk(ROOT)) {
|
||||
let c = fs.readFileSync(file, "utf8");
|
||||
const next = c
|
||||
.replace(/<el-icon([^>]*?):click=/g, "<el-icon$1@click=")
|
||||
.replace(/:class="refresh"/g, 'class="refresh"')
|
||||
.replace(/:size="20" @click="show = false" :size="20"/g, ':size="20" @click="show = false"');
|
||||
if (next !== c) {
|
||||
fs.writeFileSync(file, next);
|
||||
n++;
|
||||
}
|
||||
}
|
||||
console.log(`Fixed el-icon click in ${n} files`);
|
||||
77
buyer/scripts/fix-filters.js
Normal file
77
buyer/scripts/fix-filters.js
Normal file
@@ -0,0 +1,77 @@
|
||||
#!/usr/bin/env node
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
|
||||
const ROOT = path.join(__dirname, "..");
|
||||
|
||||
function walkDir(dir, files = []) {
|
||||
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
||||
const full = path.join(dir, entry.name);
|
||||
if (entry.isDirectory()) walkDir(full, files);
|
||||
else if (entry.name.endsWith(".vue")) files.push(full);
|
||||
}
|
||||
return files;
|
||||
}
|
||||
|
||||
function fixFilters(content) {
|
||||
let c = content;
|
||||
|
||||
// Fix broken: {{ expr {{ $filters.unitPrice(args) }}
|
||||
c = c.replace(
|
||||
/\{\{\s*([^}{]+?)\s*\{\{\s*\$filters\.unitPrice\(([^)]*)\)\s*\}\}/g,
|
||||
"{{ $filters.unitPrice($1, $2) }}"
|
||||
);
|
||||
|
||||
// Fix: {{ expr | unitPrice }} or {{ expr | unitPrice('¥') }}
|
||||
c = c.replace(
|
||||
/\{\{\s*([^}|]+?)\s*\|\s*unitPrice(?:\(([^)]*)\))?\s*\}\}/g,
|
||||
(_, expr, args) => {
|
||||
const e = expr.trim();
|
||||
return args !== undefined
|
||||
? `{{ $filters.unitPrice(${e}, ${args}) }}`
|
||||
: `{{ $filters.unitPrice(${e}) }}`;
|
||||
}
|
||||
);
|
||||
|
||||
// Fix inline without braces: expr | unitPrice("¥")
|
||||
c = c.replace(
|
||||
/(?<!\{\{)\s*([a-zA-Z0-9_.?()[\]'"\s]+?)\s*\|\s*unitPrice\(([^)]*)\)/g,
|
||||
(match, expr, args) => {
|
||||
if (match.includes("{{")) return match;
|
||||
return `{{ $filters.unitPrice(${expr.trim()}, ${args}) }}`;
|
||||
}
|
||||
);
|
||||
|
||||
// Fix inline without args: expr | unitPrice
|
||||
c = c.replace(
|
||||
/(?<!\{\{)\s*([a-zA-Z0-9_.?()[\]'"\s]+?)\s*\|\s*unitPrice(?!\()/g,
|
||||
(match, expr) => {
|
||||
if (match.includes("{{") || match.includes("$filters")) return match;
|
||||
return `{{ $filters.unitPrice(${expr.trim()}) }}`;
|
||||
}
|
||||
);
|
||||
|
||||
// Fix unixToDate filter
|
||||
c = c.replace(
|
||||
/\{\{\s*([^}|]+?)\s*\|\s*unixToDate(?:\(([^)]*)\))?\s*\}\}/g,
|
||||
(_, expr, args) => {
|
||||
const e = expr.trim();
|
||||
return args !== undefined
|
||||
? `{{ $filters.unixToDate(${e}, ${args}) }}`
|
||||
: `{{ $filters.unixToDate(${e}) }}`;
|
||||
}
|
||||
);
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
let count = 0;
|
||||
for (const file of walkDir(path.join(ROOT, "src"))) {
|
||||
const original = fs.readFileSync(file, "utf8");
|
||||
const fixed = fixFilters(original);
|
||||
if (fixed !== original) {
|
||||
fs.writeFileSync(file, fixed);
|
||||
count++;
|
||||
}
|
||||
}
|
||||
console.log(`Fixed filters in ${count} files`);
|
||||
55
buyer/scripts/fix-runtime-compat.js
Normal file
55
buyer/scripts/fix-runtime-compat.js
Normal file
@@ -0,0 +1,55 @@
|
||||
#!/usr/bin/env node
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
|
||||
const ROOT = path.join(__dirname, "..");
|
||||
|
||||
function walkDir(dir, files = []) {
|
||||
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
||||
const full = path.join(dir, entry.name);
|
||||
if (entry.isDirectory()) walkDir(full, files);
|
||||
else if (entry.name.endsWith(".vue")) files.push(full);
|
||||
}
|
||||
return files;
|
||||
}
|
||||
|
||||
function fix(content) {
|
||||
let c = content;
|
||||
|
||||
c = c.replace(/<el-pagination\b/g, "<Page");
|
||||
c = c.replace(/<\/el-pagination>/g, "</Page>");
|
||||
|
||||
c = c.replace(
|
||||
/<Icon([^>]*)\s+#prepend\s*>\s*<\/Icon>/g,
|
||||
"<template #prepend><Icon$1></Icon></template>"
|
||||
);
|
||||
c = c.replace(
|
||||
/<Icon([^>]*)\s+#prepend\s*>\s*<\/Icon>/g,
|
||||
"<template #prepend><Icon$1></Icon></template>"
|
||||
);
|
||||
c = c.replace(
|
||||
/<template #prepend><Icon([^>]*)\s*\/>/g,
|
||||
"<template #prepend><Icon$1></Icon></template>"
|
||||
);
|
||||
|
||||
c = c.replace(/@keyup\.enter\.native/g, "@keyup.enter");
|
||||
c = c.replace(/slot="append"/g, "#append");
|
||||
c = c.replace(/(<div[^>]*)\s+#append/g, "<template #append>$1");
|
||||
|
||||
c = c.replace(/(<el-button[^>]*)\slong/g, '$1 style="width:100%"');
|
||||
c = c.replace(/@on-select/g, "@select");
|
||||
c = c.replace(/@on-click/g, "@click");
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
let count = 0;
|
||||
for (const file of walkDir(path.join(ROOT, "src"))) {
|
||||
const original = fs.readFileSync(file, "utf8");
|
||||
const fixed = fix(original);
|
||||
if (fixed !== original) {
|
||||
fs.writeFileSync(file, fixed);
|
||||
count++;
|
||||
}
|
||||
}
|
||||
console.log(`Runtime compat fixes in ${count} files`);
|
||||
54
buyer/scripts/fix-slots.js
Normal file
54
buyer/scripts/fix-slots.js
Normal file
@@ -0,0 +1,54 @@
|
||||
#!/usr/bin/env node
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
|
||||
const ROOT = path.join(__dirname, "..");
|
||||
|
||||
function walkDir(dir, files = []) {
|
||||
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
||||
const full = path.join(dir, entry.name);
|
||||
if (entry.isDirectory()) walkDir(full, files);
|
||||
else if (entry.name.endsWith(".vue")) files.push(full);
|
||||
}
|
||||
return files;
|
||||
}
|
||||
|
||||
function fix(content) {
|
||||
let c = content;
|
||||
|
||||
c = c.replace(
|
||||
/<template #default="(\{[^"]+\})" #([a-zA-Z0-9_-]+)>/g,
|
||||
'<template #$2="$1">'
|
||||
);
|
||||
|
||||
c = c.replace(
|
||||
/<div([^>]*) #footer([^>]*)>/g,
|
||||
'<template #footer><div$1$2>'
|
||||
);
|
||||
c = c.replace(/<\/div>\s*(?=<\/el-dialog>)/g, "</div></template>");
|
||||
|
||||
c = c.replace(/<span #append>/g, "<template #append><span>");
|
||||
c = c.replace(/<\/span><\/el-input/g, "</span></template></el-input");
|
||||
c = c.replace(/><span #append>/g, "><template #append><span>");
|
||||
|
||||
c = c.replace(/<el-button #append/g, "<template #append><el-button");
|
||||
c = c.replace(
|
||||
/<\/el-button>(\s*<\/el-input>)/g,
|
||||
"</el-button></template>$1"
|
||||
);
|
||||
|
||||
c = c.replace(/<template #desc\b/g, "<template #default");
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
let count = 0;
|
||||
for (const file of walkDir(path.join(ROOT, "src"))) {
|
||||
const original = fs.readFileSync(file, "utf8");
|
||||
const fixed = fix(original);
|
||||
if (fixed !== original) {
|
||||
fs.writeFileSync(file, fixed);
|
||||
count++;
|
||||
}
|
||||
}
|
||||
console.log(`Fixed slots in ${count} files`);
|
||||
46
buyer/scripts/fix-span-tags.js
Normal file
46
buyer/scripts/fix-span-tags.js
Normal file
@@ -0,0 +1,46 @@
|
||||
#!/usr/bin/env node
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
|
||||
const ROOT = path.join(__dirname, "..");
|
||||
|
||||
function walkDir(dir, files = []) {
|
||||
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
||||
const full = path.join(dir, entry.name);
|
||||
if (entry.isDirectory()) walkDir(full, files);
|
||||
else if (entry.name.endsWith(".vue")) files.push(full);
|
||||
}
|
||||
return files;
|
||||
}
|
||||
|
||||
function fix(content) {
|
||||
let c = content;
|
||||
c = c.replace(/<\/span\s*\n\s*>/g, "</span>");
|
||||
c = c.replace(/<\/span\s+>/g, "</span>");
|
||||
c = c.replace(/<i-input/g, "<el-input");
|
||||
c = c.replace(/<\/i-input>/g, "</el-input>");
|
||||
c = c.replace(
|
||||
/<Icon([^>]*)\s+#prepend\s*\/>/g,
|
||||
"<template #prepend><Icon$1 /></template>"
|
||||
);
|
||||
c = c.replace(
|
||||
/<template #append><el-button([^>]*)>([\s\S]*?)<\/el-button>\s*<\/i-input>/g,
|
||||
"<template #append><el-button$1>$2</el-button></template></el-input>"
|
||||
);
|
||||
c = c.replace(
|
||||
/<template #append><el-button([^>]*)>([\s\S]*?)<\/el-button>\s*<\/el-input>/g,
|
||||
"<template #append><el-button$1>$2</el-button></template></el-input>"
|
||||
);
|
||||
return c;
|
||||
}
|
||||
|
||||
let count = 0;
|
||||
for (const file of walkDir(path.join(ROOT, "src"))) {
|
||||
const original = fs.readFileSync(file, "utf8");
|
||||
const fixed = fix(original);
|
||||
if (fixed !== original) {
|
||||
fs.writeFileSync(file, fixed);
|
||||
count++;
|
||||
}
|
||||
}
|
||||
console.log(`Fixed span/input tags in ${count} files`);
|
||||
55
buyer/scripts/fix-stray-templates.js
Normal file
55
buyer/scripts/fix-stray-templates.js
Normal file
@@ -0,0 +1,55 @@
|
||||
#!/usr/bin/env node
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
|
||||
const ROOT = path.join(__dirname, "..");
|
||||
|
||||
function walkDir(dir, files = []) {
|
||||
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
||||
const full = path.join(dir, entry.name);
|
||||
if (entry.isDirectory()) walkDir(full, files);
|
||||
else if (entry.name.endsWith(".vue")) files.push(full);
|
||||
}
|
||||
return files;
|
||||
}
|
||||
|
||||
function fix(content) {
|
||||
let c = content;
|
||||
|
||||
c = c.replace(
|
||||
/(<div[^>]*)\s+#content>/g,
|
||||
"<template #content><div$1>"
|
||||
);
|
||||
c = c.replace(
|
||||
/(<\/el-tooltip>)/g,
|
||||
(match, _m, offset, str) => {
|
||||
return match;
|
||||
}
|
||||
);
|
||||
|
||||
c = c.replace(
|
||||
/(<div style="white-space:[^"]*"[^>]*>[\s\S]*?<\/div>)(\s*<\/el-tooltip>)/g,
|
||||
"$1</template>$2"
|
||||
);
|
||||
|
||||
c = c.replace(/<\/div><\/template><\/el-dialog>/g, (m, offset, str) => {
|
||||
const before = str.slice(Math.max(0, offset - 200), offset);
|
||||
if (before.includes("#footer")) return m;
|
||||
return "</div></el-dialog>";
|
||||
});
|
||||
|
||||
c = c.replace(/<i(\s[^>]*)\/>/g, "<i$1></i>");
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
let count = 0;
|
||||
for (const file of walkDir(path.join(ROOT, "src"))) {
|
||||
const original = fs.readFileSync(file, "utf8");
|
||||
const fixed = fix(original);
|
||||
if (fixed !== original) {
|
||||
fs.writeFileSync(file, fixed);
|
||||
count++;
|
||||
}
|
||||
}
|
||||
console.log(`Fixed stray templates in ${count} files`);
|
||||
227
buyer/scripts/migrate-iview-to-element.js
Normal file
227
buyer/scripts/migrate-iview-to-element.js
Normal file
@@ -0,0 +1,227 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* Migrate buyer Vue files from iView to Element Plus.
|
||||
* Usage: node scripts/migrate-iview-to-element.js
|
||||
*/
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
|
||||
const BUYER_ROOT = path.join(__dirname, "..");
|
||||
const MANAGER_ROOT = path.join(__dirname, "../../manager");
|
||||
|
||||
const MODEL_LIST = [
|
||||
["Carousel.vue", "carousel.vue"],
|
||||
["Carousel1.vue", "carousel1.vue"],
|
||||
["Carousel2.vue", "carousel2.vue"],
|
||||
["FirstPageAdvert.vue", "firstPageAdvert.vue"],
|
||||
["NewGoodsSort.vue", "newGoodsSort.vue"],
|
||||
["NotEnough.vue", "notEnough.vue"],
|
||||
["Recommend.vue", "recommend.vue"],
|
||||
["Seckill.vue", "seckill.vue"],
|
||||
["forYour.vue", "forYour.vue"],
|
||||
["goodsAndType.vue", "goodsAndType.vue"],
|
||||
["mixModel.vue", "mixModel.vue"],
|
||||
["oneRowThreeColumns.vue", "oneRowThreeColumns.vue"],
|
||||
["onlyGoodsModel.vue", "onlyGoodsModel.vue"],
|
||||
["mixs/mix-brand.vue", "mixs/mix-brand.vue"],
|
||||
["mixs/mix-goods.vue", "mixs/mix-goods.vue"],
|
||||
];
|
||||
|
||||
const DECORATE_COPY_MAP = [
|
||||
[
|
||||
"src/components/indexDecorate/ModelForm.vue",
|
||||
"src/views/page-decoration/modelForm.vue",
|
||||
],
|
||||
[
|
||||
"src/components/indexDecorate/ModelFormItem.vue",
|
||||
"src/views/page-decoration/modelFormItem.vue",
|
||||
],
|
||||
];
|
||||
|
||||
MODEL_LIST.forEach(([buyerName, managerName]) => {
|
||||
DECORATE_COPY_MAP.push([
|
||||
`src/components/indexDecorate/modelList/${buyerName}`,
|
||||
`src/views/page-decoration/modelList/${managerName}`,
|
||||
]);
|
||||
});
|
||||
|
||||
const TARGET_DIRS = ["src/pages", "src/components"];
|
||||
|
||||
function adaptBuyerContent(content) {
|
||||
return content
|
||||
.replace(/@\/views\/page-decoration\//g, "@/components/indexDecorate/")
|
||||
.replace(/admin-setting/g, "buyer-setting")
|
||||
.replace(/adminPCPageCache/g, "buyerPCPageCache")
|
||||
.replace(/getStore\('adminPCPageCache'\)/g, "getStore('buyerPCPageCache')")
|
||||
.replace(/setStore\('adminPCPageCache'/g, "setStore('buyerPCPageCache'");
|
||||
}
|
||||
|
||||
function applyScriptTransforms(content) {
|
||||
let c = content;
|
||||
c = c.replace(/\$options\.filters\./g, "$filters.");
|
||||
c = c.replace(/\|\s*unitPrice\(/g, "{{ $filters.unitPrice(");
|
||||
c = c.replace(/\|\s*unixToDate\(/g, "{{ $filters.unixToDate(");
|
||||
c = c.replace(/\bbeforeDestroy\b/g, "beforeUnmount");
|
||||
c = c.replace(/this\.\$set\(([^,]+),\s*([^,]+),\s*([^)]+)\)/g, "$1[$2] = $3");
|
||||
c = c.replace(/::v-deep/g, ":deep");
|
||||
c = c.replace(/\/deep\//g, ":deep");
|
||||
c = c.replace(/@on-change/g, "@change");
|
||||
c = c.replace(/@on-click/g, "@click");
|
||||
c = c.replace(/@on-ok/g, "@ok");
|
||||
c = c.replace(/@on-cancel/g, "@cancel");
|
||||
c = c.replace(/@on-clear/g, "@clear");
|
||||
c = c.replace(/@on-open-change/g, "@visible-change");
|
||||
c = c.replace(/@on-page-size-change/g, "@size-change");
|
||||
c = c.replace(/@on-select/g, "@select");
|
||||
c = c.replace(/@keydown\.enter\.native/g, "@keyup.enter");
|
||||
c = c.replace(/slot-scope="([^"]+)"/g, '#default="$1"');
|
||||
c = c.replace(/slot="([^"]+)"/g, '#$1');
|
||||
c = c.replace(/from ['"]view-design['"]/g, 'from "@/utils/message"');
|
||||
c = c.replace(/import\s*\{([^}]*)\}\s*from\s*['"]view-design['"]/g, 'import {$1} from "@/utils/message"');
|
||||
return c;
|
||||
}
|
||||
|
||||
function applyTemplateTransforms(content) {
|
||||
let c = content;
|
||||
const replacements = [
|
||||
[/<\/?Card>/g, (m) => m.replace("Card", "el-card")],
|
||||
[/<Form-item/g, "<el-form-item"],
|
||||
[/<\/Form-item>/g, "</el-form-item>"],
|
||||
[/<FormItem/g, "<el-form-item"],
|
||||
[/<\/FormItem>/g, "</el-form-item>"],
|
||||
[/<Form(\s|>)/g, "<el-form$1"],
|
||||
[/<\/Form>/g, "</el-form>"],
|
||||
[/<InputNumber/g, "<el-input-number"],
|
||||
[/<\/InputNumber>/g, "</el-input-number>"],
|
||||
[/<Input(\s|>)/g, "<el-input$1"],
|
||||
[/<\/Input>/g, "</el-input>"],
|
||||
[/<Button(\s|>)/g, "<el-button$1"],
|
||||
[/<\/Button>/g, "</el-button>"],
|
||||
[/<Select(\s|>)/g, "<el-select$1"],
|
||||
[/<\/Select>/g, "</el-select>"],
|
||||
[/<Option(\s)/g, "<el-option "],
|
||||
[/<\/Option>/g, "</el-option>"],
|
||||
[/<DatePicker/g, "<el-date-picker"],
|
||||
[/<\/DatePicker>/g, "</el-date-picker>"],
|
||||
[/<TimePicker/g, "<el-time-picker"],
|
||||
[/<\/TimePicker>/g, "</el-time-picker>"],
|
||||
[/<Modal/g, "<el-dialog"],
|
||||
[/<\/Modal>/g, "</el-dialog>"],
|
||||
[/<Page(\s)/g, "<el-pagination "],
|
||||
[/<\/Page>/g, "</el-pagination>"],
|
||||
[/<Tabs(\s|>)/g, "<el-tabs$1"],
|
||||
[/<\/Tabs>/g, "</el-tabs>"],
|
||||
[/<TabPane/g, "<el-tab-pane"],
|
||||
[/<\/TabPane>/g, "</el-tab-pane>"],
|
||||
[/<Row(\s|>)/g, "<div$1"],
|
||||
[/<\/Row>/g, "</div>"],
|
||||
[/<Col(\s|>)/g, "<div$1"],
|
||||
[/<\/Col>/g, "</div>"],
|
||||
[/<Tag(\s|>)/g, "<el-tag$1"],
|
||||
[/<\/Tag>/g, "</el-tag>"],
|
||||
[/<Switch(\s|>)/g, "<el-switch$1"],
|
||||
[/<\/Switch>/g, "</el-switch>"],
|
||||
[/<RadioGroup/g, "<el-radio-group"],
|
||||
[/<\/RadioGroup>/g, "</el-radio-group>"],
|
||||
[/<Radio(\s)/g, "<el-radio "],
|
||||
[/<\/Radio>/g, "</el-radio>"],
|
||||
[/<CheckboxGroup/g, "<el-checkbox-group"],
|
||||
[/<\/CheckboxGroup>/g, "</el-checkbox-group>"],
|
||||
[/<Checkbox/g, "<el-checkbox"],
|
||||
[/<\/Checkbox>/g, "</el-checkbox>"],
|
||||
[/<Poptip/g, "<el-popover"],
|
||||
[/<\/Poptip>/g, "</el-popover>"],
|
||||
[/<Tooltip/g, "<el-tooltip"],
|
||||
[/<\/Tooltip>/g, "</el-tooltip>"],
|
||||
[/<Drawer/g, "<el-drawer"],
|
||||
[/<\/Drawer>/g, "</el-drawer>"],
|
||||
[/<Alert/g, "<el-alert"],
|
||||
[/<\/Alert>/g, "</el-alert>"],
|
||||
[/<Badge/g, "<el-badge"],
|
||||
[/<\/Badge>/g, "</el-badge>"],
|
||||
[/<Divider/g, "<el-divider"],
|
||||
[/<\/Divider>/g, "</el-divider>"],
|
||||
[/<Rate/g, "<el-rate"],
|
||||
[/<\/Rate>/g, "</el-rate>"],
|
||||
[/<Steps/g, "<el-steps"],
|
||||
[/<\/Steps>/g, "</el-steps>"],
|
||||
[/<Step(\s)/g, "<el-step "],
|
||||
[/<\/Step>/g, "</el-step>"],
|
||||
[/<Upload/g, "<el-upload"],
|
||||
[/<\/Upload>/g, "</el-upload>"],
|
||||
[/<Cascader/g, "<el-cascader"],
|
||||
[/<\/Cascader>/g, "</el-cascader>"],
|
||||
[/<Affix/g, "<el-affix"],
|
||||
[/<\/Affix>/g, "</el-affix>"],
|
||||
[/<Spin/g, "<el-skeleton"],
|
||||
[/<\/Spin>/g, "</el-skeleton>"],
|
||||
[/:label-width="(\d+)"/g, 'label-width="$1px"'],
|
||||
[/:mask-closable="false"/g, ':close-on-click-modal="false"'],
|
||||
[/:width="(\d+)"/g, 'width="$1"'],
|
||||
[/type="primary"/g, 'type="primary"'],
|
||||
];
|
||||
for (const [pattern, replacement] of replacements) {
|
||||
c = c.replace(pattern, replacement);
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
function walkDir(dir, files = []) {
|
||||
if (!fs.existsSync(dir)) return files;
|
||||
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
||||
const full = path.join(dir, entry.name);
|
||||
if (entry.isDirectory()) {
|
||||
walkDir(full, files);
|
||||
} else if (entry.name.endsWith(".vue")) {
|
||||
files.push(full);
|
||||
}
|
||||
}
|
||||
return files;
|
||||
}
|
||||
|
||||
const copied = [];
|
||||
const transformed = [];
|
||||
const skipped = [];
|
||||
|
||||
for (const [buyerRel, managerRel] of DECORATE_COPY_MAP) {
|
||||
const managerPath = path.join(MANAGER_ROOT, managerRel);
|
||||
const buyerPath = path.join(BUYER_ROOT, buyerRel);
|
||||
if (!fs.existsSync(managerPath)) {
|
||||
skipped.push({ file: buyerRel, reason: "manager counterpart missing" });
|
||||
continue;
|
||||
}
|
||||
let content = fs.readFileSync(managerPath, "utf8");
|
||||
content = adaptBuyerContent(content);
|
||||
fs.mkdirSync(path.dirname(buyerPath), { recursive: true });
|
||||
fs.writeFileSync(buyerPath, content);
|
||||
copied.push(buyerRel);
|
||||
}
|
||||
|
||||
const copiedSet = new Set(copied);
|
||||
for (const dir of TARGET_DIRS) {
|
||||
const absDir = path.join(BUYER_ROOT, dir);
|
||||
for (const filePath of walkDir(absDir)) {
|
||||
const rel = path.relative(BUYER_ROOT, filePath).replace(/\\/g, "/");
|
||||
if (copiedSet.has(rel)) continue;
|
||||
|
||||
let content = fs.readFileSync(filePath, "utf8");
|
||||
const hasIView =
|
||||
/<(Card|Form|Form-item|FormItem|Table|Button|Modal|Page|Tabs|TabPane|Input|Select|Option|DatePicker|Row|Poptip|Drawer|Upload|Checkbox|Radio|Switch|Tag|Alert|Rate|Steps|Step|Spin|Affix|TimePicker|Cascader|Divider|Badge|InputNumber)\b/.test(
|
||||
content
|
||||
);
|
||||
if (!hasIView) {
|
||||
skipped.push({ file: rel, reason: "no iView components" });
|
||||
continue;
|
||||
}
|
||||
|
||||
content = applyTemplateTransforms(content);
|
||||
content = applyScriptTransforms(content);
|
||||
fs.writeFileSync(filePath, content);
|
||||
transformed.push(rel);
|
||||
}
|
||||
}
|
||||
|
||||
console.log("=== Buyer Migration Summary ===");
|
||||
console.log(`Copied from manager: ${copied.length}`);
|
||||
console.log(`Script-transformed: ${transformed.length}`);
|
||||
console.log(`Skipped: ${skipped.length}`);
|
||||
149
buyer/scripts/replace-icon.js
Normal file
149
buyer/scripts/replace-icon.js
Normal file
@@ -0,0 +1,149 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* 将静态 <Icon type="..." /> 替换为 <el-icon><Xxx /></el-icon>
|
||||
* 动态 :type / custom 属性保留,由人工或 resolveIcon 处理。
|
||||
*/
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
|
||||
const ROOT = path.join(__dirname, "..", "src");
|
||||
|
||||
const TYPE_TO_COMPONENT = {
|
||||
"md-checkmark": "Check",
|
||||
"md-close": "Close",
|
||||
"md-trash": "Delete",
|
||||
"md-add": "Plus",
|
||||
"md-arrow-dropdown": "ArrowDown",
|
||||
"md-arrow-dropup": "ArrowUp",
|
||||
"md-person": "User",
|
||||
"md-lock": "Lock",
|
||||
"md-phone-portrait": "Iphone",
|
||||
"md-key": "Key",
|
||||
"md-link": "Link",
|
||||
"md-refresh": "Refresh",
|
||||
"md-qr-scanner": "Iphone",
|
||||
"ios-arrow-down": "ArrowDown",
|
||||
"ios-arrow-up": "ArrowUp",
|
||||
"ios-arrow-forward": "ArrowRight",
|
||||
"ios-arrow-back": "ArrowLeft",
|
||||
"ios-arrow-round-down": "ArrowDown",
|
||||
"ios-arrow-round-forward": "ArrowRight",
|
||||
"ios-cart-outline": "ShoppingCart",
|
||||
"ios-camera": "Camera",
|
||||
"ios-eye-outline": "View",
|
||||
"ios-trash-outline": "Delete",
|
||||
"ios-alert-outline": "Warning",
|
||||
"ios-help-circle-outline": "Help",
|
||||
"ios-add-circle-outline": "Plus",
|
||||
"ios-information-circle": "InfoFilled",
|
||||
"ios-search": "Search",
|
||||
"ios-text-outline": "Document",
|
||||
"ios-alarm-outline": "AlarmClock",
|
||||
"ios-heart": "StarFilled",
|
||||
"md-close-circle": "CircleClose",
|
||||
edit: "Edit",
|
||||
"trash-a": "Delete",
|
||||
location: "Location",
|
||||
};
|
||||
|
||||
function walkDir(dir, files = []) {
|
||||
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
||||
const full = path.join(dir, entry.name);
|
||||
if (entry.isDirectory() && entry.name !== "compat") walkDir(full, files);
|
||||
else if (entry.name.endsWith(".vue")) files.push(full);
|
||||
}
|
||||
return files;
|
||||
}
|
||||
|
||||
function parseAttrs(attrStr) {
|
||||
const attrs = {};
|
||||
const re = /(?::([\w-]+)|@([\w-]+)|([\w-]+))=(?:"([^"]*)"|'([^']*)')|(?::([\w-]+)|@([\w-]+)|([\w-]+))(?=\s|\/|>)/g;
|
||||
let m;
|
||||
while ((m = re.exec(attrStr)) !== null) {
|
||||
const name = m[1] || m[2] || m[3] || m[6] || m[7] || m[8];
|
||||
const value = m[4] ?? m[5] ?? true;
|
||||
attrs[name] = value;
|
||||
}
|
||||
return attrs;
|
||||
}
|
||||
|
||||
function attrsToString(attrs, exclude = []) {
|
||||
return Object.entries(attrs)
|
||||
.filter(([k]) => !exclude.includes(k))
|
||||
.map(([k, v]) => {
|
||||
if (v === true) return k;
|
||||
if (k.startsWith("@")) return `${k}="${v}"`;
|
||||
return `:${k}="${v}"`;
|
||||
})
|
||||
.join(" ");
|
||||
}
|
||||
|
||||
function transform(content, filePath) {
|
||||
if (filePath.includes("/compat/")) return content;
|
||||
let c = content;
|
||||
const used = new Set();
|
||||
|
||||
c = c.replace(/<Icon\b([^>]*?)\/>/g, (match, attrStr) => {
|
||||
if (/\bcustom=/.test(attrStr) || /:type=/.test(attrStr)) return match;
|
||||
const attrs = parseAttrs(attrStr);
|
||||
const type = attrs.type;
|
||||
if (!type || !TYPE_TO_COMPONENT[type]) return match;
|
||||
const comp = TYPE_TO_COMPONENT[type];
|
||||
used.add(comp);
|
||||
const rest = attrsToString(attrs, ["type"]);
|
||||
const size = attrs.size ? ` :size="${attrs.size}"` : "";
|
||||
const open = rest ? `<el-icon ${rest}${size}>` : `<el-icon${size}>`;
|
||||
return `${open}<${comp} /></el-icon>`;
|
||||
});
|
||||
|
||||
c = c.replace(/<Icon\b([^>]*?)><\/Icon>/g, (match, attrStr) => {
|
||||
if (/\bcustom=/.test(attrStr) || /:type=/.test(attrStr)) return match;
|
||||
const attrs = parseAttrs(attrStr);
|
||||
const type = attrs.type;
|
||||
if (!type || !TYPE_TO_COMPONENT[type]) return match;
|
||||
const comp = TYPE_TO_COMPONENT[type];
|
||||
used.add(comp);
|
||||
const rest = attrsToString(attrs, ["type"]);
|
||||
const size = attrs.size ? ` :size="${attrs.size}"` : "";
|
||||
const open = rest ? `<el-icon ${rest}${size}>` : `<el-icon${size}>`;
|
||||
return `${open}<${comp} /></el-icon>`;
|
||||
});
|
||||
|
||||
if (used.size && !c.includes("@element-plus/icons-vue")) {
|
||||
const imports = [...used].sort().join(", ");
|
||||
c = c.replace(
|
||||
/(<script[^>]*>\s*\n)/,
|
||||
`$1import { ${imports} } from '@element-plus/icons-vue';\n`
|
||||
);
|
||||
c = c.replace(/export default \{/, (m) => {
|
||||
if (/components:\s*\{/.test(c)) {
|
||||
return m;
|
||||
}
|
||||
return `${m}\n components: { ${imports} },`;
|
||||
});
|
||||
if (/components:\s*\{/.test(c) && !c.includes(`${[...used][0]},`)) {
|
||||
c = c.replace(/components:\s*\{([^}]*)\}/, (m, inner) => {
|
||||
const existing = inner.trim();
|
||||
const add = [...used].filter((n) => !inner.includes(n)).join(", ");
|
||||
if (!add) return m;
|
||||
const merged = existing ? `${existing}, ${add}` : add;
|
||||
return `components: { ${merged} }`;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
let count = 0;
|
||||
for (const file of walkDir(ROOT)) {
|
||||
const original = fs.readFileSync(file, "utf8");
|
||||
if (!/<Icon\b/.test(original)) continue;
|
||||
const fixed = transform(original, file);
|
||||
if (fixed !== original) {
|
||||
fs.writeFileSync(file, fixed);
|
||||
count++;
|
||||
console.log(path.relative(ROOT, file));
|
||||
}
|
||||
}
|
||||
console.log(`Icon replacements in ${count} files`);
|
||||
63
buyer/scripts/replace-page.js
Normal file
63
buyer/scripts/replace-page.js
Normal file
@@ -0,0 +1,63 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* 将全局 <Page> 封装替换为原生 el-pagination
|
||||
*/
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
|
||||
const ROOT = path.join(__dirname, "..", "src");
|
||||
|
||||
function walkDir(dir, files = []) {
|
||||
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
||||
const full = path.join(dir, entry.name);
|
||||
if (entry.isDirectory()) walkDir(full, files);
|
||||
else if (entry.name.endsWith(".vue")) files.push(full);
|
||||
}
|
||||
return files;
|
||||
}
|
||||
|
||||
function transformPage(content) {
|
||||
return content.replace(
|
||||
/<Page\b([^>]*?)(\/>|>(\s*)<\/Page>)/gs,
|
||||
(match, attrs, close) => {
|
||||
let a = attrs;
|
||||
const hasTotal = /\bshow-total\b/.test(a);
|
||||
const hasSizer = /\bshow-sizer\b/.test(a);
|
||||
const hasElevator = /\bshow-elevator\b/.test(a);
|
||||
|
||||
a = a.replace(/\s+show-total\b/g, "");
|
||||
a = a.replace(/\s+show-sizer\b/g, "");
|
||||
a = a.replace(/\s+show-elevator\b/g, "");
|
||||
a = a.replace(/:current=/g, ":current-page=");
|
||||
a = a.replace(/:page-size-opts=/g, ":page-sizes=");
|
||||
a = a.replace(/@change=/g, "@current-change=");
|
||||
a = a.replace(/\ssize="small"/g, " small");
|
||||
|
||||
const layoutParts = [];
|
||||
if (hasTotal) layoutParts.push("total");
|
||||
if (hasSizer) layoutParts.push("sizes");
|
||||
layoutParts.push("prev", "pager", "next");
|
||||
if (hasElevator) layoutParts.push("jumper");
|
||||
|
||||
const layoutAttr = ` layout="${layoutParts.join(", ")}"`;
|
||||
|
||||
if (close.startsWith("/>")) {
|
||||
return `<el-pagination${a}${layoutAttr} />`;
|
||||
}
|
||||
return `<el-pagination${a}${layoutAttr}></el-pagination>`;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
let count = 0;
|
||||
for (const file of walkDir(ROOT)) {
|
||||
const original = fs.readFileSync(file, "utf8");
|
||||
if (!/<Page\b/.test(original)) continue;
|
||||
const fixed = transformPage(original);
|
||||
if (fixed !== original) {
|
||||
fs.writeFileSync(file, fixed);
|
||||
count++;
|
||||
console.log(path.relative(ROOT, file));
|
||||
}
|
||||
}
|
||||
console.log(`Page → el-pagination in ${count} files`);
|
||||
Reference in New Issue
Block a user