迭代 ONE-OS 多原型:统一标注壳与操作规范,租赁明细/合同/提车应收款增强,新增任务工单,同步导航与合包页面。
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
213
scripts/remove-breadcrumbs.mjs
Normal file
213
scripts/remove-breadcrumbs.mjs
Normal file
@@ -0,0 +1,213 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* Remove breadcrumb UI from prototype pages under src/prototypes.
|
||||
* Keeps sibling actions such as「查看需求说明」when present in breadcrumbRight.
|
||||
*/
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
|
||||
const ROOT = path.resolve('src/prototypes');
|
||||
|
||||
function walk(dir, out = []) {
|
||||
for (const name of fs.readdirSync(dir)) {
|
||||
const full = path.join(dir, name);
|
||||
const stat = fs.statSync(full);
|
||||
if (stat.isDirectory()) {
|
||||
if (name === 'node_modules' || name === '.spec') continue;
|
||||
walk(full, out);
|
||||
} else if (/\.(jsx|tsx)$/.test(name)) {
|
||||
out.push(full);
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
function removeBalancedFrom(source, openIndex) {
|
||||
let i = openIndex;
|
||||
if (source[i] !== '(') return null;
|
||||
let depth = 0;
|
||||
let inStr = null;
|
||||
let escape = false;
|
||||
for (; i < source.length; i++) {
|
||||
const ch = source[i];
|
||||
if (inStr) {
|
||||
if (escape) {
|
||||
escape = false;
|
||||
continue;
|
||||
}
|
||||
if (ch === '\\') {
|
||||
escape = true;
|
||||
continue;
|
||||
}
|
||||
if (ch === inStr) inStr = null;
|
||||
continue;
|
||||
}
|
||||
if (ch === "'" || ch === '"' || ch === '`') {
|
||||
inStr = ch;
|
||||
continue;
|
||||
}
|
||||
if (ch === '(') depth++;
|
||||
else if (ch === ')') {
|
||||
depth--;
|
||||
if (depth === 0) return source.slice(openIndex, i + 1);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function removeReactBreadcrumbCalls(source) {
|
||||
const token = 'React.createElement(Breadcrumb,';
|
||||
let result = source;
|
||||
let idx = 0;
|
||||
while ((idx = result.indexOf(token, idx)) !== -1) {
|
||||
const open = result.indexOf('(', idx);
|
||||
const chunk = removeBalancedFrom(result, open);
|
||||
if (!chunk) break;
|
||||
let start = idx;
|
||||
let end = open + chunk.length;
|
||||
while (start > 0 && /[\t ]/.test(result[start - 1])) start--;
|
||||
if (result[start - 1] === ',') start--;
|
||||
if (result[start - 1] === '\n' && result[start - 2] === ',') start--;
|
||||
while (end < result.length && /[\t ,]/.test(result[end])) end++;
|
||||
if (result[end] === '\n') end++;
|
||||
result = result.slice(0, start) + result.slice(end);
|
||||
idx = start;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function removeBreadcrumbImports(source) {
|
||||
return source
|
||||
.replace(/^\s*var Breadcrumb = antd\.Breadcrumb;\n/gm, '')
|
||||
.replace(/^\s*const Breadcrumb = antd\.Breadcrumb;\n/gm, '')
|
||||
.replace(/^\s*import\s+\{[^}]*\bBreadcrumb\b[^}]*\}\s+from\s+['"][^'"]+['"];\n/gm, (line) =>
|
||||
line.replace(/\bBreadcrumb,?\s*/g, '').replace(/,\s*,/g, ',').replace(/\{\s*,/g, '{').replace(/,\s*\}/g, '}').replace(/import\s+\{\s*\}\s+from[^;]+;\n/g, '')
|
||||
);
|
||||
}
|
||||
|
||||
function removeNavBreadcrumbs(source) {
|
||||
return source.replace(
|
||||
/\n?\s*<nav[^>]*breadcrumb[^>]*>[\s\S]*?<\/nav>\n?/gi,
|
||||
'\n'
|
||||
);
|
||||
}
|
||||
|
||||
function replaceJsxBreadcrumbBar(source) {
|
||||
// JSX: breadcrumb row with optional right actions
|
||||
return source.replace(
|
||||
/\{\/\*[\s*]*面包屑[\s*]*\*\/\}\s*\n?\s*<div style=\{styles\.breadcrumb\}>[\s\S]*?<div style=\{styles\.breadcrumbRight\}>([\s\S]*?)<\/div>\s*<\/div>/g,
|
||||
(_, right) => `\n\t\t\t<div style={{ display: 'flex', justifyContent: 'flex-end', marginBottom: 16 }}>${right.trim()}</div>`
|
||||
);
|
||||
}
|
||||
|
||||
function replaceCreateElementBreadcrumbBar(source) {
|
||||
// createElement breadcrumb with Left + Right — keep Right only
|
||||
const pattern = /React\.createElement\(\s*'div',\s*\{ style: styles\.breadcrumb \},[\s\S]*?React\.createElement\(\s*'div',\s*\{ style: styles\.breadcrumbRight \},([\s\S]*?)\)\s*\)/g;
|
||||
return source.replace(
|
||||
pattern,
|
||||
(_, right) =>
|
||||
`React.createElement('div', { style: { display: 'flex', justifyContent: 'flex-end', marginBottom: 16 } }, ${right.trim()})`
|
||||
);
|
||||
}
|
||||
|
||||
function replaceSimpleCreateElementBreadcrumb(source) {
|
||||
// div with only styles.breadcrumb path segments (no Right sibling)
|
||||
return source.replace(
|
||||
/React\.createElement\(\s*'div',\s*\{ style: styles\.breadcrumb[^}]*\},[\s\S]*?\),?\n?/g,
|
||||
''
|
||||
);
|
||||
}
|
||||
|
||||
function replaceLeaseContractBreadcrumbRow(source) {
|
||||
// Long inline breadcrumb + req button row
|
||||
return source.replace(
|
||||
/React\.createElement\('div',\s*\{ style: \{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 16 \} \},\s*React\.createElement\('div',\s*\{ style: styles\.breadcrumb[^}]+\}[\s\S]*?'查看需求说明'\)\),/g,
|
||||
(match) => {
|
||||
const req = match.match(/React\.createElement\('button'[\s\S]*?'查看需求说明'\)/);
|
||||
if (!req) return '';
|
||||
return `React.createElement('div', { style: { display: 'flex', justifyContent: 'flex-end', alignItems: 'center', marginBottom: 16 } }, ${req[0]}),`;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
function removeEmptyBreadcrumbWrappers(source) {
|
||||
// div wrapper that only wrapped breadcrumb (now empty)
|
||||
return source
|
||||
.replace(
|
||||
/React\.createElement\('div',\s*\{ style: \{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 16 \} \},\s*\),?\n?/g,
|
||||
''
|
||||
)
|
||||
.replace(
|
||||
/React\.createElement\('div',\s*\{ style: \{ marginBottom: 16 \} \},\s*\),?\n?/g,
|
||||
''
|
||||
)
|
||||
.replace(
|
||||
/React\.createElement\('div',\s*\{ className: 'vpr-collect-topbar__main' \},\s*\),?\n?/g,
|
||||
''
|
||||
);
|
||||
}
|
||||
|
||||
function transformFile(filePath) {
|
||||
let source = fs.readFileSync(filePath, 'utf8');
|
||||
if (!/breadcrumb|Breadcrumb|面包屑/.test(source)) return false;
|
||||
|
||||
const original = source;
|
||||
source = removeReactBreadcrumbCalls(source);
|
||||
source = removeBreadcrumbImports(source);
|
||||
source = removeNavBreadcrumbs(source);
|
||||
source = replaceJsxBreadcrumbBar(source);
|
||||
source = replaceCreateElementBreadcrumbBar(source);
|
||||
source = replaceSimpleCreateElementBreadcrumb(source);
|
||||
source = replaceLeaseContractBreadcrumbRow(source);
|
||||
source = removeEmptyBreadcrumbWrappers(source);
|
||||
|
||||
// header with only req link after breadcrumb removed
|
||||
source = source.replace(
|
||||
/React\.createElement\(\s*'header',\s*\{ className: 'vr-page-header' \},\s*React\.createElement\(\s*Button,/g,
|
||||
`React.createElement('header', { className: 'vr-page-header', style: { display: 'flex', justifyContent: 'flex-end', marginBottom: 16 } }, React.createElement(Button,`
|
||||
);
|
||||
|
||||
// flex topbar: space-between -> flex-end when breadcrumb was first child
|
||||
source = source.replace(
|
||||
/justifyContent: 'space-between', marginBottom: 16 \} \},\s*React\.createElement\(Button, \{ type: 'link'/g,
|
||||
`justifyContent: 'flex-end', marginBottom: 16 } }, React.createElement(Button, { type: 'link'`
|
||||
);
|
||||
|
||||
if (source !== original) {
|
||||
fs.writeFileSync(filePath, source);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
const files = walk(ROOT);
|
||||
let changed = 0;
|
||||
for (const file of files) {
|
||||
if (transformFile(file)) {
|
||||
changed++;
|
||||
console.log('updated:', path.relative(process.cwd(), file));
|
||||
}
|
||||
}
|
||||
|
||||
// CSS tweak for collect topbar
|
||||
const vprCss = path.join(ROOT, 'vehicle-pickup-receivable/styles/index.css');
|
||||
if (fs.existsSync(vprCss)) {
|
||||
let css = fs.readFileSync(vprCss, 'utf8');
|
||||
const next = css
|
||||
.replace('/* 办理页顶栏:返回 + 面包屑 */', '/* 办理页顶栏:返回 + 操作 */')
|
||||
.replace(
|
||||
/\.vm-page\.vpr-collect-page \.vpr-collect-topbar \{\n display: flex;\n align-items: center;\n gap: 16px;/,
|
||||
`.vm-page.vpr-collect-page .vpr-collect-topbar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 16px;`
|
||||
)
|
||||
.replace(/\n\.vm-page\.vpr-collect-page \.vpr-collect-topbar__main \{[^}]+\}\n?/g, '\n');
|
||||
if (next !== css) {
|
||||
fs.writeFileSync(vprCss, next);
|
||||
console.log('updated:', path.relative(process.cwd(), vprCss));
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`\nDone. ${changed} file(s) updated.`);
|
||||
Reference in New Issue
Block a user