import React, { useState, useEffect } from 'react'; import type { EvaluationPoint } from '~/models/evaluation_points'; import type { EvaluationPointGroup } from '~/models/evaluation_point_groups'; import { getRulesList, getRuleTypes, getAttributeTypes, type RuleType, type AttributeTypeOption } from '~/api/evaluation_points/rules'; interface BasicInfoProps { onChange?: (data: Record) => void; initialData?: EvaluationPoint; evaluationPointGroups?: EvaluationPointGroup[]; riskOptions?: Array<{value: string, label: string}>; frontendJWT?: string; evaluationPointId?: number | string; } // 评查点基本信息组件 export function BasicInfo({ onChange, initialData, evaluationPointGroups = [], riskOptions = [], frontendJWT, evaluationPointId }: BasicInfoProps) { const [formData, setFormData] = useState({ risk: 'medium', // 风险等级 默认中风险 is_enabled: true, // 是否启用 默认启用 references_laws: { name: '', articles: [], content: '' }, ...(initialData || {}) // 合并初始数据 }); // 编码验证状态 const [codeValidating, setCodeValidating] = useState(false); const [codeError, setCodeError] = useState(''); const [codeValidationTimer, setCodeValidationTimer] = useState(null); // 异步验证编码唯一性 const validateCodeUnique = async (code: string): Promise => { if (!code.trim()) { return ''; // 空值不验证 } setCodeValidating(true); setCodeError(''); try { const response = await getRulesList({ keyword: code.trim(), pageSize: 10, token: frontendJWT }); if (response.data && response.data.rules && response.data.rules.length > 0) { // 检查是否有完全匹配的编码(排除当前编辑的评查点) const isDuplicate = response.data.rules.some(rule => rule.code === code.trim() && String(rule.id) !== String(evaluationPointId) ); if (isDuplicate) { return '该编码已被使用,请使用其他编码'; } } return ''; } catch (error) { console.error('验证编码唯一性失败:', error); return ''; // 验证失败不阻止用户输入 } finally { setCodeValidating(false); } }; // 找到当前评查点类型对应的code const getCheckpointTypeCode = () => { if (!formData.evaluation_point_groups_pid) return ""; // 优先从 API 返回的 filteredRuleTypes 中查找 const fromApi = filteredRuleTypes.find( ruleType => Number(ruleType.id) === formData.evaluation_point_groups_pid ); if (fromApi?.code) return fromApi.code; // 兜底:从 evaluationPointGroups 中查找 const typeGroup = evaluationPointGroups.find( group => group.id === formData.evaluation_point_groups_pid && (!group.pid || group.pid === 0) ); return typeGroup?.code || ""; }; // 评查点描述与法律依据 展开状态 const [isDescExpanded, setIsDescExpanded] = useState(false); // 条款号临时输入字符串(不会触发自动分割) const [lawArticlesText, setLawArticlesText] = useState(''); // 从 API 获取的评查点类型列表(根据 documentTypeIds 过滤) const [filteredRuleTypes, setFilteredRuleTypes] = useState([]); const [ruleTypesLoading, setRuleTypesLoading] = useState(false); // 适用文档属性类型相关状态 const [attributeTypeOptions, setAttributeTypeOptions] = useState([]); const [attributeTypesLoading, setAttributeTypesLoading] = useState(false); const [isCustomAttributeType, setIsCustomAttributeType] = useState(false); // 是否为自定义输入模式 const hasScopedGroupSource = filteredRuleTypes.length > 0 || evaluationPointGroups.length > 0; // 从 Session Storage 获取 documentTypeIds 并调用 API 获取评查点类型 useEffect(() => { const fetchRuleTypes = async () => { try { const storedIds = sessionStorage.getItem('documentTypeIds'); if (!storedIds) { setFilteredRuleTypes([]); return; } const parsedIds = JSON.parse(storedIds); if (!Array.isArray(parsedIds) || parsedIds.length === 0) { setFilteredRuleTypes([]); return; } const documentTypeIds = parsedIds.map(id => Number(id)); setRuleTypesLoading(true); // 调用 getRuleTypes API 获取过滤后的评查点类型 const response = await getRuleTypes(documentTypeIds, frontendJWT); if (response.data) { setFilteredRuleTypes(response.data); } else { console.error('获取评查点类型失败:', response.error); setFilteredRuleTypes([]); } } catch (error) { console.error('获取评查点类型失败:', error); setFilteredRuleTypes([]); } finally { setRuleTypesLoading(false); } }; fetchRuleTypes(); }, [frontendJWT]); // 获取适用文档属性类型选项 useEffect(() => { const fetchAttributeTypes = async () => { try { setAttributeTypesLoading(true); const response = await getAttributeTypes(frontendJWT); if (response.data) { setAttributeTypeOptions(response.data); } else { // 使用默认选项 setAttributeTypeOptions([{ code: 'ALL', label: '通用' }]); } } catch (error) { console.error('获取适用属性类型失败:', error); setAttributeTypeOptions([{ code: 'ALL', label: '通用' }]); } finally { setAttributeTypesLoading(false); } }; fetchAttributeTypes(); }, [frontendJWT]); // 检查初始数据中的 document_attribute_type 是否为自定义值 useEffect(() => { if (formData.document_attribute_type) { const isInOptions = attributeTypeOptions.some( opt => opt.code === formData.document_attribute_type ); // 如果当前值不在选项列表中,则切换到自定义模式 if (!isInOptions && attributeTypeOptions.length > 0) { setIsCustomAttributeType(true); } } }, [formData.document_attribute_type, attributeTypeOptions]); // 根据选择的评查点类型筛选可用的规则组 const filteredRuleGroups = evaluationPointGroups.filter(group => formData.evaluation_point_groups_pid && group.pid === formData.evaluation_point_groups_pid && group.is_enabled ); // 🆕 获取评查点类型选项(使用 API 返回的过滤后数据) const getCheckpointTypeOptions = () => { if (ruleTypesLoading) { return ( <> ); } // 优先使用接口按当前文档类型范围返回的一级分组 if (filteredRuleTypes.length > 0) { return ( <> {filteredRuleTypes.map(ruleType => ( ))} ); } // 兜底仅限当前页面已加载的 scoped 分组数据,禁止回退到“全库分组” if (!evaluationPointGroups || evaluationPointGroups.length === 0) { return ( <> ); } const typeGroups = evaluationPointGroups.filter(group => (!group.pid || group.pid === 0) && group.is_enabled ); return ( <> {typeGroups.map(group => ( ))} ); }; // 评查点描述与法律依据 展开状态 const handleToggleDescription = () => { setIsDescExpanded(!isDescExpanded); }; // 处理表单输入变化 const handleInputChange = (e: React.ChangeEvent) => { const { id, value } = e.target; const newData = { ...formData }; // 映射id到表单字段名 switch(id) { case 'rule-name': newData.name = value; break; case 'rule-code': newData.code = value; // 清除之前的验证定时器 if (codeValidationTimer) { clearTimeout(codeValidationTimer); } // 清除错误信息 setCodeError(''); // 设置新的验证定时器(500ms后触发验证) { const timer = setTimeout(async () => { const error = await validateCodeUnique(value); setCodeError(error); }, 500); setCodeValidationTimer(timer); } break; case 'risk-level': newData.risk = value; break; case 'is-enabled': newData.is_enabled = value === 'true'; break; case 'rule-description': newData.description = value; break; case 'law-name': newData.references_laws = { ...formData.references_laws, name: value }; break; case 'law-content': newData.references_laws = { ...formData.references_laws, content: value }; break; case 'evaluation-point-group': newData.evaluation_point_groups_id = value ? parseInt(value) : null; break; case 'checkpoint-type': // 处理评查点类型选择 if (value) { newData.evaluation_point_groups_id = null; // 优先从 API 返回的 filteredRuleTypes 中查找 const selectedFromApi = filteredRuleTypes.find(ruleType => ruleType.code === value); if (selectedFromApi) { newData.evaluation_point_groups_pid = Number(selectedFromApi.id); } else { // 兜底:从 evaluationPointGroups 中查找(pid为NULL或0表示顶级分组) const selectedType = evaluationPointGroups.find(group => group.code === value && (!group.pid || group.pid === 0)); if (selectedType) { newData.evaluation_point_groups_pid = selectedType.id; } } } else { newData.evaluation_point_groups_pid = null; newData.evaluation_point_groups_id = null; // 清空规则组选择 } break; case 'document-attribute-type': // 处理适用文档属性类型选择(下拉框模式) newData.document_attribute_type = value || 'ALL'; break; case 'document-attribute-type-custom': // 处理适用文档属性类型输入(自定义模式) newData.document_attribute_type = value; break; } setFormData(newData); if (onChange) { onChange(newData); } }; // 处理条款号输入框变化 const handleLawArticlesChange = (e: React.ChangeEvent) => { // 设置临时字符串状态,这样不会触发任何处理 setLawArticlesText(e.target.value); }; // 处理条款号输入框失去焦点 const handleLawArticlesBlur = () => { // 将输入的文本转换为数组 const articles = lawArticlesText .split(',') .map(article => article.trim()) .filter(article => article !== ''); // 创建一个新的引用法律对象,保留现有字段 const referencesLaws = { ...(formData.references_laws || {}), articles: articles // ✅ 清空时会是空数组 }; // 更新表单数据 const newData = { ...formData, references_laws: referencesLaws }; setFormData(newData); if (onChange) { onChange(newData); } }; // 初始化条款号文本字段 useEffect(() => { if (formData.references_laws?.articles && formData.references_laws.articles.length > 0) { setLawArticlesText(formData.references_laws.articles.join(',')); } else { // ✅ 当 articles 为空时,也清空输入框 setLawArticlesText(''); } }, [formData.references_laws?.articles]); // 检查是否需要自动展开描述区域(仅在初始数据加载时执行一次) useEffect(() => { // 如果初始数据中描述或法律依据相关字段有值,则自动展开 if ( initialData?.description || initialData?.references_laws?.name || (initialData?.references_laws?.articles && initialData.references_laws.articles.length > 0) || initialData?.references_laws?.content ) { setIsDescExpanded(true); } // eslint-disable-next-line react-hooks/exhaustive-deps }, []); // 注释掉自动选择规则组的逻辑,避免无限循环 // 原因:此 useEffect 依赖 onChange 和 filteredRuleGroups,每次渲染都可能触发 // 导致 onChange -> 父组件更新 -> BasicInfo 重新渲染 -> useEffect 再次触发 -> 无限循环 // useEffect(() => { // if (onChange && filteredRuleGroups.length === 1) { // onChange({ evaluation_point_groups_id: filteredRuleGroups[0].id }); // } // }, [filteredRuleGroups, onChange]); // 清理验证定时器 useEffect(() => { return () => { if (codeValidationTimer) { clearTimeout(codeValidationTimer); } }; }, [codeValidationTimer]); return (

基本信息

请使用简洁明了的名称,不超过30个字符
{codeError ? (
{codeError}
) : (
用于系统标识的唯一编码
)}
请定义评查点的风险等级
评查点类型用于分类管理,便于规则统一调用
{!hasScopedGroupSource ? "请先到系统设置为当前入口模块绑定文档类型与评查点分组" : !formData.evaluation_point_groups_pid ? "请先选择评查点类型" : filteredRuleGroups.length === 0 ? "该类型下暂无可用规则组" : "选择评查点所属的规则组"}
创建后是否立即启用此评查点
{isCustomAttributeType ? ( // 自定义输入模式 ) : ( // 下拉选择模式 )}
{isCustomAttributeType ? '输入自定义的文档属性类型,点击右侧按钮可切换回下拉选择' : '选择评查点适用的文档属性类型,点击右侧按钮可自定义输入'}
{ if (e.key === 'Enter' || e.key === ' ') { handleToggleDescription(); } }} tabIndex={0} role="button" >
详细描述有助于其他用户了解该评查点的用途
{/* 引用法典输入区域 */}
多个条款用逗号分隔,将自动转换为数组格式
引用的法律条文将在评查结果中显示,帮助用户理解评查规则的法律依据
{/* 预览区域 */}
预览效果
{formData.references_laws?.name || '《中华人民共和国民法典》'}
{formData.references_laws?.articles && formData.references_laws.articles.length > 0 ? formData.references_laws.articles.map((article, index) => ( {article} )) : ( <> 第五百八十五条 第五百八十六条 )}
{formData.references_laws?.content || '当事人应当按照约定全面履行自己的义务。'}
); }