This commit is contained in:
2025-10-17 14:39:36 +08:00
parent 40a5f4e29c
commit 7f3def2121
17 changed files with 353 additions and 272 deletions

View File

@@ -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<Object>} 表单字段和布局数据
*/
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<void>}
*/
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 表单页面
});
}
};

12
jsconfig.json Normal file
View File

@@ -0,0 +1,12 @@
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["src/*"],
}
},
"include": [
"src/**/*",
"*.js"
]
}

View File

@@ -9,6 +9,6 @@
],
"action": {},
"background": {
"service_worker": "background.js"
"service_worker": "src/background.js"
}
}

View File

@@ -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 };

View File

@@ -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<Array>} 并发的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<Object>} 表单字段和布局数据对象
*/
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<void>}
*/
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 表单页面
});
}
};

View File

@@ -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',

View File

@@ -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;
}
};

View File

@@ -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;
}
}
}

View File

@@ -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;

View File

@@ -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 });

View File

@@ -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;
}
}
}

View File

@@ -120,5 +120,5 @@ export const PAGE_TYPES = {
};
export const SCRIPT_FILES = [
'main.js' // 立即执行
'src/main.js' // 立即执行
];

View File

@@ -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;
}
};

View File

@@ -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;
}
};

View File

@@ -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, // 禁用代码压缩
},
},