Files
testing-tool/entrypoints/content.ts
T
LingandRX 60537f6e2e Develop (#17)
新功能 (Features)
智能表单引擎: 新增智能表单填充功能,内置模糊匹配引擎、Mock数据生成器与视觉反馈渲染器。

表单映射与导出: 实现表单映射页面(包含扫描器和高亮器),并支持将配置导出为 JSON 文件,附带 Snackbar 状态提示。

表单识别增强: 增加按域名保存字段类型偏好的功能;添加字段定位闪烁以辅助查找;优化填充逻辑(支持单字段覆盖默认模式);重构 FieldList 组件以提升操作体验。

♻️ 代码重构 (Refactor)
通用组件提取: 提取并统一应用通用的 PageHeader 组件,移除独立的侧边栏页面及未使用的组件文件。

状态与逻辑优化: 改进 useStorageState 钩子(增加加载状态管理与防抖处理);将二维码解析功能重构为独立模块。

类型与依赖简化: 统一使用 SnackbarOptions 类型;简化假数据生成器中 faker 的导入与使用逻辑。

💄 样式与界面 (Style)
UI 细节打磨: 统一各页面头部图标颜色,调整表单输入框与按钮交互样式;优化时间戳页面、结果视图布局(增加圆角、调整内边距/对齐方式);重构存储选项网格及自动刷新开关样式。

代码格式: 优化项目中导入语句的顺序与格式。

👷 持续集成 (CI)
流程提效: 移除 Firefox 测试步骤以减少资源消耗;收紧工作流触发条件,移除 develop 及其变体分支,仅保留 main 分支触发。

📝 文档 (Docs)
代码维护: 补充组件的文档注释与类型导入。
2026-04-30 19:56:32 +08:00

229 lines
8.6 KiB
TypeScript

import '../.wxt/types/imports.d.ts';
import {
fillAllFields,
clearAllFields,
fillSelectedFields,
scanFormFields,
highlightField,
unhighlightField,
flashField,
FillMode,
type FormFieldInfo,
} from '@/utils/dummyDataGenerator';
import { MessageAction, type MessagePayload, type MessageResponse } from '@/utils/messages';
import { SmartDetector } from '@/utils/formMapping/scanner';
import { highlighter } from '@/utils/formMapping/highlighter';
import {
FuzzyMatcher,
SmartInjectionEngine,
FeedbackRenderer,
} from '@/utils/formMapping/smartInjector';
import { storageUtil } from '@/utils/chromeStorage';
import { FormMapEntry } from '@/types/storage';
// 存储当前扫描到的字段列表,用于高亮联动
let currentFields: FormFieldInfo[] = [];
export default defineContentScript({
matches: ['<all_urls>'],
runAt: 'document_end',
main() {
// === 通用表单映射助手逻辑 ===
chrome.storage.onChanged.addListener((changes, area) => {
if (
area === 'local' &&
(changes['active_form_map'] || changes['app/formMapping/isPicking'])
) {
updateMappingUI();
}
});
async function updateMappingUI() {
const entries = ((await storageUtil.get('active_form_map')) as FormMapEntry[]) || [];
const isPicking = ((await storageUtil.get('app/formMapping/isPicking')) as boolean) || false;
if (entries.length > 0 || isPicking) {
highlighter.show();
highlighter.draw(entries);
if (isPicking) {
highlighter.enablePicker(async (el) => {
const fingerprint = SmartDetector.generateFingerprint(el);
const label = SmartDetector.extractSemanticLabel(el);
const newEntry: FormMapEntry = {
id: Math.random().toString(36).substr(2, 9),
label_display: label,
fingerprint,
action_logic: { type: 'text', strategy: 'fixed', value: '' },
ui_state: { is_selected: true },
};
const currentMap = ((await storageUtil.get('active_form_map')) as FormMapEntry[]) || [];
await storageUtil.set('active_form_map', [...currentMap, newEntry]);
await storageUtil.set('app/formMapping/isPicking', false);
});
} else {
highlighter.disablePicker();
}
} else {
highlighter.hide();
}
}
// 初始加载映射 UI
updateMappingUI();
// === 原有表单识别逻辑 ===
// 监听来自 popup/sidepanel 的消息
chrome.runtime.onMessage.addListener(
(message: MessagePayload, _sender, sendResponse: (response: MessageResponse) => void) => {
try {
switch (message.action) {
case MessageAction.SCAN_FORM_FIELDS: {
const result = scanFormFields();
currentFields = result.fields;
sendResponse({
success: true,
fields: result.fields.map((f) => ({
id: f.id,
fieldType: f.fieldType,
label: f.label,
placeholder: f.placeholder,
name: f.name,
value: f.value,
isSelected: f.isSelected,
generatedValue: f.generatedValue,
})),
totalCount: result.totalCount,
validCount: result.validCount,
hasModal: !!result.modalContainer,
});
break;
}
case MessageAction.FILL_VALID_DATA:
fillAllFields(FillMode.VALID, message.includeHidden || false);
sendResponse({ success: true, message: '已填充有效数据' });
break;
case MessageAction.FILL_INVALID_DATA:
fillAllFields(FillMode.INVALID, message.includeHidden || false);
sendResponse({ success: true, message: '已填充无效数据' });
break;
case MessageAction.FILL_SELECTED_FIELDS: {
// 使用之前扫描时存储的字段,因为它们包含element属性
const incomingFields = message.fields || [];
const fieldsToFill = currentFields.map((field) => {
const incomingField = incomingFields.find((f) => f.id === field.id);
if (incomingField) {
return {
...field,
fieldType: incomingField.fieldType,
isSelected: incomingField.isSelected,
useInvalidData: (incomingField as { useInvalidData?: boolean }).useInvalidData,
};
}
return field;
});
const count = fillSelectedFields(fieldsToFill, message.mode || FillMode.VALID);
sendResponse({ success: true, message: `已填充 ${count} 个字段` });
break;
}
case MessageAction.CLEAR_ALL_FIELDS:
clearAllFields();
sendResponse({ success: true, message: '已清空所有字段' });
break;
case MessageAction.HIGHLIGHT_FIELD: {
const fieldId = message.fieldId;
const field = currentFields.find((f) => f.id === fieldId);
if (field) {
highlightField(field.element);
sendResponse({ success: true });
} else {
sendResponse({ success: false, message: '未找到字段' });
}
break;
}
case MessageAction.UNHIGHLIGHT_FIELD: {
const fieldId = message.fieldId;
const field = currentFields.find((f) => f.id === fieldId);
if (field) {
unhighlightField(field.element);
sendResponse({ success: true });
} else {
sendResponse({ success: false, message: '未找到字段' });
}
break;
}
case MessageAction.HIGHLIGHT_ALL_FIELDS: {
const fieldIds = message.fieldIds || [];
fieldIds.forEach((id) => {
const field = currentFields.find((f) => f.id === id);
if (field) {
highlightField(field.element);
}
});
sendResponse({ success: true });
break;
}
case MessageAction.UNHIGHLIGHT_ALL_FIELDS:
currentFields.forEach((field) => {
unhighlightField(field.element);
});
sendResponse({ success: true });
break;
case MessageAction.FLASH_FIELD: {
const fieldId = message.fieldId;
const field = currentFields.find((f) => f.id === fieldId);
if (field) {
flashField(field.element);
sendResponse({ success: true });
} else {
sendResponse({ success: false, message: '未找到字段' });
}
break;
}
case 'FORM_INJECT': {
try {
const injectData =
(message.data as Array<{ entry: FormMapEntry; mockValue: string }>) || [];
const results = injectData.map((item) => {
const matchResult = FuzzyMatcher.findTargetElement(item.entry.fingerprint);
if (matchResult.element) {
const injectResult = SmartInjectionEngine.inject(
matchResult.element,
item.entry,
item.mockValue,
);
if (injectResult.success) {
FeedbackRenderer.renderSuccess(matchResult.element);
} else {
FeedbackRenderer.renderError(matchResult.element);
}
return { id: item.entry.id, success: injectResult.success };
} else {
return { id: item.entry.id, success: false };
}
});
sendResponse({ success: true, results });
} catch (error) {
console.error('智能注入失败:', error);
sendResponse({
success: false,
error: error instanceof Error ? error.message : '注入失败',
});
}
break;
}
default:
sendResponse({ success: false, message: '未知操作' });
}
} catch (error) {
console.error('执行操作失败:', error);
sendResponse({ success: false, message: '执行操作失败' });
}
},
);
},
});