20260818223354
This commit is contained in:
1 parent
f01153e9fe
commit
e91fd1d8de
2 files changed
+117
-210
No files matched your search
@@ -196,7 +196,7 @@ const MODULES = [
|
||||
desc: '海运出口全流程:订舱、海运单、集装箱、提单、船期。典型的"一模块多实体多菜单"形态。',
|
||||
deps: ['platform', 'master-data'],
|
||||
entities: [
|
||||
{ key: 'ocean.booking', table: 'ocean_booking', policy: 'lifecycle', desc: '订舱主单' },
|
||||
{ key: 'ocean.booking', table: 'ocean_booking', view: 'v_booking_with_fee', policy: 'lifecycle', desc: '订舱主单' },
|
||||
{ key: 'ocean.container', table: 'ocean_container', policy: 'cascadeOwned', desc: '箱信息(订舱的子表)' },
|
||||
{ key: 'ocean.shipment', table: 'ocean_shipment', policy: 'lifecycle', desc: '海运单' },
|
||||
{ key: 'ocean.bill_of_lading', table: 'ocean_bill_of_lading', policy: 'lifecycle', desc: '提单' },
|
||||
@@ -204,7 +204,7 @@ const MODULES = [
|
||||
],
|
||||
menus: [
|
||||
{ key: 'ocean.booking.cat', name: '订舱管理', type: 'data_category', children: [
|
||||
{ key: 'ocean.booking.list', name: '订舱列表', type: 'data', entity: 'ocean.booking', view: 'ocean.booking.default-list', form: 'ocean.booking.default-form', route: '/ocean/bookings', permission: 'ocean.booking.view' },
|
||||
{ key: 'ocean.booking.list', name: '订舱列表', type: 'data', entity: 'ocean.booking', route: '/ocean/bookings', permission: 'ocean.booking.view' },
|
||||
]},
|
||||
{ key: 'ocean.shipment.cat', name: '海运单', type: 'data_category', children: [
|
||||
{ key: 'ocean.shipment.list', name: '海运单列表', type: 'data', entity: 'ocean.shipment', route: '/ocean/shipments', permission: 'ocean.shipment.view' },
|
||||
@@ -274,16 +274,16 @@ const MODULES = [
|
||||
|
||||
const ENTITY_FIELDS = {
|
||||
'ocean.booking': [
|
||||
{ key: 'booking_no', label: '订舱号', type: 'varchar(50)', write: 'direct', req: 1, qry: 1, list: 1, edit: 1 },
|
||||
{ key: 'customer_id', label: '客户', type: 'bigint', write: 'direct', req: 1, qry: 1, list: 1, edit: 1 },
|
||||
{ key: 'carrier_id', label: '船公司', type: 'bigint', write: 'direct', req: 1, qry: 1, list: 1, edit: 1 },
|
||||
{ key: 'etd', label: '预计离港日', type: 'date', write: 'direct', req: 0, qry: 1, list: 1, edit: 1 },
|
||||
{ key: 'load_port', label: '起运港', type: 'bigint', write: 'direct', req: 1, qry: 1, list: 1, edit: 1 },
|
||||
{ key: 'discharge_port', label: '目的港', type: 'bigint', write: 'direct', req: 1, qry: 1, list: 1, edit: 1 },
|
||||
{ key: 'freight_payment',label: '运费支付', type: 'varchar(32)', write: 'direct', req: 0, qry: 1, list: 1, edit: 1 },
|
||||
{ key: 'gross_weight', label: '毛重', type: 'decimal(18,3)', write: 'direct', req: 0, qry: 0, list: 1, edit: 1 },
|
||||
{ key: 'volume', label: '体积', type: 'decimal(18,3)', write: 'direct', req: 0, qry: 0, list: 1, edit: 1 },
|
||||
{ key: 'container_count',label: '箱量', type: 'int', write: 'computed', req: 0, qry: 0, list: 1, edit: 0 },
|
||||
{ key: 'booking_no', label: '订舱号', type: 'varchar(50)', write: 'direct', req: 1, qry: 1, list: 1, edit: 1, group: 'basic' },
|
||||
{ key: 'customer_id', label: '客户', type: 'bigint', write: 'direct', req: 1, qry: 1, list: 1, edit: 1, group: 'basic' },
|
||||
{ key: 'carrier_id', label: '船公司', type: 'bigint', write: 'direct', req: 1, qry: 1, list: 1, edit: 1, group: 'basic' },
|
||||
{ key: 'etd', label: '预计离港日', type: 'date', write: 'direct', req: 0, qry: 1, list: 1, edit: 1, group: 'transport' },
|
||||
{ key: 'load_port', label: '起运港', type: 'bigint', write: 'direct', req: 1, qry: 1, list: 1, edit: 1, group: 'transport' },
|
||||
{ key: 'discharge_port', label: '目的港', type: 'bigint', write: 'direct', req: 1, qry: 1, list: 1, edit: 1, group: 'transport' },
|
||||
{ key: 'freight_payment',label: '运费支付', type: 'varchar(32)', write: 'direct', req: 0, qry: 1, list: 1, edit: 1, group: 'transport' },
|
||||
{ key: 'gross_weight', label: '毛重', type: 'decimal(18,3)', write: 'direct', req: 0, qry: 0, list: 1, edit: 1, group: 'cargo' },
|
||||
{ key: 'volume', label: '体积', type: 'decimal(18,3)', write: 'direct', req: 0, qry: 0, list: 1, edit: 1, group: 'cargo' },
|
||||
{ key: 'container_count',label: '箱量', type: 'int', write: 'computed', req: 0, qry: 0, list: 1, edit: 0, group: 'cargo' },
|
||||
{ key: 'status', label: '状态', type: 'varchar(32)', write: 'actionOnly', req: 0, qry: 1, list: 1, edit: 0 },
|
||||
{ key: 'remark', label: '备注', type: 'nvarchar(1000)', write: 'direct', req: 0, qry: 0, list: 0, edit: 1 },
|
||||
{ key: 'created_by', label: '创建人', type: 'nvarchar(128)', write: 'system', req: 0, qry: 0, list: 0, edit: 0 },
|
||||
@@ -297,15 +297,27 @@ const ENTITY_FIELDS = {
|
||||
{ key: 'vgm', label: 'VGM', type: 'decimal(18,3)', write: 'direct', req: 0, qry: 0, list: 1, edit: 1 },
|
||||
],
|
||||
'finance.invoice': [
|
||||
{ key: 'invoice_no', label: '发票号', type: 'varchar(50)', write: 'direct', req: 1, qry: 1, list: 1, edit: 0 },
|
||||
{ key: 'direction', label: '方向', type: 'varchar(16)', write: 'createOnly', req: 1, qry: 1, list: 1, edit: 0 },
|
||||
{ key: 'customer_id', label: '往来单位', type: 'bigint', write: 'direct', req: 1, qry: 1, list: 1, edit: 1 },
|
||||
{ key: 'amount', label: '含税金额', type: 'decimal(18,2)', write: 'computed', req: 0, qry: 0, list: 1, edit: 0 },
|
||||
{ key: 'currency', label: '币种', type: 'varchar(8)', write: 'direct', req: 1, qry: 1, list: 1, edit: 1 },
|
||||
{ key: 'invoice_no', label: '发票号', type: 'varchar(50)', write: 'direct', req: 1, qry: 1, list: 1, edit: 0, group: 'basic' },
|
||||
{ key: 'direction', label: '方向', type: 'varchar(16)', write: 'createOnly', req: 1, qry: 1, list: 1, edit: 0, group: 'basic' },
|
||||
{ key: 'customer_id', label: '往来单位', type: 'bigint', write: 'direct', req: 1, qry: 1, list: 1, edit: 1, group: 'basic' },
|
||||
{ key: 'amount', label: '含税金额', type: 'decimal(18,2)', write: 'computed', req: 0, qry: 0, list: 1, edit: 0, group: 'amount' },
|
||||
{ key: 'currency', label: '币种', type: 'varchar(8)', write: 'direct', req: 1, qry: 1, list: 1, edit: 1, group: 'amount' },
|
||||
{ key: 'status', label: '状态', type: 'varchar(32)', write: 'actionOnly', req: 0, qry: 1, list: 1, edit: 0 },
|
||||
],
|
||||
};
|
||||
|
||||
const ENTITY_GROUPS = {
|
||||
'ocean.booking': [
|
||||
{ key: 'basic', name: '基本信息', sort: 0 },
|
||||
{ key: 'transport', name: '运输信息', sort: 1 },
|
||||
{ key: 'cargo', name: '货物信息', sort: 2 },
|
||||
],
|
||||
'finance.invoice': [
|
||||
{ key: 'basic', name: '基本信息', sort: 0 },
|
||||
{ key: 'amount', name: '金额信息', sort: 1 },
|
||||
],
|
||||
};
|
||||
|
||||
const ENTITY_RELATIONS = {
|
||||
'ocean.booking': [
|
||||
{ key: 'ocean.booking.containers', type: 'one-to-many', target: 'ocean.container', ownership: 'composition', delete: 'cascade', save: 'with-parent' },
|
||||
@@ -314,19 +326,6 @@ const ENTITY_RELATIONS = {
|
||||
],
|
||||
};
|
||||
|
||||
const ENTITY_VIEWS = {
|
||||
'ocean.booking': [
|
||||
{ key: 'ocean.booking.default-list', queryable: 1, exportable: 1, pageSize: 50 },
|
||||
{ key: 'ocean.booking.query-simple', queryable: 1, exportable: 1, pageSize: 20 },
|
||||
],
|
||||
};
|
||||
const ENTITY_FORMS = {
|
||||
'ocean.booking': [
|
||||
{ key: 'ocean.booking.default-form', type: 'default', relations: ['ocean.booking.containers'] },
|
||||
{ key: 'ocean.booking.readonly-form', type: 'readonly', relations: [] },
|
||||
],
|
||||
};
|
||||
|
||||
/* ============ 状态与工具 ============ */
|
||||
const state = { selectedModule: null, selectedMenu: null, selectedEntity: null, openModules: new Set(['ocean']) };
|
||||
|
||||
@@ -340,7 +339,11 @@ function findMenuByKey(k) {
|
||||
function menuOwner(k) {
|
||||
return MODULES.find(m => m.menus.some(mm => mm.key === k || (mm.children && mm.children.some(c => c.key === k))));
|
||||
}
|
||||
function entityTable(ek) {
|
||||
function entityViewTable(ek) {
|
||||
for (const m of MODULES) { const e = m.entities.find(x => x.key === ek); if (e) return e.view || e.table; }
|
||||
return '—';
|
||||
}
|
||||
function entitySaveTable(ek) {
|
||||
for (const m of MODULES) { const e = m.entities.find(x => x.key === ek); if (e) return e.table; }
|
||||
return '—';
|
||||
}
|
||||
@@ -473,14 +476,14 @@ function renderModule() {
|
||||
<div class="section">
|
||||
<div class="section-title">包含实体(s_module_entity)<span class="hint">点击实体查看字段元数据</span></div>
|
||||
<table class="grid">
|
||||
<thead><tr><th>实体 key</th><th>物理表</th><th>删除策略</th><th>描述</th><th>字段数</th></tr></thead>
|
||||
<thead><tr><th>实体 key</th><th>查询源 view_table</th><th>保存目标 save_table</th><th>删除策略</th><th>字段数</th></tr></thead>
|
||||
<tbody>
|
||||
${m.entities.map(e => `
|
||||
<tr class="row-click" data-entity="${e.key}">
|
||||
<td class="mono">${e.key}</td>
|
||||
<td class="mono dim">${e.view || e.table}${e.view ? '(视图)' : ''}</td>
|
||||
<td class="mono dim">${e.table}</td>
|
||||
<td class="dim">${e.policy}</td>
|
||||
<td>${e.desc}</td>
|
||||
<td>${ENTITY_FIELDS[e.key] ? ENTITY_FIELDS[e.key].length : 0}</td>
|
||||
</tr>`).join('')}
|
||||
</tbody>
|
||||
@@ -517,10 +520,12 @@ function renderMenu() {
|
||||
const t = MENU_TYPES[menu.type];
|
||||
const owner = menuOwner(menu.key);
|
||||
const entity = menu.entity ? {
|
||||
key: menu.entity, table: entityTable(menu.entity),
|
||||
key: menu.entity,
|
||||
viewTable: entityViewTable(menu.entity),
|
||||
saveTable: entitySaveTable(menu.entity),
|
||||
fields: ENTITY_FIELDS[menu.entity] ? ENTITY_FIELDS[menu.entity].length : 0,
|
||||
views: ENTITY_VIEWS[menu.entity] ? ENTITY_VIEWS[menu.entity].length : 0,
|
||||
forms: ENTITY_FORMS[menu.entity] ? ENTITY_FORMS[menu.entity].length : 0,
|
||||
groups: ENTITY_GROUPS[menu.entity] ? ENTITY_GROUPS[menu.entity].length : 0,
|
||||
rels: ENTITY_RELATIONS[menu.entity] ? ENTITY_RELATIONS[menu.entity].length : 0,
|
||||
} : null;
|
||||
|
||||
return `
|
||||
@@ -542,13 +547,12 @@ function renderMenu() {
|
||||
|
||||
${entity ? `
|
||||
<div class="section">
|
||||
<div class="section-title">绑定的实体 / 视图 / 表单 <span class="hint">data 类型</span></div>
|
||||
<div class="section-title">绑定的实体 <span class="hint">查询走 view_table,保存走 save_table</span></div>
|
||||
<div class="kv-grid">
|
||||
<div class="kv"><div class="k">实体 entity_key</div><div class="v"><a class="link" data-entity="${entity.key}">${entity.key} ›</a></div></div>
|
||||
<div class="kv"><div class="k">物理表</div><div class="v">${entity.table}</div></div>
|
||||
<div class="kv"><div class="k">字段 / 视图 / 表单</div><div class="v">${entity.fields} / ${entity.views} / ${entity.forms}</div></div>
|
||||
<div class="kv"><div class="k">视图 key</div><div class="v">${menu.view || '—'}</div></div>
|
||||
<div class="kv"><div class="k">表单 key</div><div class="v">${menu.form || '—'}</div></div>
|
||||
<div class="kv"><div class="k">查询源 view_table</div><div class="v">${entity.viewTable}${entity.viewTable !== entity.saveTable ? '(视图)' : ''}</div></div>
|
||||
<div class="kv"><div class="k">保存目标 save_table</div><div class="v">${entity.saveTable}</div></div>
|
||||
<div class="kv"><div class="k">字段 / 分组 / 关系</div><div class="v">${entity.fields} / ${entity.groups} / ${entity.rels}</div></div>
|
||||
</div>
|
||||
</div>` : ''}
|
||||
|
||||
@@ -569,26 +573,50 @@ function renderEntity() {
|
||||
const ek = ent.key;
|
||||
const fields = ENTITY_FIELDS[ek] || [];
|
||||
const rels = ENTITY_RELATIONS[ek] || [];
|
||||
const views = ENTITY_VIEWS[ek] || [];
|
||||
const forms = ENTITY_FORMS[ek] || [];
|
||||
const groups = ENTITY_GROUPS[ek] || [];
|
||||
const viewTable = ent.view || ent.table;
|
||||
const isView = !!ent.view;
|
||||
const groupName = gk => (groups.find(g => g.key === gk) || {}).name || '未分组';
|
||||
|
||||
return `
|
||||
<div class="page-head">
|
||||
<div class="ph-title">${ent.desc} <span class="tag">${ent.policy}</span></div>
|
||||
<div class="ph-title">${ent.desc} <span class="tag">${ent.policy}</span>${isView ? ' <span class="tag">视图查询</span>' : ''}</div>
|
||||
<div class="ph-desc">实体是数据和字段的所有者,字段挂实体(s_module_field.b_entity_key)。返回<a class="link" data-back="mod">${mod.name} 模块</a>。</div>
|
||||
<div class="ph-meta">entity_key: ${ek} · 物理表: ${ent.table} · module: ${mod.key}</div>
|
||||
<div class="ph-meta">entity_key: ${ek} · 查询源: ${viewTable} · 保存目标: ${ent.table} · module: ${mod.key}</div>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<div class="section-title">字段元数据(s_module_field)<span class="hint">写入模式 / 必填 / 查询 / 列表 / 编辑</span></div>
|
||||
<div class="section-title">查询 / 保存数据源 <span class="hint">s_module_entity</span></div>
|
||||
<div class="kv-grid">
|
||||
<div class="kv"><div class="k">查询源 view_table</div><div class="v">${viewTable}${isView ? '(SQL 视图,带关联/聚合)' : ''}</div></div>
|
||||
<div class="kv"><div class="k">保存目标 save_table</div><div class="v">${ent.table}</div></div>
|
||||
<div class="kv"><div class="k">主键字段 key_field</div><div class="v">b_id</div></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<div class="section-title">字段分组(s_module_field_group)<span class="hint">分组独立定义,字段引用 group_key</span></div>
|
||||
${groups.length ? `<table class="grid">
|
||||
<thead><tr><th>分组 key</th><th>标题 name_key</th><th>排序</th></tr></thead>
|
||||
<tbody>${groups.map(g => `<tr>
|
||||
<td class="mono">${g.key}</td>
|
||||
<td>${g.name}</td>
|
||||
<td>${g.sort}</td>
|
||||
</tr>`).join('')}</tbody>
|
||||
</table>` : '<div class="empty">无分组(字段直接平铺)</div>'}
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<div class="section-title">字段元数据(s_module_field)<span class="hint">写入模式 / 分组 / 必填 / 查询 / 列表 / 编辑</span></div>
|
||||
<table class="grid">
|
||||
<thead><tr><th>字段 key</th><th>标签</th><th>数据类型</th><th>写入模式</th><th>必填</th><th>查询</th><th>列表</th><th>编辑</th></tr></thead>
|
||||
<thead><tr><th>字段 key</th><th>标签</th><th>数据类型</th><th>写入模式</th><th>分组</th><th>必填</th><th>查询</th><th>列表</th><th>编辑</th></tr></thead>
|
||||
<tbody>
|
||||
${fields.map(f => `<tr>
|
||||
<td class="mono">${f.key}</td>
|
||||
<td>${f.label}</td>
|
||||
<td class="mono dim">${f.type}</td>
|
||||
<td><span class="wm-tag">${f.write}</span></td>
|
||||
<td>${f.group ? groupName(f.group) : '<span class="dim">—</span>'}</td>
|
||||
<td>${f.req ? '必填' : '<span class="dim">—</span>'}</td>
|
||||
<td>${f.qry ? '✓' : ''}</td>
|
||||
<td>${f.list ? '✓' : ''}</td>
|
||||
@@ -615,15 +643,6 @@ function renderEntity() {
|
||||
</tr>`).join('')}</tbody>
|
||||
</table>` : '<div class="empty">无</div>'}
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<div class="section-title">视图与表单 <span class="hint">一个实体可有多视图/多表单</span></div>
|
||||
<div class="kv-grid">
|
||||
<div class="kv"><div class="k">视图 s_module_view</div><div class="v">${views.map(v => v.key).join('、') || '—'}</div></div>
|
||||
<div class="kv"><div class="k">表单 s_module_form</div><div class="v">${forms.map(v => v.key).join('、') || '—'}</div></div>
|
||||
</div>
|
||||
${forms.some(f => f.relations.length) ? `<div style="margin-top:10px;font-size:12.5px;color:var(--text-3)">主子表区域(s_module_form_relation):${forms.find(f => f.relations.length).relations.join('、')} 以 table 模式内嵌于表单</div>` : ''}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user