This commit is contained in:
2025-04-10 02:06:56 +08:00
parent 90317d2b4b
commit f99a1f05d4
4 changed files with 759 additions and 1294 deletions
File diff suppressed because it is too large Load Diff
+128 -91
View File
@@ -1,14 +1,7 @@
import React, { useState, useEffect, useContext, useCallback, useRef } from 'react';
import { SimpleCodeEditor } from './SimpleCodeEditor';
import { RuleContext } from '~/contexts/RuleContext';
import { processFieldNames, areArraysDifferent, getArrayDifference, debounce } from '~/utils';
import type {
Rule as ModelRule,
RuleType as ModelRuleType,
LogicType,
SuggestionMessageType,
PostActionType
} from '~/models/evaluation_points';
import { processFieldNames, areArraysDifferent, getArrayDifference } from '~/utils';
interface RuleType {
id: string;
@@ -60,15 +53,6 @@ interface ReviewSettingsProps {
export function ReviewSettings({
onChange,
initialData,
ruleTypeOptions = [],
logicTypeOptions = [],
logicOperatorOptions = [],
compareMethodOptions = [],
formatTypeOptions = [],
comparisonOperatorOptions = [],
matchTypeOptions = [],
suggestionMessageTypeOptions = [],
postActionOptions = []
}: ReviewSettingsProps) {
const [rules, setRules] = useState<RuleType[]>([
{ id: '1', type: '', config: {} }
@@ -227,7 +211,7 @@ export function ReviewSettings({
// 只在字段列表实际发生变化时更新
if (areArraysDifferent(uniqueFields, availableFields)) {
// 检查删除和新增的字段
const { added, removed } = getArrayDifference(uniqueFields, availableFields);
const { removed } = getArrayDifference(uniqueFields, availableFields);
// 处理删除的字段
if (removed.length > 0) {
@@ -467,67 +451,73 @@ export function ReviewSettings({
const handleRuleTypeChange = (id: string, type: string) => {
const newRules = rules.map(rule => {
if (rule.id === id) {
// 查找原始规则以获取现有配置
const originalRule = rules.find(r => r.id === id);
const originalConfig = originalRule ? originalRule.config : {};
// 为新类型初始化配置
let initialConfig: Record<string, unknown> = {};
// 如果类型没变,保留原配置
if (type === rule.type) {
initialConfig = { ...rule.config };
initialConfig = { ...originalConfig };
} else {
// 根据类型设置初始配置
switch(type) {
case 'exists':
initialConfig = {
fields: [], // 使用fields替代selectedFields
logic: 'and', // 使用logic替代existsLogic
fields: Array.isArray(originalConfig.fields) ? originalConfig.fields : [],
logic: originalConfig.logic || originalConfig.logicRelation || 'and',
availableFields: availableFields
};
break;
case 'consistency':
initialConfig = {
pairs: [],
logic: 'and', // 使用logic替代logicRelation
pairs: Array.isArray(originalConfig.pairs) ? originalConfig.pairs : [],
logic: originalConfig.logic || originalConfig.logicRelation || 'and',
availableFields: availableFields
};
break;
case 'format':
initialConfig = {
field: '', // 用于内部运算
checkField: '', // 用于UI展示
formatType: '',
formatParams: '',
field: originalConfig.field || '',
checkField: originalConfig.checkField || originalConfig.field || '',
formatType: originalConfig.formatType || '',
formatParams: originalConfig.formatParams || originalConfig.parameters || '',
availableFields: availableFields
};
break;
case 'logic':
initialConfig = {
conditions: [],
logic: 'and', // 使用logic替代logicRelation
conditions: Array.isArray(originalConfig.conditions) ? originalConfig.conditions : [],
logic: originalConfig.logic || originalConfig.logicRelation || 'and',
availableFields: availableFields
};
break;
case 'regex':
initialConfig = {
field: '', // 用于内部运算
checkField: '', // 用于UI展示
pattern: '',
regexPattern: '', // 用于UI展示
matchType: 'match', // 默认为必须匹配
field: originalConfig.field || '',
checkField: originalConfig.checkField || originalConfig.field || '',
pattern: originalConfig.pattern || '',
regexPattern: originalConfig.regexPattern || originalConfig.pattern || '',
matchType: originalConfig.matchType || 'match',
availableFields: availableFields
};
break;
case 'ai':
initialConfig = {
model: 'qwen14b',
temperature: 0.1,
prompt: '',
model: originalConfig.model || 'qwen14b',
temperature: typeof originalConfig.temperature === 'number' ? originalConfig.temperature : 0.1,
prompt: originalConfig.prompt || `请判断以下{字段}内容是否符合规范要求,仅回答"符合"或"不符合",并简要说明理由。
{字段内容}`,
availableFields: availableFields
};
break;
case 'code':
initialConfig = {
language: 'javascript',
code: '',
language: originalConfig.language || 'javascript',
code: originalConfig.code || '',
availableFields: availableFields
};
break;
@@ -595,7 +585,10 @@ export function ReviewSettings({
// 渲染字段标签,确保已选择的字段即使在新的字段列表中不存在也会显示
const renderFieldTags = (ruleId: string, config: Record<string, unknown>) => {
// 获取规则的当前已选字段
const selectedFields = Array.isArray(config.selectedFields) ? config.selectedFields as string[] : [];
// 修复:对于exists类型规则,应该使用fields而不是selectedFields
const selectedFields = Array.isArray(config.fields) ?
config.fields as string[] :
(Array.isArray(config.selectedFields) ? config.selectedFields as string[] : []);
// 优先使用配置中存储的可用字段,如果没有则使用当前可用字段
const fieldsToRender = Array.isArray(config.availableFields) ?
@@ -960,8 +953,15 @@ export function ReviewSettings({
name={`logicRelation_${id}`}
className="form-radio"
value="and"
checked={!config.logicRelation || config.logicRelation === 'and'}
onChange={(e) => handleRuleConfigChange(id, { logicRelation: e.target.value })}
checked={!config.logic && !config.logicRelation ? true : (config.logic === 'and' || config.logicRelation === 'and')}
onChange={(e) => {
handleRuleConfigChange(id, {
logicRelation: e.target.value,
logic: e.target.value
});
// 触发配置更新
generateEvaluationConfig();
}}
/>
<span>AND</span>
</label>
@@ -971,8 +971,15 @@ export function ReviewSettings({
name={`logicRelation_${id}`}
className="form-radio"
value="or"
checked={config.logicRelation === 'or'}
onChange={(e) => handleRuleConfigChange(id, { logicRelation: e.target.value })}
checked={config.logic === 'or' || config.logicRelation === 'or'}
onChange={(e) => {
handleRuleConfigChange(id, {
logicRelation: e.target.value,
logic: e.target.value
});
// 触发配置更新
generateEvaluationConfig();
}}
/>
<span>OR</span>
</label>
@@ -1188,9 +1195,12 @@ export function ReviewSettings({
name={`logicRelation_${id}`}
className="form-radio"
value="and"
checked={!config.logicRelation || config.logicRelation === 'and'}
checked={!config.logic && !config.logicRelation ? true : (config.logic === 'and' || config.logicRelation === 'and')}
onChange={(e) => {
handleRuleConfigChange(id, { logicRelation: e.target.value });
handleRuleConfigChange(id, {
logicRelation: e.target.value,
logic: e.target.value
});
// 触发配置更新
generateEvaluationConfig();
}}
@@ -1203,9 +1213,12 @@ export function ReviewSettings({
name={`logicRelation_${id}`}
className="form-radio"
value="or"
checked={config.logicRelation === 'or'}
checked={config.logic === 'or' || config.logicRelation === 'or'}
onChange={(e) => {
handleRuleConfigChange(id, { logicRelation: e.target.value });
handleRuleConfigChange(id, {
logicRelation: e.target.value,
logic: e.target.value
});
// 触发配置更新
generateEvaluationConfig();
}}
@@ -1302,7 +1315,12 @@ export function ReviewSettings({
<select
id={`ai-model-${id}`}
className="form-select"
onChange={(e) => handleRuleConfigChange(id, { model: e.target.value })}
value={config.model as string || 'qwen14b'}
onChange={(e) => {
handleRuleConfigChange(id, { model: e.target.value });
// 直接触发配置更新
generateEvaluationConfig();
}}
>
<option value="deepseek">DeepSeek</option>
<option value="qwen72b">Qwen72B-VL</option>
@@ -1316,11 +1334,17 @@ export function ReviewSettings({
id={`ai-temp-${id}`}
className="form-input"
placeholder="0.1"
defaultValue="0.1"
value={typeof config.temperature === 'number' ? config.temperature : (config.temperature ? parseFloat(String(config.temperature)) : 0.1)}
min="0"
max="1"
step="0.1"
onChange={(e) => handleRuleConfigChange(id, { temperature: e.target.value })}
onChange={(e) => {
const value = e.target.value;
const numberValue = value === '' ? 0.1 : parseFloat(value);
handleRuleConfigChange(id, { temperature: numberValue });
// 直接触发配置更新
generateEvaluationConfig();
}}
/>
</div>
<div className="col-span-1 md:col-span-2">
@@ -1329,9 +1353,13 @@ export function ReviewSettings({
id={`ai-prompt-${id}`}
className="form-textarea"
placeholder="请输入提示词,引导模型进行判断"
defaultValue={`请判断以下{字段}内容是否符合规范要求,仅回答"符合"或"不符合",并简要说明理由。
value={
typeof config.prompt === 'string' && config.prompt
? config.prompt
: `请判断以下{字段}内容是否符合规范要求,仅回答"符合"或"不符合",并简要说明理由。
{字段内容}`}
{字段内容}`
}
onChange={(e) => handleRuleConfigChange(id, { prompt: e.target.value })}
></textarea>
</div>
@@ -1382,8 +1410,11 @@ export function ReviewSettings({
name={`codeLanguage_${id}`}
className="form-radio"
value="javascript"
defaultChecked
onChange={(e) => handleRuleConfigChange(id, { language: e.target.value })}
checked={!config.language || config.language === 'javascript'}
onChange={(e) => {
handleRuleConfigChange(id, { language: e.target.value });
generateEvaluationConfig();
}}
/>
<span>JavaScript</span>
</label>
@@ -1393,7 +1424,11 @@ export function ReviewSettings({
name={`codeLanguage_${id}`}
className="form-radio"
value="python"
onChange={(e) => handleRuleConfigChange(id, { language: e.target.value })}
checked={config.language === 'python'}
onChange={(e) => {
handleRuleConfigChange(id, { language: e.target.value });
generateEvaluationConfig();
}}
/>
<span>Python</span>
</label>
@@ -1402,10 +1437,15 @@ export function ReviewSettings({
</div>
<div className="mb-4">
<label className="form-label" htmlFor={`code-editor-${id}`}> <span className="required-mark">*</span></label>
<div className="form-tip mb-2">true或false的评查函数使</div>
<SimpleCodeEditor
id={`code-editor-${id}`}
language={rule.config.language as 'javascript' | 'python' || 'javascript'}
onChange={(value) => handleRuleConfigChange(id, { code: value })}
initialValue={rule.config.code as string || ''}
onChange={(value) => {
handleRuleConfigChange(id, { code: value });
generateEvaluationConfig();
}}
/>
</div>
</div>
@@ -1490,7 +1530,8 @@ export function ReviewSettings({
// 生成完整的评查配置数据并在提交保存时使用
const generateEvaluationConfig = useCallback(() => {
const config = {
// 创建符合数据库模式的evaluation_config对象
const evaluationConfig = {
logicType: combinationLogic,
customLogic: combinationLogic === 'custom' ? customLogic : '',
rules: rules.map(rule => {
@@ -1506,7 +1547,6 @@ export function ReviewSettings({
delete processedConfig.selectedFields;
}
if (processedConfig.existsLogic) {
console.log(`[调试] exists规则 ${rule.id} 转换: existsLogic=${processedConfig.existsLogic} 映射到 logic`);
processedConfig.logic = processedConfig.existsLogic;
delete processedConfig.existsLogic;
}
@@ -1567,24 +1607,13 @@ export function ReviewSettings({
// 使用setTimeout避免连锁更新
setTimeout(() => {
if (onChange) {
onChange({
rules: config.rules,
combinationLogic: config.logicType,
customLogic: config.customLogic,
pass_message: pass_message,
fail_message: fail_message,
suggestion_message: suggestion_message,
suggestion_message_type: suggestion_message_type,
post_action: post_action,
action_config: action_config,
score: score,
scoreDisplay: scoreDisplay
});
// 仅将一个evaluation_config对象传递给父组件
onChange({ evaluation_config: evaluationConfig });
}
}, 0);
return config;
}, [rules, combinationLogic, customLogic, pass_message, fail_message, suggestion_message, suggestion_message_type, post_action, action_config, score, scoreDisplay, onChange]);
return evaluationConfig;
}, [rules, combinationLogic, customLogic, onChange]);
// 组件初次渲染后,主动发送一次完整配置数据
useEffect(() => {
@@ -1610,9 +1639,11 @@ export function ReviewSettings({
}
if (onChange) {
onChange({ [`${type}_message`]: value });
// 触发完整配置生成以确保数据保存
generateEvaluationConfig();
// 使用正确的字段名
const fieldName = type === 'pass' ? 'pass_message' :
type === 'fail' ? 'fail_message' :
'suggestion_message';
onChange({ [fieldName]: value });
}
};
@@ -1622,8 +1653,6 @@ export function ReviewSettings({
if (onChange) {
onChange({ suggestion_message_type: value });
// 触发完整配置生成以确保数据保存
generateEvaluationConfig();
}
};
@@ -1632,26 +1661,21 @@ export function ReviewSettings({
// 保存用户输入的显示值
setScoreDisplay(value);
let scoreValue = 0;
// 只在值不为空时更新实际分数
if (value.trim() !== '') {
const numValue = parseFloat(value);
if (!isNaN(numValue)) {
const validScore = Math.min(Math.max(numValue, 0), 100);
setScore(validScore);
} else {
setScore(0);
scoreValue = Math.min(Math.max(numValue, 0), 100);
}
} else {
setScore(0);
}
// 更新状态
setScore(scoreValue);
// 通知父组件
if (onChange) {
if (value.trim() === '') {
// 空值处理
onChange({ score: 0, scoreDisplay: '' });
} else {
onChange({ score: score, scoreDisplay: value });
}
onChange({ score: scoreValue });
}
};
@@ -2025,6 +2049,19 @@ export function ReviewSettings({
placeholder="请输入分数 (0-100)"
value={scoreDisplay}
onChange={(e) => handleScoreChange(e.target.value)}
onBlur={() => {
// 在失去焦点时,如果显示值为空,则设置为0
if (scoreDisplay.trim() === '') {
setScoreDisplay('0');
setScore(0);
if (onChange) {
onChange({ score: 0 });
}
} else {
// 否则更新为实际分数值的字符串表示
setScoreDisplay(String(score));
}
}}
/>
<span className="ml-2 text-gray-600"></span>
</div>
+5 -55
View File
@@ -13,7 +13,7 @@ export function SimpleCodeEditor({
language = 'javascript',
onChange
}: SimpleCodeEditorProps) {
const [code, setCode] = useState(initialValue || getDefaultCode(language));
const [code, setCode] = useState(initialValue);
const [copySuccess, setCopySuccess] = useState(false);
// 复制代码到剪贴板
@@ -33,62 +33,12 @@ export function SimpleCodeEditor({
}
};
// 初始示例代码
function getDefaultCode(lang: string) {
if (lang === 'javascript') {
return `// 示例代码
function checkRule(data) {
// data 包含抽取的字段
try {
// 在此编写检查逻辑
if (data.fieldName && condition) {
return {
pass: true,
message: "检查通过"
};
} else {
return {
pass: false,
message: "检查不通过,原因:..."
};
}
} catch (error) {
return {
pass: false,
message: "执行出错:" + error.message
};
}
}`;
} else {
return `# 示例代码
def check_rule(data):
# data 包含抽取的字段
try:
# 在此编写检查逻辑
if 'field_name' in data and condition:
return {
'pass': True,
'message': "检查通过"
}
else:
return {
'pass': False,
'message': "检查不通过,原因:..."
}
except Exception as error:
return {
'pass': False,
'message': f"执行出错:{str(error)}"
}`;
}
}
// 当语言变化时更新代码
// 更新初始值
useEffect(() => {
if (!initialValue) {
setCode(getDefaultCode(language));
if (initialValue !== undefined) {
setCode(initialValue);
}
}, [language, initialValue]);
}, [initialValue]);
return (
<div>