Files
wukuang/docs/物料组件添加流程.html
T

328 lines
26 KiB
HTML
Raw Normal View History

2026-05-23 14:05:22 +08:00
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>物料组件添加流程 - 灵枢低代码平台</title>
<style>
:root {
--bg-primary: #0a0e1a; --bg-secondary: #111827; --bg-card: #1a2035;
--bg-card-hover: #1f2a45; --border-color: #2d3a5c; --text-primary: #e2e8f0;
--text-secondary: #94a3b8; --text-muted: #64748b;
--accent-blue: #3b82f6; --accent-blue-light: #60a5fa;
--accent-green: #10b981; --accent-green-light: #34d399;
--accent-orange: #f59e0b; --accent-orange-light: #fbbf24;
--accent-purple: #8b5cf6; --accent-purple-light: #a78bfa;
--accent-red: #ef4444; --accent-pink: #ec4899; --accent-cyan: #06b6d4;
--gradient-blue: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%);
--gradient-green: linear-gradient(135deg, #10b981 0%, #059669 100%);
--gradient-orange: linear-gradient(135deg, #f59e0b 0%, #d97706 100%);
--gradient-purple: linear-gradient(135deg, #8b5cf6 0%, #7c3aed 100%);
--gradient-pink: linear-gradient(135deg, #ec4899 0%, #db2777 100%);
--shadow-lg: 0 10px 40px rgba(0, 0, 0, 0.4); --shadow-md: 0 4px 20px rgba(0, 0, 0, 0.3);
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; background: var(--bg-primary); color: var(--text-primary); line-height: 1.6; padding: 2rem; }
.container { max-width: 1400px; margin: 0 auto; }
.header { text-align: center; padding: 3rem 2rem; background: var(--bg-card); border-radius: 16px; margin-bottom: 2.5rem; border: 1px solid var(--border-color); box-shadow: var(--shadow-lg); }
.header h1 { font-size: 2.5rem; background: linear-gradient(135deg, #60a5fa, #a78bfa, #f472b6); -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text; margin-bottom: 0.75rem; }
.header .subtitle { color: var(--text-secondary); font-size: 1.1rem; }
.header .stats { display: flex; justify-content: center; gap: 3rem; margin-top: 2rem; padding-top: 2rem; border-top: 1px solid var(--border-color); }
.stat-item { text-align: center; }
.stat-number { font-size: 2.5rem; font-weight: bold; background: linear-gradient(135deg, #60a5fa, #a78bfa); -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text; }
.stat-label { color: var(--text-muted); font-size: 0.9rem; margin-top: 0.25rem; }
.section { margin-bottom: 2.5rem; }
.section-title { font-size: 1.5rem; color: var(--text-primary); margin-bottom: 1.5rem; padding-left: 1rem; border-left: 4px solid var(--accent-blue); }
.flowchart-container { background: var(--bg-card); border-radius: 16px; padding: 2rem; border: 1px solid var(--border-color); box-shadow: var(--shadow-lg); overflow-x: auto; }
.flowchart { display: flex; flex-direction: column; align-items: center; gap: 0; min-width: 800px; padding: 1rem 0; }
.flow-row { display: flex; align-items: center; justify-content: center; gap: 1.5rem; width: 100%; }
.flow-node { padding: 1rem 1.5rem; border-radius: 10px; text-align: center; min-width: 180px; position: relative; transition: all 0.3s ease; }
.flow-node:hover { transform: translateY(-3px); box-shadow: var(--shadow-md); }
.flow-node.clickable { cursor: pointer; text-decoration: none; color: inherit; display: block; }
.flow-node.clickable::after { content: "🔍 点击查看详解"; position: absolute; bottom: -8px; left: 50%; transform: translateX(-50%) scale(0); background: var(--accent-blue); color: white; padding: 0.2rem 0.6rem; border-radius: 10px; font-size: 0.65rem; white-space: nowrap; transition: transform 0.2s ease; pointer-events: none; }
.flow-node.clickable:hover::after { transform: translateX(-50%) scale(1); }
.flow-node.start { background: var(--gradient-green); color: white; border-radius: 50px; padding: 1rem 2.5rem; }
.flow-node.step { background: var(--bg-secondary); border: 2px solid var(--accent-blue); }
.flow-node.step .step-num { position: absolute; top: -12px; left: -12px; width: 28px; height: 28px; background: var(--gradient-blue); border-radius: 50%; display: flex; align-items: center; justify-content: center; font-weight: bold; font-size: 0.85rem; }
.flow-node.step.create { border-color: var(--accent-green); }
.flow-node.step.create .step-num { background: var(--gradient-green); }
.flow-node.step.modify { border-color: var(--accent-orange); }
.flow-node.step.modify .step-num { background: var(--gradient-orange); }
.flow-node.step.type { border-color: var(--accent-purple); }
.flow-node.step.type .step-num { background: var(--gradient-purple); }
.flow-node.end { background: var(--gradient-pink); color: white; border-radius: 50px; padding: 1rem 2.5rem; }
.flow-node h4 { font-size: 0.95rem; margin-bottom: 0.25rem; }
.flow-node p { font-size: 0.75rem; color: var(--text-secondary); margin: 0; }
.flow-arrow { color: var(--accent-blue); font-size: 1.8rem; margin: 0.5rem 0; }
.flow-arrow-h { color: var(--text-muted); font-size: 1.5rem; }
.flow-branch { display: flex; align-items: flex-start; gap: 2rem; width: 100%; justify-content: center; }
.flow-branch-item { display: flex; flex-direction: column; align-items: center; }
.flow-label { font-size: 0.8rem; color: var(--text-muted); margin-top: 0.25rem; background: var(--bg-secondary); padding: 0.2rem 0.6rem; border-radius: 4px; }
.file-table { width: 100%; border-collapse: separate; border-spacing: 0; background: var(--bg-card); border-radius: 12px; overflow: hidden; border: 1px solid var(--border-color); box-shadow: var(--shadow-lg); }
.file-table thead { background: linear-gradient(135deg, #1e3a5f 0%, #1a2744 100%); }
.file-table th { padding: 1rem 1.25rem; text-align: left; font-weight: 600; color: var(--text-primary); font-size: 0.9rem; text-transform: uppercase; letter-spacing: 0.05em; }
.file-table td { padding: 1rem 1.25rem; border-top: 1px solid var(--border-color); font-size: 0.9rem; }
.file-table tbody tr { transition: background 0.2s ease; }
.file-table tbody tr:hover { background: var(--bg-card-hover); }
.file-path { font-family: 'Fira Code', 'Cascadia Code', monospace; font-size: 0.8rem; color: var(--accent-cyan); background: rgba(6, 182, 212, 0.1); padding: 0.25rem 0.5rem; border-radius: 4px; word-break: break-all; }
.badge { display: inline-block; padding: 0.2rem 0.6rem; border-radius: 20px; font-size: 0.7rem; font-weight: 600; text-transform: uppercase; }
.badge-create { background: rgba(16, 185, 129, 0.2); color: var(--accent-green-light); border: 1px solid rgba(16, 185, 129, 0.3); }
.badge-modify { background: rgba(245, 158, 11, 0.2); color: var(--accent-orange-light); border: 1px solid rgba(245, 158, 11, 0.3); }
.badge-optional { background: rgba(100, 116, 139, 0.2); color: var(--text-muted); border: 1px solid rgba(100, 116, 139, 0.3); }
.scenario-cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(350px, 1fr)); gap: 1.5rem; }
.scenario-card { background: var(--bg-card); border-radius: 12px; padding: 1.5rem; border: 1px solid var(--border-color); transition: all 0.3s ease; }
.scenario-card:hover { transform: translateY(-4px); box-shadow: var(--shadow-lg); }
.scenario-card h3 { font-size: 1.2rem; margin-bottom: 0.75rem; display: flex; align-items: center; gap: 0.5rem; }
.scenario-card .icon { font-size: 1.5rem; }
.scenario-card p { color: var(--text-secondary); font-size: 0.9rem; margin-bottom: 1rem; }
.scenario-card .file-count { font-size: 2rem; font-weight: bold; margin-bottom: 0.5rem; }
.scenario-card.pure-widget .file-count { color: var(--accent-green); }
.scenario-card.field-widget .file-count { color: var(--accent-orange); }
.scenario-card.pure-field .file-count { color: var(--accent-purple); }
.steps-detail { display: flex; flex-direction: column; gap: 1.5rem; }
.step-detail { background: var(--bg-card); border-radius: 12px; padding: 1.5rem; border: 1px solid var(--border-color); border-left: 4px solid var(--accent-blue); }
.step-detail.create-step { border-left-color: var(--accent-green); }
.step-detail.modify-step { border-left-color: var(--accent-orange); }
.step-detail.type-step { border-left-color: var(--accent-purple); }
.step-header { display: flex; align-items: center; gap: 1rem; margin-bottom: 1rem; }
.step-number { width: 40px; height: 40px; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-weight: bold; font-size: 1.1rem; flex-shrink: 0; }
.create-step .step-number { background: var(--gradient-green); }
.modify-step .step-number { background: var(--gradient-orange); }
.type-step .step-number { background: var(--gradient-purple); }
.step-title { font-size: 1.1rem; font-weight: 600; }
.step-content { padding-left: 3.5rem; }
.step-content p { color: var(--text-secondary); margin-bottom: 0.75rem; }
.step-content .code-block { background: var(--bg-secondary); padding: 0.75rem 1rem; border-radius: 8px; font-family: 'Fira Code', 'Cascadia Code', monospace; font-size: 0.8rem; color: var(--accent-cyan); overflow-x: auto; margin-top: 0.5rem; }
.step-content ul { list-style: none; padding: 0; }
.step-content ul li { padding: 0.4rem 0; color: var(--text-secondary); display: flex; align-items: flex-start; gap: 0.5rem; }
.step-content ul li::before { content: "▸"; color: var(--accent-blue); flex-shrink: 0; }
.architecture-diagram { background: var(--bg-card); border-radius: 16px; padding: 2rem; border: 1px solid var(--border-color); box-shadow: var(--shadow-lg); }
.arch-layer { display: flex; align-items: center; margin-bottom: 1.5rem; gap: 1rem; }
.arch-layer:last-child { margin-bottom: 0; }
.arch-label { min-width: 140px; padding: 0.75rem 1rem; border-radius: 8px; text-align: center; font-weight: 600; font-size: 0.85rem; }
.arch-label.types { background: var(--gradient-purple); }
.arch-label.form-designer { background: var(--gradient-blue); }
.arch-label.business { background: var(--gradient-green); }
.arch-label.modeling { background: var(--gradient-orange); }
.arch-label.mobile { background: var(--gradient-pink); }
.arch-files { display: flex; flex-wrap: wrap; gap: 0.5rem; flex: 1; }
.arch-file { padding: 0.4rem 0.75rem; background: var(--bg-secondary); border-radius: 6px; font-size: 0.75rem; font-family: 'Fira Code', monospace; color: var(--text-secondary); border: 1px solid var(--border-color); transition: all 0.2s ease; }
.arch-file:hover { border-color: var(--accent-blue); color: var(--accent-blue-light); }
.arch-file.highlight { border-color: var(--accent-green); color: var(--accent-green-light); background: rgba(16, 185, 129, 0.1); }
.arch-arrow { text-align: center; color: var(--text-muted); font-size: 1.2rem; margin: 0.5rem 0; padding-left: 140px; }
.footer { text-align: center; padding: 2rem; color: var(--text-muted); font-size: 0.85rem; border-top: 1px solid var(--border-color); margin-top: 3rem; }
@media (max-width: 768px) { body { padding: 1rem; } .header h1 { font-size: 1.8rem; } .header .stats { flex-direction: column; gap: 1.5rem; } .scenario-cards { grid-template-columns: 1fr; } .file-table { font-size: 0.8rem; } .file-table th, .file-table td { padding: 0.75rem; } }
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>设计页面添加物料组件 - 文件改动分析</h1>
<p class="subtitle">灵枢低代码平台 (LCDP) · 物料组件开发完整指南 · 点击流程图节点查看详细文档</p>
<div class="stats">
<div class="stat-item"><div class="stat-number">7</div><div class="stat-label">必改文件数</div></div>
<div class="stat-item"><div class="stat-number">3</div><div class="stat-label">场景类型</div></div>
<div class="stat-item"><div class="stat-number">5</div><div class="stat-label">层级涉及</div></div>
</div>
</div>
<div class="section">
<h2 class="section-title">三种开发场景</h2>
<div class="scenario-cards">
<div class="scenario-card pure-widget">
<h3><span class="icon">🧩</span> 纯物料组件</h3>
<div class="file-count">7 个文件</div>
<p>不对应业务字段类型,仅是页面设计器中的纯 UI/交互/容器能力。如 CommonBtn、CommonTxt、GroupPanel、Tabs 等。</p>
<p><strong>推荐参考样板</strong>CommonBtn、GroupPanel</p>
</div>
<div class="scenario-card field-widget">
<h3><span class="icon">🔗</span> 字段类型 + 物料组件</h3>
<div class="file-count">20+ 个文件</div>
<p>既有业务字段定义,又有页面物料渲染。如金额、币种、手机号码、业务组织/人员等。</p>
<p><strong>推荐参考样板</strong>mobile-number</p>
</div>
<div class="scenario-card pure-field">
<h3><span class="icon">📝</span> 纯字段类型</h3>
<div class="file-count">5 个文件</div>
<p>只存在于业务模型,没有页面物料组件。如 BinaryObject 等仅服务后端的字段类型。</p>
<p><strong>推荐参考样板</strong>BinaryObject</p>
</div>
</div>
</div>
<div class="section">
<h2 class="section-title">纯物料组件添加流程图(点击节点下钻查看详解)</h2>
<div class="flowchart-container">
<div class="flowchart">
<div class="flow-node start">
<h4>开始:新增纯物料组件</h4>
<p>如 CommonBtn、GroupPanel 等</p>
</div>
<div class="flow-arrow"></div>
<div class="flow-row">
<a href="widget-steps/01-widget-type.html" target="_blank" class="flow-node clickable step type">
<div class="step-num">1</div>
<h4>定义 WidgetType</h4>
<p>packages/types/widget-pc/WidgetType.ts</p>
</a>
</div>
<div class="flow-arrow"></div>
<div class="flow-row">
<a href="widget-steps/02-vue-component.html" target="_blank" class="flow-node clickable step create">
<div class="step-num">2</div>
<h4>创建 Vue 组件</h4>
<p>form-designer/src/config/widget/xxx.vue</p>
</a>
<div class="flow-arrow-h">+</div>
<a href="widget-steps/03-helper-config.html" target="_blank" class="flow-node clickable step create">
<div class="step-num">3</div>
<h4>创建 Helper 配置</h4>
<p>form-designer/src/config/widget-helper/xxx.ts</p>
</a>
</div>
<div class="flow-arrow"></div>
<div class="flow-row">
<a href="widget-steps/04-register-widget.html" target="_blank" class="flow-node clickable step modify">
<div class="step-num">4</div>
<h4>注册组件实例</h4>
<p>RegisterWidget.ts</p>
</a>
<div class="flow-arrow-h">+</div>
<a href="widget-steps/05-register-helper.html" target="_blank" class="flow-node clickable step modify">
<div class="step-num">5</div>
<h4>注册 Helper</h4>
<p>RegisterWidgetHelper.ts</p>
</a>
<div class="flow-arrow-h">+</div>
<a href="widget-steps/06-material-panel.html" target="_blank" class="flow-node clickable step modify">
<div class="step-num">6</div>
<h4>注册物料面板</h4>
<p>Material.ts</p>
</a>
</div>
<div class="flow-arrow"></div>
<div class="flow-row">
<a href="widget-steps/07-i18n.html" target="_blank" class="flow-node clickable step modify">
<div class="step-num">7</div>
<h4>补充国际化</h4>
<p>form-designer/src/locales/model/Info.json</p>
</a>
</div>
<div class="flow-arrow"></div>
<div class="flow-branch">
<div class="flow-branch-item">
<a href="widget-steps/08-optional-steps.html" target="_blank" class="flow-node clickable step modify" style="border-style: dashed; opacity: 0.7; text-decoration: none; color: inherit;">
<h4>可选:自定义设置项/移动端</h4>
<p>settings-helper + widget-mobile</p>
</a>
<div class="flow-label">按需补充</div>
</div>
</div>
<div class="flow-arrow"></div>
<div class="flow-node end">
<h4>完成:物料组件已添加</h4>
<p>可在设计器中拖拽使用</p>
</div>
</div>
</div>
</div>
<div class="section">
<h2 class="section-title">纯物料组件 - 文件改动清单(7个必改)</h2>
<table class="file-table">
<thead><tr><th style="width: 60px;">步骤</th><th>文件路径</th><th style="width: 100px;">操作</th><th>作用说明</th></tr></thead>
<tbody>
<tr><td><strong>1</strong></td><td><span class="file-path">packages/types/widget-pc/WidgetType.ts</span></td><td><span class="badge badge-modify">修改</span></td><td>在 WidgetType 枚举中添加新组件类型值,作为全局唯一标识</td></tr>
<tr><td><strong>2</strong></td><td><span class="file-path">packages/form-designer/src/config/widget/&lt;组件名&gt;.vue</span></td><td><span class="badge badge-create">新建</span></td><td>实际的 Vue 渲染组件,定义 UI 结构和样式</td></tr>
<tr><td><strong>3</strong></td><td><span class="file-path">packages/form-designer/src/config/widget-helper/&lt;组件名&gt;Helper.ts</span></td><td><span class="badge badge-create">新建</span></td><td>Helper 配置对象,实现 buildMaterialConfig、buildSettings、buildWidget、dragInfo 等接口</td></tr>
<tr><td><strong>4</strong></td><td><span class="file-path">packages/form-designer/src/config/RegisterWidget.ts</span></td><td><span class="badge badge-modify">修改</span></td><td>导入 Vue 组件并在 SYS_COMPS_MAP 中注册:[WidgetType.Xxx]: { instance: XxxWidget }</td></tr>
<tr><td><strong>5</strong></td><td><span class="file-path">packages/form-designer/src/config/RegisterWidgetHelper.ts</span></td><td><span class="badge badge-modify">修改</span></td><td>导入 Helper 并添加到 SYS_WIDGET_HELPERS 数组</td></tr>
<tr><td><strong>6</strong></td><td><span class="file-path">packages/form-designer/src/config/Material.ts</span></td><td><span class="badge badge-modify">修改</span></td><td>在对应分组(通用/容器/导航等)的 widgetTypeList 中添加 WidgetType,使其出现在左侧物料面板</td></tr>
<tr><td><strong>7</strong></td><td><span class="file-path">packages/form-designer/src/locales/model/Info.json</span></td><td><span class="badge badge-modify">修改</span></td><td>添加物料显示名称、属性面板文案等国际化文案</td></tr>
</tbody>
</table>
</div>
<div class="section">
<h2 class="section-title">架构分层与文件分布</h2>
<div class="architecture-diagram">
<div class="arch-layer">
<div class="arch-label types">共享契约层<br><small>packages/types</small></div>
<div class="arch-files"><span class="arch-file highlight">WidgetType.ts</span><span class="arch-file">FieldType.ts</span><span class="arch-file">SettingType.ts</span><span class="arch-file">registry.ts</span></div>
</div>
<div class="arch-arrow">↓ 类型定义</div>
<div class="arch-layer">
<div class="arch-label form-designer">页面设计器层<br><small>packages/form-designer</small></div>
<div class="arch-files"><span class="arch-file highlight">Material.ts</span><span class="arch-file highlight">RegisterWidget.ts</span><span class="arch-file highlight">RegisterWidgetHelper.ts</span><span class="arch-file highlight">widget/*.vue</span><span class="arch-file highlight">widget-helper/*.ts</span><span class="arch-file">RegisterSettings.ts</span><span class="arch-file">settings-helper/*.ts</span><span class="arch-file highlight">locales/Info.json</span></div>
</div>
<div class="arch-arrow">↓ (纯物料通常不涉及以下层)</div>
<div class="arch-layer">
<div class="arch-label business">业务组件层<br><small>packages/business-components</small></div>
<div class="arch-files"><span class="arch-file" style="opacity: 0.5;">enum/settings/&lt;field&gt;/</span><span class="arch-file" style="opacity: 0.5;">utils/*Utils.ts</span><span class="arch-file" style="opacity: 0.5;">service/*.ts</span></div>
</div>
<div class="arch-arrow">↓ (纯物料通常不涉及以下层)</div>
<div class="arch-layer">
<div class="arch-label modeling">建模层<br><small>apps/lcdp</small></div>
<div class="arch-files"><span class="arch-file" style="opacity: 0.5;">FieldType.ts</span><span class="arch-file" style="opacity: 0.5;">helper/&lt;field&gt;/Schema.ts</span><span class="arch-file" style="opacity: 0.5;">modeling.json</span></div>
</div>
<div class="arch-arrow">↓ (按需)</div>
<div class="arch-layer">
<div class="arch-label mobile">移动端镜像层<br><small>form-designer/widget-mobile</small></div>
<div class="arch-files"><span class="arch-file" style="opacity: 0.5;">widget/*.vue</span><span class="arch-file" style="opacity: 0.5;">widget-helper/*.ts</span><span class="arch-file" style="opacity: 0.5;">Material.ts</span></div>
</div>
</div>
<p style="margin-top: 1rem; color: var(--text-muted); font-size: 0.85rem;"><span style="color: var(--accent-green);"></span> 高亮文件 = 纯物料组件必改文件 &nbsp;&nbsp;<span style="opacity: 0.5;"></span> 灰色文件 = 纯物料通常可跳过</p>
</div>
<div class="section">
<h2 class="section-title">注册执行链</h2>
<div class="flowchart-container">
<div class="flowchart">
<div class="flow-row"><div class="flow-node step" style="border-color: var(--accent-purple);"><h4>RegisterWeb.ts</h4><p>setPcConfigAll()</p></div></div>
<div class="flow-arrow">↓ 调用 setConfigAll(PC, config)</div>
<div class="flow-row"><div class="flow-node step" style="border-color: var(--accent-purple);"><h4>registry.ts</h4><p>registerConfigAll(platform)</p></div></div>
<div class="flow-arrow">↓ 依次调用</div>
<div class="flow-row">
<div class="flow-node step create" style="min-width: 150px;"><h4>register/widget.ts</h4><p>注册 widget 实例</p></div>
<div class="flow-arrow-h">+</div>
<div class="flow-node step create" style="min-width: 150px;"><h4>register/material.ts</h4><p>注册物料面板</p></div>
<div class="flow-arrow-h">+</div>
<div class="flow-node step create" style="min-width: 150px;"><h4>register/setting.ts</h4><p>注册设置项</p></div>
<div class="flow-arrow-h">+</div>
<div class="flow-node step create" style="min-width: 150px;"><h4>register/helper.ts</h4><p>注册 helper</p></div>
</div>
</div>
</div>
</div>
<div class="section">
<h2 class="section-title">最小验证建议</h2>
<table class="file-table">
<thead><tr><th>场景</th><th>验证方式</th></tr></thead>
<tbody>
<tr><td><strong>纯物料组件</strong></td><td><span class="file-path">pnpm form-designer:dev</span> → 访问 <code>/designer/:pageId</code><br><span style="color: var(--text-muted); margin-top: 0.5rem; display: inline-block;">验证:左侧物料面板出现新组件 → 拖入画布 → 右侧属性面板正常 → 画布渲染正常</span></td></tr>
<tr><td><strong>运行型物料</strong></td><td>额外验证:<code>/form/:pageId</code><code>/list/:pageId</code><br><span style="color: var(--text-muted); margin-top: 0.5rem; display: inline-block;">验证:运行时页面能正确渲染该组件</span></td></tr>
</tbody>
</table>
</div>
<div class="footer">
<p>灵枢低代码平台 · 物料组件开发指南 · 基于当前 Live Source 分析</p>
<p style="margin-top: 0.5rem;">文档生成日期:2026-05-13</p>
</div>
</div>
</body>
</html>