(function () {
'use strict';
// 配置区:命中指定流程、节点和端类型后,把隐藏二级明细渲染到一级明细行内。
var NESTED_DETAIL_ECODE_CONFIG = window.NS_NESTED_DETAIL_ECODE_CONFIG || {
enable: true,
debug: false,
desktopHashPrefixList: ['#/main/workflow/req'],
mobileHashPrefixList: ['#/main/workflow/req', '#/workflow/req', '#/workflow/request', '#/req'],
mobilePathPrefixList: ['/spa/workflow/', '/workflow/', '/mobile/', '/emobile/'],
workflowRules: [
{
workflowId: 'WORKFLOW_1',
nodeIdList: [],
pageTypeList: ['desktop', 'mobile'],
mappings: [
{
mappingKey: 'nested_detail',
parentDetailTableMark: 'detail_1',
childDetailTableMark: 'detail_2',
renderFieldName: 'render_field',
parentLinkFieldName: 'parent_id',
childLinkFieldName: 'parent_id',
childRowKeyFieldName: 'child_id',
parentTitleFieldName: 'parent_title',
summaryFieldName: 'weight',
hideChildTableHeader: true,
selectionColumnWidth: '2.5%',
childSelectionEnabledNodeIdList: ['NODE_1','NODE_2','NODE_3'],
childActionEnabledNodeIdList: ['NODE_1','NODE_2','NODE_3'],
editableSuffixNodeIdList: ['NODE_1'],
expectedTotal: 100,
summaryTolerance: 0.0001,
validateOnSubmit: true,
hideRenderFieldEditor: true,
autoCleanupOrphanChildren: false,
childFields: [
{
fieldName: 'action_name',
label: '行动项',
type: 'text',
required: true,
columnWidth: '22.5%',
},
{
fieldName: 'weight',
label: '权重(%)',
type: 'number',
required: true,
decimalScale: 2,
suffixText: '%',
inputWidth: '56px',
columnWidth: '25%',
},
{
fieldName: 'finish_date',
label: '完成日期',
type: 'date',
required: true,
columnWidth: '25%'
},
{
fieldName: 'measure',
label: '衡量标准',
type: 'textarea',
required: true,
columnWidth: '25%'
}
],
formulaRules: [
]
}
]
},
{
workflowId: 'WORKFLOW_2',
nodeIdList: [],
pageTypeList: ['desktop', 'mobile'],
mappings: [
{
mappingKey: 'nested_detail',
parentDetailTableMark: 'detail_1',
childDetailTableMark: 'detail_2',
renderFieldName: 'render_field',
parentLinkFieldName: 'parent_id',
childLinkFieldName: 'parent_id',
childRowKeyFieldName: 'child_id',
parentTitleFieldName: 'parent_title',
summaryFieldName: 'changed_weight',
hideChildTableHeader: true,
selectionColumnWidth: '2%',
childSelectionEnabledNodeIdList: ['NODE_4','NODE_5'],
childActionEnabledNodeIdList: ['NODE_4','NODE_5'],
editableSuffixNodeIdList: ['NODE_4'],
expectedTotal: 100,
summaryTolerance: 0.0001,
validateOnSubmit: true,
hideRenderFieldEditor: true,
autoCleanupOrphanChildren: false,
childFields: [
{
fieldName: 'action_name',
label: '行动项',
type: 'text',
required: true,
columnWidth: '12.7%',
},
{
fieldName: 'changed_action_name',
label: '变更后行动项',
type: 'text',
required: true,
columnWidth: '17.3%',
style: 'color: red;'
},
{
fieldName: 'weight',
label: '权重(%)',
type: 'number',
required: true,
decimalScale: 2,
suffixText: '%',
inputWidth: '56px',
columnWidth: '10%',
},
{
fieldName: 'changed_weight',
label: '变更后权重(%)',
type: 'number',
required: true,
decimalScale: 2,
suffixText: '%',
inputWidth: '60px',
columnWidth: '11.2%',
style: 'color: red;'
},
{
fieldName: 'finish_date',
label: '完成日期',
type: 'date',
required: true,
columnWidth: '10.5%'
},
{
fieldName: 'changed_finish_date',
label: '变更后完成日期',
type: 'date',
required: true,
columnWidth: '11.5%',
style: 'color: red;'
},
{
fieldName: 'measure',
label: '衡量标准',
type: 'textarea',
required: true,
columnWidth: '13.5%'
},
{
fieldName: 'changed_measure',
label: '变更后衡量标准',
type: 'textarea',
required: true,
columnWidth: '13%',
style: 'color: red;'
}
],
formulaRules: [
]
}
]
},
{
workflowId: 'WORKFLOW_3',
nodeIdList: [],
pageTypeList: ['desktop', 'mobile'],
mappings: [
{
mappingKey: 'nested_detail',
parentDetailTableMark: 'detail_1',
childDetailTableMark: 'detail_2',
renderFieldName: 'render_field',
parentLinkFieldName: 'parent_id',
childLinkFieldName: 'parent_id',
childRowKeyFieldName: 'child_id',
parentTitleFieldName: 'parent_title',
summaryFieldName: 'weight',
hideChildTableHeader: true,
selectionColumnWidth: '2.5%',
childSelectionEnabledNodeIdList: [],
childActionEnabledNodeIdList: [],
editableSuffixNodeIdList: [],
expectedTotal: 100,
summaryTolerance: 0.0001,
validateOnSubmit: true,
hideRenderFieldEditor: true,
autoCleanupOrphanChildren: false,
childFields: [
{
fieldName: 'action_name',
label: '行动项',
type: 'text',
required: true,
columnWidth: '22.5%',
},
{
fieldName: 'weight',
label: '权重(%)',
type: 'number',
required: true,
decimalScale: 2,
suffixText: '%',
inputWidth: '56px',
columnWidth: '25%',
},
{
fieldName: 'finish_date',
label: '完成日期',
type: 'date',
required: true,
columnWidth: '25%'
},
{
fieldName: 'measure',
label: '衡量标准',
type: 'textarea',
required: true,
columnWidth: '25%'
}
],
formulaRules: [
]
}
]
},
{
workflowId: 'WORKFLOW_4',
nodeIdList: [],
pageTypeList: ['desktop', 'mobile'],
mappings: [
{
mappingKey: 'nested_detail',
parentDetailTableMark: 'detail_1',
childDetailTableMark: 'detail_2',
renderFieldName: 'render_field',
parentLinkFieldName: 'parent_id',
childLinkFieldName: 'parent_id',
childRowKeyFieldName: 'child_id',
parentTitleFieldName: 'parent_title',
summaryFieldName: 'weight',
hideChildTableHeader: true,
selectionColumnWidth: '2.5%',
childSelectionEnabledNodeIdList: [],
childActionEnabledNodeIdList: [],
editableSuffixNodeIdList: [],
expectedTotal: 100,
summaryTolerance: 0.0001,
validateOnSubmit: true,
hideRenderFieldEditor: true,
autoCleanupOrphanChildren: false,
childFields: [
{
fieldName: 'action_name',
label: '行动项',
type: 'text',
required: true,
columnWidth: '22.5%',
},
{
fieldName: 'weight',
label: '权重(%)',
type: 'number',
required: true,
decimalScale: 2,
suffixText: '%',
inputWidth: '56px',
columnWidth: '25%',
},
{
fieldName: 'finish_date',
label: '完成日期',
type: 'date',
required: true,
columnWidth: '25%'
},
{
fieldName: 'measure',
label: '衡量标准',
type: 'textarea',
required: true,
columnWidth: '25%'
}
],
formulaRules: [
]
}
]
}
]
};
// 运行时状态:缓存运行实例、刷新定时器、字段绑定和选中行,避免脚本重复加载后状态丢失。
var GLOBAL_STATE_KEY = '__NS_NESTED_DETAIL_RUNTIME__';
var STYLE_ID = 'ns-nested-detail-style';
var ROOT_ATTR = 'data-ns-nested-root';
var ROOT_KEY_ATTR = 'data-ns-runtime-key';
var INLINE_VALIDATION_MESSAGE = '__NS_NESTED_INLINE_VALIDATION__';
function getGlobalState() {
if (!window[GLOBAL_STATE_KEY]) {
window[GLOBAL_STATE_KEY] = {
styleInjected: false,
runtimeMap: {},
bindingMap: {},
observerMap: {},
refreshTimerMap: {},
initTimerMap: {},
submitGuardMap: {},
uiBound: false,
internalUpdateDepth: 0,
modalContext: null
};
}
return window[GLOBAL_STATE_KEY];
}
function log() {
if (!NESTED_DETAIL_ECODE_CONFIG.debug || !window.console || !console.log) {
return;
}
var args = Array.prototype.slice.call(arguments || []);
args.unshift('[nested-detail]');
console.log.apply(console, args);
}
// 基础工具:统一处理空值、数组、数字、CSS 尺寸和安全调用,后续读写字段都会复用。
function normalizeValue(value) {
if (value === undefined || value === null) {
return '';
}
return String(value).trim();
}
function toArray(value) {
if (Array.isArray(value)) {
return value;
}
if (value === undefined || value === null || value === '') {
return [];
}
return [value];
}
function uniqueArray(list) {
var result = [];
var cache = {};
var index;
var key;
for (index = 0; index < list.length; index += 1) {
key = normalizeValue(list[index]);
if (!key || cache[key]) {
continue;
}
cache[key] = true;
result.push(key);
}
return result;
}
function hasOwnPropertySafe(target, key) {
return !!target && Object.prototype.hasOwnProperty.call(target, key);
}
function resolveNodeAccessEnabled(config, keyList, currentNodeId, defaultEnabled) {
var index;
var key;
var list;
for (index = 0; index < keyList.length; index += 1) {
key = keyList[index];
if (!hasOwnPropertySafe(config, key)) {
continue;
}
list = uniqueArray(toArray(config[key]).map(function (item) {
return normalizeValue(item);
}).filter(function (item) {
return !!item;
}));
if (!list.length) {
return false;
}
return list.indexOf(normalizeValue(currentNodeId)) >= 0;
}
return defaultEnabled;
}
function parseNumber(value) {
var text = normalizeValue(value).replace(/,/g, '');
if (!text) {
return 0;
}
var result = Number(text);
return isFiniteNumber(result) ? result : 0;
}
function isFiniteNumber(value) {
return typeof value === 'number' && isFinite(value);
}
function formatNumber(value, decimalScale) {
if (value === '' || value === undefined || value === null) {
return '';
}
var num = Number(value);
if (!isFiniteNumber(num)) {
return normalizeValue(value);
}
if (decimalScale === undefined || decimalScale === null || decimalScale === '') {
return String(num);
}
return num.toFixed(Number(decimalScale));
}
function appendSuffixText(value, suffixText) {
var normalizedText = normalizeValue(value);
var normalizedSuffix = normalizeValue(suffixText);
if (!normalizedText || !normalizedSuffix) {
return normalizedText;
}
return normalizedText + normalizedSuffix;
}
function normalizeCssSize(value) {
if (value === undefined || value === null || value === '') {
return '';
}
if (typeof value === 'number' && isFinite(value)) {
return String(value) + 'px';
}
return normalizeValue(value);
}
function escapeSelectorValue(value) {
return String(value || '').replace(/\\/g, '\\\\').replace(/"/g, '\\"');
}
function safeCall(fn, fallback) {
try {
return fn();
} catch (e) {
return fallback;
}
}
// 页面识别:读取流程基础信息、当前节点、路由和端类型,只在目标流程页面启用。
function getBaseInfoSafe() {
if (!window.WfForm || typeof window.WfForm.getBaseInfo !== 'function') {
return null;
}
return safeCall(function () {
return window.WfForm.getBaseInfo() || null;
}, null);
}
function getLayoutStoreSafe() {
if (!window.WfForm || typeof window.WfForm.getLayoutStore !== 'function') {
return null;
}
return safeCall(function () {
return window.WfForm.getLayoutStore() || null;
}, null);
}
function getCurrentNodeIdSafe(baseInfo) {
var nodeId = safeCall(function () {
var globalStore = window.WfForm && typeof window.WfForm.getGlobalStore === 'function'
? window.WfForm.getGlobalStore()
: null;
return globalStore && globalStore.commonParam ? globalStore.commonParam.currentnodeid : '';
}, '');
return normalizeValue(nodeId || (baseInfo && baseInfo.nodeid));
}
function isHashMatched(prefixList) {
var hash = window.location && window.location.hash ? window.location.hash : '';
var list = toArray(prefixList);
var index;
if (!hash || !list.length) {
return false;
}
for (index = 0; index < list.length; index += 1) {
if (hash.indexOf(list[index]) === 0) {
return true;
}
}
return false;
}
function isPathMatched(prefixList) {
var pathname = window.location && window.location.pathname ? window.location.pathname.toLowerCase() : '';
var list = toArray(prefixList);
var index;
var prefix;
if (!pathname || !list.length) {
return false;
}
for (index = 0; index < list.length; index += 1) {
prefix = normalizeValue(list[index]).toLowerCase();
if (prefix && pathname.indexOf(prefix) === 0) {
return true;
}
}
return false;
}
function isMobileUserAgent() {
var ua = window.navigator && window.navigator.userAgent ? window.navigator.userAgent.toLowerCase() : '';
return /mobile|android|iphone|ipad|ipod|micromessenger|wxwork|emobile/.test(ua);
}
function detectPageType() {
if (isPathMatched(NESTED_DETAIL_ECODE_CONFIG.mobilePathPrefixList) || isMobileUserAgent()) {
return 'mobile';
}
return 'desktop';
}
function isPageLocationMatched(pageType, config) {
if (pageType === 'mobile') {
return isHashMatched(config.mobileHashPrefixList) ||
isPathMatched(config.mobilePathPrefixList) ||
isMobileUserAgent() ||
!!(getBaseInfoSafe() && getBaseInfoSafe().workflowid);
}
return isHashMatched(config.desktopHashPrefixList);
}
function isWorkflowMatched(rule, baseInfo, pageType) {
var workflowId = normalizeValue(baseInfo && baseInfo.workflowid);
var nodeId = getCurrentNodeIdSafe(baseInfo);
var workflowRuleId = normalizeValue(rule && rule.workflowId);
var nodeIdList = toArray(rule && rule.nodeIdList);
var pageTypeList = toArray(rule && rule.pageTypeList);
var matchedNode = !nodeIdList.length;
var index;
if (!workflowId || !workflowRuleId || workflowId !== workflowRuleId) {
return false;
}
if (pageTypeList.length && pageTypeList.indexOf(pageType) < 0) {
return false;
}
for (index = 0; index < nodeIdList.length; index += 1) {
if (normalizeValue(nodeIdList[index]) === nodeId) {
matchedNode = true;
break;
}
}
return matchedNode;
}
// 字段读写:把配置字段名转换成运行时字段 ID,并封装 WfForm 的安全读写。
function toDetailFieldId(fieldName, detailTableMark) {
if (!window.WfForm || typeof window.WfForm.convertFieldNameToId !== 'function') {
return '';
}
return safeCall(function () {
return window.WfForm.convertFieldNameToId(fieldName, detailTableMark) || '';
}, '');
}
function getFieldValueSafe(fieldMark) {
if (!window.WfForm || typeof window.WfForm.getFieldValue !== 'function') {
return '';
}
return safeCall(function () {
return window.WfForm.getFieldValue(fieldMark) || '';
}, '');
}
function getBrowserShowNameSafe(fieldMark) {
if (!window.WfForm || typeof window.WfForm.getBrowserShowName !== 'function') {
return '';
}
return safeCall(function () {
return window.WfForm.getBrowserShowName(fieldMark) || '';
}, '');
}
function withInternalUpdate(fn) {
var state = getGlobalState();
state.internalUpdateDepth += 1;
try {
return fn();
} finally {
state.internalUpdateDepth -= 1;
}
}
function isInternalUpdating() {
return getGlobalState().internalUpdateDepth > 0;
}
function setFieldValueSafe(fieldMark, value) {
var normalized = value === undefined || value === null ? '' : value;
var currentValue = normalizeValue(getFieldValueSafe(fieldMark));
var nextValue = normalizeValue(normalized);
var success = false;
if (!window.WfForm || typeof window.WfForm.changeFieldValue !== 'function') {
return false;
}
if (currentValue === nextValue) {
return true;
}
withInternalUpdate(function () {
success = safeCall(function () {
window.WfForm.changeFieldValue(fieldMark, { value: normalized });
return true;
}, false);
if (!success) {
success = safeCall(function () {
window.WfForm.changeFieldValue(fieldMark, normalized);
return true;
}, false);
}
});
return success;
}
function changeFieldAttrSafe(fieldMark, viewAttr) {
if (!window.WfForm || typeof window.WfForm.changeFieldAttr !== 'function') {
return false;
}
return safeCall(function () {
window.WfForm.changeFieldAttr(fieldMark, viewAttr);
return true;
}, false);
}
function disableNativeRequiredForRuntime(runtime) {
var rowIndexList;
if (!runtime || runtime.pageType !== 'mobile' || !runtime.childFieldList || !runtime.childFieldList.length) {
return;
}
rowIndexList = getAllDetailRowIndexList(runtime.childDetailTableMark);
rowIndexList.forEach(function (rowIndex) {
runtime.childFieldList.forEach(function (fieldMeta) {
if (!fieldMeta.required || !fieldMeta.fieldId) {
return;
}
changeFieldAttrSafe(fieldMeta.fieldId, 2);
changeFieldAttrSafe(getChildRowFieldMark(runtime, fieldMeta.fieldId, rowIndex), 2);
});
});
}
// 明细行操作:读取、新增、删除真实明细行,确保数据仍落在标准明细表里。
function getAllDetailRowIndexList(detailTableMark) {
if (!window.WfForm || typeof window.WfForm.getDetailAllRowIndexStr !== 'function') {
return [];
}
return safeCall(function () {
var rowIndexStr = window.WfForm.getDetailAllRowIndexStr(detailTableMark);
if (!rowIndexStr) {
return [];
}
return rowIndexStr.split(',').map(function (item) {
return normalizeValue(item);
}).filter(function (item) {
return !!item;
});
}, []);
}
function addDetailRowSafe(detailTableMark) {
var beforeList = getAllDetailRowIndexList(detailTableMark);
var index;
var result;
if (!window.WfForm || typeof window.WfForm.addDetailRow !== 'function') {
return '';
}
result = safeCall(function () {
return window.WfForm.addDetailRow(detailTableMark);
}, '');
if (normalizeValue(result)) {
return normalizeValue(result);
}
safeCall(function () {
return window.WfForm.addDetailRow(detailTableMark, {});
}, null);
var afterList = getAllDetailRowIndexList(detailTableMark);
for (index = 0; index < afterList.length; index += 1) {
if (beforeList.indexOf(afterList[index]) < 0) {
return afterList[index];
}
}
return afterList.length ? afterList[afterList.length - 1] : '';
}
function findNewDetailRowIndex(detailTableMark, beforeList) {
var afterList = getAllDetailRowIndexList(detailTableMark);
var index;
for (index = 0; index < afterList.length; index += 1) {
if (beforeList.indexOf(afterList[index]) < 0) {
return afterList[index];
}
}
return '';
}
function delDetailRowSafe(detailTableMark, rowIndex) {
if (!window.WfForm || typeof window.WfForm.delDetailRow !== 'function') {
return false;
}
return safeCall(function () {
withInternalUpdate(function () {
window.WfForm.delDetailRow(detailTableMark, rowIndex);
});
return true;
}, false);
}
// DOM 工具:定位字段宿主、嵌套面板根节点和输入控件,兼容 PC 与移动端页面结构。
function queryAllSafe(selector, root) {
var baseNode = root || document;
return safeCall(function () {
return Array.prototype.slice.call(baseNode.querySelectorAll(selector) || []);
}, []);
}
function isElementNode(node) {
return !!node && node.nodeType === 1;
}
function getNestedRootFromNode(node) {
var currentNode = node;
if (!currentNode) {
return null;
}
if (!isElementNode(currentNode)) {
currentNode = currentNode.parentElement || currentNode.parentNode;
}
if (!isElementNode(currentNode) || !currentNode.closest) {
return null;
}
return currentNode.closest('[' + ROOT_ATTR + ']');
}
function isNodeInsideNestedRoot(node) {
return !!getNestedRootFromNode(node);
}
function nodeContainsNestedRoot(node) {
if (!isElementNode(node)) {
return false;
}
if (node.getAttribute && node.getAttribute(ROOT_ATTR)) {
return true;
}
return !!(node.querySelector && node.querySelector('[' + ROOT_ATTR + ']'));
}
function isEditableFieldNode(node) {
var tagName;
var typeName;
if (!isElementNode(node)) {
return false;
}
if (node.isContentEditable) {
return true;
}
tagName = (node.tagName || '').toUpperCase();
if (tagName === 'TEXTAREA') {
return true;
}
if (tagName !== 'INPUT') {
return false;
}
typeName = normalizeValue(node.getAttribute('type')).toLowerCase();
return ['button', 'submit', 'reset', 'checkbox', 'radio', 'file'].indexOf(typeName) === -1;
}
function isEditingInsideRuntime(runtime) {
var activeNode = document.activeElement;
var rootNode = getNestedRootFromNode(activeNode);
if (!rootNode) {
return false;
}
if (normalizeValue(rootNode.getAttribute(ROOT_ATTR)) !== runtime.runtimeKey) {
return false;
}
return isEditableFieldNode(activeNode);
}
function findFieldHostNode(node) {
if (!node || !node.closest) {
return null;
}
return node.closest('td') ||
node.closest('.wea-form-cell') ||
node.closest('.wea-form-item') ||
node.closest('.wea-mobile-form-item') ||
node.closest('.mobile-wf-form-item') ||
node.closest('.am-list-item') ||
node.closest('[data-fieldname]') ||
node.closest('[data-fieldmark]') ||
node.parentElement ||
null;
}
function findFieldValueTd(fieldName, fieldMarkWithRow) {
var escapedFieldName = escapeSelectorValue(fieldName);
var escapedFieldMark = escapeSelectorValue(fieldMarkWithRow);
var selectorList = [
'[data-fieldname="' + escapedFieldName + '"][data-fieldmark="' + escapedFieldMark + '"]',
'[data-fieldmark="' + escapedFieldMark + '"]',
'#' + escapedFieldMark,
'[name="' + escapedFieldMark + '"]'
];
var selectorIndex;
var nodeList;
var nodeIndex;
var hostNode;
for (selectorIndex = 0; selectorIndex < selectorList.length; selectorIndex += 1) {
nodeList = queryAllSafe(selectorList[selectorIndex]);
for (nodeIndex = 0; nodeIndex < nodeList.length; nodeIndex += 1) {
hostNode = findFieldHostNode(nodeList[nodeIndex]);
if (hostNode) {
return hostNode;
}
}
}
return null;
}
function hideOriginalHostChildren(hostCell, rootNode) {
if (!hostCell) {
return;
}
Array.prototype.slice.call(hostCell.children || []).forEach(function (child) {
if (child === rootNode) {
return;
}
if (child.nodeType !== 1) {
return;
}
child.style.display = 'none';
});
}
function setNodeWidth(node, widthPx) {
if (!node || !widthPx) {
return;
}
node.style.width = widthPx;
node.style.minWidth = widthPx;
node.style.maxWidth = widthPx;
node.setAttribute('width', widthPx.replace(/px$/i, ''));
}
function applyChildSelectionColumnWidth(runtime, rootNode) {
var widthValue;
if (!runtime || !rootNode) {
return;
}
widthValue = runtime.selectionColumnWidth;
queryAllSafe('th.ant-table-selection-column,td.ant-table-selection-column', rootNode).forEach(function (node) {
setNodeWidth(node, widthValue);
node.style.paddingLeft = '0';
node.style.paddingRight = '0';
node.style.textAlign = 'center';
});
queryAllSafe('table', rootNode).forEach(function (tableNode) {
var firstCol = tableNode.querySelector('colgroup col:first-child');
if (firstCol) {
setNodeWidth(firstCol, widthValue);
}
});
}
// 样式区:注入嵌套面板所需的 PC 表格、移动端列表、校验提示和字段后缀样式。
function injectStyleOnce() {
var styleNode;
var cssText;
var state = getGlobalState();
if (state.styleInjected || document.getElementById(STYLE_ID)) {
state.styleInjected = true;
return;
}
cssText = [
'.wf-input-required{right:1px}',
'.ns-nested-panel{border:1.2px solid #dee6fe;border-radius:0;background:#fff;padding:0;margin:0;box-shadow:none;}',
'.ns-nested-panel--embedded{padding:0;}',
'.ns-nested-panel__toolbar{display:flex;align-items:center;justify-content:space-between;gap:8px;margin:0;padding:2px 6px 2px 0;background:#ffffff;border-bottom:1.2px solid #dee6fe;}',
'.ns-nested-panel__toolbar--plain{justify-content:flex-end;margin:0;}',
'.ns-nested-panel__toolbar-actions{display:flex;align-items:center;gap:8px;}',
'.ns-nested-panel__title{font-size:13px;font-weight:600;color:#0f335c;}',
'.ns-nested-panel__button{border:0;border-radius:2px;background:#7a6cf0;color:#fff;padding:6px 12px;cursor:pointer;font-size:12px;line-height:1.2;}',
'.ns-nested-panel__button--ghost{background:#9aa3b2;color:#fff;}',
'.ns-nested-panel__button--icon{width:20px;height:20px;padding:0;display:inline-flex;align-items:center;justify-content:center;border-radius:2px;font-size:12px;}',
'.ns-nested-panel__button:disabled{opacity:.45;cursor:not-allowed;}',
'.ns-nested-panel__empty{padding:12px 8px;text-align:center;color:#7c8ba0;background:#fff;border:1px dashed #dee6fe;border-radius:0;}',
'.ns-nested-panel__table{width:100%;border-collapse:collapse;background:#fff;border-radius:0;overflow:hidden;table-layout:fixed;}',
'.ns-nested-panel__table-wrap{background:#fff;border-radius:0;overflow:hidden;}',
'.ns-nested-panel__table-wrap .ant-table{border:0;border-radius:0;background:#fff;}',
'.ns-nested-panel__table-wrap .ant-table-small{border-radius:0;}',
'.ns-nested-panel__table-wrap .ant-table-content,.ns-nested-panel__table-wrap .ant-table-body,.ns-nested-panel__table-wrap .ant-table-body-outer{margin:0 !important;}',
'.ns-nested-panel__table-wrap .ant-table table{table-layout:fixed;border-collapse:collapse;}',
'.ns-nested-panel__table-wrap .ant-table-thead > tr > th,.ns-nested-panel__table-wrap .ant-table-tbody > tr > td{border-right:1.2px solid #dee6fe;border-bottom:1.2px solid #dee6fe;padding:4px 6px;font-size:12px;vertical-align:middle;background:#fff;}',
'.ns-nested-panel__table-wrap .ant-table-thead > tr > th{background:#eef3ff;color:#32557e;font-weight:600;}',
'.ns-nested-panel__table-wrap .ant-table-thead > tr > th:last-child,.ns-nested-panel__table-wrap .ant-table-tbody > tr > td:last-child{border-right:0;}',
'.ns-nested-panel__table-wrap .ant-table-tbody > tr:last-child > td{border-bottom:0;}',
'.ns-nested-panel__table-wrap .ant-table-selection-column{width:var(--ns-selection-column-width,22px) !important;min-width:var(--ns-selection-column-width,22px) !important;max-width:var(--ns-selection-column-width,22px) !important;padding-left:0 !important;padding-right:0 !important;text-align:center;}',
'.ns-nested-panel__table-wrap .ant-table-thead > tr > .ant-table-selection-column,.ns-nested-panel__table-wrap .ant-table-tbody > tr > .ant-table-selection-column{width:var(--ns-selection-column-width,22px) !important;min-width:var(--ns-selection-column-width,22px) !important;max-width:var(--ns-selection-column-width,22px) !important;}',
'.ns-nested-panel__table-wrap .ant-table-selection-column .ant-checkbox-wrapper{display:flex;align-items:center;justify-content:center;width:100%;margin:0;}',
'.ns-nested-panel__table-wrap .ant-table-selection-column .ant-checkbox{transform:scale(.9);transform-origin:center;}',
'.ns-nested-panel__table th,.ns-nested-panel__table td{border-right:1.2px solid #dee6fe;border-bottom:1.2px solid #dee6fe;padding:4px 6px;text-align:left;font-size:12px;vertical-align:middle;}',
'.ns-nested-panel__table th{background:#eef3ff;color:#32557e;font-weight:600;}',
'.ns-nested-panel__table th:last-child,.ns-nested-panel__table td:last-child{border-right:0;}',
'.ns-nested-panel__table tr:last-child td{border-bottom:0;}',
'.ns-nested-panel__field{min-width:0;min-height:100%;align-items:center;}',
'.ns-nested-panel__field--colored{color:var(--ns-field-text-color);}',
'.ns-nested-panel__field--colored input,.ns-nested-panel__field--colored textarea,.ns-nested-panel__field--colored .ant-input,.ns-nested-panel__field--colored .ant-select-selection-selected-value,.ns-nested-panel__field--colored .browserName,.ns-nested-panel__field--colored .wea-browser-span,.ns-nested-panel__field--colored .wf-form-control,.ns-nested-panel__field--colored span{color:var(--ns-field-text-color) !important;}',
'.ns-nested-panel__field--suffix{min-width:0;}',
'.ns-suffix-field-host{display:inline-flex !important;align-items:center;gap:4px;vertical-align:middle;padding:0 !important;}',
'.ns-suffix-field-host input,.ns-suffix-field-host textarea,.ns-suffix-field-host .ant-input{width:var(--ns-field-input-width,auto) !important;padding:0 !important;}',
'.ns-suffix-input-text{color:#667085;font-size:12px;line-height:1;pointer-events:none;flex:0 0 auto;}',
'.ns-nested-panel__required-mark.wf-input-required{position:absolute !important;right:0 !important;top:50% !important;transform:translateY(-50%) !important;color:#ff4d4f !important;font-size:20px !important;font-weight:600 !important;line-height:1 !important;margin-left:0 !important;z-index:2;}',
'.ns-nested-panel__op{display:flex;gap:8px;align-items:center;white-space:nowrap;}',
'.ns-nested-panel__link{color:#0f6cbd;cursor:pointer;background:none;border:0;padding:0;font-size:12px;}',
'.ns-nested-panel__summary{margin-top:0;padding:4px 8px;display:flex;justify-content:flex-end;gap:10px;align-items:center;color:#334e68;font-size:12px;border-top:1.2px solid #dee6fe;}',
'.ns-nested-panel__badge{display:inline-flex;align-items:center;padding:0;border-radius:0;font-weight:600;background:none;}',
'.ns-nested-panel__badge--ok{color:#111111;}',
'.ns-nested-panel__badge--error{color:#d92d20;}',
'.ns-nested-mobile-panel{border:1px solid #dee6fe;background:#fff;margin:0;padding:0;}',
'.ns-nested-mobile-panel__toolbar{display:flex;justify-content:flex-end;gap:8px;padding:4px 6px;border-bottom:1px solid #dee6fe;background:#f7f9ff;}',
'.ns-nested-mobile-panel__list{display:flex;flex-direction:column;padding:0;}',
'.ns-nested-mobile-panel__row{display:grid;align-items:center;min-height:46px;border-bottom:1px solid #dee6fe;background:#fff;}',
'.ns-nested-mobile-panel__row:last-child{border-bottom:0;}',
'.ns-nested-mobile-panel__select-cell{display:flex;align-items:center;justify-content:center;min-width:0;height:100%;border-right:1px solid #edf1f5;}',
'.ns-nested-mobile-panel__cell{min-width:0;height:100%;display:flex;align-items:center;padding:4px 8px;border-right:1px solid #edf1f5;box-sizing:border-box;}',
'.ns-nested-mobile-panel__cell--error{align-items:flex-start;justify-content:center;flex-direction:column;}',
'.ns-nested-mobile-panel__cell:last-child{border-right:0;}',
'.ns-nested-mobile-panel__control{min-width:0;width:100%;}',
'.ns-nested-mobile-panel__control .ns-nested-panel__field{width:100%;min-height:0;display:block;position:relative;padding-right:16px;box-sizing:border-box;}',
'.ns-nested-mobile-panel__control input,.ns-nested-mobile-panel__control textarea,.ns-nested-mobile-panel__control .ant-input{width:100%;max-width:100%;min-height:26px;box-sizing:border-box;border:1px solid #cfd8e3;border-radius:2px;padding:2px 4px;font-size:12px;background:#fff;}',
'.ns-nested-mobile-panel__control textarea{min-height:34px;resize:vertical;}',
'.ns-nested-mobile-panel__error{display:inline-flex;align-items:center;margin-top:4px;padding:2px 8px;border:1px solid #ff9b9b;border-radius:8px;background:#fff4f4;color:#e5484d;font-size:12px;line-height:1.4;}',
'.ns-nested-mobile-panel__empty{padding:14px 8px;text-align:center;color:#7c8ba0;font-size:12px;}',
'.ns-nested-mobile-panel__summary{display:flex;flex-wrap:wrap;justify-content:flex-end;gap:8px;padding:7px 8px;border-top:1px solid #dee6fe;font-size:12px;color:#334e68;}',
'.ns-nested-modal{position:fixed;inset:0;display:none;align-items:center;justify-content:center;z-index:100000;background:rgba(15,35,59,.32);}',
'.ns-nested-modal--show{display:flex;}',
'.ns-nested-modal__dialog{width:min(760px,92vw);max-height:86vh;overflow:auto;background:#fff;border-radius:14px;box-shadow:0 16px 44px rgba(15,35,59,.22);}',
'.ns-nested-modal__header{padding:18px 20px 12px;border-bottom:1.2px solid #edf1f5;font-size:16px;font-weight:700;color:#0f335c;}',
'.ns-nested-modal__body{padding:18px 20px;display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:14px 16px;}',
'.ns-nested-modal__field{display:flex;flex-direction:column;gap:6px;}',
'.ns-nested-modal__label{font-size:12px;color:#334e68;font-weight:600;}',
'.ns-nested-modal__required{color:#d92d20;margin-left:4px;}',
'.ns-nested-modal__input,.ns-nested-modal__textarea{width:100%;border:1.2px solid #c9d6e5;border-radius:8px;padding:9px 10px;font-size:13px;box-sizing:border-box;}',
'.ns-nested-modal__textarea{min-height:96px;resize:vertical;}',
'.ns-nested-modal__footer{padding:14px 20px 20px;display:flex;justify-content:flex-end;gap:10px;}',
'.ns-nested-modal__error{grid-column:1/-1;font-size:12px;color:#b42318;display:none;}',
'.ns-nested-modal__error--show{display:block;}'
].join('');
styleNode = document.createElement('style');
styleNode.id = STYLE_ID;
styleNode.type = 'text/css';
styleNode.appendChild(document.createTextNode(cssText));
document.head.appendChild(styleNode);
state.styleInjected = true;
}
// 字段配置解析:从 childFields 中读取标题、类型、列宽、后缀、颜色和输入框尺寸。
function findChildFieldLabel(childFieldList, fieldName) {
var fieldMeta = findChildFieldMeta(childFieldList, fieldName);
return fieldMeta ? normalizeValue(fieldMeta.label || fieldMeta.fieldName) : '';
}
function findChildFieldMeta(childFieldList, fieldName) {
var normalizedFieldName = normalizeValue(fieldName);
var index;
var fieldMeta;
for (index = 0; index < childFieldList.length; index += 1) {
fieldMeta = childFieldList[index];
if (normalizeValue(fieldMeta.fieldName) === normalizedFieldName) {
return fieldMeta;
}
}
return null;
}
function findChildFieldSuffixText(childFieldList, fieldName) {
var fieldMeta = findChildFieldMeta(childFieldList, fieldName);
return fieldMeta ? normalizeValue(fieldMeta.suffixText) : '';
}
function parseStyleColor(styleConfig) {
var matched;
if (!styleConfig) {
return '';
}
if (typeof styleConfig === 'string') {
matched = styleConfig.match(/(?:^|;)\s*color\s*:\s*([^;]+)/i);
return matched && matched[1] ? normalizeValue(matched[1]) : '';
}
if (typeof styleConfig === 'object' && styleConfig.color !== undefined && styleConfig.color !== null) {
return normalizeValue(styleConfig.color);
}
return '';
}
function resolveChildFieldTextColor(fieldConfig) {
return normalizeValue(fieldConfig.textColor || fieldConfig.color || parseStyleColor(fieldConfig.style));
}
function resolveFieldSuffixText(fieldConfig) {
var suffixText = normalizeValue(fieldConfig.suffixText);
if (suffixText) {
return suffixText;
}
return fieldConfig && fieldConfig.showPercent ? '%' : '';
}
function resolveFieldInputWidth(fieldConfig) {
return normalizeCssSize(fieldConfig && (fieldConfig.inputWidth || fieldConfig.width));
}
function resolveFieldColumnWidth(fieldConfig) {
return normalizeCssSize(fieldConfig && fieldConfig.columnWidth);
}
function resolveSelectionColumnWidth(config) {
var widthValue = config && (
config.selectionCheckboxColumnWidth !== undefined && config.selectionCheckboxColumnWidth !== null && config.selectionCheckboxColumnWidth !== ''
? config.selectionCheckboxColumnWidth
: config.selectionColumnWidth !== undefined && config.selectionColumnWidth !== null && config.selectionColumnWidth !== ''
? config.selectionColumnWidth
: config.rowSelectionWidth
);
return normalizeCssSize(widthValue || 22);
}
// 运行时编译:把命中的配置整理成字段 ID、权限、公式和渲染参数。
function buildRuntime(baseInfo, pageType, workflowRule, mapping) {
var state = getGlobalState();
var prevRuntime;
var childFieldList = [];
var formulaRules = [];
var summaryFieldName = normalizeValue(mapping.summaryFieldName);
var summaryFieldLabel = normalizeValue(mapping.summaryFieldLabel);
var summaryFieldSuffixText = '';
var runtimeKey = [
normalizeValue(baseInfo.workflowid),
normalizeValue(baseInfo.requestid),
normalizeValue(mapping.mappingKey || mapping.parentDetailTableMark || 'mapping')
].join('|');
prevRuntime = state.runtimeMap[runtimeKey];
var childFieldConfigs = toArray(mapping.childFields);
var formulaConfigs = toArray(mapping.formulaRules);
var index;
var fieldConfig;
var fieldId;
var formulaRule;
var targetDetailMark;
var targetFieldId;
if (!mapping || !mapping.parentDetailTableMark || !mapping.childDetailTableMark) {
return null;
}
for (index = 0; index < childFieldConfigs.length; index += 1) {
fieldConfig = childFieldConfigs[index];
fieldId = toDetailFieldId(fieldConfig.fieldName, mapping.childDetailTableMark);
if (!fieldId) {
continue;
}
childFieldList.push({
fieldName: normalizeValue(fieldConfig.fieldName),
fieldId: fieldId,
label: normalizeValue(fieldConfig.label || fieldConfig.fieldName),
type: normalizeValue(fieldConfig.type || 'text'),
required: !!fieldConfig.required,
decimalScale: fieldConfig.decimalScale,
placeholder: normalizeValue(fieldConfig.placeholder),
useBrowserShowName: !!fieldConfig.useBrowserShowName,
defaultValue: fieldConfig.defaultValue,
textColor: resolveChildFieldTextColor(fieldConfig),
suffixText: resolveFieldSuffixText(fieldConfig),
inputWidth: resolveFieldInputWidth(fieldConfig),
columnWidth: resolveFieldColumnWidth(fieldConfig)
});
}
if (!summaryFieldLabel) {
summaryFieldLabel = findChildFieldLabel(childFieldList, summaryFieldName);
}
summaryFieldSuffixText = findChildFieldSuffixText(childFieldList, summaryFieldName);
for (index = 0; index < formulaConfigs.length; index += 1) {
formulaRule = formulaConfigs[index];
targetDetailMark = normalizeValue(formulaRule.scope) === 'parent'
? mapping.parentDetailTableMark
: mapping.childDetailTableMark;
targetFieldId = toDetailFieldId(formulaRule.targetFieldName, targetDetailMark);
if (!targetFieldId) {
continue;
}
formulaRules.push({
scope: normalizeValue(formulaRule.scope || 'child') === 'parent' ? 'parent' : 'child',
targetFieldName: normalizeValue(formulaRule.targetFieldName),
targetFieldId: targetFieldId,
expression: normalizeValue(formulaRule.expression),
decimalScale: formulaRule.decimalScale
});
}
var runtime = {
runtimeKey: runtimeKey,
pageType: pageType,
workflowId: normalizeValue(baseInfo.workflowid),
requestId: normalizeValue(baseInfo.requestid),
nodeId: getCurrentNodeIdSafe(baseInfo),
workflowRule: workflowRule,
mapping: mapping,
layoutStore: getLayoutStoreSafe(),
parentDetailTableMark: normalizeValue(mapping.parentDetailTableMark),
childDetailTableMark: normalizeValue(mapping.childDetailTableMark),
renderFieldName: normalizeValue(mapping.renderFieldName),
renderFieldId: toDetailFieldId(mapping.renderFieldName, mapping.parentDetailTableMark),
parentLinkFieldName: normalizeValue(mapping.parentLinkFieldName),
parentLinkFieldId: toDetailFieldId(mapping.parentLinkFieldName, mapping.parentDetailTableMark),
childLinkFieldName: normalizeValue(mapping.childLinkFieldName),
childLinkFieldId: toDetailFieldId(mapping.childLinkFieldName, mapping.childDetailTableMark),
childRowKeyFieldName: normalizeValue(mapping.childRowKeyFieldName),
childRowKeyFieldId: toDetailFieldId(mapping.childRowKeyFieldName, mapping.childDetailTableMark),
parentTitleFieldName: normalizeValue(mapping.parentTitleFieldName),
parentTitleFieldId: toDetailFieldId(mapping.parentTitleFieldName, mapping.parentDetailTableMark),
summaryFieldName: summaryFieldName,
summaryFieldLabel: summaryFieldLabel || summaryFieldName,
summaryFieldSuffixText: summaryFieldSuffixText,
summaryFieldId: toDetailFieldId(summaryFieldName, mapping.childDetailTableMark),
hideChildTableHeader: mapping.hideChildTableHeader !== false,
selectionColumnWidth: resolveSelectionColumnWidth(mapping),
selectionEnabled: resolveNodeAccessEnabled(
mapping,
['childSelectionEnabledNodeIdList', 'selectionEnabledNodeIdList', 'selectableNodeIdList'],
getCurrentNodeIdSafe(baseInfo),
true
),
actionEnabled: resolveNodeAccessEnabled(
mapping,
['childActionEnabledNodeIdList', 'actionEnabledNodeIdList', 'actionNodeIdList', 'toolbarEnabledNodeIdList'],
getCurrentNodeIdSafe(baseInfo),
true
),
editableSuffixEnabled: resolveNodeAccessEnabled(
mapping,
['editableSuffixNodeIdList', 'createNodeIdList'],
getCurrentNodeIdSafe(baseInfo),
false
),
lastObservedParentRowCount: prevRuntime && isFiniteNumber(prevRuntime.lastObservedParentRowCount)
? prevRuntime.lastObservedParentRowCount
: getAllDetailRowIndexList(mapping.parentDetailTableMark).length,
lastObservedChildRowCount: prevRuntime && isFiniteNumber(prevRuntime.lastObservedChildRowCount)
? prevRuntime.lastObservedChildRowCount
: getAllDetailRowIndexList(mapping.childDetailTableMark).length,
expectedTotal: mapping.expectedTotal === undefined ? null : Number(mapping.expectedTotal),
summaryTolerance: mapping.summaryTolerance === undefined ? 0.0001 : Number(mapping.summaryTolerance),
validateOnSubmit: mapping.validateOnSubmit !== false,
hideRenderFieldEditor: mapping.hideRenderFieldEditor !== false,
autoCleanupOrphanChildren: !!mapping.autoCleanupOrphanChildren,
selectedRowMap: prevRuntime && prevRuntime.selectedRowMap ? prevRuntime.selectedRowMap : {},
childFieldList: childFieldList,
formulaRules: formulaRules
};
if (!runtime.renderFieldId || !runtime.parentLinkFieldId || !runtime.childLinkFieldId) {
log('runtime skipped because required fields are missing', runtime.runtimeKey, runtime);
return null;
}
return runtime;
}
// 父子关联:给一级行和二级行补稳定 key,用隐藏字段维护从属关系。
function generateRowKey(prefix) {
return [
prefix || 'NK',
new Date().getTime(),
Math.floor(Math.random() * 1000000)
].join('_');
}
function getParentRowFieldMark(runtime, fieldId, rowIndex) {
return fieldId + '_' + rowIndex;
}
function getChildRowFieldMark(runtime, fieldId, rowIndex) {
return fieldId + '_' + rowIndex;
}
function ensureParentLinkValue(runtime, parentRowIndex) {
var fieldMark = getParentRowFieldMark(runtime, runtime.parentLinkFieldId, parentRowIndex);
var currentValue = normalizeValue(getFieldValueSafe(fieldMark));
if (currentValue) {
return currentValue;
}
currentValue = generateRowKey('P');
setFieldValueSafe(fieldMark, currentValue);
return currentValue;
}
function ensureChildRowKeyValue(runtime, childRowIndex) {
if (!runtime.childRowKeyFieldId) {
return '';
}
var fieldMark = getChildRowFieldMark(runtime, runtime.childRowKeyFieldId, childRowIndex);
var currentValue = normalizeValue(getFieldValueSafe(fieldMark));
if (currentValue) {
return currentValue;
}
currentValue = generateRowKey('C');
setFieldValueSafe(fieldMark, currentValue);
return currentValue;
}
function getParentRowTitle(runtime, parentRowIndex) {
if (!runtime.parentTitleFieldId) {
return '';
}
return normalizeValue(
getFieldValueSafe(getParentRowFieldMark(runtime, runtime.parentTitleFieldId, parentRowIndex))
);
}
function getFieldDisplayValue(fieldMeta, fieldMark) {
var showName;
var rawValue;
if (fieldMeta.useBrowserShowName) {
showName = normalizeValue(getBrowserShowNameSafe(fieldMark));
if (showName) {
return showName;
}
}
rawValue = getFieldValueSafe(fieldMark);
if (fieldMeta.type === 'number' && rawValue !== '') {
return formatNumber(rawValue, fieldMeta.decimalScale);
}
return normalizeValue(rawValue);
}
// 数据建模:读取真实二级明细行,转换成面板渲染和校验需要的数据。
function buildChildRowData(runtime, childRowIndex) {
var data = {
rowIndex: childRowIndex,
rowKey: runtime.childRowKeyFieldId ? ensureChildRowKeyValue(runtime, childRowIndex) : '',
parentLinkValue: normalizeValue(
getFieldValueSafe(getChildRowFieldMark(runtime, runtime.childLinkFieldId, childRowIndex))
),
fieldValueMap: {},
fieldDisplayMap: {}
};
runtime.childFieldList.forEach(function (fieldMeta) {
var fieldMark = getChildRowFieldMark(runtime, fieldMeta.fieldId, childRowIndex);
data.fieldValueMap[fieldMeta.fieldName] = normalizeValue(getFieldValueSafe(fieldMark));
data.fieldDisplayMap[fieldMeta.fieldName] = getFieldDisplayValue(fieldMeta, fieldMark);
});
if (runtime.summaryFieldId) {
data.summaryValue = normalizeValue(
getFieldValueSafe(getChildRowFieldMark(runtime, runtime.summaryFieldId, childRowIndex))
);
} else {
data.summaryValue = '';
}
return data;
}
function buildChildRowsByParent(runtime) {
var childRowsByParent = {};
var rowIndexList = getAllDetailRowIndexList(runtime.childDetailTableMark);
rowIndexList.forEach(function (rowIndex) {
var rowData = buildChildRowData(runtime, rowIndex);
if (!rowData.parentLinkValue) {
return;
}
if (!childRowsByParent[rowData.parentLinkValue]) {
childRowsByParent[rowData.parentLinkValue] = [];
}
childRowsByParent[rowData.parentLinkValue].push(rowData);
});
return childRowsByParent;
}
// 公式处理:解析表达式里的父子字段 token,并把计算结果写回真实字段。
function parseFormulaTokens(expression) {
var regexp = /\{([^}]+)\}/g;
var match;
var result = [];
while ((match = regexp.exec(expression))) {
result.push(normalizeValue(match[1]));
}
return uniqueArray(result);
}
function getFormulaTokenValue(runtime, token, parentRowIndex, childRowIndex) {
var fieldName = token;
var scope = 'auto';
var fieldId;
var fieldMark;
if (token.indexOf('parent.') === 0) {
scope = 'parent';
fieldName = token.substring(7);
} else if (token.indexOf('child.') === 0) {
scope = 'child';
fieldName = token.substring(6);
}
if ((scope === 'auto' || scope === 'child') && childRowIndex !== '' && childRowIndex !== undefined && childRowIndex !== null) {
fieldId = toDetailFieldId(fieldName, runtime.childDetailTableMark);
if (fieldId) {
fieldMark = getChildRowFieldMark(runtime, fieldId, childRowIndex);
return getFieldValueSafe(fieldMark);
}
}
fieldId = toDetailFieldId(fieldName, runtime.parentDetailTableMark);
if (fieldId && parentRowIndex !== '' && parentRowIndex !== undefined && parentRowIndex !== null) {
fieldMark = getParentRowFieldMark(runtime, fieldId, parentRowIndex);
return getFieldValueSafe(fieldMark);
}
return '';
}
function evaluateFormulaExpression(runtime, expression, parentRowIndex, childRowIndex) {
var compiledExpression = String(expression || '').replace(/\{([^}]+)\}/g, function (all, token) {
var value = getFormulaTokenValue(runtime, normalizeValue(token), parentRowIndex, childRowIndex);
var num = parseNumber(value);
return isFiniteNumber(num) ? String(num) : '0';
});
return safeCall(function () {
return Function('"use strict";return (' + compiledExpression + ');')();
}, '');
}
function applyFormulaRules(runtime) {
var parentRows = getAllDetailRowIndexList(runtime.parentDetailTableMark);
var childRowsByParent = buildChildRowsByParent(runtime);
runtime.formulaRules.forEach(function (formulaRule) {
if (!formulaRule.expression) {
return;
}
if (formulaRule.scope === 'parent') {
parentRows.forEach(function (parentRowIndex) {
var nextValue = evaluateFormulaExpression(runtime, formulaRule.expression, parentRowIndex, '');
var output = nextValue === '' ? '' : formatNumber(nextValue, formulaRule.decimalScale);
setFieldValueSafe(
getParentRowFieldMark(runtime, formulaRule.targetFieldId, parentRowIndex),
output
);
});
return;
}
parentRows.forEach(function (parentRowIndex) {
var parentLinkValue = ensureParentLinkValue(runtime, parentRowIndex);
var childRows = childRowsByParent[parentLinkValue] || [];
childRows.forEach(function (childRow) {
var nextValue = evaluateFormulaExpression(
runtime,
formulaRule.expression,
parentRowIndex,
childRow.rowIndex
);
var output = nextValue === '' ? '' : formatNumber(nextValue, formulaRule.decimalScale);
setFieldValueSafe(
getChildRowFieldMark(runtime, formulaRule.targetFieldId, childRow.rowIndex),
output
);
});
});
});
}
// 数据清理与汇总:可清理孤儿子行,并按父行统计二级明细合计。
function cleanupOrphanChildren(runtime) {
var parentRows = getAllDetailRowIndexList(runtime.parentDetailTableMark);
var parentKeyMap = {};
var childRows = getAllDetailRowIndexList(runtime.childDetailTableMark);
var deletedRowMap = {};
var parentRowIndex;
var childRowIndex;
var parentKey;
if (!runtime.autoCleanupOrphanChildren) {
return;
}
parentRows.forEach(function (parentRowIndex) {
parentKeyMap[ensureParentLinkValue(runtime, parentRowIndex)] = true;
});
childRows.forEach(function (childRowIndex) {
parentKey = normalizeValue(
getFieldValueSafe(getChildRowFieldMark(runtime, runtime.childLinkFieldId, childRowIndex))
);
if (parentKey && !parentKeyMap[parentKey]) {
delDetailRowSafe(runtime.childDetailTableMark, childRowIndex);
deletedRowMap[String(childRowIndex)] = true;
}
});
Object.keys(runtime.selectedRowMap || {}).forEach(function (rowKey) {
runtime.selectedRowMap[rowKey] = toArray(runtime.selectedRowMap[rowKey]).filter(function (selectedRowIndex) {
return !deletedRowMap[String(selectedRowIndex)];
});
});
}
function calculateSummary(childRows, runtime) {
var sum = 0;
childRows.forEach(function (childRow) {
sum += parseNumber(childRow.summaryValue);
});
var expected = runtime.expectedTotal;
var passed = true;
if (expected !== null && expected !== undefined && expected !== '' && isFiniteNumber(expected)) {
passed = Math.abs(sum - expected) <= runtime.summaryTolerance;
}
return {
sum: sum,
expected: expected,
passed: passed
};
}
// 渲染环境:复用泛微页面已有 React、ReactDOM、组件库和 store 上下文。
function getReactEnv() {
return {
React: window.React,
ReactDOM: window.ReactDOM,
Provider: window.mobxReact && window.mobxReact.Provider,
WeaTable: window.ecCom && window.ecCom.WeaTable,
Button: window.antd && window.antd.Button
};
}
function buildProviderStoreProps(globalStore, layoutStore) {
var props = {};
if (globalStore) {
props.globalStore = globalStore;
props.wfGlobalStore = globalStore;
}
if (layoutStore) {
props.layoutStore = layoutStore;
props.wfLayoutStore = layoutStore;
}
return props;
}
// 选择状态:记录每个一级行下勾选的二级行,供批量删除使用。
function getSelectedChildRows(runtime, parentRowIndex) {
return toArray(runtime.selectedRowMap[String(parentRowIndex)]).map(function (rowIndex) {
return normalizeValue(rowIndex);
}).filter(function (rowIndex) {
return !!rowIndex;
});
}
function setSelectedChildRows(runtime, parentRowIndex, rowIndexList) {
runtime.selectedRowMap[String(parentRowIndex)] = uniqueArray(
toArray(rowIndexList).map(function (rowIndex) {
return normalizeValue(rowIndex);
}).filter(function (rowIndex) {
return !!rowIndex;
})
);
}
function buildChildDataSource(runtime, childRows) {
return childRows.map(function (childRow) {
var fieldMarkMap = {};
runtime.childFieldList.forEach(function (fieldMeta) {
fieldMarkMap[fieldMeta.fieldName] = getChildRowFieldMark(runtime, fieldMeta.fieldId, childRow.rowIndex);
});
return {
key: String(childRow.rowIndex),
__rowIndex: String(childRow.rowIndex),
__fieldMarkMap: fieldMarkMap
};
});
}
// 移动端辅助:生成移动端网格布局,刷新合计行并展示字段级校验错误。
function buildMobileGridTemplate(runtime) {
var columnList = [];
if (runtime.selectionEnabled) {
columnList.push(normalizeCssSize(runtime.selectionColumnWidth || 28) || '28px');
}
runtime.childFieldList.forEach(function (fieldMeta) {
columnList.push(fieldMeta.columnWidth || 'minmax(96px,1fr)');
});
return columnList.join(' ');
}
function updateMobileSummaryNode(runtime, parentRowIndex) {
var rootNode;
var parentKey;
var childRows;
var summaryInfo;
var sumNode;
var expectedNode;
var statusNode;
if (!runtime || runtime.pageType !== 'mobile') {
return;
}
rootNode = document.querySelector(
'[' + ROOT_ATTR + '="' + escapeSelectorValue(runtime.runtimeKey) + '"][data-parent-row="' + escapeSelectorValue(parentRowIndex) + '"]'
);
if (!rootNode) {
return;
}
parentKey = ensureParentLinkValue(runtime, parentRowIndex);
childRows = buildChildRowsByParent(runtime)[parentKey] || [];
summaryInfo = calculateSummary(childRows, runtime);
sumNode = rootNode.querySelector('[data-ns-mobile-summary="sum"]');
expectedNode = rootNode.querySelector('[data-ns-mobile-summary="expected"]');
statusNode = rootNode.querySelector('[data-ns-mobile-summary="status"]');
if (sumNode) {
sumNode.textContent = '当前汇总:' + appendSuffixText(formatNumber(summaryInfo.sum, 2), runtime.summaryFieldSuffixText);
}
if (expectedNode) {
expectedNode.textContent = '目标值:' + appendSuffixText(formatNumber(summaryInfo.expected, 2), runtime.summaryFieldSuffixText);
}
if (statusNode) {
statusNode.textContent = summaryInfo.passed ? '校验通过' : '校验未通过';
statusNode.className = summaryInfo.passed
? 'ns-nested-panel__badge ns-nested-panel__badge--ok'
: 'ns-nested-panel__badge ns-nested-panel__badge--error';
}
}
function scheduleMobileSummaryUpdate(runtime, parentRowIndex) {
if (!runtime || runtime.pageType !== 'mobile') {
return;
}
[0, 80].forEach(function (delay) {
setTimeout(function () {
updateMobileSummaryNode(runtime, parentRowIndex);
}, delay);
});
}
function getMobileFieldCell(runtime, parentRowIndex, childRowIndex, fieldName) {
var rootNode;
if (!runtime || runtime.pageType !== 'mobile') {
return null;
}
rootNode = document.querySelector(
'[' + ROOT_ATTR + '="' + escapeSelectorValue(runtime.runtimeKey) + '"][data-parent-row="' + escapeSelectorValue(parentRowIndex) + '"]'
);
if (!rootNode) {
return null;
}
return rootNode.querySelector(
'[data-ns-child-row-index="' + escapeSelectorValue(childRowIndex) + '"][data-ns-child-field-name="' + escapeSelectorValue(fieldName) + '"]'
);
}
function clearMobileValidationErrors(runtime) {
var selector;
if (!runtime || runtime.pageType !== 'mobile') {
return;
}
selector = '[' + ROOT_ATTR + '="' + escapeSelectorValue(runtime.runtimeKey) + '"]';
queryAllSafe(selector).forEach(function (rootNode) {
queryAllSafe('.ns-nested-mobile-panel__cell--error', rootNode).forEach(function (cellNode) {
cellNode.classList.remove('ns-nested-mobile-panel__cell--error');
});
queryAllSafe('.ns-nested-mobile-panel__error', rootNode).forEach(function (errorNode) {
if (errorNode.parentNode) {
errorNode.parentNode.removeChild(errorNode);
}
});
});
}
function clearMobileFieldValidationError(runtime, parentRowIndex, childRowIndex, fieldName) {
var cellNode = getMobileFieldCell(runtime, parentRowIndex, childRowIndex, fieldName);
if (!cellNode) {
return;
}
cellNode.classList.remove('ns-nested-mobile-panel__cell--error');
queryAllSafe('.ns-nested-mobile-panel__error', cellNode).forEach(function (errorNode) {
if (errorNode.parentNode) {
errorNode.parentNode.removeChild(errorNode);
}
});
}
function updateMobileRequiredMark(runtime, parentRowIndex, childRowIndex, fieldName, value) {
var cellNode = getMobileFieldCell(runtime, parentRowIndex, childRowIndex, fieldName);
var markNode;
if (!cellNode) {
return;
}
markNode = cellNode.querySelector('.ns-nested-panel__required-mark');
if (!markNode) {
return;
}
markNode.style.display = normalizeValue(value) ? 'none' : 'inline-block';
}
function showMobileFieldValidationError(runtime, parentRowIndex, childRowIndex, fieldName, message) {
var cellNode = getMobileFieldCell(runtime, parentRowIndex, childRowIndex, fieldName);
var errorNode;
var inputNode;
if (!cellNode) {
return false;
}
clearMobileFieldValidationError(runtime, parentRowIndex, childRowIndex, fieldName);
cellNode.classList.add('ns-nested-mobile-panel__cell--error');
errorNode = document.createElement('div');
errorNode.className = 'ns-nested-mobile-panel__error';
errorNode.textContent = message;
cellNode.appendChild(errorNode);
safeCall(function () {
cellNode.scrollIntoView({
block: 'center',
inline: 'center',
behavior: 'smooth'
});
}, null);
inputNode = cellNode.querySelector('input,textarea,select');
if (inputNode && typeof inputNode.focus === 'function') {
setTimeout(function () {
safeCall(function () {
inputNode.focus();
}, null);
}, 260);
}
return true;
}
// 新增与删除:新增子行后补关联字段、默认值和唯一 key,删除时同步真实明细表。
function initializeChildRow(runtime, parentRowIndex, childRowIndex) {
var parentLinkValue;
if (!childRowIndex) {
return false;
}
parentLinkValue = ensureParentLinkValue(runtime, parentRowIndex);
setFieldValueSafe(
getChildRowFieldMark(runtime, runtime.childLinkFieldId, childRowIndex),
parentLinkValue
);
if (runtime.childRowKeyFieldId) {
ensureChildRowKeyValue(runtime, childRowIndex);
}
runtime.childFieldList.forEach(function (fieldMeta) {
if (fieldMeta.defaultValue !== undefined && fieldMeta.defaultValue !== null && fieldMeta.defaultValue !== '') {
setFieldValueSafe(
getChildRowFieldMark(runtime, fieldMeta.fieldId, childRowIndex),
fieldMeta.defaultValue
);
}
});
setSelectedChildRows(runtime, parentRowIndex, []);
applyFormulaRules(runtime);
log('child row initialized', {
runtimeKey: runtime.runtimeKey,
nodeId: runtime.nodeId,
parentRowIndex: parentRowIndex,
childRowIndex: childRowIndex,
childRowCount: getAllDetailRowIndexList(runtime.childDetailTableMark).length
});
renderRuntime(runtime);
scheduleRefresh(runtime, 20);
scheduleMobileRenderRecovery(runtime);
return true;
}
function finalizePendingChildRow(runtime, parentRowIndex, beforeList, attempt) {
var childRowIndex = findNewDetailRowIndex(runtime.childDetailTableMark, beforeList);
if (childRowIndex) {
log('child row finalized', {
runtimeKey: runtime.runtimeKey,
nodeId: runtime.nodeId,
parentRowIndex: parentRowIndex,
childRowIndex: childRowIndex,
attempt: attempt
});
initializeChildRow(runtime, parentRowIndex, childRowIndex);
return;
}
if (attempt >= 6) {
log('child row finalize failed', {
runtimeKey: runtime.runtimeKey,
nodeId: runtime.nodeId,
parentRowIndex: parentRowIndex,
beforeList: beforeList,
currentList: getAllDetailRowIndexList(runtime.childDetailTableMark)
});
showBlockMessage('新增隐藏二级明细行失败,请检查当前流程页面是否已完成加载。');
return;
}
setTimeout(function () {
finalizePendingChildRow(runtime, parentRowIndex, beforeList, attempt + 1);
}, [30, 60, 120, 200, 320, 500][attempt] || 200);
}
function deleteSelectedChildRows(runtime, parentRowIndex) {
var selectedRows = getSelectedChildRows(runtime, parentRowIndex);
if (!selectedRows.length) {
return;
}
log('child delete click', {
runtimeKey: runtime.runtimeKey,
nodeId: runtime.nodeId,
parentRowIndex: parentRowIndex,
selectedRows: selectedRows
});
selectedRows.forEach(function (childRowIndex) {
delDetailRowSafe(runtime.childDetailTableMark, childRowIndex);
});
setSelectedChildRows(runtime, parentRowIndex, []);
renderRuntime(runtime);
scheduleRefresh(runtime, 20);
scheduleMobileRenderRecovery(runtime);
}
function addChildRowForParent(runtime, parentRowIndex) {
var beforeList = getAllDetailRowIndexList(runtime.childDetailTableMark);
var childRowIndex = addDetailRowSafe(runtime.childDetailTableMark);
var currentList = getAllDetailRowIndexList(runtime.childDetailTableMark);
log('child add click', {
runtimeKey: runtime.runtimeKey,
nodeId: runtime.nodeId,
parentRowIndex: parentRowIndex,
beforeList: beforeList,
addDetailRowResult: childRowIndex,
currentList: currentList
});
if (!childRowIndex && !currentList.length) {
showBlockMessage('新增隐藏二级明细行失败,请检查当前流程页面是否已完成加载。');
return;
}
if (childRowIndex && beforeList.indexOf(String(childRowIndex)) < 0) {
initializeChildRow(runtime, parentRowIndex, childRowIndex);
return;
}
finalizePendingChildRow(runtime, parentRowIndex, beforeList, 0);
}
function getChildFieldWrapperProps(fieldMeta, fieldMark) {
var style = {};
var className = 'ns-nested-panel__field';
var suffixText = normalizeValue(fieldMeta && fieldMeta.suffixText);
var inputWidth = normalizeCssSize(fieldMeta && fieldMeta.inputWidth);
var result;
if (fieldMeta && fieldMeta.textColor) {
className += ' ns-nested-panel__field--colored';
style['--ns-field-text-color'] = fieldMeta.textColor;
}
if (inputWidth) {
style['--ns-field-input-width'] = inputWidth;
}
if (suffixText) {
className += ' ns-nested-panel__field--suffix';
}
result = {
className: className,
style: style
};
if (fieldMark) {
result['data-ns-field-mark'] = fieldMark;
}
if (suffixText) {
result['data-ns-suffix-text'] = suffixText;
}
return result;
}
// 字段渲染:把真实表单字段搬进自定义面板,必要时使用降级输入控件。
function buildChildFieldNode(fieldMeta, fieldMark, React, runtime, parentRowIndex, childRowIndex) {
var wrapperProps = getChildFieldWrapperProps(fieldMeta, fieldMark);
var fieldContent;
if (runtime && runtime.pageType === 'mobile') {
fieldContent = buildFallbackFieldNode(fieldMeta, fieldMark, React, runtime, parentRowIndex, childRowIndex);
} else if (window.WfForm && typeof window.WfForm.generateFieldContentComp === 'function') {
fieldContent = window.WfForm.generateFieldContentComp(fieldMark);
} else {
fieldContent = buildFallbackFieldNode(fieldMeta, fieldMark, React, runtime, parentRowIndex, childRowIndex);
}
return React.createElement(
'div',
wrapperProps,
fieldContent,
runtime && runtime.pageType === 'mobile' && fieldMeta.required
? React.createElement('span', {
className: 'wf-input-required ns-nested-panel__required-mark',
style: {
display: normalizeValue(getFieldValueSafe(fieldMark)) ? 'none' : 'inline-block'
}
}, '*')
: null
);
}
function shouldRenderFallbackTextarea(fieldMeta, runtime) {
var fieldName = normalizeValue(fieldMeta && fieldMeta.fieldName).toLowerCase();
if (fieldMeta && fieldMeta.type === 'textarea') {
return true;
}
return !!(runtime && runtime.pageType === 'mobile' && (fieldName === 'action_name' || fieldName === 'changed_action_name'));
}
function buildFallbackFieldNode(fieldMeta, fieldMark, React, runtime, parentRowIndex, childRowIndex) {
var value = getFieldValueSafe(fieldMark);
var inputType = fieldMeta.type === 'number' ? 'number' : fieldMeta.type === 'date' ? 'date' : 'text';
var commonProps = {
defaultValue: value,
placeholder: fieldMeta.placeholder || '',
required: !!fieldMeta.required,
onChange: function onFallbackChange(event) {
var value = event && event.target ? event.target.value : '';
setFieldValueSafe(fieldMark, value);
updateMobileRequiredMark(runtime, parentRowIndex, childRowIndex, fieldMeta.fieldName, value);
if (normalizeValue(value)) {
clearMobileFieldValidationError(runtime, parentRowIndex, childRowIndex, fieldMeta.fieldName);
}
if (runtime) {
applyFormulaRules(runtime);
scheduleMobileSummaryUpdate(runtime, parentRowIndex);
}
},
onBlur: function onFallbackBlur(event) {
var value = event && event.target ? event.target.value : '';
setFieldValueSafe(fieldMark, value);
updateMobileRequiredMark(runtime, parentRowIndex, childRowIndex, fieldMeta.fieldName, value);
if (normalizeValue(value)) {
clearMobileFieldValidationError(runtime, parentRowIndex, childRowIndex, fieldMeta.fieldName);
}
if (runtime) {
applyFormulaRules(runtime);
scheduleMobileSummaryUpdate(runtime, parentRowIndex);
scheduleRefresh(runtime, 30, true);
}
}
};
if (shouldRenderFallbackTextarea(fieldMeta, runtime)) {
return React.createElement('textarea', commonProps);
}
commonProps.type = inputType;
if (fieldMeta.type === 'date') {
commonProps.onClick = function onFallbackDateClick(event) {
if (event && typeof event.stopPropagation === 'function') {
event.stopPropagation();
}
openDatePickerSafe(event && event.currentTarget);
};
}
if (fieldMeta.type === 'number') {
commonProps.step = fieldMeta.decimalScale ? String(1 / Math.pow(10, Number(fieldMeta.decimalScale))) : 'any';
}
return React.createElement('input', commonProps);
}
// 字段装饰:给只读和编辑态字段补单位后缀,处理泛微异步重渲染后的样式恢复。
function getReadonlyTextNodeByFieldMark(fieldMark, hostCell) {
var candidateIdList;
var index;
var targetNode;
var editableDescendant;
if (!fieldMark) {
return null;
}
candidateIdList = [
fieldMark + 'span',
fieldMark + '_Ospan'
];
for (index = 0; index < candidateIdList.length; index += 1) {
targetNode = safeCall(function () {
return document.getElementById(candidateIdList[index]);
}, null);
if (!targetNode) {
continue;
}
if (hostCell && hostCell.contains && !hostCell.contains(targetNode)) {
continue;
}
editableDescendant = safeCall(function () {
return targetNode.querySelector('input:not([type="hidden"]), textarea, select');
}, null);
if (editableDescendant) {
continue;
}
return targetNode;
}
return null;
}
function findFieldInputHost(hostCell, fieldMark) {
var targetNode;
var readonlyNode;
if (!hostCell) {
return null;
}
if (fieldMark) {
readonlyNode = getReadonlyTextNodeByFieldMark(fieldMark, hostCell);
if (readonlyNode && hostCell.contains(readonlyNode)) {
return readonlyNode;
}
readonlyNode = safeCall(function () {
return document.getElementById(fieldMark);
}, null);
if (readonlyNode && hostCell.contains(readonlyNode) && isEditableInputNode(readonlyNode)) {
return readonlyNode;
}
}
targetNode = hostCell.querySelector('input:not([type="hidden"])') ||
hostCell.querySelector('textarea') ||
hostCell.querySelector('span[id$="_Ospan"]') ||
hostCell.querySelector('.browserName') ||
hostCell.querySelector('.wea-browser-span') ||
hostCell.querySelector('.wf-form-control') ||
hostCell.querySelector('.ant-select-selection-selected-value');
if (!targetNode) {
return null;
}
return targetNode;
}
function isEditableInputNode(node) {
var tagName;
var typeName;
if (!node || !node.tagName) {
return false;
}
tagName = String(node.tagName).toUpperCase();
if (tagName === 'TEXTAREA') {
return true;
}
if (tagName !== 'INPUT') {
return false;
}
typeName = normalizeValue(node.getAttribute('type') || node.type).toLowerCase();
return typeName !== 'hidden';
}
function openDatePickerSafe(inputNode) {
if (!inputNode) {
return;
}
safeCall(function () {
if (typeof inputNode.focus === 'function') {
inputNode.focus();
}
if (typeof inputNode.showPicker === 'function') {
inputNode.showPicker();
}
}, null);
}
function getSuffixHostNode(inputNode) {
if (!inputNode) {
return null;
}
return inputNode.parentElement || inputNode;
}
function ensureSuffixTextNode(hostNode, inputNode, suffixText) {
var suffixNode;
var nextNode;
if (!hostNode || !inputNode) {
return null;
}
suffixNode = hostNode.querySelector('.ns-suffix-input-text[data-ns-generated="1"]');
if (!suffixNode) {
suffixNode = document.createElement('span');
suffixNode.className = 'ns-suffix-input-text';
suffixNode.setAttribute('data-ns-generated', '1');
}
suffixNode.textContent = suffixText;
nextNode = inputNode.nextSibling;
if (suffixNode.parentNode !== hostNode || suffixNode.previousSibling !== inputNode) {
if (suffixNode.parentNode) {
suffixNode.parentNode.removeChild(suffixNode);
}
hostNode.insertBefore(suffixNode, nextNode);
}
return suffixNode;
}
function applyReadonlyTextSuffix(hostCell, fieldMark, suffixText, runtime) {
var readonlyNode;
var hostNode;
var textValue;
if (!hostCell || !fieldMark || !suffixText) {
return false;
}
if (runtime && runtime.editableSuffixEnabled) {
return false;
}
readonlyNode = getReadonlyTextNodeByFieldMark(fieldMark, hostCell);
if (!readonlyNode) {
return false;
}
hostNode = getSuffixHostNode(readonlyNode);
if (hostNode) {
queryAllSafe('.ns-suffix-input-text[data-ns-generated="1"]', hostNode).forEach(function (suffixNode) {
if (suffixNode.parentNode) {
suffixNode.parentNode.removeChild(suffixNode);
}
});
}
textValue = normalizeValue(readonlyNode.textContent || readonlyNode.innerText);
if (!textValue) {
return true;
}
if (textValue.slice(-suffixText.length) !== suffixText) {
readonlyNode.textContent = textValue + suffixText;
}
return true;
}
function applyConfiguredReadonlyTextSuffixes(runtime, rootNode) {
var rowIndexList;
if (!runtime || runtime.editableSuffixEnabled) {
return;
}
rowIndexList = getAllDetailRowIndexList(runtime.childDetailTableMark);
runtime.childFieldList.forEach(function (fieldMeta) {
var suffixText = normalizeValue(fieldMeta && fieldMeta.suffixText);
if (!suffixText || !fieldMeta.fieldId) {
return;
}
rowIndexList.forEach(function (rowIndex) {
var fieldMark = getChildRowFieldMark(runtime, fieldMeta.fieldId, rowIndex);
var readonlyNode = getReadonlyTextNodeByFieldMark(fieldMark, rootNode);
var textValue;
var hostNode;
if (!readonlyNode) {
return;
}
hostNode = getSuffixHostNode(readonlyNode);
if (hostNode) {
queryAllSafe('.ns-suffix-input-text[data-ns-generated="1"]', hostNode).forEach(function (suffixNode) {
if (suffixNode.parentNode) {
suffixNode.parentNode.removeChild(suffixNode);
}
});
}
textValue = normalizeValue(readonlyNode.textContent || readonlyNode.innerText);
if (!textValue) {
return;
}
if (textValue.slice(-suffixText.length) !== suffixText) {
readonlyNode.textContent = textValue + suffixText;
}
});
});
}
function decorateFieldHostWithSuffix(hostCell, suffixText, inputWidth, fieldMark, runtime) {
var normalizedSuffix = normalizeValue(suffixText);
var normalizedWidth = normalizeCssSize(inputWidth);
var valueNode;
var hostNode;
if (!hostCell || !normalizedSuffix) {
return;
}
if (applyReadonlyTextSuffix(hostCell, fieldMark, normalizedSuffix, runtime)) {
return;
}
valueNode = findFieldInputHost(hostCell, fieldMark);
hostNode = getSuffixHostNode(valueNode);
if (!valueNode || !hostNode || !hostNode.classList) {
return;
}
hostNode.classList.add('ns-suffix-field-host');
if (normalizedWidth && isEditableInputNode(valueNode)) {
hostNode.style.setProperty('--ns-field-input-width', normalizedWidth);
valueNode.style.width = normalizedWidth;
}
if (isEditableInputNode(valueNode)) {
valueNode.style.padding = '0';
}
hostNode.style.padding = '0';
ensureSuffixTextNode(hostNode, valueNode, normalizedSuffix);
}
function applyChildFieldSuffixDecorators(rootNode, runtime) {
if (!rootNode) {
return;
}
queryAllSafe('.ns-nested-panel__field[data-ns-suffix-text]', rootNode).forEach(function (fieldNode) {
decorateFieldHostWithSuffix(
fieldNode,
fieldNode.getAttribute('data-ns-suffix-text'),
fieldNode.style.getPropertyValue('--ns-field-input-width'),
fieldNode.getAttribute('data-ns-field-mark'),
runtime
);
});
}
function scheduleChildFieldDecorators(rootNode, runtime) {
[0, 80, 180, 360, 700, 1200].forEach(function (delay) {
setTimeout(function () {
if (!rootNode || !rootNode.parentNode) {
return;
}
applyChildSelectionColumnWidth(runtime, rootNode);
applyChildFieldSuffixDecorators(rootNode, runtime);
applyConfiguredReadonlyTextSuffixes(runtime, rootNode);
}, delay);
});
}
function ensureChildDecoratorObserver(rootNode, runtime) {
if (!rootNode || !window.MutationObserver || rootNode.__nsChildDecoratorObserver) {
return;
}
rootNode.__nsChildDecoratorTimer = null;
rootNode.__nsChildDecoratorObserver = new MutationObserver(function () {
clearTimeout(rootNode.__nsChildDecoratorTimer);
rootNode.__nsChildDecoratorTimer = setTimeout(function () {
if (!rootNode || !rootNode.parentNode) {
return;
}
applyChildSelectionColumnWidth(runtime, rootNode);
applyChildFieldSuffixDecorators(rootNode, runtime);
applyConfiguredReadonlyTextSuffixes(runtime, rootNode);
}, 30);
});
rootNode.__nsChildDecoratorObserver.observe(rootNode, {
childList: true,
subtree: true
});
}
function destroyChildDecoratorObserver(rootNode) {
if (!rootNode) {
return;
}
clearTimeout(rootNode.__nsChildDecoratorTimer);
rootNode.__nsChildDecoratorTimer = null;
if (rootNode.__nsChildDecoratorObserver) {
rootNode.__nsChildDecoratorObserver.disconnect();
rootNode.__nsChildDecoratorObserver = null;
}
}
function cleanupSuffixHostNode(hostNode) {
var inputNode;
if (!hostNode || !hostNode.classList) {
return;
}
hostNode.classList.remove('ns-suffix-field-host');
hostNode.style.removeProperty('--ns-field-input-width');
hostNode.style.padding = '';
inputNode = hostNode.querySelector('input,textarea,.ant-input');
if (inputNode) {
inputNode.style.width = '';
inputNode.style.padding = '';
}
}
function cleanupNonRuntimeSuffixDecorators() {
queryAllSafe('.ns-suffix-input-text[data-ns-generated="1"]').forEach(function (suffixNode) {
var nestedRoot = suffixNode.closest('[' + ROOT_ATTR + ']');
var hostNode = suffixNode.parentElement;
if (nestedRoot) {
return;
}
if (hostNode) {
hostNode.removeChild(suffixNode);
cleanupSuffixHostNode(hostNode);
}
});
}
// 降级渲染:组件库不可用时仍提供可操作的简易表格。
function renderFallbackTable(runtime, dataSource, React) {
var headerNode = runtime.hideChildTableHeader
? null
: React.createElement(
'thead',
null,
React.createElement(
'tr',
null,
runtime.childFieldList.map(function (fieldMeta) {
return React.createElement(
'th',
{
key: fieldMeta.fieldName,
style: fieldMeta.columnWidth ? { width: fieldMeta.columnWidth } : null
},
fieldMeta.label
);
})
)
);
return React.createElement(
'table',
{ className: 'ns-nested-panel__table' },
headerNode,
React.createElement(
'tbody',
null,
dataSource.map(function (record) {
return React.createElement(
'tr',
{ key: record.key },
runtime.childFieldList.map(function (fieldMeta) {
return React.createElement(
'td',
{
key: fieldMeta.fieldName,
style: fieldMeta.columnWidth ? { width: fieldMeta.columnWidth } : null
},
buildChildFieldNode(fieldMeta, record.__fieldMarkMap[fieldMeta.fieldName], React)
);
})
);
})
)
);
}
// 移动端渲染:用列表面板展示二级明细,包含新增、删除、勾选、字段编辑和汇总。
function renderMobileRuntimePanel(runtime, parentRowIndex, childRows, summaryInfo, rootNode) {
var reactEnv = getReactEnv();
var React = reactEnv.React;
var ReactDOM = reactEnv.ReactDOM;
var Provider = reactEnv.Provider;
var globalStore;
var layoutStore;
var selectedRows;
var dataSource;
var toolbarNode;
var contentNode;
var summaryNode;
var panelNode;
if (!React || !ReactDOM || !window.WfForm) {
return;
}
globalStore = safeCall(function () {
return window.WfForm.getGlobalStore ? window.WfForm.getGlobalStore() : null;
}, null);
layoutStore = getLayoutStoreSafe();
selectedRows = runtime.selectionEnabled ? getSelectedChildRows(runtime, parentRowIndex) : [];
dataSource = buildChildDataSource(runtime, childRows);
toolbarNode = React.createElement(
'div',
{ className: 'ns-nested-mobile-panel__toolbar' },
React.createElement(
'button',
{
type: 'button',
className: 'ns-nested-panel__button ns-nested-panel__button--icon',
disabled: !runtime.actionEnabled,
onClick: function onMobileAdd() {
if (!runtime.actionEnabled) {
return;
}
addChildRowForParent(runtime, parentRowIndex);
}
},
'+'
),
React.createElement(
'button',
{
type: 'button',
className: 'ns-nested-panel__button ns-nested-panel__button--ghost ns-nested-panel__button--icon',
disabled: !runtime.actionEnabled || !selectedRows.length,
onClick: function onMobileDelete() {
if (!runtime.actionEnabled) {
return;
}
deleteSelectedChildRows(runtime, parentRowIndex);
}
},
'-'
)
);
if (dataSource.length) {
contentNode = React.createElement(
'div',
{ className: 'ns-nested-mobile-panel__list' },
dataSource.map(function (record) {
var rowIndex = record.__rowIndex;
var checked = selectedRows.indexOf(rowIndex) >= 0;
return React.createElement(
'div',
{
key: record.key,
className: 'ns-nested-mobile-panel__row',
style: {
gridTemplateColumns: buildMobileGridTemplate(runtime)
}
},
runtime.selectionEnabled
? React.createElement(
'div',
{ className: 'ns-nested-mobile-panel__select-cell' },
React.createElement('input', {
type: 'checkbox',
disabled: !runtime.selectionEnabled,
checked: checked,
onChange: function onMobileSelect(event) {
var nextSelectedRows = selectedRows.slice();
var selectedIndex = nextSelectedRows.indexOf(rowIndex);
if (event && event.target && event.target.checked) {
if (selectedIndex < 0) {
nextSelectedRows.push(rowIndex);
}
} else if (selectedIndex >= 0) {
nextSelectedRows.splice(selectedIndex, 1);
}
setSelectedChildRows(runtime, parentRowIndex, nextSelectedRows);
renderRuntime(runtime);
scheduleRefresh(runtime, 0);
}
})
)
: null,
runtime.childFieldList.map(function (fieldMeta) {
return React.createElement(
'div',
{
key: fieldMeta.fieldName,
className: 'ns-nested-mobile-panel__cell',
'data-ns-child-row-index': rowIndex,
'data-ns-child-field-name': fieldMeta.fieldName,
onClick: function onMobileFieldCellClick(event) {
var inputNode;
if (fieldMeta.type !== 'date') {
return;
}
inputNode = event.currentTarget.querySelector('input[type="date"]');
openDatePickerSafe(inputNode);
}
},
React.createElement(
'div',
{ className: 'ns-nested-mobile-panel__control' },
buildChildFieldNode(fieldMeta, record.__fieldMarkMap[fieldMeta.fieldName], React, runtime, parentRowIndex, rowIndex)
)
);
})
);
})
);
} else {
contentNode = React.createElement(
'div',
{ className: 'ns-nested-mobile-panel__empty' },
'当前主行下还没有子明细,点击右上角加号直接新增。'
);
}
summaryNode = runtime.summaryFieldId && summaryInfo.expected !== null && summaryInfo.expected !== undefined && summaryInfo.expected !== ''
? React.createElement(
'div',
{ className: 'ns-nested-mobile-panel__summary' },
React.createElement('span', { 'data-ns-mobile-summary': 'sum' }, '当前汇总:' + appendSuffixText(formatNumber(summaryInfo.sum, 2), runtime.summaryFieldSuffixText)),
React.createElement('span', { 'data-ns-mobile-summary': 'expected' }, '目标值:' + appendSuffixText(formatNumber(summaryInfo.expected, 2), runtime.summaryFieldSuffixText)),
React.createElement(
'span',
{
'data-ns-mobile-summary': 'status',
className: summaryInfo.passed
? 'ns-nested-panel__badge ns-nested-panel__badge--ok'
: 'ns-nested-panel__badge ns-nested-panel__badge--error'
},
summaryInfo.passed ? '校验通过' : '校验未通过'
)
)
: null;
panelNode = React.createElement(
'div',
{ className: 'ns-nested-mobile-panel' },
toolbarNode,
contentNode,
summaryNode
);
if (Provider && globalStore) {
panelNode = React.createElement(
Provider,
buildProviderStoreProps(globalStore, layoutStore),
panelNode
);
}
ReactDOM.render(panelNode, rootNode);
ensureChildDecoratorObserver(rootNode, runtime);
scheduleChildFieldDecorators(rootNode, runtime);
}
// PC 端渲染:用表格面板展示二级明细,保留真实字段承载数据。
function renderRuntimePanel(runtime, parentRowIndex, childRows, summaryInfo, rootNode) {
if (runtime && runtime.pageType === 'mobile') {
renderMobileRuntimePanel(runtime, parentRowIndex, childRows, summaryInfo, rootNode);
return;
}
var reactEnv = getReactEnv();
var React = reactEnv.React;
var ReactDOM = reactEnv.ReactDOM;
var Provider = reactEnv.Provider;
var WeaTable = reactEnv.WeaTable;
var Button = reactEnv.Button;
var globalStore;
var layoutStore;
var selectedRows;
var dataSource;
var columns;
var rowSelection;
var tableNode;
var toolbarNode;
var summaryNode;
var contentNode;
var panelNode;
var tableWrapStyle;
if (!React || !ReactDOM || !window.WfForm) {
return;
}
globalStore = safeCall(function () {
return window.WfForm.getGlobalStore ? window.WfForm.getGlobalStore() : null;
}, null);
layoutStore = getLayoutStoreSafe();
selectedRows = runtime.selectionEnabled ? getSelectedChildRows(runtime, parentRowIndex) : [];
dataSource = buildChildDataSource(runtime, childRows);
tableWrapStyle = {
'--ns-selection-column-width': runtime.selectionColumnWidth
};
columns = runtime.childFieldList.map(function (fieldMeta) {
return {
title: fieldMeta.label,
dataIndex: fieldMeta.fieldName,
key: fieldMeta.fieldName,
width: fieldMeta.columnWidth || undefined,
render: function renderFieldCell(value, record) {
return buildChildFieldNode(fieldMeta, record.__fieldMarkMap[fieldMeta.fieldName], React);
}
};
});
rowSelection = childRows.length ? {
columnWidth: runtime.selectionColumnWidth,
selectedRowKeys: selectedRows,
getCheckboxProps: function getCheckboxProps() {
return {
disabled: !runtime.selectionEnabled
};
},
onChange: function onSelectionChange(selectedRowKeys) {
log('child selection change', {
runtimeKey: runtime.runtimeKey,
nodeId: runtime.nodeId,
parentRowIndex: parentRowIndex,
selectionEnabled: runtime.selectionEnabled,
selectedRowKeys: selectedRowKeys
});
if (!runtime.selectionEnabled) {
log('child selection blocked', {
runtimeKey: runtime.runtimeKey,
nodeId: runtime.nodeId,
parentRowIndex: parentRowIndex
});
return;
}
setSelectedChildRows(runtime, parentRowIndex, selectedRowKeys);
renderRuntime(runtime);
scheduleRefresh(runtime, 0);
}
} : null;
if (Button) {
toolbarNode = React.createElement(
'div',
{ className: 'ns-nested-panel__toolbar ns-nested-panel__toolbar--plain' },
React.createElement(
'div',
{ className: 'ns-nested-panel__toolbar-actions' },
React.createElement(Button, {
type: 'primary',
size: 'small',
icon: 'plus',
className: 'ns-nested-panel__button--icon',
disabled: !runtime.actionEnabled,
onClick: function onAdd() {
if (!runtime.actionEnabled) {
return;
}
addChildRowForParent(runtime, parentRowIndex);
}
}),
React.createElement(Button, {
size: 'small',
icon: 'minus',
className: 'ns-nested-panel__button--icon',
disabled: !runtime.actionEnabled || !selectedRows.length,
onClick: function onDelete() {
if (!runtime.actionEnabled) {
return;
}
deleteSelectedChildRows(runtime, parentRowIndex);
}
})
)
);
} else {
toolbarNode = React.createElement(
'div',
{ className: 'ns-nested-panel__toolbar ns-nested-panel__toolbar--plain' },
React.createElement(
'div',
{ className: 'ns-nested-panel__toolbar-actions' },
React.createElement(
'button',
{
type: 'button',
className: 'ns-nested-panel__button ns-nested-panel__button--icon',
disabled: !runtime.actionEnabled,
onClick: function onAddFallback() {
if (!runtime.actionEnabled) {
return;
}
addChildRowForParent(runtime, parentRowIndex);
}
},
'+'
),
React.createElement(
'button',
{
type: 'button',
className: 'ns-nested-panel__button ns-nested-panel__button--ghost ns-nested-panel__button--icon',
disabled: !runtime.actionEnabled || !selectedRows.length,
onClick: function onDeleteFallback() {
if (!runtime.actionEnabled) {
return;
}
deleteSelectedChildRows(runtime, parentRowIndex);
}
},
'-'
)
)
);
}
if (childRows.length) {
tableNode = WeaTable
? React.createElement(WeaTable, {
columns: columns,
dataSource: dataSource,
rowKey: 'key',
pagination: false,
rowSelection: rowSelection,
showHeader: !runtime.hideChildTableHeader,
bordered: true,
size: 'small'
})
: renderFallbackTable(runtime, dataSource, React);
contentNode = React.createElement(
'div',
{
className: 'ns-nested-panel__table-wrap',
style: tableWrapStyle
},
tableNode
);
} else {
contentNode = React.createElement(
'div',
{ className: 'ns-nested-panel__empty' },
'当前主行下还没有子明细,点击右上角加号直接新增。'
);
}
summaryNode = runtime.summaryFieldId && summaryInfo.expected !== null && summaryInfo.expected !== undefined && summaryInfo.expected !== ''
? React.createElement(
'div',
{ className: 'ns-nested-panel__summary' },
React.createElement('span', null, '当前汇总:' + appendSuffixText(formatNumber(summaryInfo.sum, 2), runtime.summaryFieldSuffixText)),
React.createElement('span', null, '目标值:' + appendSuffixText(formatNumber(summaryInfo.expected, 2), runtime.summaryFieldSuffixText)),
React.createElement(
'span',
{
className: summaryInfo.passed
? 'ns-nested-panel__badge ns-nested-panel__badge--ok'
: 'ns-nested-panel__badge ns-nested-panel__badge--error'
},
summaryInfo.passed ? '校验通过' : '校验未通过'
)
)
: null;
panelNode = React.createElement(
'div',
{ className: 'ns-nested-panel ns-nested-panel--embedded' },
toolbarNode,
contentNode,
summaryNode
);
if (Provider && globalStore) {
panelNode = React.createElement(
Provider,
buildProviderStoreProps(globalStore, layoutStore),
panelNode
);
}
ReactDOM.render(panelNode, rootNode);
ensureChildDecoratorObserver(rootNode, runtime);
scheduleChildFieldDecorators(rootNode, runtime);
}
// 面板挂载:在一级明细占位字段中创建根节点,并清理已经失效的面板。
function cleanupStaleRoots(runtime, currentParentRowList) {
var currentMap = {};
var selector = '[' + ROOT_ATTR + '="' + escapeSelectorValue(runtime.runtimeKey) + '"]';
var reactDOM = window.ReactDOM;
currentParentRowList.forEach(function (rowIndex) {
currentMap[String(rowIndex)] = true;
});
queryAllSafe(selector).forEach(function (rootNode) {
var rowIndex = normalizeValue(rootNode.getAttribute('data-parent-row'));
if (!currentMap[rowIndex] && rootNode.parentNode) {
destroyChildDecoratorObserver(rootNode);
if (reactDOM && typeof reactDOM.unmountComponentAtNode === 'function') {
safeCall(function () {
reactDOM.unmountComponentAtNode(rootNode);
}, null);
}
rootNode.parentNode.removeChild(rootNode);
}
});
Object.keys(runtime.selectedRowMap || {}).forEach(function (rowIndex) {
if (!currentMap[String(rowIndex)]) {
delete runtime.selectedRowMap[rowIndex];
}
});
}
function ensurePanelRoot(runtime, parentRowIndex) {
var fieldMarkWithRow = runtime.renderFieldId + '_' + parentRowIndex;
var hostCell = findFieldValueTd(runtime.renderFieldName, fieldMarkWithRow);
var selector;
var rootNode;
if (!hostCell) {
return null;
}
selector = '[' + ROOT_ATTR + '="' + escapeSelectorValue(runtime.runtimeKey) + '"][data-parent-row="' + escapeSelectorValue(parentRowIndex) + '"]';
rootNode = hostCell.querySelector(selector);
if (!rootNode) {
rootNode = document.createElement('div');
rootNode.setAttribute(ROOT_ATTR, runtime.runtimeKey);
rootNode.setAttribute(ROOT_KEY_ATTR, runtime.runtimeKey);
rootNode.setAttribute('data-parent-row', parentRowIndex);
hostCell.appendChild(rootNode);
}
if (runtime.hideRenderFieldEditor) {
hideOriginalHostChildren(hostCell, rootNode);
}
return rootNode;
}
// 主刷新流程:同步数据、执行公式、按父行分组二级明细,然后重新渲染面板。
function renderRuntime(runtime) {
var parentRowList = getAllDetailRowIndexList(runtime.parentDetailTableMark);
var childRowsByParent;
var missingRootCount = 0;
cleanupNonRuntimeSuffixDecorators();
cleanupOrphanChildren(runtime);
applyFormulaRules(runtime);
childRowsByParent = buildChildRowsByParent(runtime);
disableNativeRequiredForRuntime(runtime);
log('render runtime', {
runtimeKey: runtime.runtimeKey,
nodeId: runtime.nodeId,
parentRowCount: parentRowList.length,
childRowCount: getAllDetailRowIndexList(runtime.childDetailTableMark).length,
selectedRowMap: runtime.selectedRowMap
});
parentRowList.forEach(function (parentRowIndex) {
var parentKey = ensureParentLinkValue(runtime, parentRowIndex);
var childRows = childRowsByParent[parentKey] || [];
var summaryInfo = calculateSummary(childRows, runtime);
var rootNode = ensurePanelRoot(runtime, parentRowIndex);
if (!rootNode) {
missingRootCount += 1;
log('panel root missing', {
runtimeKey: runtime.runtimeKey,
nodeId: runtime.nodeId,
parentRowIndex: parentRowIndex,
renderFieldId: runtime.renderFieldId
});
return;
}
withInternalUpdate(function () {
renderRuntimePanel(runtime, parentRowIndex, childRows, summaryInfo, rootNode);
});
});
cleanupStaleRoots(runtime, parentRowList);
if (missingRootCount > 0) {
scheduleRefresh(runtime, 150, true);
}
}
// 刷新调度:防抖并延迟刷新,避免字段连续变化或移动端异步加载导致重复渲染。
function scheduleRefresh(runtime, delay, forceRender) {
var state = getGlobalState();
var timerKey = runtime.runtimeKey;
clearTimeout(state.refreshTimerMap[timerKey]);
state.refreshTimerMap[timerKey] = setTimeout(function () {
if (!forceRender && isEditingInsideRuntime(runtime)) {
log('refresh deferred while editing', {
runtimeKey: runtime.runtimeKey,
nodeId: runtime.nodeId,
delay: delay || 60
});
scheduleRefresh(runtime, 120, false);
return;
}
log('refresh execute', {
runtimeKey: runtime.runtimeKey,
nodeId: runtime.nodeId,
delay: delay || 60,
forceRender: !!forceRender
});
renderRuntime(runtime);
}, delay || 60);
}
function scheduleMobileRenderRecovery(runtime) {
if (!runtime || runtime.pageType !== 'mobile') {
return;
}
[80, 220, 520, 1000].forEach(function (delay) {
setTimeout(function () {
if (isEditingInsideRuntime(runtime)) {
return;
}
renderRuntime(runtime);
}, delay);
});
}
function validateRuntimeBeforeSubmit(runtime) {
var parentRows = getAllDetailRowIndexList(runtime.parentDetailTableMark);
var childRowsByParent = buildChildRowsByParent(runtime);
var index;
var childIndex;
var fieldIndex;
var parentRowIndex;
var parentKey;
var childRows;
var childRow;
var fieldMeta;
var summaryInfo;
var parentTitle;
if (!runtime.validateOnSubmit) {
return '';
}
clearMobileValidationErrors(runtime);
for (index = 0; index < parentRows.length; index += 1) {
parentRowIndex = parentRows[index];
parentKey = ensureParentLinkValue(runtime, parentRowIndex);
childRows = childRowsByParent[parentKey] || [];
for (childIndex = 0; childIndex < childRows.length; childIndex += 1) {
childRow = childRows[childIndex];
for (fieldIndex = 0; fieldIndex < runtime.childFieldList.length; fieldIndex += 1) {
fieldMeta = runtime.childFieldList[fieldIndex];
if (!fieldMeta.required) {
continue;
}
if (!normalizeValue(childRow.fieldValueMap[fieldMeta.fieldName])) {
parentTitle = getParentRowTitle(runtime, parentRowIndex) || ('一级明细行 #' + parentRowIndex);
if (showMobileFieldValidationError(
runtime,
parentRowIndex,
childRow.rowIndex,
fieldMeta.fieldName,
'“' + fieldMeta.label + '”未填写'
)) {
return INLINE_VALIDATION_MESSAGE;
}
return parentTitle + ' 的子明细第 ' + (childIndex + 1) + ' 行“' + fieldMeta.label + '”不能为空';
}
}
}
if (runtime.summaryFieldId) {
summaryInfo = calculateSummary(childRows, runtime);
if (!summaryInfo.passed) {
parentTitle = getParentRowTitle(runtime, parentRowIndex) || ('一级明细行 #' + parentRowIndex);
return parentTitle + ' 的“' + (runtime.summaryFieldLabel || runtime.summaryFieldName) + '”汇总必须等于 ' +
appendSuffixText(formatNumber(summaryInfo.expected, 2), runtime.summaryFieldSuffixText) +
',当前为 ' + appendSuffixText(formatNumber(summaryInfo.sum, 2), runtime.summaryFieldSuffixText);
}
}
}
return '';
}
function showBlockMessage(message) {
if (window.WfForm && typeof window.WfForm.showConfirm === 'function') {
window.WfForm.showConfirm(message, function () {});
return;
}
window.alert(message);
}
function bindSubmitCheckOnce(baseInfo) {
var state = getGlobalState();
var guardKey = [normalizeValue(baseInfo.workflowid), normalizeValue(baseInfo.requestid), getCurrentNodeIdSafe(baseInfo)].join('|');
var registered = false;
if (state.submitGuardMap[guardKey] || !window.WfForm || typeof window.WfForm.registerCheckEvent !== 'function') {
return;
}
function createHandler() {
return function (callback) {
var runtimeKeys = Object.keys(state.runtimeMap);
var index;
var runtime;
var message;
for (index = 0; index < runtimeKeys.length; index += 1) {
runtime = state.runtimeMap[runtimeKeys[index]];
if (
normalizeValue(runtime.workflowId) !== normalizeValue(baseInfo.workflowid) ||
normalizeValue(runtime.requestId) !== normalizeValue(baseInfo.requestid)
) {
continue;
}
message = validateRuntimeBeforeSubmit(runtime);
if (message) {
if (message !== INLINE_VALIDATION_MESSAGE) {
showBlockMessage(message);
}
return;
}
}
callback();
};
}
if (window.WfForm.OPER_SAVE !== undefined && window.WfForm.OPER_SAVE !== null) {
window.WfForm.registerCheckEvent(window.WfForm.OPER_SAVE, createHandler());
registered = true;
}
if (window.WfForm.OPER_SUBMIT !== undefined && window.WfForm.OPER_SUBMIT !== null) {
window.WfForm.registerCheckEvent(window.WfForm.OPER_SUBMIT, createHandler());
registered = true;
}
if (!registered) {
return;
}
state.submitGuardMap[guardKey] = true;
}
function bindRuntimeEvents(runtime) {
var state = getGlobalState();
var bindingKey = runtime.runtimeKey;
var boundFieldIdList = [];
var childFormulaTokens = [];
if (state.bindingMap[bindingKey]) {
return;
}
if (runtime.summaryFieldId) {
boundFieldIdList.push(runtime.summaryFieldId);
}
if (runtime.parentTitleFieldId) {
boundFieldIdList.push(runtime.parentTitleFieldId);
}
boundFieldIdList.push(runtime.parentLinkFieldId);
boundFieldIdList.push(runtime.childLinkFieldId);
if (runtime.childRowKeyFieldId) {
boundFieldIdList.push(runtime.childRowKeyFieldId);
}
runtime.formulaRules.forEach(function (formulaRule) {
boundFieldIdList.push(formulaRule.targetFieldId);
childFormulaTokens = childFormulaTokens.concat(parseFormulaTokens(formulaRule.expression));
});
childFormulaTokens.forEach(function (token) {
var fieldName = token;
if (token.indexOf('parent.') === 0) {
fieldName = token.substring(7);
boundFieldIdList.push(toDetailFieldId(fieldName, runtime.parentDetailTableMark));
return;
}
if (token.indexOf('child.') === 0) {
fieldName = token.substring(6);
}
boundFieldIdList.push(toDetailFieldId(fieldName, runtime.childDetailTableMark));
boundFieldIdList.push(toDetailFieldId(fieldName, runtime.parentDetailTableMark));
});
uniqueArray(boundFieldIdList).forEach(function (fieldId) {
if (!fieldId || !window.WfForm || typeof window.WfForm.bindDetailFieldChangeEvent !== 'function') {
return;
}
window.WfForm.bindDetailFieldChangeEvent(fieldId, function () {
if (isInternalUpdating()) {
return;
}
scheduleRefresh(runtime, 40);
});
});
state.bindingMap[bindingKey] = true;
}
function bindMutationObserver(runtime) {
var state = getGlobalState();
if (state.observerMap[runtime.runtimeKey] || !window.MutationObserver || !document.body) {
return;
}
state.observerMap[runtime.runtimeKey] = new MutationObserver(function (mutationList) {
var shouldRefresh = false;
var nestedRootTouched = false;
var parentRowCount;
var childRowCount;
if (isInternalUpdating()) {
return;
}
mutationList.forEach(function (mutation) {
var targetNode = mutation.target;
var addedNodes = Array.prototype.slice.call(mutation.addedNodes || []);
var removedNodes = Array.prototype.slice.call(mutation.removedNodes || []);
var allInsideNestedRoot = isNodeInsideNestedRoot(targetNode);
if (nodeContainsNestedRoot(targetNode)) {
nestedRootTouched = true;
}
if (!allInsideNestedRoot) {
shouldRefresh = true;
}
addedNodes.concat(removedNodes).forEach(function (node) {
if (nodeContainsNestedRoot(node)) {
nestedRootTouched = true;
}
if (!isNodeInsideNestedRoot(node)) {
allInsideNestedRoot = false;
}
});
if (!allInsideNestedRoot) {
shouldRefresh = true;
}
});
if (shouldRefresh) {
if (nestedRootTouched) {
log('nested root touched, force refresh requested', {
runtimeKey: runtime.runtimeKey,
nodeId: runtime.nodeId
});
scheduleRefresh(runtime, 80, true);
scheduleMobileRenderRecovery(runtime);
return;
}
parentRowCount = getAllDetailRowIndexList(runtime.parentDetailTableMark).length;
childRowCount = getAllDetailRowIndexList(runtime.childDetailTableMark).length;
if (
parentRowCount === runtime.lastObservedParentRowCount &&
childRowCount === runtime.lastObservedChildRowCount
) {
return;
}
runtime.lastObservedParentRowCount = parentRowCount;
runtime.lastObservedChildRowCount = childRowCount;
log('mutation refresh requested', {
runtimeKey: runtime.runtimeKey,
nodeId: runtime.nodeId,
parentRowCount: parentRowCount,
childRowCount: childRowCount
});
scheduleRefresh(runtime, 80, true);
}
});
state.observerMap[runtime.runtimeKey].observe(document.body, {
childList: true,
subtree: true
});
}
function initPage(pageType, forceLocation) {
var config = NESTED_DETAIL_ECODE_CONFIG;
var baseInfo;
var ruleIndex;
var workflowRuleList = toArray(config.workflowRules);
var workflowRule;
var mappingIndex;
var mappingList;
var mapping;
var runtime;
var state = getGlobalState();
if (!config || !config.enable || !window.WfForm) {
return;
}
if (!forceLocation && !isPageLocationMatched(pageType, config)) {
return;
}
baseInfo = getBaseInfoSafe();
if (!baseInfo || !baseInfo.workflowid) {
return;
}
injectStyleOnce();
bindSubmitCheckOnce(baseInfo);
for (ruleIndex = 0; ruleIndex < workflowRuleList.length; ruleIndex += 1) {
workflowRule = workflowRuleList[ruleIndex];
if (!isWorkflowMatched(workflowRule, baseInfo, pageType)) {
continue;
}
mappingList = toArray(workflowRule.mappings);
for (mappingIndex = 0; mappingIndex < mappingList.length; mappingIndex += 1) {
mapping = mappingList[mappingIndex];
runtime = buildRuntime(baseInfo, pageType, workflowRule, mapping);
if (!runtime) {
continue;
}
state.runtimeMap[runtime.runtimeKey] = runtime;
bindRuntimeEvents(runtime);
bindMutationObserver(runtime);
scheduleRefresh(runtime, 20);
}
}
}
function scheduleInitPage(pageType, delay, attempt, forceLocation) {
var state = getGlobalState();
var normalizedPageType = pageType || detectPageType();
var timerKey = 'init|' + normalizedPageType;
var nextAttempt = attempt || 0;
clearTimeout(state.initTimerMap[timerKey]);
state.initTimerMap[timerKey] = setTimeout(function () {
if (!forceLocation && !isPageLocationMatched(normalizedPageType, NESTED_DETAIL_ECODE_CONFIG)) {
return;
}
initPage(normalizedPageType, forceLocation);
if ((!window.WfForm || !getBaseInfoSafe()) && nextAttempt < 12) {
scheduleInitPage(normalizedPageType, Math.min(200 + nextAttempt * 150, 1500), nextAttempt + 1, forceLocation);
}
}, delay || 30);
}
function bindGlobalInitListenersOnce() {
var state = getGlobalState();
if (state.uiBound) {
return;
}
state.uiBound = true;
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', function () {
scheduleInitPage(detectPageType(), 80, 0);
});
} else {
scheduleInitPage(detectPageType(), 80, 0);
}
window.addEventListener('hashchange', function () {
scheduleInitPage(detectPageType(), 80, 0);
});
window.addEventListener('popstate', function () {
scheduleInitPage(detectPageType(), 80, 0);
});
}
function registerMobileMount(comName) {
if (!window.ecodeSDK) {
return;
}
if (typeof window.ecodeSDK.overwriteMobilePropsFnQueueMapSet === 'function') {
window.ecodeSDK.overwriteMobilePropsFnQueueMapSet(comName, {
fn: function (newProps) {
scheduleInitPage('mobile', 30, 0, true);
return newProps;
},
order: 1999,
desc: '一级明细嵌套二级明细 - 移动端流程页挂载'
});
}
if (typeof window.ecodeSDK.overwriteMobileClassFnQueueMapSet === 'function') {
window.ecodeSDK.overwriteMobileClassFnQueueMapSet(comName, {
fn: function (Com, newProps) {
scheduleInitPage('mobile', 30, 0, true);
return {
com: Com,
props: newProps
};
},
order: 1999,
desc: '一级明细嵌套二级明细 - 移动端流程页挂载'
});
}
}
window.NS_NESTED_DETAIL_DEBUG_INFO = function () {
var state = getGlobalState();
var baseInfo = getBaseInfoSafe();
var runtimeSnapshots = {};
Object.keys(state.runtimeMap || {}).forEach(function (runtimeKey) {
var runtime = state.runtimeMap[runtimeKey];
runtimeSnapshots[runtimeKey] = {
pageType: runtime.pageType,
workflowId: runtime.workflowId,
requestId: runtime.requestId,
nodeId: runtime.nodeId,
parentDetailTableMark: runtime.parentDetailTableMark,
childDetailTableMark: runtime.childDetailTableMark,
renderFieldId: runtime.renderFieldId,
parentRows: getAllDetailRowIndexList(runtime.parentDetailTableMark),
childRows: getAllDetailRowIndexList(runtime.childDetailTableMark),
rootCount: queryAllSafe('[' + ROOT_ATTR + '="' + escapeSelectorValue(runtime.runtimeKey) + '"]').length
};
});
return {
href: window.location.href,
hash: window.location.hash,
pathname: window.location.pathname,
userAgent: window.navigator && window.navigator.userAgent,
detectedPageType: detectPageType(),
desktopLocationMatched: isPageLocationMatched('desktop', NESTED_DETAIL_ECODE_CONFIG),
mobileLocationMatched: isPageLocationMatched('mobile', NESTED_DETAIL_ECODE_CONFIG),
hasEcodeSDK: !!window.ecodeSDK,
hasMobilePropsHook: !!(window.ecodeSDK && window.ecodeSDK.overwriteMobilePropsFnQueueMapSet),
hasMobileClassHook: !!(window.ecodeSDK && window.ecodeSDK.overwriteMobileClassFnQueueMapSet),
hasWfForm: !!window.WfForm,
baseInfo: baseInfo,
runtimeKeys: Object.keys(state.runtimeMap || {}),
bindingKeys: Object.keys(state.bindingMap || {}),
observerKeys: Object.keys(state.observerMap || {}),
runtimeSnapshots: runtimeSnapshots
};
};
if (window.ecodeSDK && typeof window.ecodeSDK.overwritePropsFnQueueMapSet === 'function') {
window.ecodeSDK.overwritePropsFnQueueMapSet('WeaReqTop', {
fn: function (newProps) {
scheduleInitPage('desktop', 30, 0);
return newProps;
},
order: 1999,
desc: '一级明细嵌套二级明细 - PC 流程页挂载'
});
}
[
'WeaReqTop',
'WorkflowReq',
'WorkflowForm',
'ReqForm',
'RequestForm',
'RequestDetail',
'TabPage',
'ListView',
'DetailList',
'Form'
].forEach(registerMobileMount);
bindGlobalInitListenersOnce();
})();