第一版完成
This commit is contained in:
@@ -50,6 +50,23 @@ export function ExtractionSettings({ onChange }: ExtractionSettingsProps) {
|
||||
llm: ''
|
||||
});
|
||||
|
||||
// 获取所有可用字段(合并大模型、多模态和正则抽取的字段)
|
||||
const getAllFields = (): string[] => {
|
||||
const llm_ocr_fields = fields.llm_ocr || [];
|
||||
// 从多模态字段中提取基本字段名(去除类型后缀)
|
||||
const llm_fields = (fields.llm || []).map(field => {
|
||||
const [fieldName] = field.split('_');
|
||||
return fieldName;
|
||||
});
|
||||
// 获取正则字段名
|
||||
const regex_fields = regexFields
|
||||
.map(field => field.fieldName)
|
||||
.filter(name => name.trim() !== '');
|
||||
|
||||
// 合并并去重
|
||||
return [...new Set([...llm_ocr_fields, ...llm_fields, ...regex_fields])];
|
||||
};
|
||||
|
||||
// 在组件初始化时,如果Context中已有字段数据,则使用Context数据初始化
|
||||
useEffect(() => {
|
||||
if (ruleContext && ruleContext.extractionFields.length > 0) {
|
||||
@@ -61,14 +78,47 @@ export function ExtractionSettings({ onChange }: ExtractionSettingsProps) {
|
||||
}
|
||||
}, []);
|
||||
|
||||
// 更新所有抽取字段到Context
|
||||
const updateAllFields = () => {
|
||||
const allFields = getAllFields();
|
||||
|
||||
// 更新全局Context中的字段
|
||||
if (ruleContext) {
|
||||
ruleContext.updateExtractionFields(allFields);
|
||||
}
|
||||
|
||||
// 触发自定义事件,通知字段已更新(兼容非Context的实现)
|
||||
const event = new CustomEvent('extraction-fields-updated', {
|
||||
detail: {
|
||||
fields: allFields,
|
||||
tab: currentTab,
|
||||
fieldsData: {
|
||||
llm_ocr: fields.llm_ocr || [],
|
||||
llm: fields.llm || [],
|
||||
regex: regexFields.map(f => f.fieldName).filter(name => name.trim() !== '')
|
||||
}
|
||||
}
|
||||
});
|
||||
document.dispatchEvent(event);
|
||||
|
||||
if (onChange) {
|
||||
onChange({
|
||||
extractionMethod: currentTab,
|
||||
fields,
|
||||
regexFields,
|
||||
allFields // 添加合并后的所有字段
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// 在所有字段集合变化时自动更新
|
||||
useEffect(() => {
|
||||
updateAllFields();
|
||||
}, [fields, regexFields]);
|
||||
|
||||
const handleTabChange = (tab: string) => {
|
||||
setCurrentTab(tab);
|
||||
|
||||
// 当切换抽取方法时,更新全局Context中的字段
|
||||
if (ruleContext && fields[tab]) {
|
||||
ruleContext.updateExtractionFields(fields[tab]);
|
||||
}
|
||||
|
||||
if (onChange) {
|
||||
onChange({ extractionMethod: tab });
|
||||
}
|
||||
@@ -114,26 +164,8 @@ export function ExtractionSettings({ onChange }: ExtractionSettingsProps) {
|
||||
setSelectedFieldType('default');
|
||||
}
|
||||
|
||||
// 更新全局Context中的字段
|
||||
if (ruleContext) {
|
||||
ruleContext.updateExtractionFields(newFields);
|
||||
}
|
||||
|
||||
if (onChange) {
|
||||
onChange({
|
||||
extractionMethod: currentTab,
|
||||
fields: {
|
||||
...fields,
|
||||
[type]: newFields
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 触发自定义事件,通知字段已更新(兼容非Context的实现)
|
||||
const event = new CustomEvent('extraction-fields-updated', {
|
||||
detail: { fields: newFields }
|
||||
});
|
||||
document.dispatchEvent(event);
|
||||
// 立即触发字段更新事件,通知评查设置组件
|
||||
setTimeout(() => updateAllFields(), 0);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -153,26 +185,8 @@ export function ExtractionSettings({ onChange }: ExtractionSettingsProps) {
|
||||
[type]: newFields
|
||||
});
|
||||
|
||||
// 更新全局Context中的字段
|
||||
if (ruleContext) {
|
||||
ruleContext.updateExtractionFields(newFields);
|
||||
}
|
||||
|
||||
if (onChange) {
|
||||
onChange({
|
||||
extractionMethod: currentTab,
|
||||
fields: {
|
||||
...fields,
|
||||
[type]: newFields
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 触发自定义事件,通知字段已更新(兼容非Context的实现)
|
||||
const event = new CustomEvent('extraction-fields-updated', {
|
||||
detail: { fields: newFields }
|
||||
});
|
||||
document.dispatchEvent(event);
|
||||
// 立即触发字段更新事件,通知评查设置组件
|
||||
setTimeout(() => updateAllFields(), 0);
|
||||
};
|
||||
|
||||
// 添加正则表达式字段行
|
||||
@@ -180,14 +194,15 @@ export function ExtractionSettings({ onChange }: ExtractionSettingsProps) {
|
||||
const newId = `${regexFields.length + 1}`;
|
||||
setRegexFields([...regexFields, { id: newId, fieldName: '', regex: '' }]);
|
||||
|
||||
// 如果是新增了regex字段,也要更新字段列表通知评查设置组件
|
||||
setTimeout(() => updateAllFields(), 0);
|
||||
|
||||
if (onChange) {
|
||||
onChange({
|
||||
extractionMethod: currentTab,
|
||||
regexFields: [...regexFields, { id: newId, fieldName: '', regex: '' }]
|
||||
});
|
||||
}
|
||||
|
||||
// 添加字段时不触发事件,因为此时字段名称尚未填写
|
||||
};
|
||||
|
||||
// 删除正则表达式字段行
|
||||
@@ -216,13 +231,9 @@ export function ExtractionSettings({ onChange }: ExtractionSettingsProps) {
|
||||
|
||||
setRegexFields(newRegexFields);
|
||||
|
||||
// 如果是字段名更新且当前抽取方法是正则抽取,则更新Context
|
||||
if (key === 'fieldName' && currentTab === 'ocr_regex' && ruleContext) {
|
||||
const fieldNames = newRegexFields
|
||||
.map(field => field.fieldName)
|
||||
.filter(name => name.trim() !== '');
|
||||
|
||||
ruleContext.updateExtractionFields(fieldNames);
|
||||
// 如果更新的是字段名,则触发字段更新事件
|
||||
if (key === 'fieldName') {
|
||||
setTimeout(() => updateAllFields(), 0);
|
||||
}
|
||||
|
||||
if (onChange) {
|
||||
@@ -231,17 +242,6 @@ export function ExtractionSettings({ onChange }: ExtractionSettingsProps) {
|
||||
regexFields: newRegexFields
|
||||
});
|
||||
}
|
||||
|
||||
// 如果更新的是字段名,触发自定义事件通知字段已更新
|
||||
if (key === 'fieldName' && value.trim()) {
|
||||
const allFieldNames = [...fields[currentTab], ...newRegexFields.map(f => f.fieldName).filter(f => f)];
|
||||
|
||||
// 触发自定义事件,通知字段已更新(兼容非Context的实现)
|
||||
const event = new CustomEvent('extraction-fields-updated', {
|
||||
detail: { fields: allFieldNames }
|
||||
});
|
||||
document.dispatchEvent(event);
|
||||
}
|
||||
};
|
||||
|
||||
// 应用正则模板
|
||||
|
||||
@@ -57,60 +57,51 @@ export function ReviewSettings({ onChange }: ReviewSettingsProps) {
|
||||
|
||||
// 监听抽取设置中的字段变化
|
||||
useEffect(() => {
|
||||
// 当Context中的字段发生变化时,立即更新可用字段
|
||||
// 当Context中的字段发生变化时,更新可用字段但保留已有配置
|
||||
if (extractionFields.length > 0) {
|
||||
setAvailableFields(extractionFields);
|
||||
// 检查是否有字段被删除
|
||||
const deletedFields = availableFields.filter(field => !extractionFields.includes(field));
|
||||
|
||||
// 同时更新已有规则中的可选字段状态
|
||||
updateRulesWithNewFields(extractionFields);
|
||||
return;
|
||||
// 处理新增的字段
|
||||
const newFields = extractionFields.filter((field: string) => !availableFields.includes(field));
|
||||
|
||||
if (newFields.length > 0 || deletedFields.length > 0) {
|
||||
// 设置最新的可用字段列表
|
||||
setAvailableFields(extractionFields);
|
||||
|
||||
// 处理规则中已删除的字段
|
||||
if (deletedFields.length > 0) {
|
||||
handleDeletedFields(deletedFields);
|
||||
}
|
||||
|
||||
// 使用最新的字段列表更新规则配置
|
||||
updateRulesWithNewFields(extractionFields);
|
||||
}
|
||||
}
|
||||
|
||||
// 获取抽取字段函数 - 当Context不可用时的备选方案
|
||||
const getExtractedFields = (): string[] => {
|
||||
const extractedFields: string[] = [];
|
||||
|
||||
// 查找当前活跃的抽取设置容器
|
||||
const activeExtractConfig = document.querySelector('.extraction-config:not(.hidden)');
|
||||
if (!activeExtractConfig) {
|
||||
// 如果没有找到活跃的配置,返回空数组
|
||||
return [];
|
||||
}
|
||||
|
||||
// 查找字段容器
|
||||
const fieldsContainer = activeExtractConfig.querySelector('.chips-container');
|
||||
if (fieldsContainer) {
|
||||
// 获取所有字段芯片
|
||||
const chips = fieldsContainer.querySelectorAll('.chip');
|
||||
chips.forEach(chip => {
|
||||
const fieldName = chip.textContent?.replace('×', '').trim();
|
||||
if (fieldName) {
|
||||
extractedFields.push(fieldName);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 查找正则字段
|
||||
const regexFields = document.querySelectorAll('.regex-field-row');
|
||||
regexFields.forEach(row => {
|
||||
const fieldNameInput = row.querySelector('input[placeholder="字段名称"]');
|
||||
if (fieldNameInput instanceof HTMLInputElement && fieldNameInput.value.trim()) {
|
||||
extractedFields.push(fieldNameInput.value.trim());
|
||||
}
|
||||
});
|
||||
|
||||
return extractedFields;
|
||||
};
|
||||
|
||||
// 初始化获取字段 - 仅在Context不可用时使用
|
||||
setAvailableFields(getExtractedFields());
|
||||
|
||||
// 监听抽取设置的变化 - 仅在Context不可用时使用
|
||||
// 监听抽取设置的变化 - 用于捕获非Context更新的情况
|
||||
const handleExtractionChange = (event: Event) => {
|
||||
if (event instanceof CustomEvent && event.detail && Array.isArray(event.detail.fields)) {
|
||||
setAvailableFields(event.detail.fields);
|
||||
} else {
|
||||
setAvailableFields(getExtractedFields());
|
||||
const incomingFields = event.detail.fields;
|
||||
|
||||
// 检查是否有字段被删除
|
||||
const deletedFields = availableFields.filter(field => !incomingFields.includes(field));
|
||||
|
||||
// 识别新增的字段
|
||||
const newFields = incomingFields.filter((field: string) => !availableFields.includes(field));
|
||||
|
||||
if (newFields.length > 0 || deletedFields.length > 0) {
|
||||
// 设置最新的可用字段列表
|
||||
setAvailableFields(incomingFields);
|
||||
|
||||
// 处理规则中已删除的字段
|
||||
if (deletedFields.length > 0) {
|
||||
handleDeletedFields(deletedFields);
|
||||
}
|
||||
|
||||
// 使用最新的字段列表更新规则配置
|
||||
updateRulesWithNewFields(incomingFields);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -121,39 +112,110 @@ export function ReviewSettings({ onChange }: ReviewSettingsProps) {
|
||||
return () => {
|
||||
document.removeEventListener('extraction-fields-updated', handleExtractionChange);
|
||||
};
|
||||
}, [extractionFields]);
|
||||
}, [extractionFields, availableFields]);
|
||||
|
||||
// 当可用字段发生变化时,更新已有规则的配置
|
||||
const updateRulesWithNewFields = (newFields: string[]) => {
|
||||
// 更新已有规则中的字段选择,保留已选择的字段
|
||||
const updatedRules = rules.map(rule => {
|
||||
if (rule.type === 'exists' || rule.type === 'consistency' || rule.type === 'logic' || rule.type === 'regex') {
|
||||
// 检查和更新字段相关的配置
|
||||
const currentSelectedFields = Array.isArray(rule.config.selectedFields)
|
||||
? rule.config.selectedFields as string[]
|
||||
: [];
|
||||
|
||||
// 使用已选中的字段和新字段的交集作为更新后的选中字段
|
||||
const validSelectedFields = currentSelectedFields.filter(field =>
|
||||
newFields.includes(field) || field.trim() !== ''
|
||||
);
|
||||
// 处理已删除字段的函数
|
||||
const handleDeletedFields = (deletedFields: string[]) => {
|
||||
setRules(prevRules => {
|
||||
return prevRules.map(rule => {
|
||||
const updatedConfig = { ...rule.config };
|
||||
|
||||
switch (rule.type) {
|
||||
case 'exists':
|
||||
case 'logic':
|
||||
case 'regex':
|
||||
// 从已选字段中移除被删除的字段
|
||||
if (Array.isArray(updatedConfig.selectedFields)) {
|
||||
updatedConfig.selectedFields = (updatedConfig.selectedFields as string[]).filter(
|
||||
field => !deletedFields.includes(field)
|
||||
);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'consistency':
|
||||
// 从配对字段中移除被删除的字段
|
||||
if (Array.isArray(updatedConfig.pairs)) {
|
||||
updatedConfig.pairs = (updatedConfig.pairs as ComparisonPair[]).filter(
|
||||
pair => !deletedFields.includes(pair.sourceField) && !deletedFields.includes(pair.targetField)
|
||||
);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'format':
|
||||
// 如果判断字段被删除,则清空字段
|
||||
if (updatedConfig.checkField && deletedFields.includes(updatedConfig.checkField as string)) {
|
||||
updatedConfig.checkField = '';
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// 更新可用字段列表,移除被删除的字段
|
||||
if (Array.isArray(updatedConfig.availableFields)) {
|
||||
updatedConfig.availableFields = (updatedConfig.availableFields as string[]).filter(
|
||||
field => !deletedFields.includes(field)
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
...rule,
|
||||
config: {
|
||||
...rule.config,
|
||||
selectedFields: validSelectedFields
|
||||
}
|
||||
config: updatedConfig
|
||||
};
|
||||
}
|
||||
return rule;
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
// 更新规则配置中的可用字段但保留已选择的字段和规则配置
|
||||
const updateRulesWithNewFields = (newFields: string[]) => {
|
||||
// 更新每个规则的可用字段列表,但保留现有配置
|
||||
setRules(prevRules => {
|
||||
return prevRules.map(rule => {
|
||||
const updatedConfig = { ...rule.config };
|
||||
|
||||
// 对所有规则类型都更新availableFields字段
|
||||
updatedConfig.availableFields = newFields;
|
||||
|
||||
// 根据规则类型更新其他相关字段
|
||||
if (rule.type) {
|
||||
switch (rule.type) {
|
||||
case 'field_validation':
|
||||
// 保留已有的字段选择,只添加新字段
|
||||
if (!updatedConfig.fields) {
|
||||
updatedConfig.fields = [];
|
||||
}
|
||||
break;
|
||||
|
||||
case 'field_comparison':
|
||||
// 保留已配置的比较项
|
||||
if (!updatedConfig.pairs) {
|
||||
updatedConfig.pairs = [];
|
||||
}
|
||||
break;
|
||||
|
||||
case 'field_regex':
|
||||
// 保留正则表达式配置
|
||||
break;
|
||||
|
||||
case 'custom_code':
|
||||
break;
|
||||
|
||||
default:
|
||||
// 对于所有类型规则,确保selectedFields字段存在
|
||||
if (!updatedConfig.selectedFields) {
|
||||
updatedConfig.selectedFields = [];
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
...rule,
|
||||
config: updatedConfig
|
||||
};
|
||||
});
|
||||
});
|
||||
|
||||
setRules(updatedRules);
|
||||
|
||||
if (onChange) {
|
||||
onChange({ rules: updatedRules });
|
||||
}
|
||||
};
|
||||
|
||||
const handleLogicChange = (logic: string) => {
|
||||
@@ -218,46 +280,73 @@ export function ReviewSettings({ onChange }: ReviewSettingsProps) {
|
||||
};
|
||||
|
||||
const handleRuleTypeChange = (id: string, type: string) => {
|
||||
const updatedRules = rules.map(rule =>
|
||||
rule.id === id ? { ...rule, type, config: {} } : rule
|
||||
);
|
||||
|
||||
setRules(updatedRules);
|
||||
|
||||
if (onChange) {
|
||||
onChange({ rules: updatedRules });
|
||||
}
|
||||
};
|
||||
|
||||
const handleRuleConfigChange = (id: string, config: Record<string, unknown>) => {
|
||||
const updatedRules = rules.map(rule =>
|
||||
rule.id === id ? { ...rule, config: { ...rule.config, ...config } } : rule
|
||||
);
|
||||
|
||||
setRules(updatedRules);
|
||||
|
||||
if (onChange) {
|
||||
onChange({ rules: updatedRules });
|
||||
}
|
||||
};
|
||||
|
||||
// 处理字段选择
|
||||
const handleFieldSelection = (id: string, fieldName: string, selected: boolean) => {
|
||||
// 更新规则类型
|
||||
const updatedRules = rules.map(rule => {
|
||||
if (rule.id === id) {
|
||||
// 获取当前已选字段
|
||||
const selectedFields = Array.isArray(rule.config.selectedFields)
|
||||
? rule.config.selectedFields as string[]
|
||||
: [];
|
||||
// 为新类型初始化配置
|
||||
let initialConfig: Record<string, unknown> = {};
|
||||
|
||||
// 添加或删除字段
|
||||
const newSelectedFields = selected
|
||||
? [...selectedFields, fieldName]
|
||||
: selectedFields.filter(f => f !== fieldName);
|
||||
// 根据类型设置初始配置
|
||||
switch(type) {
|
||||
case 'exists':
|
||||
case 'logic':
|
||||
case 'regex':
|
||||
initialConfig = {
|
||||
selectedFields: [],
|
||||
availableFields: availableFields // 使用当前最新的可用字段列表
|
||||
};
|
||||
break;
|
||||
case 'consistency':
|
||||
initialConfig = {
|
||||
pairs: [],
|
||||
availableFields: availableFields // 使用当前最新的可用字段列表
|
||||
};
|
||||
break;
|
||||
case 'format':
|
||||
initialConfig = {
|
||||
formatType: '',
|
||||
formatParams: '',
|
||||
availableFields: availableFields // 使用当前最新的可用字段列表
|
||||
};
|
||||
break;
|
||||
default:
|
||||
// 确保所有规则类型都包含availableFields
|
||||
initialConfig = {
|
||||
availableFields: availableFields
|
||||
};
|
||||
break;
|
||||
}
|
||||
|
||||
return {
|
||||
...rule,
|
||||
config: { ...rule.config, selectedFields: newSelectedFields }
|
||||
type,
|
||||
config: initialConfig
|
||||
};
|
||||
}
|
||||
return rule;
|
||||
});
|
||||
|
||||
setRules(updatedRules);
|
||||
|
||||
if (onChange) {
|
||||
onChange({ rules: updatedRules });
|
||||
}
|
||||
};
|
||||
|
||||
const handleRuleConfigChange = (id: string, configChanges: Record<string, unknown>) => {
|
||||
const updatedRules = rules.map(rule => {
|
||||
if (rule.id === id) {
|
||||
// 合并现有配置和变更
|
||||
const updatedConfig = { ...rule.config, ...configChanges };
|
||||
|
||||
// 确保availableFields字段存在
|
||||
if (!updatedConfig.availableFields) {
|
||||
updatedConfig.availableFields = availableFields;
|
||||
}
|
||||
|
||||
return {
|
||||
...rule,
|
||||
config: updatedConfig
|
||||
};
|
||||
}
|
||||
return rule;
|
||||
@@ -271,49 +360,50 @@ export function ReviewSettings({ onChange }: ReviewSettingsProps) {
|
||||
};
|
||||
|
||||
// 渲染字段标签,确保已选择的字段即使在新的字段列表中不存在也会显示
|
||||
const renderFieldTags = (ruleId: string, ruleConfig: Record<string, unknown>) => {
|
||||
const selectedFields = Array.isArray(ruleConfig.selectedFields)
|
||||
? ruleConfig.selectedFields as string[]
|
||||
: [];
|
||||
const renderFieldTags = (ruleId: string, config: Record<string, unknown>) => {
|
||||
// 获取规则的当前已选字段
|
||||
const selectedFields = Array.isArray(config.selectedFields) ? config.selectedFields as string[] : [];
|
||||
|
||||
// 合并已选择的字段和可用字段,以确保已选择但不再可用的字段仍然显示
|
||||
const allFieldsToRender = [...new Set([...availableFields, ...selectedFields])];
|
||||
// 优先使用配置中存储的可用字段,如果没有则使用当前可用字段
|
||||
const fieldsToRender = Array.isArray(config.availableFields) ?
|
||||
config.availableFields as string[] :
|
||||
availableFields;
|
||||
|
||||
return allFieldsToRender.map((field, index) => {
|
||||
const isSelected = selectedFields.includes(field);
|
||||
const isAvailable = availableFields.includes(field);
|
||||
const buttonId = `field-tag-${ruleId}-${index}`;
|
||||
|
||||
// 如果字段不再可用但已被选中,用特殊样式显示
|
||||
const fieldClass = !isAvailable && isSelected ? 'field-tag selected unavailable' :
|
||||
isSelected ? 'field-tag selected' : 'field-tag';
|
||||
|
||||
// 只显示可用的字段或已选择的字段
|
||||
if (!isAvailable && !isSelected) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
key={buttonId}
|
||||
id={buttonId}
|
||||
className={fieldClass}
|
||||
data-field={field}
|
||||
onClick={() => handleFieldSelection(ruleId, field, !isSelected)}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === 'Enter' || e.key === ' ') {
|
||||
e.preventDefault();
|
||||
handleFieldSelection(ruleId, field, !isSelected);
|
||||
}
|
||||
}}
|
||||
aria-selected={isSelected}
|
||||
role="option"
|
||||
>
|
||||
{field}
|
||||
</button>
|
||||
);
|
||||
}).filter(Boolean); // 过滤掉null值
|
||||
return (
|
||||
<div className="field-tags">
|
||||
{fieldsToRender.map((field, index) => (
|
||||
<div
|
||||
key={`field-${ruleId}-${index}`}
|
||||
className={`field-tag ${selectedFields.includes(field) ? 'selected' : ''}`}
|
||||
onClick={() => {
|
||||
// 切换选中状态
|
||||
const newSelectedFields = selectedFields.includes(field)
|
||||
? selectedFields.filter(f => f !== field)
|
||||
: [...selectedFields, field];
|
||||
|
||||
handleRuleConfigChange(ruleId, {
|
||||
selectedFields: newSelectedFields
|
||||
});
|
||||
}}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === 'Enter' || e.key === ' ') {
|
||||
const newSelectedFields = selectedFields.includes(field)
|
||||
? selectedFields.filter(f => f !== field)
|
||||
: [...selectedFields, field];
|
||||
|
||||
handleRuleConfigChange(ruleId, {
|
||||
selectedFields: newSelectedFields
|
||||
});
|
||||
}
|
||||
}}
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
>
|
||||
{field}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
// 获取规则类型的Badge样式
|
||||
@@ -334,6 +424,17 @@ export function ReviewSettings({ onChange }: ReviewSettingsProps) {
|
||||
const renderRuleConfig = (rule: RuleType) => {
|
||||
const { id, type, config } = rule;
|
||||
|
||||
// 如果规则中的availableFields不是最新的,则更新它
|
||||
if (type && config && (!config.availableFields ||
|
||||
(Array.isArray(config.availableFields) &&
|
||||
!availableFields.every((field) => (config.availableFields as string[]).includes(field))))) {
|
||||
// 延迟更新以避免在渲染过程中修改状态
|
||||
setTimeout(() => {
|
||||
const updatedConfig = { ...config, availableFields: availableFields };
|
||||
handleRuleConfigChange(id, updatedConfig);
|
||||
}, 0);
|
||||
}
|
||||
|
||||
if (!type) {
|
||||
return (
|
||||
<div className="rule-placeholder text-center py-6 text-secondary">
|
||||
@@ -1167,9 +1268,7 @@ export function ReviewSettings({ onChange }: ReviewSettingsProps) {
|
||||
|
||||
{/* 评查结果提示信息 */}
|
||||
<div className="mb-4">
|
||||
<label className="form-label">
|
||||
评查结果提示信息
|
||||
</label>
|
||||
<h4 className="form-label">评查结果提示信息</h4>
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
|
||||
<div>
|
||||
<label className="form-label text-sm" htmlFor="pass-message">评查通过信息</label>
|
||||
@@ -1209,57 +1308,114 @@ export function ReviewSettings({ onChange }: ReviewSettingsProps) {
|
||||
|
||||
{/* 不通过提示类别 */}
|
||||
<div className="mb-4">
|
||||
<label className="form-label" htmlFor="error-severity">建议信息类别</label>
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-4" id="error-severity">
|
||||
<label className={`flex items-center cursor-pointer border-l-4 border-blue-500 bg-blue-50 p-3 hover:bg-blue-100 rounded-r-md severity-option ${errorSeverity === 'info' ? 'selected-severity' : ''}`}>
|
||||
<h4 className="form-label">建议信息类别</h4>
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-4" role="radiogroup" aria-label="建议信息类别">
|
||||
<div
|
||||
className={`flex items-center cursor-pointer border-l-4 border-blue-500 ${errorSeverity === 'info'
|
||||
? 'bg-blue-100 shadow-md ring-2 ring-blue-200'
|
||||
: 'bg-blue-50 hover:bg-blue-100'} p-3 rounded-r-md transition-all duration-200`}
|
||||
onClick={() => handleSeverityChange('info')}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === 'Enter' || e.key === ' ') {
|
||||
handleSeverityChange('info');
|
||||
}
|
||||
}}
|
||||
role="radio"
|
||||
aria-checked={errorSeverity === 'info'}
|
||||
tabIndex={0}
|
||||
>
|
||||
<input
|
||||
type="radio"
|
||||
name="errorSeverity"
|
||||
className="severity-radio"
|
||||
name="errorSeverity"
|
||||
id="severity-info"
|
||||
className="severity-radio hidden"
|
||||
value="info"
|
||||
checked={errorSeverity === 'info'}
|
||||
onChange={() => handleSeverityChange('info')}
|
||||
/>
|
||||
<i className="ri-information-line text-blue-500 text-xl mr-3"></i>
|
||||
<i className={`ri-information-line text-blue-500 text-xl mr-3 ${errorSeverity === 'info' ? 'animate-pulse' : ''}`}></i>
|
||||
<div>
|
||||
<div className="font-medium">提示 (Info)</div>
|
||||
<div className="text-sm text-gray-500">提示性信息,不影响</div>
|
||||
</div>
|
||||
</label>
|
||||
{errorSeverity === 'info' && (
|
||||
<div className="ml-auto">
|
||||
<i className="ri-check-line text-blue-500 text-lg"></i>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<label className={`flex items-center cursor-pointer border-l-4 border-yellow-500 bg-yellow-50 p-3 hover:bg-yellow-100 rounded-r-md severity-option ${errorSeverity === 'warning' ? 'selected-severity' : ''}`}>
|
||||
<div
|
||||
className={`flex items-center cursor-pointer border-l-4 border-yellow-500 ${errorSeverity === 'warning'
|
||||
? 'bg-yellow-100 shadow-md ring-2 ring-yellow-200'
|
||||
: 'bg-yellow-50 hover:bg-yellow-100'} p-3 rounded-r-md transition-all duration-200`}
|
||||
onClick={() => handleSeverityChange('warning')}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === 'Enter' || e.key === ' ') {
|
||||
handleSeverityChange('warning');
|
||||
}
|
||||
}}
|
||||
role="radio"
|
||||
aria-checked={errorSeverity === 'warning'}
|
||||
tabIndex={0}
|
||||
>
|
||||
<input
|
||||
type="radio"
|
||||
name="errorSeverity"
|
||||
className="severity-radio"
|
||||
name="errorSeverity"
|
||||
id="severity-warning"
|
||||
className="severity-radio hidden"
|
||||
value="warning"
|
||||
checked={errorSeverity === 'warning'}
|
||||
onChange={() => handleSeverityChange('warning')}
|
||||
/>
|
||||
<i className="ri-alert-line text-yellow-500 text-xl mr-3"></i>
|
||||
<i className={`ri-alert-line text-yellow-500 text-xl mr-3 ${errorSeverity === 'warning' ? 'animate-pulse' : ''}`}></i>
|
||||
<div>
|
||||
<div className="font-medium">警告 (Warning)</div>
|
||||
<div className="text-sm text-gray-500">警告信息,建议修改但不强制</div>
|
||||
</div>
|
||||
</label>
|
||||
{errorSeverity === 'warning' && (
|
||||
<div className="ml-auto">
|
||||
<i className="ri-check-line text-yellow-500 text-lg"></i>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<label className={`flex items-center cursor-pointer border-l-4 border-red-500 bg-red-50 p-3 hover:bg-red-100 rounded-r-md severity-option ${errorSeverity === 'error' ? 'selected-severity' : ''}`}>
|
||||
<div
|
||||
className={`flex items-center cursor-pointer border-l-4 border-red-500 ${errorSeverity === 'error'
|
||||
? 'bg-red-100 shadow-md ring-2 ring-red-200'
|
||||
: 'bg-red-50 hover:bg-red-100'} p-3 rounded-r-md transition-all duration-200`}
|
||||
onClick={() => handleSeverityChange('error')}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === 'Enter' || e.key === ' ') {
|
||||
handleSeverityChange('error');
|
||||
}
|
||||
}}
|
||||
role="radio"
|
||||
aria-checked={errorSeverity === 'error'}
|
||||
tabIndex={0}
|
||||
>
|
||||
<input
|
||||
type="radio"
|
||||
name="errorSeverity"
|
||||
className="severity-radio"
|
||||
name="errorSeverity"
|
||||
id="severity-error"
|
||||
className="severity-radio hidden"
|
||||
value="error"
|
||||
checked={errorSeverity === 'error'}
|
||||
onChange={() => handleSeverityChange('error')}
|
||||
/>
|
||||
<i className="ri-error-warning-line text-red-500 text-xl mr-3"></i>
|
||||
<i className={`ri-error-warning-line text-red-500 text-xl mr-3 ${errorSeverity === 'error' ? 'animate-pulse' : ''}`}></i>
|
||||
<div>
|
||||
<div className="font-medium">错误 (Error)</div>
|
||||
<div className="text-sm text-gray-500">严重错误,必须修正</div>
|
||||
</div>
|
||||
</label>
|
||||
{errorSeverity === 'error' && (
|
||||
<div className="ml-auto">
|
||||
<i className="ri-check-line text-red-500 text-lg"></i>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="form-tip">不同类别会影响问题的展示方式和处理流程</div>
|
||||
<div className="form-tip mt-2">不同类别会影响问题的展示方式和处理流程</div>
|
||||
</div>
|
||||
|
||||
{/* 评查后动作 */}
|
||||
|
||||
+18
-7
@@ -44,14 +44,25 @@ export default function RuleNew() {
|
||||
};
|
||||
|
||||
const handleExtractionChange = (data: Record<string, unknown>) => {
|
||||
// 当抽取设置更新时,获取最新的字段列表
|
||||
// 使用合并后的所有字段列表
|
||||
if (data.allFields && Array.isArray(data.allFields)) {
|
||||
updateExtractionFields(data.allFields);
|
||||
return;
|
||||
}
|
||||
|
||||
// 旧版本兼容逻辑
|
||||
if (data.fields) {
|
||||
const fieldData = data.fields as Record<string, string[]>;
|
||||
const currentMethod = data.extractionMethod as string;
|
||||
|
||||
// 提取当前抽取方法的字段
|
||||
if (fieldData[currentMethod]) {
|
||||
updateExtractionFields(fieldData[currentMethod]);
|
||||
// 尝试获取合并的字段列表
|
||||
if (Array.isArray(data.fields)) {
|
||||
updateExtractionFields(data.fields);
|
||||
} else {
|
||||
const fieldData = data.fields as Record<string, string[]>;
|
||||
const currentMethod = data.extractionMethod as string;
|
||||
|
||||
// 提取当前抽取方法的字段
|
||||
if (fieldData[currentMethod]) {
|
||||
updateExtractionFields(fieldData[currentMethod]);
|
||||
}
|
||||
}
|
||||
} else if (data.regexFields) {
|
||||
// 处理正则字段情况
|
||||
|
||||
Reference in New Issue
Block a user