Files
kintone-helper-extenstion/page/admin/form/admin-field-label-processor.js
2025-10-17 14:39:35 +08:00

326 lines
10 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* 管理表单页面字段标签处理器模块
* 负责处理 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;
}
}
}