diff --git a/features/add-field-label/main.js b/features/add-field-label/main.js deleted file mode 100644 index 6138541..0000000 --- a/features/add-field-label/main.js +++ /dev/null @@ -1,107 +0,0 @@ -/** - * 添加字段信息功能模块 - * 负责获取表单配置并添加字段信息标签 - */ - -import { generateFields } from './fields.js'; -import { getGuestSpaceId, getAppId, isInDetailPage, isInAdminFormPage } from '../../utils/kintone-utils.js'; -import { FieldLabelProcessor } from '../../page/detail/field-label-processor.js'; -import { AdminFieldLabelProcessor } from '../../page/admin/form/admin-field-label-processor.js'; -import { KintoneRestAPIClient } from '@kintone/rest-api-client'; -import { PAGE_TYPES } from '../../utils/constants.js'; - -/** - * 从 Kintone API 获取表单数据 - * @param {number} appId - 应用 ID - * @param {boolean} isPreview - 是否为预览模式 - * @returns {Promise} 表单字段和布局数据 - */ -const fetchFormData = async (appId, isPreview) => { - try { - // 初始化 Kintone REST API 客户端 - const client = new KintoneRestAPIClient({ - guestSpaceId: getGuestSpaceId(), // 访客空间 ID,支持在访客空间中工作 - }); - - const [formFieldsResult, layoutResult] = await Promise.all([ - client.app.getFormFields({ - app: appId, - preview: isPreview, - }), - client.app.getFormLayout({ - app: appId, - preview: isPreview, - }), - ]); - - if (!formFieldsResult?.properties) { - throw new Error('Failed to retrieve form field data'); - } - - if (!layoutResult?.layout) { - throw new Error('Failed to retrieve form layout data'); - } - - return { - formFields: formFieldsResult.properties, // 表单字段属性 - layout: layoutResult.layout, // 表单布局配置 - }; - } catch (error) { - console.error('Failed to fetch form data: ', error); - throw new Error(`Unable to retrieve form configuration: ${error.message}`); - } -}; - -/** - * 主函数:为 Kintone 表单添加字段信息标签 - * @returns {Promise} - */ -export const addFieldLabel = async () => { - try { - const appId = getAppId(); - if (!appId || typeof appId !== 'number') { - return; - } - - // 创建字段标签处理器实例 - const labelProcessor = getFieldLabelProcessor(appId); - if (!labelProcessor) { - console.warn('Unable to create field label processor') - return; - } - - // 从 API 获取表单配置数据 - const { formFields, layout } = await fetchFormData(appId, labelProcessor.isPreview()); - - // 处理字段并生成标签数据 - const { fields: fieldsWithLabels, spacers: spacerElements } = - generateFields(formFields, layout); - - console.log(`Processed ${fieldsWithLabels.length} fields and ${spacerElements.length} spacer elements`); - - labelProcessor.beforeProcess(formFields, layout, fieldsWithLabels, spacerElements); - // 字段元素 - labelProcessor.processFieldLabels(fieldsWithLabels, layout); - // 间距元素 - labelProcessor.processSpacerLabels(spacerElements, layout); - - } catch (error) { - console.error('Failed to add field information: ', error); - throw error; - } -}; - -const getFieldLabelProcessor = (appId) => { - if (isInDetailPage()) { - return new FieldLabelProcessor({ - appId, - pageType: PAGE_TYPES.DETAIL // 当前专注于详情页面 - }); - } - if (isInAdminFormPage()) { - return new AdminFieldLabelProcessor({ - appId, - pageType: PAGE_TYPES.ADMIN // admin 表单页面 - }); - } -}; diff --git a/jsconfig.json b/jsconfig.json new file mode 100644 index 0000000..c34a92d --- /dev/null +++ b/jsconfig.json @@ -0,0 +1,12 @@ +{ + "compilerOptions": { + "baseUrl": ".", + "paths": { + "@/*": ["src/*"], + } + }, + "include": [ + "src/**/*", + "*.js" + ] +} diff --git a/manifest.json b/manifest.json index bc42eac..fd99ca0 100644 --- a/manifest.json +++ b/manifest.json @@ -9,6 +9,6 @@ ], "action": {}, "background": { - "service_worker": "background.js" + "service_worker": "src/background.js" } } diff --git a/background.js b/src/background.js similarity index 100% rename from background.js rename to src/background.js diff --git a/features/add-field-label/fields.js b/src/features/add-field-label/fields.js similarity index 89% rename from features/add-field-label/fields.js rename to src/features/add-field-label/fields.js index 711f6ca..6d8dbb2 100644 --- a/features/add-field-label/fields.js +++ b/src/features/add-field-label/fields.js @@ -4,12 +4,12 @@ import { SYSTEM_STATUS_FIELD_TYPES, SYSTEM_FIELD_TYPES, EXCLUDED_GROUP_TYPES -} from '../../utils/constants.js'; +} from '@/utils/constants.js'; import { sortFieldOptions, shouldSortOptions, markLookupCopies, -} from '../../utils/field-utils.js'; +} from '@/utils/field-utils.js'; /** * 从属性列表中添加系统状态字段到字段数组中 @@ -150,19 +150,13 @@ const addMissingSystemFields = (properties, fields) => { }; /** - * 从表单属性和布局生成处理后的字段和间距数组 - * @param {Object} properties - 字段属性对象 - * @param {Array} layouts - 布局定义 - * @returns {Object} 包含字段和间距数组的对象 + * 处理所有布局,返回字段和间距数据 + * @param {Array} layouts - 布局定义数组 + * @param {Object} properties - 字段属性映射 + * @param {Array} fields - 字段数组引用 + * @param {Array} spacers - 间距数组引用 */ -export const generateFields = (properties, layouts) => { - const fields = []; - const spacers = []; - - // 首先添加系统状态字段 - addSystemStatusFields(properties, fields); - - // 处理每个布局项 +const processAllLayouts = (layouts, properties, fields, spacers) => { layouts.forEach(layout => { switch (layout.type) { case LAYOUT_TYPES.ROW: @@ -176,9 +170,21 @@ export const generateFields = (properties, layouts) => { break; } }); +}; +/** + * 从表单属性和布局生成处理后的字段和间距数组 + * @param {Object} properties - 字段属性对象 + * @param {Array} layouts - 布局定义 + * @returns {Object} 包含字段和间距数组的对象 + */ +export const generateFields = (properties, layouts) => { + const fields = []; + const spacers = []; + + addSystemStatusFields(properties, fields); + processAllLayouts(layouts, properties, fields, spacers); markLookupCopies(fields); - addMissingSystemFields(properties, fields); return { fields, spacers }; diff --git a/src/features/add-field-label/main.js b/src/features/add-field-label/main.js new file mode 100644 index 0000000..2286cfb --- /dev/null +++ b/src/features/add-field-label/main.js @@ -0,0 +1,156 @@ +/** + * 添加字段信息功能模块 + * 负责获取表单配置并添加字段信息标签 + */ + +import { generateFields } from './fields.js'; +import { getGuestSpaceId, getAppId, isInDetailPage, isInAdminFormPage } from '@/utils/kintone-utils.js'; +import { FieldLabelProcessor } from '@/page/detail/field-label-processor.js'; +import { AdminFieldLabelProcessor } from '@/page/admin/form/admin-field-label-processor.js'; +import { KintoneRestAPIClient } from '@kintone/rest-api-client'; +import { PAGE_TYPES } from '@/utils/constants.js'; + +/** + * 创建配置化的API客户端实例 + * @returns {KintoneRestAPIClient} 配置好的客户端实例 + */ +const createApiClient = () => { + const guestSpaceId = getGuestSpaceId(); + const clientConfig = guestSpaceId ? { guestSpaceId } : {}; + return new KintoneRestAPIClient(clientConfig); +}; + +/** + * 获取表单字段数据配置 + * @param {number} appId - 应用ID + * @param {boolean} isPreview - 是否预览模式 + * @returns {Object} API请求配置 + */ +const getFieldRequestConfig = (appId, isPreview) => ({ + app: appId, + preview: isPreview, +}); + +/** + * 获取表单布局数据配置 + * @param {number} appId - 应用ID + * @param {boolean} isPreview - 是否预览模式 + * @returns {Object} API请求配置 + */ +const getLayoutRequestConfig = (appId, isPreview) => ({ + app: appId, + preview: isPreview, +}); + +/** + * 并发获取表单数据 + * @param {KintoneRestAPIClient} client - API客户端 + * @param {number} appId - 应用ID + * @param {boolean} isPreview - 是否预览模式 + * @returns {Promise} 并发的API响应数组 + */ +const fetchFormDataConcurrently = async (client, appId, isPreview) => { + return Promise.all([ + client.app.getFormFields(getFieldRequestConfig(appId, isPreview)), + client.app.getFormLayout(getLayoutRequestConfig(appId, isPreview)), + ]); +}; + +/** + * 验证和提取表单API响应数据 + * @param {Object} formFieldsResult - 字段API响应 + * @param {Object} layoutResult - 布局API响应 + * @returns {Object} 提取的数据对象 + * @throws {Error} 当数据缺失时抛出错误 + */ +const validateAndExtractData = (formFieldsResult, layoutResult) => { + const errors = []; + + if (!formFieldsResult?.properties) { + errors.push('form field data'); + } + + if (!layoutResult?.layout) { + errors.push('form layout data'); + } + + if (errors.length > 0) { + throw new Error(`[Kintone Helper Extension] Failed to retrieve ${errors.join(' & ')}`); + } + + return { + formFields: formFieldsResult.properties, + layout: layoutResult.layout, + }; +}; + +/** + * 从 Kintone API 获取表单数据 + * @param {number} appId - 应用 ID + * @param {boolean} isPreview - 是否预览模式 + * @returns {Promise} 表单字段和布局数据对象 + */ +const fetchFormData = async (appId, isPreview) => { + try { + const client = createApiClient(); + const [formFieldsResult, layoutResult] = await fetchFormDataConcurrently(client, appId, isPreview); + return validateAndExtractData(formFieldsResult, layoutResult); + } catch (error) { + console.error('[Kintone Helper Extension] Error fetching form data:', error.message); + throw new Error(`[Kintone Helper Extension] Unable to retrieve form configuration: ${error.message}`); + } +}; + +/** + * 主函数:为 Kintone 表单添加字段信息标签 + * @returns {Promise} + */ +export const addFieldLabel = async () => { + try { + const appId = getAppId(); + if (!appId || typeof appId !== 'number') { + return; + } + + // 创建字段标签处理器实例 + const labelProcessor = getFieldLabelProcessor(appId); + if (!labelProcessor) { + console.warn('[Kintone Helper Extension] Unable to create field label processor') + return; + } + + // 从 API 获取表单配置数据 + const { formFields, layout } = await fetchFormData(appId, labelProcessor.isPreview()); + + // 处理字段并生成标签数据 + const { fields: fieldsWithLabels, spacers: spacerElements } = + generateFields(formFields, layout); + + console.log(`[Kintone Helper Extension] Processed ${fieldsWithLabels.length} fields and ${spacerElements.length} spacer elements`); + + labelProcessor.beforeProcess(formFields, layout, fieldsWithLabels, spacerElements); + // 字段元素 + labelProcessor.processFieldLabels(fieldsWithLabels, layout); + // 间距元素 + labelProcessor.processSpacerLabels(spacerElements, layout); + + } catch (error) { + console.error('[Kintone Helper Extension] Failed to add field information: ', error); + throw error; + } +}; + +const getFieldLabelProcessor = (appId) => { + if (isInDetailPage()) { + return new FieldLabelProcessor({ + appId, + pageType: PAGE_TYPES.DETAIL // 当前专注于详情页面 + }); + } + if (isInAdminFormPage()) { + return new AdminFieldLabelProcessor({ + appId, + pageType: PAGE_TYPES.ADMIN // admin 表单页面 + }); + } +}; diff --git a/features/add-field-label/settings.js b/src/features/add-field-label/settings.js similarity index 77% rename from features/add-field-label/settings.js rename to src/features/add-field-label/settings.js index 5a58f73..78d59ed 100644 --- a/features/add-field-label/settings.js +++ b/src/features/add-field-label/settings.js @@ -1,9 +1,3 @@ -// 颜色配置常量 -export const COLORS = { - LABEL_TEXT: 'red', - SPACER_BORDER: '1px dotted red', -}; - // 间距和尺寸配置常量 export const SPACING = { GROUP_MARGIN_LEFT: '20px', @@ -17,8 +11,18 @@ export const STYLES = { BASIC_LABEL_TOP: 'calc(5px + 1rem)', SUBTABLE_LABEL_TOP: 'calc(12px + 1rem)', GROUP_LABEL_TOP: 'calc(20px + 1rem)', + SPACER_BORDER: '1px dotted red', }; +// LABEL 文字样式 +export const LABEL_TEXT_STYLE = { + color: 'red', + display: 'inline-block', + width: '100%', + overflowWrap: 'anywhere', + whiteSpace: 'pre-wrap', +} + // LABEL 基本样式 export const ADMIN_LABEL_STYLE = { position: 'absolute', diff --git a/main.js b/src/main.js similarity index 85% rename from main.js rename to src/main.js index 007762d..22aa4c3 100644 --- a/main.js +++ b/src/main.js @@ -13,7 +13,7 @@ export const runKintoneHelper = async () => { try { await addFieldLabel(); } catch (error) { - console.error('❌ Kintone Helper Extension execution failed:', error); + console.error('[Kintone Helper Extension] ❌ Execution failed:', error); throw error; } }; diff --git a/page/admin/form/admin-field-label-processor.js b/src/page/admin/form/admin-field-label-processor.js similarity index 84% rename from page/admin/form/admin-field-label-processor.js rename to src/page/admin/form/admin-field-label-processor.js index e8f9e0f..9ee898b 100644 --- a/page/admin/form/admin-field-label-processor.js +++ b/src/page/admin/form/admin-field-label-processor.js @@ -8,14 +8,14 @@ import { safelyInsertLabel, safelyAppendLabel, FieldTypeCheckerForAdminDom -} from '../../../utils/dom-utils.js'; +} 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'; +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 表单页面上字段和间距标签的字段标签处理器类 @@ -48,8 +48,11 @@ export class AdminFieldLabelProcessor { this.groupLayoutMap = {}; this.spacerMap = {}; const rows = document.querySelector(`.${DOM_CLASSES.CANVAS_ELEMENT} > .${DOM_CLASSES.CONTENT_ELEMENT}`).children; + console.log('[Kintone Helper Extension] Building DOM layout for admin form page'); this._buildDomLayout(this.domLayout, rows); + + console.log(`[Kintone Helper Extension] DOM layout built: ${this.domLayout.length} rows, ${Object.keys(this.fieldMap).length} fields, ${Object.keys(this.spacerMap).length} spacers`); } _buildDomLayout(layout, rowElements, parentGroupLayout) { @@ -176,7 +179,7 @@ export class AdminFieldLabelProcessor { } catch (error) { - console.error(`Failed to add label for subtable field ${field.code}:`, error); + console.error(`[Kintone Helper Extension] Failed to add label for subtable field ${field.code}:`, error); } } @@ -207,7 +210,7 @@ export class AdminFieldLabelProcessor { // } } catch (error) { - console.error(`Failed to add label for reference table field ${field.code}:`, error); + console.error(`[Kintone Helper Extension] Failed to add label for reference table field ${field.code}:`, error); } } @@ -221,6 +224,7 @@ export class AdminFieldLabelProcessor { // 在组的父元素之前添加标签 const parentElement = field.dom.querySelector(`.${DOM_CLASSES.INSERT_GROUP_LABEL_ELEMENT}`); if (!parentElement) { + console.warn(`[Kintone Helper Extension] Parent element for group field ${field.code} does not exist`); // TODO error return; } @@ -241,7 +245,7 @@ export class AdminFieldLabelProcessor { } } catch (error) { - console.error(`Failed to add label for group field ${field.code}:`, error); + console.error(`[Kintone Helper Extension] Failed to add label for group field ${field.code}:`, error); } } @@ -264,7 +268,7 @@ export class AdminFieldLabelProcessor { safelyAppendLabel(fieldElement, fieldLabel); } catch (error) { - console.error(`Failed to add label for standard field ${field.code}:`, error); + console.error(`[Kintone Helper Extension] Failed to add label for standard field ${field.code}:`, error); } } @@ -291,11 +295,11 @@ export class AdminFieldLabelProcessor { } } catch (fieldError) { - console.error(`Error occurred while processing label for field ${field.code}:`, fieldError); + console.error(`[Kintone Helper Extension] Error occurred while processing label for field ${field.code}:`, fieldError); } } } catch (error) { - console.error('Error occurred while processing field labels:', error); + console.error('[Kintone Helper Extension] Error occurred while processing field labels:', error); throw error; } } @@ -317,9 +321,9 @@ export class AdminFieldLabelProcessor { safelyAppendLabel(spacerElement, spacerLabel); } - } catch (error) { - console.error('Error occurred while processing spacer element labels:', error); - throw error; - } + } catch (error) { + console.error('[Kintone Helper Extension] Error occurred while processing spacer element labels:', error); + throw error; + } } } diff --git a/page/admin/form/dom.js b/src/page/admin/form/dom.js similarity index 65% rename from page/admin/form/dom.js rename to src/page/admin/form/dom.js index 1d8ff9f..ced395f 100644 --- a/page/admin/form/dom.js +++ b/src/page/admin/form/dom.js @@ -3,12 +3,24 @@ * 提供用于创建和管理字段标签 DOM 元素的辅助函数 */ -import { FIELD_TYPES } from '../../../utils/constants.js'; -import { COLORS, SPACING, IS_FIELD_TYPE_DISPLAY, ADMIN_LABEL_STYLE } from '../../../features/add-field-label/settings.js'; -import { isInDetailPage } from '../../../utils/kintone-utils.js'; +import { FIELD_TYPES } from '@/utils/constants.js'; +import { SPACING, IS_FIELD_TYPE_DISPLAY, ADMIN_LABEL_STYLE, LABEL_TEXT_STYLE } from '@/features/add-field-label/settings.js'; /** - * 创建带有适当样式的字段标签 span 元素 + * 创建带样式的span元素 + * @param {string} textContent - 文本内容 + * @param {string} type - 字段类型(用于特殊样式) + * @returns {HTMLSpanElement} 配置好的span元素 + */ +const createStyledSpan = (textContent, type) => { + const span = document.createElement('span'); + span.textContent = textContent; + Object.assign(span.style, LABEL_TEXT_STYLE) + return span; +}; + +/** + * 创建字段标签容器元素 * @param {Object} params - 标签参数配置 * @param {string} params.code - 字段代码 * @param {string} params.type - 字段类型(可选) @@ -18,27 +30,19 @@ import { isInDetailPage } from '../../../utils/kintone-utils.js'; const createFieldSpanElement = ({ code, type, width }) => { const container = document.createElement('div'); - // 处理宽度和边距设置 + // 设置容器宽度样式 if (width !== undefined) { - container.style.width = `${Number(width) - SPACING.TABLE_COLUMN_PADDING}px`; // 减去列填充 - container.style.marginLeft = `${SPACING.TABLE_COLUMN_PADDING}px`; // 添加左边距 + Object.assign(container.style, { + width: `${Number(width) - SPACING.TABLE_COLUMN_PADDING}px`, + marginLeft: `${SPACING.TABLE_COLUMN_PADDING}px`, + }); } else { - container.style.width = '100%'; // 默认全宽度 + container.style.width = SPACING.FIELD_CONTAINER_WIDTH; } - // 创建和样式化 span 元素 - const fieldSpan = document.createElement('span'); - fieldSpan.textContent = (IS_FIELD_TYPE_DISPLAY && type !== undefined) ? `${code} (${type})` : code; // 显示代码和类型 - fieldSpan.style.display = 'inline-block'; - fieldSpan.style.width = '100%'; - fieldSpan.style.color = COLORS.LABEL_TEXT; // 使用定义的工具提示文本颜色 - fieldSpan.style.overflowWrap = 'anywhere'; // 支持长文本换行 - fieldSpan.style.whiteSpace = 'pre-wrap'; // 保留空格和换行 - - // GROUP 类型字段的特殊样式 - if (type === FIELD_TYPES.GROUP && isInDetailPage()) { - fieldSpan.style.marginLeft = SPACING.GROUP_MARGIN_LEFT; - } + // 创建文本内容 + const textContent = (IS_FIELD_TYPE_DISPLAY && type) ? `${code} (${type})` : code; + const fieldSpan = createStyledSpan(textContent, type); container.appendChild(fieldSpan); return container; diff --git a/page/detail/dom.js b/src/page/detail/dom.js similarity index 66% rename from page/detail/dom.js rename to src/page/detail/dom.js index 1489c60..ed72edd 100644 --- a/page/detail/dom.js +++ b/src/page/detail/dom.js @@ -3,11 +3,30 @@ * 提供用于创建和管理字段标签 DOM 元素的辅助函数 */ -import { FIELD_TYPES } from '../../utils/constants.js'; -import { COLORS, SPACING, IS_FIELD_TYPE_DISPLAY, LABEL_STYLE } from '../../features/add-field-label/settings.js'; +import { FIELD_TYPES } from '@/utils/constants.js'; +import { LABEL_TEXT_STYLE, SPACING, IS_FIELD_TYPE_DISPLAY, LABEL_STYLE } from '@/features/add-field-label/settings.js'; /** - * 创建带有适当样式的字段标签 span 元素 + * 创建带样式的span元素 + * @param {string} textContent - 文本内容 + * @param {string} type - 字段类型(用于特殊样式) + * @returns {HTMLSpanElement} 配置好的span元素 + */ +const createStyledSpan = (textContent, type) => { + const span = document.createElement('span'); + span.textContent = textContent; + Object.assign(span.style, LABEL_TEXT_STYLE) + + // GROUP类型字段的特殊样式 + if (type === FIELD_TYPES.GROUP) { + span.style.marginLeft = SPACING.GROUP_MARGIN_LEFT; + } + + return span; +}; + +/** + * 创建字段标签容器元素 * @param {Object} params - 标签参数配置 * @param {string} params.code - 字段代码 * @param {string} params.type - 字段类型(可选) @@ -17,27 +36,19 @@ import { COLORS, SPACING, IS_FIELD_TYPE_DISPLAY, LABEL_STYLE } from '../../featu const createFieldSpanElement = ({ code, type, width }) => { const container = document.createElement('div'); - // 处理宽度和边距设置 + // 设置容器宽度样式 if (width !== undefined) { - container.style.width = `${Number(width) - SPACING.TABLE_COLUMN_PADDING}px`; // 减去列填充 - container.style.marginLeft = `${SPACING.TABLE_COLUMN_PADDING}px`; // 添加左边距 + Object.assign(container.style, { + width: `${Number(width) - SPACING.TABLE_COLUMN_PADDING}px`, + marginLeft: `${SPACING.TABLE_COLUMN_PADDING}px`, + }); } else { - container.style.width = '100%'; // 默认全宽度 + container.style.width = SPACING.FIELD_CONTAINER_WIDTH; } - // 创建和样式化 span 元素 - const fieldSpan = document.createElement('span'); - fieldSpan.textContent = (IS_FIELD_TYPE_DISPLAY && type !== undefined) ? `${code} (${type})` : code; // 显示代码和类型 - fieldSpan.style.display = 'inline-block'; - fieldSpan.style.width = '100%'; - fieldSpan.style.color = COLORS.LABEL_TEXT; // 使用定义的工具提示文本颜色 - fieldSpan.style.overflowWrap = 'anywhere'; // 支持长文本换行 - fieldSpan.style.whiteSpace = 'pre-wrap'; // 保留空格和换行 - - // GROUP 类型字段的特殊样式 - if (type === FIELD_TYPES.GROUP) { - fieldSpan.style.marginLeft = SPACING.GROUP_MARGIN_LEFT; - } + // 创建文本内容 + const textContent = (IS_FIELD_TYPE_DISPLAY && type) ? `${code} (${type})` : code; + const fieldSpan = createStyledSpan(textContent, type); container.appendChild(fieldSpan); return container; @@ -53,7 +64,7 @@ const createFieldSpanElement = ({ code, type, width }) => { */ export const createFieldWithLabels = ({ code, type, width }) => { const container = document.createElement('div'); - // container.style.display = 'inline-block'; // 布局的内联块显示 + Object.assign(container.style, LABEL_STYLE) const fieldSpan = createFieldSpanElement({ code, type, width }); diff --git a/page/detail/field-label-processor.js b/src/page/detail/field-label-processor.js similarity index 57% rename from page/detail/field-label-processor.js rename to src/page/detail/field-label-processor.js index 28e2c0d..4ad47d9 100644 --- a/page/detail/field-label-processor.js +++ b/src/page/detail/field-label-processor.js @@ -7,14 +7,14 @@ import { getColumnWidths, safelyInsertLabel, safelyAppendLabel -} from '../../utils/dom-utils.js'; +} from '@/utils/dom-utils.js'; import { createFieldWithLabels, createFieldLabels } from './dom.js'; -import { FieldTypeChecker } from '../../utils/field-utils.js'; -import { COLORS } from '../../features/add-field-label/settings.js'; -import { LAYOUT_TYPES } from '../../utils/constants.js'; +import { FieldTypeChecker } from '@/utils/field-utils.js'; +import { STYLES } from '@/features/add-field-label/settings.js'; +import { LAYOUT_TYPES } from '@/utils/constants.js'; /** * 用于处理不同页面上字段和间距标签的字段标签处理器类 @@ -43,30 +43,36 @@ export class FieldLabelProcessor { } + /** + * 创建表类型标签元素 + * @param {Object} field - 字段配置 + * @returns {HTMLElement} 标签元素 + */ + createTableLabel(field) { + const tableCodeLabel = createFieldWithLabels({ + code: field.code, + type: field.type, + }); + tableCodeLabel.style.display = 'block'; + return tableCodeLabel; + } + /** * 为子表字段元素添加标签 * @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); - // 添加列标签 - const fieldNames = Object.keys(field.fields); - const columnWidths = getColumnWidths(fieldElement, field.type); - const columnLabels = createFieldLabels(fieldNames, columnWidths, field.type); + // 在表上方添加字段代码标签 + const tableCodeLabel = this.createTableLabel(field); + safelyInsertLabel(fieldElement, tableCodeLabel); - safelyInsertLabel(fieldElement, columnLabels); + // 添加列标签 + const fieldNames = Object.keys(field.fields); + const columnWidths = getColumnWidths(fieldElement, field.type); + const columnLabels = createFieldLabels(fieldNames, columnWidths, field.type); - } catch (error) { - console.error(`Failed to add label for subtable field ${field.code}:`, error); - } + safelyInsertLabel(fieldElement, columnLabels); } /** @@ -75,28 +81,17 @@ export class FieldLabelProcessor { * @param {HTMLElement} fieldElement - 字段 DOM 元素 */ addReferenceTableLabel(field, fieldElement) { - try { - // 在引用表上方添加字段代码标签 - const tableCodeLabel = createFieldWithLabels({ - code: field.code, - type: field.type, - }); - tableCodeLabel.style.display = 'block'; + // 在引用表上方添加字段代码标签 + safelyInsertLabel(fieldElement, this.createTableLabel(field)); - safelyInsertLabel(fieldElement, tableCodeLabel); + // 添加显示字段标签(如果存在) + if (field.referenceTable?.displayFields) { + const displayFields = field.referenceTable.displayFields; + const columnWidths = getColumnWidths(fieldElement, field.type); + const hasData = !!fieldElement.querySelector('tbody > tr'); + const displayLabels = createFieldLabels(displayFields, columnWidths, field.type, hasData); - // 如果可用,添加显示字段标签 - 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); + safelyInsertLabel(fieldElement, displayLabels); } } @@ -106,23 +101,18 @@ export class FieldLabelProcessor { * @param {HTMLElement} fieldElement - 字段 DOM 元素 */ addGroupLabel(field, fieldElement) { - try { - // 在组的父元素之前添加标签 - const parentElement = fieldElement.parentElement; - if (parentElement) { - const groupLabel = createFieldWithLabels({ - code: field.code, - type: field.type, - }); - - safelyInsertLabel(parentElement, groupLabel); - } else { - console.warn(`Parent element for group field ${field.code} does not exist`); - } - - } catch (error) { - console.error(`Failed to add label for group field ${field.code}:`, error); + const parentElement = fieldElement.parentElement; + if (!parentElement) { + console.warn(`[Kintone Helper Extension] Parent element for group field ${field.code} does not exist`); + return; } + + const groupLabel = createFieldWithLabels({ + code: field.code, + type: field.type, + }); + + safelyInsertLabel(parentElement, groupLabel); } /** @@ -131,17 +121,12 @@ export class FieldLabelProcessor { * @param {HTMLElement} fieldElement - 字段 DOM 元素 */ addStandardFieldLabel(field, fieldElement) { - try { const fieldLabel = createFieldWithLabels({ code: field.code, type: field.type, }); safelyInsertLabel(fieldElement, fieldLabel); - - } catch (error) { - console.error(`Failed to add label for standard field ${field.code}:`, error); - } } /** @@ -178,15 +163,15 @@ export class FieldLabelProcessor { processedCount++; } catch (fieldError) { - console.error(`Error occurred while processing label for field ${field.code}:`, fieldError); + console.error(`[Kintone Helper Extension] Error occurred while processing label for field ${field.code}:`, fieldError); skippedCount++; } } - console.log(`Field label processing completed: ${processedCount} successful, ${skippedCount} skipped`); + console.log(`[Kintone Helper Extension] Field label processing completed: ${processedCount} successful, ${skippedCount} skipped`); } catch (error) { - console.error('Error occurred while processing field labels:', error); + console.error('[Kintone Helper Extension] Error occurred while processing field labels:', error); throw error; } } @@ -219,27 +204,27 @@ export class FieldLabelProcessor { safelyAppendLabel(spacerElement, spacerLabel); // 添加红色虚线边框 - spacerElement.style.border = COLORS.SPACER_BORDER; + spacerElement.style.border = STYLES.SPACER_BORDER; processedCount++; } catch (spacerError) { - console.error(`Error occurred while processing label for spacer element ${spacer.elementId}:`, spacerError); + console.error(`[Kintone Helper Extension] Error occurred while processing label for spacer element ${spacer.elementId}:`, spacerError); skippedCount++; } } document.querySelectorAll('.spacer-cybozu:not([id])').forEach(spacerElement => { - spacerElement.style.border = COLORS.SPACER_BORDER; + spacerElement.style.border = STYLES.SPACER_BORDER; safelyAppendLabel(spacerElement, createFieldWithLabels({ code: '', type: LAYOUT_TYPES.SPACER, })); }); - } catch (error) { - console.error('Error occurred while processing spacer element labels:', error); - throw error; - } + } catch (error) { + console.error('[Kintone Helper Extension] Error occurred while processing spacer element labels:', error); + throw error; + } } } diff --git a/utils/constants.js b/src/utils/constants.js similarity index 95% rename from utils/constants.js rename to src/utils/constants.js index 84132f7..c76c60a 100644 --- a/utils/constants.js +++ b/src/utils/constants.js @@ -120,5 +120,5 @@ export const PAGE_TYPES = { }; export const SCRIPT_FILES = [ - 'main.js' // 立即执行 + 'src/main.js' // 立即执行 ]; diff --git a/utils/dom-utils.js b/src/utils/dom-utils.js similarity index 83% rename from utils/dom-utils.js rename to src/utils/dom-utils.js index 0b8a1c4..9fbf65f 100644 --- a/utils/dom-utils.js +++ b/src/utils/dom-utils.js @@ -31,19 +31,19 @@ export const getColumnWidths = (tableElement, fieldType) => { export const safelyInsertLabel = (targetElement, labelElement) => { try { if (!targetElement || !labelElement) { - console.warn('Failed to insert label: target element or label element does not exist'); + console.warn('[Kintone Helper Extension] 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'); + console.warn('[Kintone Helper Extension] 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); + console.error('[Kintone Helper Extension] Error inserting label: ', error); return false; } }; @@ -57,14 +57,14 @@ export const safelyInsertLabel = (targetElement, labelElement) => { export const safelyAppendLabel = (targetElement, labelElement) => { try { if (!targetElement || !labelElement) { - console.warn('Failed to append label: target element or label element does not exist'); + console.warn('[Kintone Helper Extension] 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); + console.error('[Kintone Helper Extension] Error appending label: ', error); return false; } }; diff --git a/utils/field-utils.js b/src/utils/field-utils.js similarity index 100% rename from utils/field-utils.js rename to src/utils/field-utils.js diff --git a/utils/kintone-utils.js b/src/utils/kintone-utils.js similarity index 85% rename from utils/kintone-utils.js rename to src/utils/kintone-utils.js index 5b3113c..d867ce0 100644 --- a/utils/kintone-utils.js +++ b/src/utils/kintone-utils.js @@ -11,8 +11,8 @@ export const getGuestSpaceId = () => { try { const match = window.location.pathname.match(/\/guest\/([0-9]+)\//); return match ? match[1] : undefined; - } catch (error) { - console.warn(error); +} catch (error) { + console.warn('[Kintone Helper Extension]', error); return undefined; } }; @@ -22,7 +22,7 @@ const getAppIdFromUrl = () => { const match = window.location.search.match(/\?app=([0-9]+)/); return match ? Number(match[1]) : undefined; } catch (error) { - console.warn(error); + console.warn('[Kintone Helper Extension]', error); return undefined; } }; @@ -43,13 +43,13 @@ export const getAppId = () => { const appId = kintone.app.getId(); if (!appId || isNaN(appId)) { - console.warn('Retrieved app ID is invalid:', appId); + console.warn('[Kintone Helper Extension] Retrieved app ID is invalid:', appId); return null; } return appId; } catch (error) { - console.warn('Failed to get app ID: ', error); + console.warn('[Kintone Helper Extension] Failed to get app ID: ', error); return null; } }; diff --git a/vite.config.js b/vite.config.js index 0e9e80c..a81ab62 100644 --- a/vite.config.js +++ b/vite.config.js @@ -1,10 +1,16 @@ import { defineConfig } from 'vite'; import webExtension from 'vite-plugin-web-extension'; -import { SCRIPT_FILES } from './utils/constants.js'; +import { SCRIPT_FILES } from './src/utils/constants.js'; +import path from 'path'; const isDev = process.env.NODE_ENV === 'development'; export default defineConfig({ + resolve: { + alias: { + '@': path.resolve(__dirname, 'src'), + }, + }, plugins: [ webExtension({ // 这个项目中所有需要注入的脚本都需要在这里指定,因为它们不会自动被插件检测到 @@ -22,9 +28,9 @@ export default defineConfig({ rollupOptions: !isDev ? undefined : { output: { // 开发模式下不压缩文件名,便于调试 - chunkFileNames: '[name].js', - entryFileNames: '[name].js', - assetFileNames: '[name].[ext]', + chunkFileNames: 'src/[name].js', + entryFileNames: 'src/[name].js', + assetFileNames: 'src/[name].[ext]', compact: false, // 禁用代码压缩 }, },