feat(web): 新增车辆氢费明细台账页面及需求文档
包含氢费录入、保存对账、筛选导入导出、异常标黄与需求明细弹窗等功能原型。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
336
web端/台账数据/车辆氢费明细-需求渲染.js
Normal file
336
web端/台账数据/车辆氢费明细-需求渲染.js
Normal file
@@ -0,0 +1,336 @@
|
||||
/** 将 PRD Markdown 排版为 React 节点(车辆氢费明细) */
|
||||
function parsePrdInlineText(text) {
|
||||
var parts = String(text || '').split(/(\*\*[^*]+\*\*)/g);
|
||||
var nodes = [];
|
||||
var i;
|
||||
for (i = 0; i < parts.length; i++) {
|
||||
var p = parts[i];
|
||||
if (!p) continue;
|
||||
if (p.indexOf('**') === 0 && p.lastIndexOf('**') === p.length - 2) {
|
||||
nodes.push(
|
||||
React.createElement('strong', { key: i }, p.slice(2, -2))
|
||||
);
|
||||
} else {
|
||||
nodes.push(p);
|
||||
}
|
||||
}
|
||||
return nodes.length === 1 ? nodes[0] : nodes;
|
||||
}
|
||||
|
||||
function isPrdTableRow(line) {
|
||||
return /^\|.+\|$/.test(String(line || '').trim());
|
||||
}
|
||||
|
||||
function isPrdTableSep(line) {
|
||||
return /^\|[\s\-:|]+\|$/.test(String(line || '').trim());
|
||||
}
|
||||
|
||||
function renderPrdTableRow(line, rowKey, isHeader) {
|
||||
var cells = String(line)
|
||||
.trim()
|
||||
.replace(/^\|/, '')
|
||||
.replace(/\|$/, '')
|
||||
.split('|')
|
||||
.map(function (c) {
|
||||
return c.trim();
|
||||
});
|
||||
return React.createElement(
|
||||
'tr',
|
||||
{ key: rowKey },
|
||||
cells.map(function (cell, ci) {
|
||||
var Tag = isHeader ? 'th' : 'td';
|
||||
return React.createElement(
|
||||
Tag,
|
||||
{
|
||||
key: ci,
|
||||
style: {
|
||||
border: '1px solid #e5e7eb',
|
||||
padding: '8px 10px',
|
||||
textAlign: 'left',
|
||||
verticalAlign: 'top',
|
||||
fontWeight: isHeader ? 600 : 400,
|
||||
background: isHeader ? '#f8fafc' : '#fff',
|
||||
fontSize: 13,
|
||||
lineHeight: 1.5
|
||||
}
|
||||
},
|
||||
parsePrdInlineText(cell)
|
||||
);
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
function renderPrdMarkdown(markdown) {
|
||||
var lines = String(markdown || '').split(/\r?\n/);
|
||||
var nodes = [];
|
||||
var i = 0;
|
||||
var inCode = false;
|
||||
var codeBuf = [];
|
||||
|
||||
while (i < lines.length) {
|
||||
var line = lines[i];
|
||||
var trimmed = String(line || '').trim();
|
||||
|
||||
if (trimmed.indexOf('```') === 0) {
|
||||
if (inCode) {
|
||||
nodes.push(
|
||||
React.createElement(
|
||||
'pre',
|
||||
{
|
||||
key: 'code-' + i,
|
||||
style: {
|
||||
margin: '12px 0',
|
||||
padding: '12px 14px',
|
||||
background: '#f6f8fa',
|
||||
border: '1px solid #e5e7eb',
|
||||
borderRadius: 8,
|
||||
fontSize: 12,
|
||||
lineHeight: 1.6,
|
||||
overflow: 'auto',
|
||||
color: '#334155',
|
||||
whiteSpace: 'pre-wrap'
|
||||
}
|
||||
},
|
||||
codeBuf.join('\n')
|
||||
)
|
||||
);
|
||||
codeBuf = [];
|
||||
inCode = false;
|
||||
} else {
|
||||
inCode = true;
|
||||
}
|
||||
i += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (inCode) {
|
||||
codeBuf.push(line);
|
||||
i += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (trimmed === '---') {
|
||||
nodes.push(
|
||||
React.createElement('hr', {
|
||||
key: 'hr-' + i,
|
||||
style: { border: 'none', borderTop: '1px solid #e8ecf0', margin: '20px 0' }
|
||||
})
|
||||
);
|
||||
i += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isPrdTableRow(trimmed)) {
|
||||
var tableLines = [];
|
||||
while (i < lines.length && isPrdTableRow(String(lines[i]).trim())) {
|
||||
tableLines.push(String(lines[i]).trim());
|
||||
i += 1;
|
||||
}
|
||||
var bodyRows = [];
|
||||
var ti;
|
||||
for (ti = 0; ti < tableLines.length; ti++) {
|
||||
if (isPrdTableSep(tableLines[ti])) continue;
|
||||
bodyRows.push(
|
||||
renderPrdTableRow(tableLines[ti], 'tr-' + i + '-' + ti, ti === 0)
|
||||
);
|
||||
}
|
||||
if (bodyRows.length) {
|
||||
nodes.push(
|
||||
React.createElement(
|
||||
'div',
|
||||
{
|
||||
key: 'tbl-' + i,
|
||||
style: { overflowX: 'auto', margin: '12px 0 16px' }
|
||||
},
|
||||
React.createElement(
|
||||
'table',
|
||||
{
|
||||
style: {
|
||||
width: '100%',
|
||||
borderCollapse: 'collapse',
|
||||
fontSize: 13
|
||||
}
|
||||
},
|
||||
React.createElement('tbody', null, bodyRows)
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!trimmed) {
|
||||
i += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (trimmed.indexOf('# ') === 0) {
|
||||
nodes.push(
|
||||
React.createElement(
|
||||
'h1',
|
||||
{
|
||||
key: 'h1-' + i,
|
||||
style: {
|
||||
fontSize: 20,
|
||||
fontWeight: 700,
|
||||
color: '#0f172a',
|
||||
margin: '0 0 16px',
|
||||
lineHeight: 1.35
|
||||
}
|
||||
},
|
||||
parsePrdInlineText(trimmed.slice(2).trim())
|
||||
)
|
||||
);
|
||||
i += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (trimmed.indexOf('## ') === 0) {
|
||||
nodes.push(
|
||||
React.createElement(
|
||||
'h2',
|
||||
{
|
||||
key: 'h2-' + i,
|
||||
style: {
|
||||
fontSize: 16,
|
||||
fontWeight: 700,
|
||||
color: '#1e293b',
|
||||
margin: '24px 0 12px',
|
||||
paddingBottom: 6,
|
||||
borderBottom: '2px solid #e0f2fe',
|
||||
lineHeight: 1.4
|
||||
}
|
||||
},
|
||||
parsePrdInlineText(trimmed.slice(3).trim())
|
||||
)
|
||||
);
|
||||
i += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (trimmed.indexOf('### ') === 0) {
|
||||
nodes.push(
|
||||
React.createElement(
|
||||
'h3',
|
||||
{
|
||||
key: 'h3-' + i,
|
||||
style: {
|
||||
fontSize: 14,
|
||||
fontWeight: 600,
|
||||
color: '#334155',
|
||||
margin: '16px 0 8px',
|
||||
lineHeight: 1.45
|
||||
}
|
||||
},
|
||||
parsePrdInlineText(trimmed.slice(4).trim())
|
||||
)
|
||||
);
|
||||
i += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (trimmed === '**文档结束**') {
|
||||
nodes.push(
|
||||
React.createElement(
|
||||
'div',
|
||||
{
|
||||
key: 'end-' + i,
|
||||
style: {
|
||||
marginTop: 24,
|
||||
paddingTop: 16,
|
||||
borderTop: '1px dashed #e2e8f0',
|
||||
color: '#94a3b8',
|
||||
fontSize: 13,
|
||||
textAlign: 'center'
|
||||
}
|
||||
},
|
||||
'— 文档结束 —'
|
||||
)
|
||||
);
|
||||
i += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (/^\d+\.\s/.test(trimmed)) {
|
||||
nodes.push(
|
||||
React.createElement(
|
||||
'div',
|
||||
{
|
||||
key: 'ol-' + i,
|
||||
style: {
|
||||
fontSize: 13,
|
||||
color: '#475569',
|
||||
lineHeight: 1.75,
|
||||
margin: '6px 0 6px 4px',
|
||||
paddingLeft: 4
|
||||
}
|
||||
},
|
||||
parsePrdInlineText(trimmed)
|
||||
)
|
||||
);
|
||||
i += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (trimmed.indexOf('- ') === 0) {
|
||||
nodes.push(
|
||||
React.createElement(
|
||||
'div',
|
||||
{
|
||||
key: 'ul-' + i,
|
||||
style: {
|
||||
display: 'flex',
|
||||
gap: 8,
|
||||
fontSize: 13,
|
||||
color: '#475569',
|
||||
lineHeight: 1.75,
|
||||
margin: '4px 0 4px 2px'
|
||||
}
|
||||
},
|
||||
React.createElement('span', { style: { color: '#1677ff', flexShrink: 0 } }, '•'),
|
||||
React.createElement('span', { style: { flex: 1 } }, parsePrdInlineText(trimmed.slice(2).trim()))
|
||||
)
|
||||
);
|
||||
i += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
nodes.push(
|
||||
React.createElement(
|
||||
'p',
|
||||
{
|
||||
key: 'p-' + i,
|
||||
style: {
|
||||
fontSize: 13,
|
||||
color: '#475569',
|
||||
lineHeight: 1.75,
|
||||
margin: '6px 0'
|
||||
}
|
||||
},
|
||||
parsePrdInlineText(trimmed)
|
||||
)
|
||||
);
|
||||
i += 1;
|
||||
}
|
||||
|
||||
return nodes;
|
||||
}
|
||||
|
||||
function renderH2RequirementDocPanel() {
|
||||
var md =
|
||||
typeof H2_LEDGER_REQUIREMENT_DOC !== 'undefined' && H2_LEDGER_REQUIREMENT_DOC
|
||||
? H2_LEDGER_REQUIREMENT_DOC
|
||||
: '';
|
||||
if (!md) {
|
||||
return React.createElement(
|
||||
'div',
|
||||
{ style: { padding: 24, color: '#64748b', textAlign: 'center' } },
|
||||
'需求文档未加载,请确认已引入同目录文件:车辆氢费明细-需求内容.js'
|
||||
);
|
||||
}
|
||||
return React.createElement(
|
||||
'div',
|
||||
{ className: 'h2-req-doc-panel', style: { padding: '4px 4px 16px' } },
|
||||
renderPrdMarkdown(md)
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user