326 lines
10 KiB
JavaScript
326 lines
10 KiB
JavaScript
/**
|
||
* 管理表单页面字段标签处理器模块
|
||
* 负责处理 admin 表单页面上的字段和间距标签
|
||
*/
|
||
|
||
import {
|
||
getColumnWidths,
|
||
safelyInsertLabel,
|
||
safelyAppendLabel,
|
||
FieldTypeCheckerForAdminDom
|
||
} from '../../../utils/dom-utils.js';
|
||
import {
|
||
createFieldWithLabels,
|
||
createFieldLabels
|
||
} from './dom.js';
|
||
import { FieldTypeChecker } from '../../../utils/field-utils.js';
|
||
import { DOM_CLASSES, FIELD_TYPES, LAYOUT_TYPES } from '../../../utils/constants.js';
|
||
import { STYLES } from '../../../features/add-field-label/settings.js';
|
||
|
||
/**
|
||
* 用于处理 admin 表单页面上字段和间距标签的字段标签处理器类
|
||
*/
|
||
export class AdminFieldLabelProcessor {
|
||
/**
|
||
* 构造函数
|
||
* @param {Object} options - 配置选项
|
||
* @param {number} options.appId - App ID
|
||
* @param {string} options.pageType - 页面类型上下文(例如,'admin')
|
||
*/
|
||
constructor(options = {}) {
|
||
this.appId = options.appId;
|
||
this.pageType = options.pageType;
|
||
this.id = 0;
|
||
this.buildDomLayout();
|
||
}
|
||
|
||
/**
|
||
* 返回是否为预览模式
|
||
* @returns {boolean} 是否预览
|
||
*/
|
||
isPreview() {
|
||
return true;
|
||
}
|
||
|
||
buildDomLayout() {
|
||
this.domLayout = [];
|
||
this.fieldMap = [];
|
||
this.groupLayoutMap = {};
|
||
this.spacerMap = {};
|
||
const rows = document.querySelector(`.${DOM_CLASSES.CANVAS_ELEMENT} > .${DOM_CLASSES.CONTENT_ELEMENT}`).children;
|
||
|
||
this._buildDomLayout(this.domLayout, rows);
|
||
}
|
||
|
||
_buildDomLayout(layout, rowElements, parentGroupLayout) {
|
||
for (const row of rowElements) {
|
||
const rowLayout = {
|
||
id: ++this.id,
|
||
dom: row,
|
||
fields: []
|
||
};
|
||
row.setAttribute("data-kintone-helper-id", this.id)
|
||
layout.push(rowLayout);
|
||
this.fieldMap[rowLayout.id] = rowLayout
|
||
|
||
if (!!parentGroupLayout) {
|
||
delete this.fieldMap[rowLayout.id]
|
||
} else if (FieldTypeCheckerForAdminDom.isGroup(row)) {
|
||
rowLayout.isGroup = true;
|
||
const groupField = {
|
||
id: this.id,
|
||
dom: row,
|
||
isGroup: true,
|
||
layout: []
|
||
};
|
||
const rows = row.querySelector(`.${DOM_CLASSES.GROUP} .${DOM_CLASSES.CONTENT_ELEMENT}`).children;
|
||
this._buildDomLayout(groupField.layout, rows, groupField);
|
||
this.groupLayoutMap[groupField.id] = groupField;
|
||
continue;
|
||
} else if (FieldTypeCheckerForAdminDom.isSubtable(row)) {
|
||
rowLayout.isSubtable = true;
|
||
} else if (FieldTypeCheckerForAdminDom.isReferenceTable(row)) {
|
||
rowLayout.isReferenceTable = true;
|
||
delete this.fieldMap[rowLayout.id]
|
||
}
|
||
|
||
for (const field of row.children) {
|
||
if (!FieldTypeCheckerForAdminDom.isFieldElement(field)) {
|
||
continue;
|
||
}
|
||
const fieldLayout = {
|
||
id: ++this.id,
|
||
dom: field,
|
||
};
|
||
field.setAttribute("data-kintone-helper-id", this.id)
|
||
|
||
if (FieldTypeCheckerForAdminDom.isHr(field)) {
|
||
fieldLayout.isHr = true;
|
||
} else if (FieldTypeCheckerForAdminDom.isLabel(field)) {
|
||
fieldLayout.isLabel = true;
|
||
} else if (FieldTypeCheckerForAdminDom.isSpacer(field)) {
|
||
fieldLayout.isSpacer = true;
|
||
this.spacerMap[this.id] = fieldLayout;
|
||
}
|
||
|
||
if (!rowLayout.isSubtable && !parentGroupLayout) {
|
||
this.fieldMap[fieldLayout.id] = fieldLayout
|
||
}
|
||
rowLayout.fields.push(fieldLayout);
|
||
}
|
||
}
|
||
}
|
||
|
||
beforeProcess(rawFieldsMap, rawLayout, fields, spacers, targetLayout = this.domLayout) {
|
||
if (rawLayout.length !== targetLayout.length) {
|
||
// TODO throw error
|
||
return;
|
||
};
|
||
for (let i = 0; i < rawLayout.length; i++) {
|
||
const layoutRow = rawLayout[i];
|
||
const domLayoutRow = targetLayout[i];
|
||
const { fields: rawFields, ...layoutRowWithoutFields } = layoutRow;
|
||
Object.assign(domLayoutRow, layoutRowWithoutFields);
|
||
|
||
if (layoutRow.type === LAYOUT_TYPES.GROUP && domLayoutRow.isGroup) {
|
||
const domGroupLayoutRow = this.groupLayoutMap[domLayoutRow.id];
|
||
const { layout, ...layoutRowWithoutLayout } = layoutRow;
|
||
Object.assign(domGroupLayoutRow, layoutRowWithoutLayout);
|
||
this.beforeProcess(rawFieldsMap, layoutRow.layout, fields, spacers, domGroupLayoutRow.layout);
|
||
continue;
|
||
}
|
||
|
||
this.processFields(layoutRow.fields, domLayoutRow.fields, rawFieldsMap);
|
||
}
|
||
}
|
||
|
||
processFields(rowFields, domFields, rawFieldsMap) {
|
||
if (!rowFields || !domFields || rowFields.length !== domFields.length) {
|
||
// TODO error
|
||
return
|
||
}
|
||
for (let i = 0; i < rowFields.length; i++) {
|
||
Object.assign(domFields[i], rowFields[i]);
|
||
|
||
switch (rowFields[i].type) {
|
||
case FIELD_TYPES.REFERENCE_TABLE:
|
||
Object.assign(domFields[i], rawFieldsMap[rowFields[i].code]);
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 为子表字段元素添加标签
|
||
* @param {Object} field - 字段配置信息
|
||
* @param {HTMLElement} fieldElement - 字段 DOM 元素
|
||
*/
|
||
addSubtableLabel(field, fieldElement) {
|
||
try {
|
||
// 在表上方添加字段代码标签
|
||
const tableCodeLabel = createFieldWithLabels({
|
||
code: field.code,
|
||
type: field.type,
|
||
});
|
||
tableCodeLabel.style.display = 'block';
|
||
safelyInsertLabel(fieldElement, tableCodeLabel);
|
||
|
||
for (const col of field.fields) {
|
||
const fieldLabel = createFieldWithLabels({
|
||
code: col.code,
|
||
type: col.type,
|
||
});
|
||
fieldLabel.style.top = STYLES.SUBTABLE_LABEL_TOP;
|
||
safelyAppendLabel(col.dom.querySelector(`.${DOM_CLASSES.INSERT_LABEL_ELEMENT}`), fieldLabel);
|
||
}
|
||
|
||
|
||
} catch (error) {
|
||
console.error(`Failed to add label for subtable field ${field.code}:`, error);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 为引用表字段元素添加标签
|
||
* @param {Object} field - 字段配置信息
|
||
* @param {HTMLElement} fieldElement - 字段 DOM 元素
|
||
*/
|
||
addReferenceTableLabel(field, fieldElement) {
|
||
try {
|
||
// 在引用表上方添加字段代码标签
|
||
const tableCodeLabel = createFieldWithLabels({
|
||
code: field.code,
|
||
type: field.type,
|
||
});
|
||
tableCodeLabel.style.display = 'block';
|
||
|
||
safelyInsertLabel(fieldElement, tableCodeLabel);
|
||
|
||
// 如果可用,添加显示字段标签
|
||
// if (field.referenceTable?.displayFields) {
|
||
// const displayFields = field.referenceTable.displayFields;
|
||
// const columnWidths = getColumnWidths(fieldElement, field.type);
|
||
// const dataExist = !!fieldElement.querySelector('tbody > tr');
|
||
// const displayLabels = createFieldLabels(displayFields, columnWidths, field.type, dataExist);
|
||
|
||
// safelyInsertLabel(fieldElement, displayLabels);
|
||
// }
|
||
|
||
} catch (error) {
|
||
console.error(`Failed to add label for reference table field ${field.code}:`, error);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 为组字段元素添加标签
|
||
* @param {Object} field - 字段配置信息
|
||
* @param {HTMLElement} fieldElement - 字段 DOM 元素
|
||
*/
|
||
addGroupLabel(field, fieldElement) {
|
||
try {
|
||
// 在组的父元素之前添加标签
|
||
const parentElement = field.dom.querySelector(`.${DOM_CLASSES.INSERT_GROUP_LABEL_ELEMENT}`);
|
||
if (!parentElement) {
|
||
// TODO error
|
||
return;
|
||
}
|
||
const groupLabel = createFieldWithLabels({
|
||
code: field.code,
|
||
type: field.type,
|
||
});
|
||
|
||
groupLabel.style.top = STYLES.GROUP_LABEL_TOP;
|
||
safelyAppendLabel(parentElement, groupLabel);
|
||
|
||
const subLayout = this.groupLayoutMap[field.id]?.layout || [];
|
||
for (const subRow of subLayout) {
|
||
for (const subField of subRow.fields) {
|
||
const subFieldElement = subField.dom.querySelector(`.${DOM_CLASSES.INSERT_LABEL_ELEMENT}`);
|
||
this.addStandardFieldLabel(subField, subFieldElement);
|
||
}
|
||
}
|
||
|
||
} catch (error) {
|
||
console.error(`Failed to add label for group field ${field.code}:`, error);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 为标准字段元素添加标签
|
||
* @param {Object} field - 字段配置信息
|
||
* @param {HTMLElement} fieldElement - 字段 DOM 元素
|
||
*/
|
||
addStandardFieldLabel(field, fieldElement) {
|
||
if (!fieldElement) {
|
||
return;
|
||
}
|
||
|
||
try {
|
||
const fieldLabel = createFieldWithLabels({
|
||
code: field.code,
|
||
type: field.type,
|
||
});
|
||
|
||
safelyAppendLabel(fieldElement, fieldLabel);
|
||
|
||
} catch (error) {
|
||
console.error(`Failed to add label for standard field ${field.code}:`, error);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 处理并为页面上的所有字段元素添加标签
|
||
* @param {Array} fieldsWithLabels - 已处理的字段对象数组
|
||
*/
|
||
processFieldLabels(fieldsWithLabels) {
|
||
try {
|
||
for (const field of Object.values(this.fieldMap)) {
|
||
try {
|
||
// 获取字段元素
|
||
const fieldElement = field.dom.querySelector(`.${DOM_CLASSES.INSERT_LABEL_ELEMENT}`);
|
||
|
||
// 根据字段类型选择合适的标签添加方法
|
||
if (FieldTypeChecker.isSubtable(field)) {
|
||
this.addSubtableLabel(field, fieldElement);
|
||
} else if (FieldTypeChecker.isReferenceTable(field)) {
|
||
this.addReferenceTableLabel(field, fieldElement);
|
||
} else if (FieldTypeChecker.isGroup(field)) {
|
||
this.addGroupLabel(field, fieldElement);
|
||
} else {
|
||
this.addStandardFieldLabel(field, fieldElement);
|
||
}
|
||
|
||
} catch (fieldError) {
|
||
console.error(`Error occurred while processing label for field ${field.code}:`, fieldError);
|
||
}
|
||
}
|
||
} catch (error) {
|
||
console.error('Error occurred while processing field labels:', error);
|
||
throw error;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 处理并为页面上的间距元素添加标签
|
||
* @param {Array} spacerElements - 间距元素配置数组
|
||
*/
|
||
processSpacerLabels() {
|
||
try {
|
||
for (const spacer of Object.values(this.spacerMap)) {
|
||
const spacerElement = spacer.dom.querySelector(`.${DOM_CLASSES.INSERT_SPACER_LABEL_ELEMENT}`);
|
||
|
||
// 添加标签并设置边框
|
||
const spacerLabel = createFieldWithLabels({
|
||
code: spacer.elementId,
|
||
type: spacer.type,
|
||
});
|
||
|
||
safelyAppendLabel(spacerElement, spacerLabel);
|
||
}
|
||
} catch (error) {
|
||
console.error('Error occurred while processing spacer element labels:', error);
|
||
throw error;
|
||
}
|
||
}
|
||
}
|