Compare commits
3 Commits
2793c70010
...
fce1a690f2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fce1a690f2 | ||
|
|
9d32452cb9 | ||
|
|
c88e1faa7c |
@@ -1,106 +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.processFieldLabels(fieldsWithLabels);
|
|
||||||
// 间距元素
|
|
||||||
labelProcessor.processSpacerLabels(spacerElements);
|
|
||||||
|
|
||||||
} 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 表单页面
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
// 颜色配置常量
|
|
||||||
export const COLORS = {
|
|
||||||
LABEL_TEXT: 'red',
|
|
||||||
SPACER_BORDER: '1px dotted red',
|
|
||||||
};
|
|
||||||
|
|
||||||
// 间距和尺寸配置常量
|
|
||||||
export const SPACING = {
|
|
||||||
GROUP_MARGIN_LEFT: '20px',
|
|
||||||
REFERENCE_TABLE_SPACER: '30px',
|
|
||||||
TABLE_COLUMN_PADDING: 8,
|
|
||||||
FIELD_CONTAINER_WIDTH: '100%',
|
|
||||||
};
|
|
||||||
|
|
||||||
export let IS_FIELD_TYPE_DISPLAY = false;
|
|
||||||
12
jsconfig.json
Normal file
12
jsconfig.json
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"baseUrl": ".",
|
||||||
|
"paths": {
|
||||||
|
"@/*": ["src/*"],
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
"src/**/*",
|
||||||
|
"*.js"
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -9,6 +9,6 @@
|
|||||||
],
|
],
|
||||||
"action": {},
|
"action": {},
|
||||||
"background": {
|
"background": {
|
||||||
"service_worker": "background.js"
|
"service_worker": "src/background.js"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,81 +0,0 @@
|
|||||||
/**
|
|
||||||
* 管理表单页面字段标签处理器模块
|
|
||||||
* 负责处理 admin 表单页面上的字段和间距标签
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 用于处理 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;
|
|
||||||
console.log(`AdminFieldLabelProcessor initialized with appId: ${this.appId}, pageType: ${this.pageType}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 返回是否为预览模式
|
|
||||||
* @returns {boolean} 是否预览
|
|
||||||
*/
|
|
||||||
isPreview() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 为子表字段元素添加标签
|
|
||||||
* @param {Object} field - 字段配置信息
|
|
||||||
* @param {HTMLElement} fieldElement - 字段 DOM 元素
|
|
||||||
*/
|
|
||||||
addSubtableLabel(field, fieldElement) {
|
|
||||||
console.log(`AdminFieldLabelProcessor.addSubtableLabel called with field: ${field.code}, fieldElement exists: ${!!fieldElement}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 为引用表字段元素添加标签
|
|
||||||
* @param {Object} field - 字段配置信息
|
|
||||||
* @param {HTMLElement} fieldElement - 字段 DOM 元素
|
|
||||||
*/
|
|
||||||
addReferenceTableLabel(field, fieldElement) {
|
|
||||||
console.log(`AdminFieldLabelProcessor.addReferenceTableLabel called with field: ${field.code}, fieldElement exists: ${!!fieldElement}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 为组字段元素添加标签
|
|
||||||
* @param {Object} field - 字段配置信息
|
|
||||||
* @param {HTMLElement} fieldElement - 字段 DOM 元素
|
|
||||||
*/
|
|
||||||
addGroupLabel(field, fieldElement) {
|
|
||||||
console.log(`AdminFieldLabelProcessor.addGroupLabel called with field: ${field.code}, fieldElement exists: ${!!fieldElement}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 为标准字段元素添加标签
|
|
||||||
* @param {Object} field - 字段配置信息
|
|
||||||
* @param {HTMLElement} fieldElement - 字段 DOM 元素
|
|
||||||
*/
|
|
||||||
addStandardFieldLabel(field, fieldElement) {
|
|
||||||
console.log(`AdminFieldLabelProcessor.addStandardFieldLabel called with field: ${field.code}, fieldElement exists: ${!!fieldElement}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 处理并为页面上的所有字段元素添加标签
|
|
||||||
* @param {Array} fieldsWithLabels - 已处理的字段对象数组
|
|
||||||
*/
|
|
||||||
processFieldLabels(fieldsWithLabels) {
|
|
||||||
console.log(`AdminFieldLabelProcessor.processFieldLabels called with ${fieldsWithLabels.length} fields`);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 处理并为页面上的间距元素添加标签
|
|
||||||
* @param {Array} spacerElements - 间距元素配置数组
|
|
||||||
*/
|
|
||||||
processSpacerLabels(spacerElements) {
|
|
||||||
console.log(`AdminFieldLabelProcessor.processSpacerLabels called with ${spacerElements.length} spacer elements`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -3,11 +3,33 @@
|
|||||||
* 提供用于创建和管理字段标签 DOM 元素的辅助函数
|
* 提供用于创建和管理字段标签 DOM 元素的辅助函数
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { FIELD_TYPES } from '../../utils/constants.js';
|
import { FIELD_TYPES } from '@/utils/constants.js';
|
||||||
import { COLORS, SPACING, IS_FIELD_TYPE_DISPLAY } from '../../features/add-field-label/settings.js';
|
import { LABEL_TEXT_STYLE, SPACING, IS_FIELD_TYPE_DISPLAY, LABEL_STYLE, ADMIN_LABEL_STYLE } from '@/features/add-field-label/settings.js';
|
||||||
|
import { isInDetailPage } from '@/utils/kintone-utils';
|
||||||
|
|
||||||
|
const _LABEL_STYLE = isInDetailPage() ? LABEL_STYLE : ADMIN_LABEL_STYLE
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 创建带有适当样式的字段标签 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 && isInDetailPage()) {
|
||||||
|
span.style.marginLeft = SPACING.GROUP_MARGIN_LEFT;
|
||||||
|
}
|
||||||
|
|
||||||
|
return span;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建字段标签容器元素
|
||||||
* @param {Object} params - 标签参数配置
|
* @param {Object} params - 标签参数配置
|
||||||
* @param {string} params.code - 字段代码
|
* @param {string} params.code - 字段代码
|
||||||
* @param {string} params.type - 字段类型(可选)
|
* @param {string} params.type - 字段类型(可选)
|
||||||
@@ -17,27 +39,19 @@ import { COLORS, SPACING, IS_FIELD_TYPE_DISPLAY } from '../../features/add-field
|
|||||||
const createFieldSpanElement = ({ code, type, width }) => {
|
const createFieldSpanElement = ({ code, type, width }) => {
|
||||||
const container = document.createElement('div');
|
const container = document.createElement('div');
|
||||||
|
|
||||||
// 处理宽度和边距设置
|
// 设置容器宽度样式
|
||||||
if (width !== undefined) {
|
if (width !== undefined) {
|
||||||
container.style.width = `${Number(width) - SPACING.TABLE_COLUMN_PADDING}px`; // 减去列填充
|
Object.assign(container.style, {
|
||||||
container.style.marginLeft = `${SPACING.TABLE_COLUMN_PADDING}px`; // 添加左边距
|
width: `${Number(width) - SPACING.TABLE_COLUMN_PADDING}px`,
|
||||||
|
marginLeft: `${SPACING.TABLE_COLUMN_PADDING}px`,
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
container.style.width = '100%'; // 默认全宽度
|
container.style.width = SPACING.FIELD_CONTAINER_WIDTH;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 创建和样式化 span 元素
|
// 创建文本内容
|
||||||
const fieldSpan = document.createElement('span');
|
const textContent = (IS_FIELD_TYPE_DISPLAY && type) ? `${code} (${type})` : code;
|
||||||
fieldSpan.textContent = (IS_FIELD_TYPE_DISPLAY && type !== undefined) ? `${code} (${type})` : code; // 显示代码和类型
|
const fieldSpan = createStyledSpan(textContent, type);
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
container.appendChild(fieldSpan);
|
container.appendChild(fieldSpan);
|
||||||
return container;
|
return container;
|
||||||
@@ -53,7 +67,8 @@ const createFieldSpanElement = ({ code, type, width }) => {
|
|||||||
*/
|
*/
|
||||||
export const createFieldWithLabels = ({ code, type, width }) => {
|
export const createFieldWithLabels = ({ code, type, width }) => {
|
||||||
const container = document.createElement('div');
|
const container = document.createElement('div');
|
||||||
container.style.display = 'inline-block'; // 布局的内联块显示
|
|
||||||
|
Object.assign(container.style, _LABEL_STYLE)
|
||||||
|
|
||||||
const fieldSpan = createFieldSpanElement({ code, type, width });
|
const fieldSpan = createFieldSpanElement({ code, type, width });
|
||||||
container.appendChild(fieldSpan);
|
container.appendChild(fieldSpan);
|
||||||
@@ -4,12 +4,12 @@ import {
|
|||||||
SYSTEM_STATUS_FIELD_TYPES,
|
SYSTEM_STATUS_FIELD_TYPES,
|
||||||
SYSTEM_FIELD_TYPES,
|
SYSTEM_FIELD_TYPES,
|
||||||
EXCLUDED_GROUP_TYPES
|
EXCLUDED_GROUP_TYPES
|
||||||
} from '../../utils/constants.js';
|
} from '@/utils/constants.js';
|
||||||
import {
|
import {
|
||||||
sortFieldOptions,
|
sortFieldOptions,
|
||||||
shouldSortOptions,
|
shouldSortOptions,
|
||||||
markLookupCopies,
|
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 - 布局定义数组
|
||||||
* @param {Array} layouts - 布局定义
|
* @param {Object} properties - 字段属性映射
|
||||||
* @returns {Object} 包含字段和间距数组的对象
|
* @param {Array} fields - 字段数组引用
|
||||||
|
* @param {Array} spacers - 间距数组引用
|
||||||
*/
|
*/
|
||||||
export const generateFields = (properties, layouts) => {
|
const processAllLayouts = (layouts, properties, fields, spacers) => {
|
||||||
const fields = [];
|
|
||||||
const spacers = [];
|
|
||||||
|
|
||||||
// 首先添加系统状态字段
|
|
||||||
addSystemStatusFields(properties, fields);
|
|
||||||
|
|
||||||
// 处理每个布局项
|
|
||||||
layouts.forEach(layout => {
|
layouts.forEach(layout => {
|
||||||
switch (layout.type) {
|
switch (layout.type) {
|
||||||
case LAYOUT_TYPES.ROW:
|
case LAYOUT_TYPES.ROW:
|
||||||
@@ -176,9 +170,21 @@ export const generateFields = (properties, layouts) => {
|
|||||||
break;
|
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);
|
markLookupCopies(fields);
|
||||||
|
|
||||||
addMissingSystemFields(properties, fields);
|
addMissingSystemFields(properties, fields);
|
||||||
|
|
||||||
return { fields, spacers };
|
return { fields, spacers };
|
||||||
156
src/features/add-field-label/main.js
Normal file
156
src/features/add-field-label/main.js
Normal 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 表单页面
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
42
src/features/add-field-label/settings.js
Normal file
42
src/features/add-field-label/settings.js
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
// 间距和尺寸配置常量
|
||||||
|
export const SPACING = {
|
||||||
|
GROUP_MARGIN_LEFT: '20px',
|
||||||
|
REFERENCE_TABLE_SPACER: '30px',
|
||||||
|
TABLE_COLUMN_PADDING: 8,
|
||||||
|
FIELD_CONTAINER_WIDTH: '100%',
|
||||||
|
};
|
||||||
|
|
||||||
|
// 样式配置常量
|
||||||
|
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',
|
||||||
|
zIndex: '1',
|
||||||
|
lineHeight: '1',
|
||||||
|
background: '#ffd9d9',
|
||||||
|
padding: '2px',
|
||||||
|
top: STYLES.BASIC_LABEL_TOP,
|
||||||
|
border: 'dotted red 1px',
|
||||||
|
fontSize: '0.75rem',
|
||||||
|
};
|
||||||
|
|
||||||
|
export const LABEL_STYLE = {
|
||||||
|
display: 'inline-block'
|
||||||
|
}
|
||||||
|
|
||||||
|
export let IS_FIELD_TYPE_DISPLAY = false;
|
||||||
@@ -6,17 +6,17 @@
|
|||||||
import { addFieldLabel } from './features/add-field-label/main.js';
|
import { addFieldLabel } from './features/add-field-label/main.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 运行主要的 Kintone Helper 函数
|
* 主入口函数:协调并运行所有扩展功能
|
||||||
* @returns {Promise<void>}
|
* @returns {Promise<void>}
|
||||||
*/
|
*/
|
||||||
export const runKintoneHelper = async () => {
|
export const runKintoneHelper = async () => {
|
||||||
try {
|
try {
|
||||||
await addFieldLabel();
|
await addFieldLabel();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Kintone Helper execution failed:', error);
|
console.error('[Kintone Helper Extension] ❌ Execution failed:', error);
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 目前立即执行
|
// 立即执行主函数
|
||||||
runKintoneHelper();
|
runKintoneHelper();
|
||||||
328
src/page/admin/form/admin-field-label-processor.js
Normal file
328
src/page/admin/form/admin-field-label-processor.js
Normal file
@@ -0,0 +1,328 @@
|
|||||||
|
/**
|
||||||
|
* 管理表单页面字段标签处理器模块
|
||||||
|
* 负责处理 admin 表单页面上的字段和间距标签
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {
|
||||||
|
getColumnWidths,
|
||||||
|
safelyInsertLabel,
|
||||||
|
safelyAppendLabel,
|
||||||
|
FieldTypeCheckerForAdminDom
|
||||||
|
} from '@/utils/dom-utils.js';
|
||||||
|
import {
|
||||||
|
createFieldWithLabels,
|
||||||
|
} from '@/features/add-field-label/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;
|
||||||
|
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) {
|
||||||
|
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(`[Kintone Helper Extension] 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(`[Kintone Helper Extension] 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) {
|
||||||
|
console.warn(`[Kintone Helper Extension] Parent element for group field ${field.code} does not exist`);
|
||||||
|
// 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(`[Kintone Helper Extension] 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(`[Kintone Helper Extension] 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(`[Kintone Helper Extension] Error occurred while processing label for field ${field.code}:`, fieldError);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('[Kintone Helper Extension] 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('[Kintone Helper Extension] Error occurred while processing spacer element labels:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,14 +7,14 @@ import {
|
|||||||
getColumnWidths,
|
getColumnWidths,
|
||||||
safelyInsertLabel,
|
safelyInsertLabel,
|
||||||
safelyAppendLabel
|
safelyAppendLabel
|
||||||
} from '../../utils/dom-utils.js';
|
} from '@/utils/dom-utils.js';
|
||||||
import {
|
import {
|
||||||
createFieldWithLabels,
|
createFieldWithLabels,
|
||||||
createFieldLabels
|
createFieldLabels
|
||||||
} from './dom.js';
|
} from '@/features/add-field-label/dom.js';
|
||||||
import { FieldTypeChecker } from '../../utils/field-utils.js';
|
import { FieldTypeChecker } from '@/utils/field-utils.js';
|
||||||
import { COLORS } from '../../features/add-field-label/settings.js';
|
import { STYLES } from '@/features/add-field-label/settings.js';
|
||||||
import { LAYOUT_TYPES } from '../../utils/constants.js';
|
import { LAYOUT_TYPES } from '@/utils/constants.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 用于处理不同页面上字段和间距标签的字段标签处理器类
|
* 用于处理不同页面上字段和间距标签的字段标签处理器类
|
||||||
@@ -39,30 +39,40 @@ export class FieldLabelProcessor {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
beforeProcess(rawFieldsMap, rawLayout, fields, spacers) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建表类型标签元素
|
||||||
|
* @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 {Object} field - 字段配置信息
|
||||||
* @param {HTMLElement} fieldElement - 字段 DOM 元素
|
* @param {HTMLElement} fieldElement - 字段 DOM 元素
|
||||||
*/
|
*/
|
||||||
addSubtableLabel(field, fieldElement) {
|
addSubtableLabel(field, fieldElement) {
|
||||||
try {
|
// 在表上方添加字段代码标签
|
||||||
// 在表上方添加字段代码标签
|
const tableCodeLabel = this.createTableLabel(field);
|
||||||
const tableCodeLabel = createFieldWithLabels({
|
safelyInsertLabel(fieldElement, tableCodeLabel);
|
||||||
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);
|
|
||||||
|
|
||||||
safelyInsertLabel(fieldElement, columnLabels);
|
// 添加列标签
|
||||||
|
const fieldNames = Object.keys(field.fields);
|
||||||
|
const columnWidths = getColumnWidths(fieldElement, field.type);
|
||||||
|
const columnLabels = createFieldLabels(fieldNames, columnWidths, field.type);
|
||||||
|
|
||||||
} catch (error) {
|
safelyInsertLabel(fieldElement, columnLabels);
|
||||||
console.error(`Failed to add label for subtable field ${field.code}:`, error);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -71,28 +81,17 @@ export class FieldLabelProcessor {
|
|||||||
* @param {HTMLElement} fieldElement - 字段 DOM 元素
|
* @param {HTMLElement} fieldElement - 字段 DOM 元素
|
||||||
*/
|
*/
|
||||||
addReferenceTableLabel(field, fieldElement) {
|
addReferenceTableLabel(field, fieldElement) {
|
||||||
try {
|
// 在引用表上方添加字段代码标签
|
||||||
// 在引用表上方添加字段代码标签
|
safelyInsertLabel(fieldElement, this.createTableLabel(field));
|
||||||
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 hasData = !!fieldElement.querySelector('tbody > tr');
|
||||||
|
const displayLabels = createFieldLabels(displayFields, columnWidths, field.type, hasData);
|
||||||
|
|
||||||
// 如果可用,添加显示字段标签
|
safelyInsertLabel(fieldElement, displayLabels);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -102,23 +101,18 @@ export class FieldLabelProcessor {
|
|||||||
* @param {HTMLElement} fieldElement - 字段 DOM 元素
|
* @param {HTMLElement} fieldElement - 字段 DOM 元素
|
||||||
*/
|
*/
|
||||||
addGroupLabel(field, fieldElement) {
|
addGroupLabel(field, fieldElement) {
|
||||||
try {
|
const parentElement = fieldElement.parentElement;
|
||||||
// 在组的父元素之前添加标签
|
if (!parentElement) {
|
||||||
const parentElement = fieldElement.parentElement;
|
console.warn(`[Kintone Helper Extension] Parent element for group field ${field.code} does not exist`);
|
||||||
if (parentElement) {
|
return;
|
||||||
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 groupLabel = createFieldWithLabels({
|
||||||
|
code: field.code,
|
||||||
|
type: field.type,
|
||||||
|
});
|
||||||
|
|
||||||
|
safelyInsertLabel(parentElement, groupLabel);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -127,17 +121,12 @@ export class FieldLabelProcessor {
|
|||||||
* @param {HTMLElement} fieldElement - 字段 DOM 元素
|
* @param {HTMLElement} fieldElement - 字段 DOM 元素
|
||||||
*/
|
*/
|
||||||
addStandardFieldLabel(field, fieldElement) {
|
addStandardFieldLabel(field, fieldElement) {
|
||||||
try {
|
|
||||||
const fieldLabel = createFieldWithLabels({
|
const fieldLabel = createFieldWithLabels({
|
||||||
code: field.code,
|
code: field.code,
|
||||||
type: field.type,
|
type: field.type,
|
||||||
});
|
});
|
||||||
|
|
||||||
safelyInsertLabel(fieldElement, fieldLabel);
|
safelyInsertLabel(fieldElement, fieldLabel);
|
||||||
|
|
||||||
} catch (error) {
|
|
||||||
console.error(`Failed to add label for standard field ${field.code}:`, error);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -174,15 +163,15 @@ export class FieldLabelProcessor {
|
|||||||
processedCount++;
|
processedCount++;
|
||||||
|
|
||||||
} catch (fieldError) {
|
} 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++;
|
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) {
|
} 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;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -215,27 +204,27 @@ export class FieldLabelProcessor {
|
|||||||
safelyAppendLabel(spacerElement, spacerLabel);
|
safelyAppendLabel(spacerElement, spacerLabel);
|
||||||
|
|
||||||
// 添加红色虚线边框
|
// 添加红色虚线边框
|
||||||
spacerElement.style.border = COLORS.SPACER_BORDER;
|
spacerElement.style.border = STYLES.SPACER_BORDER;
|
||||||
|
|
||||||
processedCount++;
|
processedCount++;
|
||||||
|
|
||||||
} catch (spacerError) {
|
} 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++;
|
skippedCount++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
document.querySelectorAll('.spacer-cybozu:not([id])').forEach(spacerElement => {
|
document.querySelectorAll('.spacer-cybozu:not([id])').forEach(spacerElement => {
|
||||||
spacerElement.style.border = COLORS.SPACER_BORDER;
|
spacerElement.style.border = STYLES.SPACER_BORDER;
|
||||||
safelyAppendLabel(spacerElement, createFieldWithLabels({
|
safelyAppendLabel(spacerElement, createFieldWithLabels({
|
||||||
code: '',
|
code: '',
|
||||||
type: LAYOUT_TYPES.SPACER,
|
type: LAYOUT_TYPES.SPACER,
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error occurred while processing spacer element labels:', error);
|
console.error('[Kintone Helper Extension] Error occurred while processing spacer element labels:', error);
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -94,6 +94,23 @@ export const LOOKUP_COPY_SUPPORTED_TYPES = [
|
|||||||
FIELD_TYPES.RECORD_NUMBER,
|
FIELD_TYPES.RECORD_NUMBER,
|
||||||
];
|
];
|
||||||
|
|
||||||
|
// DOM 类名常量定义(用于 admin 表单页面)
|
||||||
|
export const DOM_CLASSES = {
|
||||||
|
CANVAS_ELEMENT: 'fm-canvas-gaia',
|
||||||
|
CONTENT_ELEMENT: 'fm-canvas-contentElement-gaia',
|
||||||
|
INSERT_LABEL_ELEMENT: 'input-label-cybozu',
|
||||||
|
INSERT_GROUP_LABEL_ELEMENT: 'group-label-gaia',
|
||||||
|
INSERT_SPACER_LABEL_ELEMENT: 'fm-control-spacer-gaia',
|
||||||
|
ROW_ELEMENT: 'fm-row-gaia',
|
||||||
|
FIELD_ELEMENT: 'fm-control-gaia',
|
||||||
|
SUBTABLE: 'fm-subtable-gaia',
|
||||||
|
GROUP: 'fm-control-group-gaia',
|
||||||
|
REFERENCE_TABLE: 'fm-control-reference_table-field-gaia',
|
||||||
|
SPACER: 'fm-control-spacer-field-gaia',
|
||||||
|
HR: 'fm-control-hr-field-gaia',
|
||||||
|
LABEL: 'fm-control-label-field-gaia',
|
||||||
|
};
|
||||||
|
|
||||||
// 页面类型常量定义
|
// 页面类型常量定义
|
||||||
export const PAGE_TYPES = {
|
export const PAGE_TYPES = {
|
||||||
DETAIL: 'detail',
|
DETAIL: 'detail',
|
||||||
@@ -103,5 +120,5 @@ export const PAGE_TYPES = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const SCRIPT_FILES = [
|
export const SCRIPT_FILES = [
|
||||||
'main.js' // 立即执行
|
'src/main.js' // 立即执行
|
||||||
];
|
];
|
||||||
107
src/utils/dom-utils.js
Normal file
107
src/utils/dom-utils.js
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
/**
|
||||||
|
* DOM 操作工具模块
|
||||||
|
* 提供用于创建和管理 DOM 元素的辅助函数
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { FIELD_TYPES, DOM_CLASSES } 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('[Kintone Helper Extension] Failed to insert label: target element or label element does not exist');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!targetElement.before) {
|
||||||
|
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('[Kintone Helper Extension] 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('[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('[Kintone Helper Extension] Error appending label: ', error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const isRowElement = (element) => element?.classList?.contains(DOM_CLASSES.ROW_ELEMENT);
|
||||||
|
const isFieldElement = (element) => element?.classList?.contains(DOM_CLASSES.FIELD_ELEMENT);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 基于 DOM 类名判断字段类型的工具对象
|
||||||
|
* 用于 admin 表单页面通过 DOM 元素判断字段类型
|
||||||
|
*/
|
||||||
|
export const FieldTypeCheckerForAdminDom = {
|
||||||
|
isRowElement,
|
||||||
|
isFieldElement,
|
||||||
|
|
||||||
|
isSubtable: (element) => {
|
||||||
|
return !isRowElement(element) && !isFieldElement(element) &&
|
||||||
|
element?.classList?.contains(DOM_CLASSES.SUBTABLE);
|
||||||
|
},
|
||||||
|
isGroup: (element) => {
|
||||||
|
return isRowElement(element) &&
|
||||||
|
!!element?.querySelector(`.${DOM_CLASSES.GROUP}`);
|
||||||
|
},
|
||||||
|
isReferenceTable: (element) => {
|
||||||
|
return isRowElement(element) &&
|
||||||
|
!!element?.querySelector(`.${DOM_CLASSES.REFERENCE_TABLE}`);
|
||||||
|
},
|
||||||
|
isSpacer: (element) => {
|
||||||
|
return isFieldElement(element) &&
|
||||||
|
element?.classList?.contains(DOM_CLASSES.SPACER);
|
||||||
|
},
|
||||||
|
isLabel: (element) => {
|
||||||
|
return isFieldElement(element) &&
|
||||||
|
element?.classList?.contains(DOM_CLASSES.LABEL);
|
||||||
|
},
|
||||||
|
isHr: (element) => {
|
||||||
|
return isFieldElement(element) &&
|
||||||
|
element?.classList?.contains(DOM_CLASSES.HR);
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -11,8 +11,8 @@ export const getGuestSpaceId = () => {
|
|||||||
try {
|
try {
|
||||||
const match = window.location.pathname.match(/\/guest\/([0-9]+)\//);
|
const match = window.location.pathname.match(/\/guest\/([0-9]+)\//);
|
||||||
return match ? match[1] : undefined;
|
return match ? match[1] : undefined;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.warn(error);
|
console.warn('[Kintone Helper Extension]', error);
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -22,7 +22,7 @@ const getAppIdFromUrl = () => {
|
|||||||
const match = window.location.search.match(/\?app=([0-9]+)/);
|
const match = window.location.search.match(/\?app=([0-9]+)/);
|
||||||
return match ? Number(match[1]) : undefined;
|
return match ? Number(match[1]) : undefined;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.warn(error);
|
console.warn('[Kintone Helper Extension]', error);
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -34,7 +34,7 @@ const getAppIdFromUrl = () => {
|
|||||||
export const getAppId = () => {
|
export const getAppId = () => {
|
||||||
try {
|
try {
|
||||||
if (isInAdminPage()) {
|
if (isInAdminPage()) {
|
||||||
return getAppIdFromUrl() || null;
|
return Number(cybozu?.data?.page?.APP_ID) || getAppIdFromUrl() || null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isGlobalKintoneExist()) {
|
if (!isGlobalKintoneExist()) {
|
||||||
@@ -43,13 +43,13 @@ export const getAppId = () => {
|
|||||||
|
|
||||||
const appId = kintone.app.getId();
|
const appId = kintone.app.getId();
|
||||||
if (!appId || isNaN(appId)) {
|
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 null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return appId;
|
return appId;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.warn('Failed to get app ID: ', error);
|
console.warn('[Kintone Helper Extension] Failed to get app ID: ', error);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -1,70 +0,0 @@
|
|||||||
/**
|
|
||||||
* 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;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
@@ -1,10 +1,16 @@
|
|||||||
import { defineConfig } from 'vite';
|
import { defineConfig } from 'vite';
|
||||||
import webExtension from 'vite-plugin-web-extension';
|
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';
|
const isDev = process.env.NODE_ENV === 'development';
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
|
resolve: {
|
||||||
|
alias: {
|
||||||
|
'@': path.resolve(__dirname, 'src'),
|
||||||
|
},
|
||||||
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
webExtension({
|
webExtension({
|
||||||
// 这个项目中所有需要注入的脚本都需要在这里指定,因为它们不会自动被插件检测到
|
// 这个项目中所有需要注入的脚本都需要在这里指定,因为它们不会自动被插件检测到
|
||||||
@@ -22,9 +28,9 @@ export default defineConfig({
|
|||||||
rollupOptions: !isDev ? undefined : {
|
rollupOptions: !isDev ? undefined : {
|
||||||
output: {
|
output: {
|
||||||
// 开发模式下不压缩文件名,便于调试
|
// 开发模式下不压缩文件名,便于调试
|
||||||
chunkFileNames: '[name].js',
|
chunkFileNames: 'src/[name].js',
|
||||||
entryFileNames: '[name].js',
|
entryFileNames: 'src/[name].js',
|
||||||
assetFileNames: '[name].[ext]',
|
assetFileNames: 'src/[name].[ext]',
|
||||||
compact: false, // 禁用代码压缩
|
compact: false, // 禁用代码压缩
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user