修复好前段逻辑,
This commit is contained in:
@@ -3,3 +3,6 @@ node_modules
|
|||||||
/.cache
|
/.cache
|
||||||
/build
|
/build
|
||||||
.env
|
.env
|
||||||
|
|
||||||
|
.idea
|
||||||
|
|
||||||
|
|||||||
@@ -50,23 +50,6 @@ export function ExtractionSettings({ onChange }: ExtractionSettingsProps) {
|
|||||||
llm: ''
|
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数据初始化
|
// 在组件初始化时,如果Context中已有字段数据,则使用Context数据初始化
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (ruleContext && ruleContext.extractionFields.length > 0) {
|
if (ruleContext && ruleContext.extractionFields.length > 0) {
|
||||||
@@ -78,6 +61,79 @@ export function ExtractionSettings({ onChange }: ExtractionSettingsProps) {
|
|||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
// 当组件首次加载时更新字段
|
||||||
|
useEffect(() => {
|
||||||
|
updateAllFields();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
// 获取所有可用字段(合并大模型、多模态和正则抽取的字段)
|
||||||
|
const getAllFields = (): string[] => {
|
||||||
|
// 从大模型OCR抽取中获取字段
|
||||||
|
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() !== '');
|
||||||
|
|
||||||
|
// 合并并去重
|
||||||
|
const allFields = [...new Set([...llm_ocr_fields, ...llm_fields, ...regex_fields])];
|
||||||
|
console.log("所有可用字段:", allFields);
|
||||||
|
return allFields;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 检查字段名是否存在(精确匹配)
|
||||||
|
const isFieldNameExists = (fieldName: string, excludeId?: string): boolean => {
|
||||||
|
// 获取所有字段名称(不转换为小写)
|
||||||
|
const existingFields = getAllFields();
|
||||||
|
|
||||||
|
// 检查精确匹配(区分大小写)
|
||||||
|
for (const existingField of existingFields) {
|
||||||
|
// 严格相等比较,确保完全匹配而不是部分匹配
|
||||||
|
if (existingField === fieldName) {
|
||||||
|
console.log(`字段名 '${fieldName}' 在现有字段中存在(严格匹配)`);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查正则字段组中的其他字段(精确匹配)
|
||||||
|
// 排除当前正在编辑的字段ID
|
||||||
|
const otherRegexFields = regexFields
|
||||||
|
.filter(f => !excludeId || f.id !== excludeId)
|
||||||
|
.map(f => f.fieldName);
|
||||||
|
|
||||||
|
for (const regexField of otherRegexFields) {
|
||||||
|
// 严格相等比较
|
||||||
|
if (regexField === fieldName) {
|
||||||
|
console.log(`字段名 '${fieldName}' 在正则字段中存在(严格匹配)`);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 不区分大小写的检查(保留这部分功能,但仍然是精确匹配)
|
||||||
|
const fieldNameLower = fieldName.toLowerCase();
|
||||||
|
const existingFieldsLower = existingFields.map(f => f.toLowerCase());
|
||||||
|
const otherRegexFieldsLower = otherRegexFields.map(f => f.toLowerCase());
|
||||||
|
|
||||||
|
if (existingFieldsLower.includes(fieldNameLower)) {
|
||||||
|
console.log(`字段名 '${fieldName}' 在现有字段中存在(不区分大小写)`);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (otherRegexFieldsLower.includes(fieldNameLower)) {
|
||||||
|
console.log(`字段名 '${fieldName}' 在正则字段中存在(不区分大小写)`);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
// 更新所有抽取字段到Context
|
// 更新所有抽取字段到Context
|
||||||
const updateAllFields = () => {
|
const updateAllFields = () => {
|
||||||
const allFields = getAllFields();
|
const allFields = getAllFields();
|
||||||
@@ -111,10 +167,13 @@ export function ExtractionSettings({ onChange }: ExtractionSettingsProps) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 在所有字段集合变化时自动更新
|
// 使用useEffect监听字段变化并更新Context
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
// 立即更新字段列表
|
||||||
updateAllFields();
|
updateAllFields();
|
||||||
}, [fields, regexFields]);
|
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [fields.llm_ocr, fields.llm, regexFields]);
|
||||||
|
|
||||||
const handleTabChange = (tab: string) => {
|
const handleTabChange = (tab: string) => {
|
||||||
setCurrentTab(tab);
|
setCurrentTab(tab);
|
||||||
@@ -141,19 +200,40 @@ export function ExtractionSettings({ onChange }: ExtractionSettingsProps) {
|
|||||||
|
|
||||||
// OCR+LLM模式下,支持多个字段同时添加(用逗号、顿号或空格分隔)
|
// OCR+LLM模式下,支持多个字段同时添加(用逗号、顿号或空格分隔)
|
||||||
if (type === 'llm_ocr') {
|
if (type === 'llm_ocr') {
|
||||||
newFields = [
|
const fieldsToAdd = inputValue[type].split(/[\s、,]+/)
|
||||||
...fields[type],
|
.map(f => f.trim())
|
||||||
...inputValue[type].split(/[\s、,]+/).map(f => f.trim()).filter(f => f !== '')
|
.filter(f => f !== '');
|
||||||
];
|
|
||||||
} else {
|
console.log(`添加OCR字段:`, fieldsToAdd);
|
||||||
// 多模态抽取模式下,一次只添加一个字段(带类型)
|
|
||||||
newFields = [...fields[type], `${inputValue[type].trim()}_${selectedFieldType}`];
|
// 仅添加不存在的字段
|
||||||
|
const uniqueFields = fieldsToAdd.filter(field => !isFieldNameExists(field));
|
||||||
|
|
||||||
|
if (uniqueFields.length === 0) {
|
||||||
|
// 如果没有唯一字段可添加,显示提示并返回
|
||||||
|
alert("所有字段名已存在,请确保字段名称唯一");
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
setFields({
|
newFields = [...fields[type], ...uniqueFields];
|
||||||
...fields,
|
} else {
|
||||||
|
// 多模态抽取模式下,处理字段名称唯一性
|
||||||
|
const fieldName = inputValue[type].trim();
|
||||||
|
console.log(`添加多模态字段:${fieldName}`);
|
||||||
|
|
||||||
|
// 检查字段名是否已存在
|
||||||
|
if (isFieldNameExists(fieldName)) {
|
||||||
|
alert(`字段名 "${fieldName}" 已存在,请确保字段名称唯一`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
newFields = [...fields[type], `${fieldName}_${selectedFieldType}`];
|
||||||
|
}
|
||||||
|
|
||||||
|
setFields(prevFields => ({
|
||||||
|
...prevFields,
|
||||||
[type]: newFields
|
[type]: newFields
|
||||||
});
|
}));
|
||||||
|
|
||||||
setInputValue({
|
setInputValue({
|
||||||
...inputValue,
|
...inputValue,
|
||||||
@@ -163,9 +243,6 @@ export function ExtractionSettings({ onChange }: ExtractionSettingsProps) {
|
|||||||
if (type === 'llm') {
|
if (type === 'llm') {
|
||||||
setSelectedFieldType('default');
|
setSelectedFieldType('default');
|
||||||
}
|
}
|
||||||
|
|
||||||
// 立即触发字段更新事件,通知评查设置组件
|
|
||||||
setTimeout(() => updateAllFields(), 0);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -180,13 +257,18 @@ export function ExtractionSettings({ onChange }: ExtractionSettingsProps) {
|
|||||||
const newFields = [...fields[type]];
|
const newFields = [...fields[type]];
|
||||||
newFields.splice(index, 1);
|
newFields.splice(index, 1);
|
||||||
|
|
||||||
setFields({
|
// 使用新的方式更新,确保状态立即更新并触发后续操作
|
||||||
...fields,
|
setFields(prevFields => {
|
||||||
|
const updatedFields = {
|
||||||
|
...prevFields,
|
||||||
[type]: newFields
|
[type]: newFields
|
||||||
});
|
};
|
||||||
|
|
||||||
// 立即触发字段更新事件,通知评查设置组件
|
// 状态更新后立即触发字段更新事件
|
||||||
setTimeout(() => updateAllFields(), 0);
|
Promise.resolve().then(() => updateAllFields());
|
||||||
|
|
||||||
|
return updatedFields;
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// 添加正则表达式字段行
|
// 添加正则表达式字段行
|
||||||
@@ -223,19 +305,20 @@ export function ExtractionSettings({ onChange }: ExtractionSettingsProps) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 更新正则表达式字段值
|
// 更新正则表达式字段
|
||||||
const updateRegexField = (id: string, key: 'fieldName' | 'regex', value: string) => {
|
const updateRegexField = (id: string, key: 'fieldName' | 'regex', value: string) => {
|
||||||
const newRegexFields = regexFields.map(field =>
|
// 更新字段值
|
||||||
field.id === id ? { ...field, [key]: value } : field
|
const newRegexFields = regexFields.map(field => {
|
||||||
);
|
if (field.id === id) {
|
||||||
|
return { ...field, [key]: value };
|
||||||
|
}
|
||||||
|
return field;
|
||||||
|
});
|
||||||
|
|
||||||
|
// 仅更新状态,不触发其他事件
|
||||||
setRegexFields(newRegexFields);
|
setRegexFields(newRegexFields);
|
||||||
|
|
||||||
// 如果更新的是字段名,则触发字段更新事件
|
// 更新onChange回调
|
||||||
if (key === 'fieldName') {
|
|
||||||
setTimeout(() => updateAllFields(), 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (onChange) {
|
if (onChange) {
|
||||||
onChange({
|
onChange({
|
||||||
extractionMethod: currentTab,
|
extractionMethod: currentTab,
|
||||||
@@ -244,6 +327,74 @@ export function ExtractionSettings({ onChange }: ExtractionSettingsProps) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 处理正则字段失去焦点事件,检查唯一性并更新字段列表
|
||||||
|
const handleRegexFieldBlur = (id: string, key: 'fieldName' | 'regex') => {
|
||||||
|
// 只有在修改字段名时需要检查唯一性并更新字段列表
|
||||||
|
if (key === 'fieldName') {
|
||||||
|
const currentField = regexFields.find(field => field.id === id);
|
||||||
|
if (currentField && currentField.fieldName.trim() !== '') {
|
||||||
|
const fieldName = currentField.fieldName.trim();
|
||||||
|
console.log(`检查正则字段 '${fieldName}' 的唯一性,ID: ${id}`);
|
||||||
|
|
||||||
|
// 检查当前正则字段组中是否有重名(排除自身)
|
||||||
|
const duplicateInRegex = regexFields
|
||||||
|
.filter(f => f.id !== id)
|
||||||
|
.find(f => f.fieldName === fieldName);
|
||||||
|
|
||||||
|
if (duplicateInRegex) {
|
||||||
|
console.log(`字段名 '${fieldName}' 在正则字段中存在重复,ID: ${duplicateInRegex.id}`);
|
||||||
|
alert(`字段名 "${fieldName}" 已存在,请确保字段名称唯一`);
|
||||||
|
|
||||||
|
// 重置为空字段名
|
||||||
|
const resetFields = regexFields.map(field => {
|
||||||
|
if (field.id === id) {
|
||||||
|
return { ...field, fieldName: '' };
|
||||||
|
}
|
||||||
|
return field;
|
||||||
|
});
|
||||||
|
|
||||||
|
setRegexFields(resetFields);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查其他抽取方法中的字段(不区分大小写)
|
||||||
|
const otherExtractFields = [
|
||||||
|
...fields.llm_ocr.map(f => f.toLowerCase()),
|
||||||
|
...fields.llm.map(f => {
|
||||||
|
const [name] = f.split('_');
|
||||||
|
return name.toLowerCase();
|
||||||
|
})
|
||||||
|
];
|
||||||
|
|
||||||
|
const fieldNameLower = fieldName.toLowerCase();
|
||||||
|
const duplicateInOtherMethods = otherExtractFields.includes(fieldNameLower);
|
||||||
|
|
||||||
|
if (duplicateInOtherMethods) {
|
||||||
|
console.log(`字段名 '${fieldName}' 在其他抽取方法中存在(不区分大小写)`);
|
||||||
|
alert(`字段名 "${fieldName}" 已存在,请确保字段名称唯一`);
|
||||||
|
|
||||||
|
// 重置为空字段名
|
||||||
|
const resetFields = regexFields.map(field => {
|
||||||
|
if (field.id === id) {
|
||||||
|
return { ...field, fieldName: '' };
|
||||||
|
}
|
||||||
|
return field;
|
||||||
|
});
|
||||||
|
|
||||||
|
setRegexFields(resetFields);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 字段名有效,更新字段列表
|
||||||
|
console.log(`字段名 '${fieldName}' 检查通过,更新字段列表`);
|
||||||
|
updateAllFields();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 对于regex字段,只需更新字段列表
|
||||||
|
updateAllFields();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// 应用正则模板
|
// 应用正则模板
|
||||||
const applyRegexTemplate = (regex: string) => {
|
const applyRegexTemplate = (regex: string) => {
|
||||||
// 找到当前正在编辑的行,或者最后一行
|
// 找到当前正在编辑的行,或者最后一行
|
||||||
@@ -909,6 +1060,7 @@ export function ExtractionSettings({ onChange }: ExtractionSettingsProps) {
|
|||||||
placeholder="如:合同编号"
|
placeholder="如:合同编号"
|
||||||
value={field.fieldName}
|
value={field.fieldName}
|
||||||
onChange={(e) => updateRegexField(field.id, 'fieldName', e.target.value)}
|
onChange={(e) => updateRegexField(field.id, 'fieldName', e.target.value)}
|
||||||
|
onBlur={() => handleRegexFieldBlur(field.id, 'fieldName')}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="w-7/10 mr-2">
|
<div className="w-7/10 mr-2">
|
||||||
@@ -920,6 +1072,7 @@ export function ExtractionSettings({ onChange }: ExtractionSettingsProps) {
|
|||||||
placeholder="如:\\d{4}[-/年](0?[1-9]|1[0-2])[-/月](0?[1-9]|[12][0-9]|3[01])[日]?"
|
placeholder="如:\\d{4}[-/年](0?[1-9]|1[0-2])[-/月](0?[1-9]|[12][0-9]|3[01])[日]?"
|
||||||
value={field.regex}
|
value={field.regex}
|
||||||
onChange={(e) => updateRegexField(field.id, 'regex', e.target.value)}
|
onChange={(e) => updateRegexField(field.id, 'regex', e.target.value)}
|
||||||
|
onBlur={() => handleRegexFieldBlur(field.id, 'regex')}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col justify-end pt-3">
|
<div className="flex flex-col justify-end pt-3">
|
||||||
|
|||||||
@@ -14,6 +14,13 @@ interface ComparisonPair {
|
|||||||
compareMethod: string;
|
compareMethod: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 添加逻辑条件接口
|
||||||
|
interface Condition {
|
||||||
|
field: string;
|
||||||
|
operator: string;
|
||||||
|
value: string;
|
||||||
|
}
|
||||||
|
|
||||||
interface ReviewSettingsProps {
|
interface ReviewSettingsProps {
|
||||||
onChange?: (data: Record<string, unknown>) => void;
|
onChange?: (data: Record<string, unknown>) => void;
|
||||||
}
|
}
|
||||||
@@ -59,12 +66,16 @@ export function ReviewSettings({ onChange }: ReviewSettingsProps) {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// 当Context中的字段发生变化时,更新可用字段但保留已有配置
|
// 当Context中的字段发生变化时,更新可用字段但保留已有配置
|
||||||
if (extractionFields.length > 0) {
|
if (extractionFields.length > 0) {
|
||||||
|
console.log('extractionFields updated in ReviewSettings:', extractionFields);
|
||||||
// 检查是否有字段被删除
|
// 检查是否有字段被删除
|
||||||
const deletedFields = availableFields.filter(field => !extractionFields.includes(field));
|
const deletedFields = availableFields.filter(field => !extractionFields.includes(field));
|
||||||
|
|
||||||
// 处理新增的字段
|
// 处理新增的字段
|
||||||
const newFields = extractionFields.filter((field: string) => !availableFields.includes(field));
|
const newFields = extractionFields.filter((field: string) => !availableFields.includes(field));
|
||||||
|
|
||||||
|
console.log('New fields:', newFields);
|
||||||
|
console.log('Deleted fields:', deletedFields);
|
||||||
|
|
||||||
if (newFields.length > 0 || deletedFields.length > 0) {
|
if (newFields.length > 0 || deletedFields.length > 0) {
|
||||||
// 设置最新的可用字段列表
|
// 设置最新的可用字段列表
|
||||||
setAvailableFields(extractionFields);
|
setAvailableFields(extractionFields);
|
||||||
@@ -83,6 +94,7 @@ export function ReviewSettings({ onChange }: ReviewSettingsProps) {
|
|||||||
const handleExtractionChange = (event: Event) => {
|
const handleExtractionChange = (event: Event) => {
|
||||||
if (event instanceof CustomEvent && event.detail && Array.isArray(event.detail.fields)) {
|
if (event instanceof CustomEvent && event.detail && Array.isArray(event.detail.fields)) {
|
||||||
const incomingFields = event.detail.fields;
|
const incomingFields = event.detail.fields;
|
||||||
|
console.log('Received extraction fields update:', incomingFields);
|
||||||
|
|
||||||
// 检查是否有字段被删除
|
// 检查是否有字段被删除
|
||||||
const deletedFields = availableFields.filter(field => !incomingFields.includes(field));
|
const deletedFields = availableFields.filter(field => !incomingFields.includes(field));
|
||||||
@@ -90,6 +102,9 @@ export function ReviewSettings({ onChange }: ReviewSettingsProps) {
|
|||||||
// 识别新增的字段
|
// 识别新增的字段
|
||||||
const newFields = incomingFields.filter((field: string) => !availableFields.includes(field));
|
const newFields = incomingFields.filter((field: string) => !availableFields.includes(field));
|
||||||
|
|
||||||
|
console.log('Deleted fields:', deletedFields);
|
||||||
|
console.log('New fields:', newFields);
|
||||||
|
|
||||||
if (newFields.length > 0 || deletedFields.length > 0) {
|
if (newFields.length > 0 || deletedFields.length > 0) {
|
||||||
// 设置最新的可用字段列表
|
// 设置最新的可用字段列表
|
||||||
setAvailableFields(incomingFields);
|
setAvailableFields(incomingFields);
|
||||||
@@ -112,7 +127,36 @@ export function ReviewSettings({ onChange }: ReviewSettingsProps) {
|
|||||||
return () => {
|
return () => {
|
||||||
document.removeEventListener('extraction-fields-updated', handleExtractionChange);
|
document.removeEventListener('extraction-fields-updated', handleExtractionChange);
|
||||||
};
|
};
|
||||||
}, [extractionFields, availableFields]);
|
}, [extractionFields]);
|
||||||
|
|
||||||
|
// 检查并更新字段(仍然保留此函数供需要时手动触发)
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
const checkAndUpdateFields = () => {
|
||||||
|
if (extractionFields.length > 0) {
|
||||||
|
// 检查是否有字段被删除
|
||||||
|
const deletedFields = availableFields.filter(field => !extractionFields.includes(field));
|
||||||
|
|
||||||
|
// 处理新增的字段
|
||||||
|
const newFields = extractionFields.filter((field: string) => !availableFields.includes(field));
|
||||||
|
|
||||||
|
if (newFields.length > 0 || deletedFields.length > 0) {
|
||||||
|
console.log('Updating fields in checkAndUpdateFields - deleted:', deletedFields, 'new:', newFields);
|
||||||
|
// 设置最新的可用字段列表
|
||||||
|
setAvailableFields(extractionFields);
|
||||||
|
|
||||||
|
// 处理规则中已删除的字段
|
||||||
|
if (deletedFields.length > 0) {
|
||||||
|
handleDeletedFields(deletedFields);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 使用最新的字段列表更新规则配置
|
||||||
|
updateRulesWithNewFields(extractionFields);
|
||||||
|
|
||||||
|
return true; // 表示字段已更新
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false; // 表示字段未更新
|
||||||
|
};
|
||||||
|
|
||||||
// 初始化评查配置
|
// 初始化评查配置
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -453,9 +497,11 @@ export function ReviewSettings({ onChange }: ReviewSettingsProps) {
|
|||||||
// 如果规则中的availableFields不是最新的,则更新它
|
// 如果规则中的availableFields不是最新的,则更新它
|
||||||
if (type && config && (!config.availableFields ||
|
if (type && config && (!config.availableFields ||
|
||||||
(Array.isArray(config.availableFields) &&
|
(Array.isArray(config.availableFields) &&
|
||||||
!availableFields.every((field) => (config.availableFields as string[]).includes(field))))) {
|
!availableFields.every((field) => (config.availableFields as string[]).includes(field)) ||
|
||||||
|
!(config.availableFields as string[]).every((field) => availableFields.includes(field))))) {
|
||||||
// 延迟更新以避免在渲染过程中修改状态
|
// 延迟更新以避免在渲染过程中修改状态
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
|
console.log('Updating rule config with new available fields:', availableFields);
|
||||||
const updatedConfig = { ...config, availableFields: availableFields };
|
const updatedConfig = { ...config, availableFields: availableFields };
|
||||||
handleRuleConfigChange(id, updatedConfig);
|
handleRuleConfigChange(id, updatedConfig);
|
||||||
}, 0);
|
}, 0);
|
||||||
@@ -601,6 +647,7 @@ export function ReviewSettings({ onChange }: ReviewSettingsProps) {
|
|||||||
id={`source-field-${id}-0`}
|
id={`source-field-${id}-0`}
|
||||||
className="form-select"
|
className="form-select"
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
|
// 直接初始化一个完整的比较对数组
|
||||||
const firstPair = { sourceField: e.target.value, targetField: '', compareMethod: '' };
|
const firstPair = { sourceField: e.target.value, targetField: '', compareMethod: '' };
|
||||||
handleRuleConfigChange(id, { pairs: [firstPair] });
|
handleRuleConfigChange(id, { pairs: [firstPair] });
|
||||||
}}
|
}}
|
||||||
@@ -617,9 +664,12 @@ export function ReviewSettings({ onChange }: ReviewSettingsProps) {
|
|||||||
id={`target-field-${id}-0`}
|
id={`target-field-${id}-0`}
|
||||||
className="form-select"
|
className="form-select"
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
const pairs = Array.isArray(config.pairs) ? [...config.pairs] : [{ sourceField: '', compareMethod: '' }];
|
// 获取sourceField的值
|
||||||
pairs[0] = { ...pairs[0], targetField: e.target.value };
|
const sourceField = document.getElementById(`source-field-${id}-0`) ?
|
||||||
handleRuleConfigChange(id, { pairs });
|
(document.getElementById(`source-field-${id}-0`) as HTMLSelectElement).value : '';
|
||||||
|
|
||||||
|
const firstPair = { sourceField, targetField: e.target.value, compareMethod: '' };
|
||||||
|
handleRuleConfigChange(id, { pairs: [firstPair] });
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<option value="">请选择目标字段</option>
|
<option value="">请选择目标字段</option>
|
||||||
@@ -634,9 +684,14 @@ export function ReviewSettings({ onChange }: ReviewSettingsProps) {
|
|||||||
id={`compare-method-${id}-0`}
|
id={`compare-method-${id}-0`}
|
||||||
className="form-select"
|
className="form-select"
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
const pairs = Array.isArray(config.pairs) ? [...config.pairs] : [{ sourceField: '', targetField: '' }];
|
// 获取sourceField和targetField的值
|
||||||
pairs[0] = { ...pairs[0], compareMethod: e.target.value };
|
const sourceField = document.getElementById(`source-field-${id}-0`) ?
|
||||||
handleRuleConfigChange(id, { pairs });
|
(document.getElementById(`source-field-${id}-0`) as HTMLSelectElement).value : '';
|
||||||
|
const targetField = document.getElementById(`target-field-${id}-0`) ?
|
||||||
|
(document.getElementById(`target-field-${id}-0`) as HTMLSelectElement).value : '';
|
||||||
|
|
||||||
|
const firstPair = { sourceField, targetField, compareMethod: e.target.value };
|
||||||
|
handleRuleConfigChange(id, { pairs: [firstPair] });
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<option value="">请选择比较方式</option>
|
<option value="">请选择比较方式</option>
|
||||||
@@ -655,9 +710,31 @@ export function ReviewSettings({ onChange }: ReviewSettingsProps) {
|
|||||||
className="ant-btn ant-btn-default"
|
className="ant-btn ant-btn-default"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
// 添加新的比较对
|
// 添加新的比较对
|
||||||
const currentPairs = Array.isArray(config.pairs) ? config.pairs : [];
|
// 直接获取当前的pairs数组,或初始化为空数组
|
||||||
|
const pairs = Array.isArray(config.pairs) ? [...(config.pairs as ComparisonPair[])] : [];
|
||||||
|
|
||||||
|
// 创建新的空白比较对
|
||||||
const newPair = { sourceField: '', targetField: '', compareMethod: '' };
|
const newPair = { sourceField: '', targetField: '', compareMethod: '' };
|
||||||
handleRuleConfigChange(id, { pairs: [...currentPairs, newPair] });
|
|
||||||
|
// 如果数组为空,确保先初始化第一个条目
|
||||||
|
if (pairs.length === 0) {
|
||||||
|
// 如果界面上已有值,则添加两行:一行是当前值,一行是新的空行
|
||||||
|
const sourceField = document.getElementById(`source-field-${id}-0`) ?
|
||||||
|
(document.getElementById(`source-field-${id}-0`) as HTMLSelectElement).value : '';
|
||||||
|
const targetField = document.getElementById(`target-field-${id}-0`) ?
|
||||||
|
(document.getElementById(`target-field-${id}-0`) as HTMLSelectElement).value : '';
|
||||||
|
const compareMethod = document.getElementById(`compare-method-${id}-0`) ?
|
||||||
|
(document.getElementById(`compare-method-${id}-0`) as HTMLSelectElement).value : '';
|
||||||
|
|
||||||
|
// 将第一行设置为当前值(如果有)
|
||||||
|
pairs.push({ sourceField, targetField, compareMethod });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 无论如何,都添加一个新的空白行
|
||||||
|
pairs.push(newPair);
|
||||||
|
|
||||||
|
// 更新配置
|
||||||
|
handleRuleConfigChange(id, { pairs });
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<i className="ri-add-line mr-1"></i> 添加比较对
|
<i className="ri-add-line mr-1"></i> 添加比较对
|
||||||
@@ -701,6 +778,82 @@ export function ReviewSettings({ onChange }: ReviewSettingsProps) {
|
|||||||
<div className="mb-4">
|
<div className="mb-4">
|
||||||
<label className="form-label" htmlFor={`conditions-container-${id}`}>条件设置 <span className="required-mark">*</span></label>
|
<label className="form-label" htmlFor={`conditions-container-${id}`}>条件设置 <span className="required-mark">*</span></label>
|
||||||
<div className="conditions-container" id={`conditions-container-${id}`}>
|
<div className="conditions-container" id={`conditions-container-${id}`}>
|
||||||
|
{Array.isArray(config.conditions) && config.conditions.length > 0 ? (
|
||||||
|
config.conditions.map((condition, conditionIndex) => (
|
||||||
|
<div key={`condition-${id}-${conditionIndex}`} className="condition-row bg-gray-50 p-4 rounded-md mb-2">
|
||||||
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-4 mb-2">
|
||||||
|
<div>
|
||||||
|
<label className="form-label text-sm" htmlFor={`field-${id}-${conditionIndex}`}>字段</label>
|
||||||
|
<select
|
||||||
|
id={`field-${id}-${conditionIndex}`}
|
||||||
|
className="form-select"
|
||||||
|
value={condition.field || ''}
|
||||||
|
onChange={(e) => {
|
||||||
|
const currentConditions = Array.isArray(config.conditions) ? [...(config.conditions as Condition[])] : [];
|
||||||
|
currentConditions[conditionIndex] = { ...currentConditions[conditionIndex], field: e.target.value };
|
||||||
|
handleRuleConfigChange(id, { conditions: currentConditions });
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<option value="">请选择字段</option>
|
||||||
|
{availableFields.map((field, idx) => (
|
||||||
|
<option key={`field-${conditionIndex}-${idx}`} value={field}>{field}</option>
|
||||||
|
))}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label className="form-label text-sm" htmlFor={`operator-${id}-${conditionIndex}`}>运算符</label>
|
||||||
|
<select
|
||||||
|
id={`operator-${id}-${conditionIndex}`}
|
||||||
|
className="form-select"
|
||||||
|
value={condition.operator || 'eq'}
|
||||||
|
onChange={(e) => {
|
||||||
|
const currentConditions = Array.isArray(config.conditions) ? [...(config.conditions as Condition[])] : [];
|
||||||
|
currentConditions[conditionIndex] = { ...currentConditions[conditionIndex], operator: e.target.value };
|
||||||
|
handleRuleConfigChange(id, { conditions: currentConditions });
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<option value="eq">等于 (=)</option>
|
||||||
|
<option value="neq">不等于 (≠)</option>
|
||||||
|
<option value="gt">大于 {`>`}</option>
|
||||||
|
<option value="gte">大于等于 {`≥`}</option>
|
||||||
|
<option value="lt">小于 {`<`}</option>
|
||||||
|
<option value="lte">小于等于 {`≤`}</option>
|
||||||
|
<option value="contains">包含</option>
|
||||||
|
<option value="not_contains">不包含</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label className="form-label text-sm" htmlFor={`value-${id}-${conditionIndex}`}>值</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
id={`value-${id}-${conditionIndex}`}
|
||||||
|
className="form-input"
|
||||||
|
value={condition.value || ''}
|
||||||
|
placeholder="请输入比较值"
|
||||||
|
onChange={(e) => {
|
||||||
|
const currentConditions = Array.isArray(config.conditions) ? [...(config.conditions as Condition[])] : [];
|
||||||
|
currentConditions[conditionIndex] = { ...currentConditions[conditionIndex], value: e.target.value };
|
||||||
|
handleRuleConfigChange(id, { conditions: currentConditions });
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex justify-end">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="text-red-500 hover:text-red-700"
|
||||||
|
onClick={() => {
|
||||||
|
const currentConditions = Array.isArray(config.conditions) ? [...(config.conditions as Condition[])] : [];
|
||||||
|
currentConditions.splice(conditionIndex, 1);
|
||||||
|
handleRuleConfigChange(id, { conditions: currentConditions });
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<i className="ri-delete-bin-line"></i> 删除
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))
|
||||||
|
) : (
|
||||||
<div className="condition-row bg-gray-50 p-4 rounded-md mb-2">
|
<div className="condition-row bg-gray-50 p-4 rounded-md mb-2">
|
||||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-4 mb-2">
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-4 mb-2">
|
||||||
<div>
|
<div>
|
||||||
@@ -709,9 +862,9 @@ export function ReviewSettings({ onChange }: ReviewSettingsProps) {
|
|||||||
id={`field-${id}-0`}
|
id={`field-${id}-0`}
|
||||||
className="form-select"
|
className="form-select"
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
const currentConditions = Array.isArray(config.conditions) ? [...config.conditions] : [];
|
// 直接初始化一个完整的条件对象
|
||||||
currentConditions[0] = { ...currentConditions[0], field: e.target.value };
|
const firstCondition = { field: e.target.value, operator: 'eq', value: '' };
|
||||||
handleRuleConfigChange(id, { conditions: currentConditions });
|
handleRuleConfigChange(id, { conditions: [firstCondition] });
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<option value="">请选择字段</option>
|
<option value="">请选择字段</option>
|
||||||
@@ -726,9 +879,12 @@ export function ReviewSettings({ onChange }: ReviewSettingsProps) {
|
|||||||
id={`operator-${id}-0`}
|
id={`operator-${id}-0`}
|
||||||
className="form-select"
|
className="form-select"
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
const currentConditions = Array.isArray(config.conditions) ? [...config.conditions] : [];
|
// 获取field的值
|
||||||
currentConditions[0] = { ...currentConditions[0], operator: e.target.value };
|
const field = document.getElementById(`field-${id}-0`) ?
|
||||||
handleRuleConfigChange(id, { conditions: currentConditions });
|
(document.getElementById(`field-${id}-0`) as HTMLSelectElement).value : '';
|
||||||
|
|
||||||
|
const firstCondition = { field, operator: e.target.value, value: '' };
|
||||||
|
handleRuleConfigChange(id, { conditions: [firstCondition] });
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<option value="eq">等于 (=)</option>
|
<option value="eq">等于 (=)</option>
|
||||||
@@ -749,28 +905,67 @@ export function ReviewSettings({ onChange }: ReviewSettingsProps) {
|
|||||||
className="form-input"
|
className="form-input"
|
||||||
placeholder="请输入比较值"
|
placeholder="请输入比较值"
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
const currentConditions = Array.isArray(config.conditions) ? [...config.conditions] : [];
|
// 获取field和operator的值
|
||||||
currentConditions[0] = { ...currentConditions[0], value: e.target.value };
|
const field = document.getElementById(`field-${id}-0`) ?
|
||||||
handleRuleConfigChange(id, { conditions: currentConditions });
|
(document.getElementById(`field-${id}-0`) as HTMLSelectElement).value : '';
|
||||||
|
const operator = document.getElementById(`operator-${id}-0`) ?
|
||||||
|
(document.getElementById(`operator-${id}-0`) as HTMLSelectElement).value : 'eq';
|
||||||
|
|
||||||
|
const firstCondition = { field, operator, value: e.target.value };
|
||||||
|
handleRuleConfigChange(id, { conditions: [firstCondition] });
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex justify-end">
|
<div className="flex justify-end">
|
||||||
<button className="text-red-500 hover:text-red-700">
|
<button
|
||||||
|
type="button"
|
||||||
|
className="text-red-500 hover:text-red-700"
|
||||||
|
onClick={() => {
|
||||||
|
const currentConditions = Array.isArray(config.conditions) ? [...(config.conditions as Condition[])] : [];
|
||||||
|
if (currentConditions.length > 1) {
|
||||||
|
currentConditions.splice(0, 1);
|
||||||
|
handleRuleConfigChange(id, { conditions: currentConditions });
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
<i className="ri-delete-bin-line"></i> 删除
|
<i className="ri-delete-bin-line"></i> 删除
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-2">
|
<div className="mt-2">
|
||||||
<button
|
<button
|
||||||
|
type="button"
|
||||||
className="ant-btn ant-btn-default"
|
className="ant-btn ant-btn-default"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
// 添加新的条件
|
// 添加新的条件
|
||||||
const currentConditions = Array.isArray(config.conditions) ? [...config.conditions] : [];
|
// 直接获取当前的conditions数组,或初始化为空数组
|
||||||
|
const conditions = Array.isArray(config.conditions) ? [...(config.conditions as Condition[])] : [];
|
||||||
|
|
||||||
|
// 创建新的空白条件
|
||||||
const newCondition = { field: '', operator: 'eq', value: '' };
|
const newCondition = { field: '', operator: 'eq', value: '' };
|
||||||
handleRuleConfigChange(id, { conditions: [...currentConditions, newCondition] });
|
|
||||||
|
// 如果数组为空,确保先初始化第一个条目
|
||||||
|
if (conditions.length === 0) {
|
||||||
|
// 如果界面上已有值,则添加两行:一行是当前值,一行是新的空行
|
||||||
|
const field = document.getElementById(`field-${id}-0`) ?
|
||||||
|
(document.getElementById(`field-${id}-0`) as HTMLSelectElement).value : '';
|
||||||
|
const operator = document.getElementById(`operator-${id}-0`) ?
|
||||||
|
(document.getElementById(`operator-${id}-0`) as HTMLSelectElement).value : 'eq';
|
||||||
|
const value = document.getElementById(`value-${id}-0`) ?
|
||||||
|
(document.getElementById(`value-${id}-0`) as HTMLInputElement).value : '';
|
||||||
|
|
||||||
|
// 将第一行设置为当前值(如果有)
|
||||||
|
conditions.push({ field, operator, value });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 无论如何,都添加一个新的空白行
|
||||||
|
conditions.push(newCondition);
|
||||||
|
|
||||||
|
// 更新配置
|
||||||
|
handleRuleConfigChange(id, { conditions });
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<i className="ri-add-line mr-1"></i> 添加条件
|
<i className="ri-add-line mr-1"></i> 添加条件
|
||||||
|
|||||||
Generated
+1
-1
@@ -13,8 +13,8 @@
|
|||||||
"@remix-run/serve": "^2.16.2",
|
"@remix-run/serve": "^2.16.2",
|
||||||
"@uiw/react-codemirror": "^4.23.10",
|
"@uiw/react-codemirror": "^4.23.10",
|
||||||
"isbot": "^4.1.0",
|
"isbot": "^4.1.0",
|
||||||
"prismjs": "^1.30.0",
|
|
||||||
"pg": "^8.14.1",
|
"pg": "^8.14.1",
|
||||||
|
"prismjs": "^1.30.0",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
"remixicon": "^4.6.0"
|
"remixicon": "^4.6.0"
|
||||||
|
|||||||
Reference in New Issue
Block a user