refactor
This commit is contained in:
107
utils/constants.js
Normal file
107
utils/constants.js
Normal file
@@ -0,0 +1,107 @@
|
||||
// 字段类型常量定义
|
||||
export const FIELD_TYPES = {
|
||||
SINGLE_LINE_TEXT: 'SINGLE_LINE_TEXT',
|
||||
NUMBER: 'NUMBER',
|
||||
MULTI_LINE_TEXT: 'MULTI_LINE_TEXT',
|
||||
RICH_TEXT: 'RICH_TEXT',
|
||||
LINK: 'LINK',
|
||||
CHECK_BOX: 'CHECK_BOX',
|
||||
RADIO_BUTTON: 'RADIO_BUTTON',
|
||||
DROP_DOWN: 'DROP_DOWN',
|
||||
MULTI_SELECT: 'MULTI_SELECT',
|
||||
DATE: 'DATE',
|
||||
TIME: 'TIME',
|
||||
DATETIME: 'DATETIME',
|
||||
USER_SELECT: 'USER_SELECT',
|
||||
ORGANIZATION_SELECT: 'ORGANIZATION_SELECT',
|
||||
GROUP_SELECT: 'GROUP_SELECT',
|
||||
CALC: 'CALC',
|
||||
RECORD_NUMBER: 'RECORD_NUMBER',
|
||||
CREATOR: 'CREATOR',
|
||||
CREATED_TIME: 'CREATED_TIME',
|
||||
MODIFIER: 'MODIFIER',
|
||||
UPDATED_TIME: 'UPDATED_TIME',
|
||||
STATUS: 'STATUS',
|
||||
STATUS_ASSIGNEE: 'STATUS_ASSIGNEE',
|
||||
CATEGORY: 'CATEGORY',
|
||||
FILE: 'FILE',
|
||||
SUBTABLE: 'SUBTABLE',
|
||||
GROUP: 'GROUP',
|
||||
REFERENCE_TABLE: 'REFERENCE_TABLE',
|
||||
};
|
||||
|
||||
// 布局类型常量定义
|
||||
export const LAYOUT_TYPES = {
|
||||
ROW: 'ROW',
|
||||
SUBTABLE: 'SUBTABLE',
|
||||
GROUP: 'GROUP',
|
||||
LABEL: 'LABEL',
|
||||
HR: 'HR',
|
||||
SPACER: 'SPACER',
|
||||
};
|
||||
|
||||
// 支持选项排序的字段类型列表
|
||||
export const OPTION_SORTABLE_TYPES = [
|
||||
FIELD_TYPES.CHECK_BOX,
|
||||
FIELD_TYPES.DROP_DOWN,
|
||||
FIELD_TYPES.MULTI_SELECT,
|
||||
FIELD_TYPES.RADIO_BUTTON,
|
||||
];
|
||||
|
||||
// 分组布局中排除的字段类型列表
|
||||
export const EXCLUDED_GROUP_TYPES = [
|
||||
FIELD_TYPES.CATEGORY,
|
||||
FIELD_TYPES.STATUS,
|
||||
FIELD_TYPES.STATUS_ASSIGNEE,
|
||||
FIELD_TYPES.SUBTABLE,
|
||||
FIELD_TYPES.GROUP,
|
||||
];
|
||||
|
||||
// 系统字段类型列表(自动添加到所有表单中)
|
||||
export const SYSTEM_FIELD_TYPES = [
|
||||
FIELD_TYPES.RECORD_NUMBER,
|
||||
FIELD_TYPES.CREATOR,
|
||||
FIELD_TYPES.CREATED_TIME,
|
||||
FIELD_TYPES.MODIFIER,
|
||||
FIELD_TYPES.UPDATED_TIME,
|
||||
];
|
||||
|
||||
// 系统状态字段类型列表
|
||||
export const SYSTEM_STATUS_FIELD_TYPES = [
|
||||
FIELD_TYPES.STATUS,
|
||||
FIELD_TYPES.STATUS_ASSIGNEE,
|
||||
FIELD_TYPES.CATEGORY,
|
||||
];
|
||||
|
||||
// 支持查找复制的字段类型列表
|
||||
export const LOOKUP_COPY_SUPPORTED_TYPES = [
|
||||
FIELD_TYPES.SINGLE_LINE_TEXT,
|
||||
FIELD_TYPES.NUMBER,
|
||||
FIELD_TYPES.MULTI_LINE_TEXT,
|
||||
FIELD_TYPES.RICH_TEXT,
|
||||
FIELD_TYPES.LINK,
|
||||
FIELD_TYPES.CHECK_BOX,
|
||||
FIELD_TYPES.RADIO_BUTTON,
|
||||
FIELD_TYPES.DROP_DOWN,
|
||||
FIELD_TYPES.MULTI_SELECT,
|
||||
FIELD_TYPES.DATE,
|
||||
FIELD_TYPES.TIME,
|
||||
FIELD_TYPES.DATETIME,
|
||||
FIELD_TYPES.USER_SELECT,
|
||||
FIELD_TYPES.ORGANIZATION_SELECT,
|
||||
FIELD_TYPES.GROUP_SELECT,
|
||||
FIELD_TYPES.CALC,
|
||||
FIELD_TYPES.RECORD_NUMBER,
|
||||
];
|
||||
|
||||
// 页面类型常量定义
|
||||
export const PAGE_TYPES = {
|
||||
DETAIL: 'detail',
|
||||
EDIT: 'edit',
|
||||
CREATE: 'create',
|
||||
ADMIN: 'admin',
|
||||
};
|
||||
|
||||
export const SCRIPT_FILES = [
|
||||
'main.js' // 立即执行
|
||||
];
|
||||
70
utils/dom-utils.js
Normal file
70
utils/dom-utils.js
Normal file
@@ -0,0 +1,70 @@
|
||||
/**
|
||||
* DOM 操作工具模块
|
||||
* 提供用于创建和管理 DOM 元素的辅助函数
|
||||
*/
|
||||
|
||||
import { FIELD_TYPES } from './constants.js';
|
||||
|
||||
/**
|
||||
* 从表元素中提取列宽度信息
|
||||
* @param {HTMLElement} tableElement - 要分析的表元素
|
||||
* @param {string} fieldType - 字段类型(影响返回的宽度数组)
|
||||
* @returns {Array<number>} 表列宽度的数组
|
||||
*/
|
||||
export const getColumnWidths = (tableElement, fieldType) => {
|
||||
// 获取所有标题元素并提取它们的客户端宽度
|
||||
const headerElements = Array.from(tableElement.querySelectorAll('th'));
|
||||
const columnWidths = headerElements.map(header => header.clientWidth);
|
||||
|
||||
// 对于子表返回所有宽度,其他表跳过第一列(通常是操作列)
|
||||
return fieldType === FIELD_TYPES.SUBTABLE
|
||||
? columnWidths // 子表保留所有列
|
||||
: columnWidths.slice(1); // 其他表跳过第一列
|
||||
};
|
||||
|
||||
/**
|
||||
* 安全地将标签元素插入到指定目标元素之前
|
||||
* @param {HTMLElement} targetElement - 要插入之前的目标元素
|
||||
* @param {HTMLElement} labelElement - 要插入的标签元素
|
||||
* @returns {boolean} 插入是否成功
|
||||
*/
|
||||
export const safelyInsertLabel = (targetElement, labelElement) => {
|
||||
try {
|
||||
if (!targetElement || !labelElement) {
|
||||
console.warn('Failed to insert label: target element or label element does not exist');
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!targetElement.before) {
|
||||
console.warn('Failed to insert label: target element does not support before method');
|
||||
return false;
|
||||
}
|
||||
|
||||
targetElement.before(labelElement);
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error('Error inserting label: ', error);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 安全地将标签元素追加到指定目标元素
|
||||
* @param {HTMLElement} targetElement - 要追加到的目标元素
|
||||
* @param {HTMLElement} labelElement - 要追加的标签元素
|
||||
* @returns {boolean} 追加是否成功
|
||||
*/
|
||||
export const safelyAppendLabel = (targetElement, labelElement) => {
|
||||
try {
|
||||
if (!targetElement || !labelElement) {
|
||||
console.warn('Failed to append label: target element or label element does not exist');
|
||||
return false;
|
||||
}
|
||||
|
||||
targetElement.appendChild(labelElement);
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error('Error appending label: ', error);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
85
utils/field-utils.js
Normal file
85
utils/field-utils.js
Normal file
@@ -0,0 +1,85 @@
|
||||
/**
|
||||
* 字段处理工具函数模块
|
||||
* 提供字段类型判断、选项排序、系统字段处理等工具函数
|
||||
*/
|
||||
|
||||
import {
|
||||
FIELD_TYPES,
|
||||
OPTION_SORTABLE_TYPES,
|
||||
LOOKUP_COPY_SUPPORTED_TYPES
|
||||
} from './constants.js';
|
||||
|
||||
/**
|
||||
* 排序字段选项,根据索引值进行升序排序
|
||||
* @param {Object} field - 字段对象
|
||||
* @returns {Array} 按索引排序后的选项标签数组
|
||||
*/
|
||||
export const sortFieldOptions = (field) => {
|
||||
if (!field.options) return [];
|
||||
return Object.values(field.options)
|
||||
.sort((a, b) => Number(a.index) - Number(b.index))
|
||||
.map(option => option.label);
|
||||
};
|
||||
|
||||
/**
|
||||
* 检查字段类型是否支持选项排序
|
||||
* @param {string} fieldType - 字段类型
|
||||
* @returns {boolean} 是否支持选项排序
|
||||
*/
|
||||
export const shouldSortOptions = (fieldType) => OPTION_SORTABLE_TYPES.includes(fieldType);
|
||||
|
||||
/**
|
||||
* 标记查找复制字段
|
||||
* @param {Array} fields - 要处理的字段数组
|
||||
*/
|
||||
export const markLookupCopies = (fields) => {
|
||||
fields.forEach(field => {
|
||||
// 检查字段是否有查找设置
|
||||
if (field.lookup?.fieldMappings) {
|
||||
field.lookup.fieldMappings.forEach(mapping => {
|
||||
// 在字段数组中找到对应的目标字段并标记为查找复制
|
||||
const targetField = fields.find(f =>
|
||||
LOOKUP_COPY_SUPPORTED_TYPES.includes(f.type) && f.code === mapping.field
|
||||
);
|
||||
if (targetField) {
|
||||
targetField.isLookupCopy = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
export const FieldTypeChecker = {
|
||||
isCategory: field => field.type === FIELD_TYPES.CATEGORY,
|
||||
isCheckBox: field => field.type === FIELD_TYPES.CHECK_BOX,
|
||||
isCreatedTime: field => field.type === FIELD_TYPES.CREATED_TIME,
|
||||
isCreator: field => field.type === FIELD_TYPES.CREATOR,
|
||||
isDate: field => field.type === FIELD_TYPES.DATE,
|
||||
isDatetime: field => field.type === FIELD_TYPES.DATETIME,
|
||||
isDropDown: field => field.type === FIELD_TYPES.DROP_DOWN,
|
||||
isFile: field => field.type === FIELD_TYPES.FILE,
|
||||
isGroup: field => field.type === FIELD_TYPES.GROUP,
|
||||
isGroupSelect: field => field.type === FIELD_TYPES.GROUP_SELECT,
|
||||
isInGroup: field => 'group' in field,
|
||||
isInSubtable: field => 'table' in field,
|
||||
isLink: field => field.type === FIELD_TYPES.LINK,
|
||||
isLookup: field => 'lookup' in field,
|
||||
isLookupCopy: field => field.isLookupCopy === true,
|
||||
isModifier: field => field.type === FIELD_TYPES.MODIFIER,
|
||||
isMultiLineText: field => field.type === FIELD_TYPES.MULTI_LINE_TEXT,
|
||||
isMultiSelect: field => field.type === FIELD_TYPES.MULTI_SELECT,
|
||||
isNotInSubtable: field => !('table' in field),
|
||||
isNumber: field => field.type === FIELD_TYPES.NUMBER,
|
||||
isOrganizationSelect: field => field.type === FIELD_TYPES.ORGANIZATION_SELECT,
|
||||
isRadioButton: field => field.type === FIELD_TYPES.RADIO_BUTTON,
|
||||
isRecordNumber: field => field.type === FIELD_TYPES.RECORD_NUMBER,
|
||||
isReferenceTable: field => field.type === FIELD_TYPES.REFERENCE_TABLE,
|
||||
isRichText: field => field.type === FIELD_TYPES.RICH_TEXT,
|
||||
isSingleLineText: field => field.type === FIELD_TYPES.SINGLE_LINE_TEXT,
|
||||
isStatus: field => field.type === FIELD_TYPES.STATUS,
|
||||
isStatusAssignee: field => field.type === FIELD_TYPES.STATUS_ASSIGNEE,
|
||||
isSubtable: field => field.type === FIELD_TYPES.SUBTABLE,
|
||||
isTime: field => field.type === FIELD_TYPES.TIME,
|
||||
isUpdatedTime: field => field.type === FIELD_TYPES.UPDATED_TIME,
|
||||
isUserSelect: field => field.type === FIELD_TYPES.USER_SELECT,
|
||||
};
|
||||
100
utils/kintone-utils.js
Normal file
100
utils/kintone-utils.js
Normal file
@@ -0,0 +1,100 @@
|
||||
/**
|
||||
* Kintone API 工具模块
|
||||
* 提供与 Kintone 应用程序交互的通用工具函数
|
||||
*/
|
||||
|
||||
/**
|
||||
* 从当前 URL 中提取 Guest Space ID
|
||||
* @returns {string|undefined} Guest Space ID,如果未找到则为 undefined
|
||||
*/
|
||||
export const getGuestSpaceId = () => {
|
||||
try {
|
||||
const match = window.location.pathname.match(/\/guest\/([0-9]+)\//);
|
||||
return match ? match[1] : undefined;
|
||||
} catch (error) {
|
||||
console.warn(error);
|
||||
return undefined;
|
||||
}
|
||||
};
|
||||
|
||||
const getAppIdFromUrl = () => {
|
||||
try {
|
||||
const match = window.location.search.match(/\?app=([0-9]+)/);
|
||||
return match ? Number(match[1]) : undefined;
|
||||
} catch (error) {
|
||||
console.warn(error);
|
||||
return undefined;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 从 Kintone 获取当前 APP ID
|
||||
* @returns {number|null} APP ID,如果没有为 null
|
||||
*/
|
||||
export const getAppId = () => {
|
||||
try {
|
||||
if (isInAdminPage()) {
|
||||
return getAppIdFromUrl() || null;
|
||||
}
|
||||
|
||||
if (!isGlobalKintoneExist()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const appId = kintone.app.getId();
|
||||
if (!appId || isNaN(appId)) {
|
||||
console.warn('Retrieved app ID is invalid:', appId);
|
||||
return null;
|
||||
}
|
||||
|
||||
return appId;
|
||||
} catch (error) {
|
||||
console.warn('Failed to get app ID: ', error);
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
export const isGlobalKintoneExist = () => {
|
||||
return typeof kintone !== 'undefined' && typeof kintone.app !== 'undefined';
|
||||
};
|
||||
|
||||
/**
|
||||
* 是否在 Kintone App 中
|
||||
* @returns {boolean} 是否在 App 中
|
||||
*/
|
||||
export const isInAppPage = () => {
|
||||
return isGlobalKintoneExist() && /^\/k\/\d+/.test(window.location.pathname);
|
||||
};
|
||||
|
||||
/**
|
||||
* 是否在 Kintone App Detail 中
|
||||
* @returns {boolean} 是否在 App 中
|
||||
*/
|
||||
export const isInDetailPage = () => {
|
||||
return isGlobalKintoneExist() && /^\/k\/\d+\/show/.test(window.location.pathname);
|
||||
};
|
||||
|
||||
/**
|
||||
* 是否在 Kintone App 的 /admin 页面
|
||||
* @returns {boolean} 是否在 App /admin 页面
|
||||
*/
|
||||
export const isInAdminPage = () => {
|
||||
return !isGlobalKintoneExist() && window.location.pathname.includes('/k/admin');
|
||||
};
|
||||
|
||||
/**
|
||||
* 是否在 Kintone App 的 /admin Form 页面
|
||||
* @returns {boolean} 是否在 App /admin Form 页面
|
||||
*/
|
||||
export const isInAdminFormPage = () => {
|
||||
return isInAdminPage() && window.location.hash.includes('#section=form');
|
||||
};
|
||||
|
||||
/**
|
||||
* 是否在 Kintone 的 space 页面
|
||||
* @returns {boolean} 是否在 space 页面
|
||||
*/
|
||||
export const isInSpacePage = () => {
|
||||
return isGlobalKintoneExist() && window.location.pathname.includes('/k/#/space');
|
||||
};
|
||||
Reference in New Issue
Block a user