Files
leaudit-platform-frontend/app/components/rules/new/BasicInfo.tsx
T
2025-04-09 01:34:14 +08:00

416 lines
16 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import React, { useState, useEffect } from 'react';
import type { EvaluationPoint } from '~/models/evaluation_points';
import type { EvaluationPointGroup } from '~/models/evaluation_point_groups';
interface BasicInfoProps {
onChange?: (data: Record<string, unknown>) => void;
initialData?: EvaluationPoint;
evaluationPointGroups?: EvaluationPointGroup[];
riskOptions?: Array<{value: string, label: string}>;
}
// 评查点基本信息组件
export function BasicInfo({ onChange, initialData, evaluationPointGroups = [], riskOptions = [] }: BasicInfoProps) {
const [formData, setFormData] = useState<EvaluationPoint>({
risk: 'medium', // 风险等级 默认中风险
is_enabled: true, // 是否启用 默认启用
...(initialData || {}) // 合并初始数据
});
// 找到当前评查点类型对应的code
const getCheckpointTypeCode = () => {
if (!formData.evaluation_point_groups_pid) return "";
const typeGroup = evaluationPointGroups.find(
group => group.id === formData.evaluation_point_groups_pid && group.pid === 0
);
return typeGroup?.code || "";
};
// 评查点描述与法律依据 展开状态
const [isDescExpanded, setIsDescExpanded] = useState(false);
// 条款号临时输入字符串(不会触发自动分割)
const [lawArticlesText, setLawArticlesText] = useState('');
// 根据选择的评查点类型筛选可用的规则组
const filteredRuleGroups = evaluationPointGroups.filter(group =>
formData.evaluation_point_groups_pid &&
group.pid === formData.evaluation_point_groups_pid &&
group.is_enabled
);
// 获取评查点类型选项(pid=0的数据)
const getCheckpointTypeOptions = () => {
if (!evaluationPointGroups || evaluationPointGroups.length === 0) {
return (
<>
<option value=""></option>
</>
);
}
const typeGroups = evaluationPointGroups.filter(group => group.pid === 0 && group.is_enabled);
return (
<>
<option value=""></option>
{typeGroups.map(group => (
<option key={group.id} value={group.code}>
{group.name}
</option>
))}
</>
);
};
// 评查点描述与法律依据 展开状态
const handleToggleDescription = () => {
setIsDescExpanded(!isDescExpanded);
};
// 处理表单输入变化
const handleInputChange = (e: React.ChangeEvent<HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement>) => {
const { id, value } = e.target;
const newData = { ...formData };
// 映射id到表单字段名
switch(id) {
case 'rule-name':
newData.name = value;
break;
case 'rule-code':
newData.code = value;
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) {
// 找到选中的类型组
const selectedType = evaluationPointGroups.find(group => group.code === value && 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;
}
setFormData(newData);
if (onChange) {
onChange(newData);
}
};
// 处理条款号输入框变化
const handleLawArticlesChange = (e: React.ChangeEvent<HTMLInputElement>) => {
// 设置临时字符串状态,这样不会触发任何处理
setLawArticlesText(e.target.value);
};
// 处理条款号输入框失去焦点
const handleLawArticlesBlur = () => {
if (!lawArticlesText) return;
// 将输入的文本转换为数组
const articles = lawArticlesText
.split(',')
.map(article => article.trim())
.filter(article => article !== '');
// 创建一个新的引用法律对象,保留现有字段
const referencesLaws = {
...(formData.references_laws || {}),
articles: articles.length > 0 ? 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(','));
}
}, [formData.references_laws?.articles]);
// 检查是否需要自动展开描述区域
useEffect(() => {
// 如果描述或法律依据相关字段有值,则自动展开
if (
formData.description ||
formData.references_laws?.name ||
(formData.references_laws?.articles && formData.references_laws.articles.length > 0) ||
formData.references_laws?.content
) {
setIsDescExpanded(true);
}
}, [formData]);
useEffect(() => {
// 可以在这里通知父组件
if (onChange && filteredRuleGroups.length === 1) {
onChange({ evaluation_point_groups_id: filteredRuleGroups[0].id });
}
}, [filteredRuleGroups, onChange]);
return (
<div className="ant-card">
<div className="ant-card-header">
<h3></h3>
</div>
<div className="ant-card-body">
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
<div>
<label className="form-label" htmlFor="rule-name">
<span className="required-mark">*</span>
</label>
<input
type="text"
id="rule-name"
className="form-input"
placeholder="请输入评查点名称,简洁明了"
value={formData.name}
onChange={handleInputChange}
/>
<div className="form-tip">使30</div>
</div>
<div>
<label className="form-label" htmlFor="rule-code">
<span className="required-mark">*</span>
</label>
<input
type="text"
id="rule-code"
className="form-input"
placeholder="请输入评查点编码"
value={formData.code}
onChange={handleInputChange}
/>
<div className="form-tip"></div>
</div>
<div>
<label className="form-label" htmlFor="risk-level">
<span className="required-mark">*</span>
</label>
<select
id="risk-level"
className="form-select"
value={formData.risk}
onChange={handleInputChange}
>
{riskOptions.length > 0 ? (
riskOptions.map(option => (
<option key={option.value} value={option.value}>
{option.label}
</option>
))
) : (
<>
<option value="high"></option>
<option value="medium"></option>
<option value="low"></option>
</>
)}
</select>
<div className="form-tip"></div>
</div>
<div>
<label className="form-label" htmlFor="checkpoint-type">
<span className="required-mark">*</span>
</label>
<select
id="checkpoint-type"
className="form-select"
value={getCheckpointTypeCode()}
onChange={handleInputChange}
>
{getCheckpointTypeOptions()}
</select>
<div className="form-tip">便</div>
</div>
<div>
<label className="form-label" htmlFor="evaluation-point-group">
<span className="required-mark">*</span>
</label>
<select
id="evaluation-point-group"
className={`form-select ${!formData.evaluation_point_groups_pid || filteredRuleGroups.length === 0 ? 'bg-gray-100 cursor-not-allowed' : ''}`}
value={formData.evaluation_point_groups_id?.toString() || ""}
onChange={handleInputChange}
disabled={!formData.evaluation_point_groups_pid || filteredRuleGroups.length === 0}
>
<option value="">
{!formData.evaluation_point_groups_pid ? "请先选择评查点类型" :
filteredRuleGroups.length === 0 ? "该类型下暂无可用规则组" :
"请选择规则组"}
</option>
{filteredRuleGroups.map(group => (
<option key={group.id} value={group.id.toString()}>
{group.name}
</option>
))}
</select>
<div className="form-tip">
{!formData.evaluation_point_groups_pid ? "请先选择评查点类型" :
filteredRuleGroups.length === 0 ? "该类型下暂无可用规则组" :
"选择评查点所属的规则组"}
</div>
</div>
<div>
<label className="form-label" htmlFor="is-enabled"></label>
<select
id="is-enabled"
className="form-select"
value={formData.is_enabled ? 'true' : 'false'}
onChange={handleInputChange}
>
<option value="true"></option>
<option value="false"></option>
</select>
<div className="form-tip"></div>
</div>
</div>
<div className="mt-8">
<div
className={`flex justify-between items-center cursor-pointer ${isDescExpanded ? 'expanded' : ''}`}
onClick={handleToggleDescription}
onKeyDown={(e) => {
if (e.key === 'Enter' || e.key === ' ') {
handleToggleDescription();
}
}}
tabIndex={0}
role="button"
>
<label className="form-label mb-0" htmlFor="description-section"></label>
<i className={`ri-arrow-${isDescExpanded ? 'up' : 'down'}-s-line text-lg expand-icon`}></i>
</div>
<div className={`mt-2 ${isDescExpanded ? '' : 'hidden'}`} id="description-section">
<div className="mb-4">
<textarea
id="rule-description"
className="form-textarea"
placeholder="请输入评查点的详细描述"
style={{ minHeight: '80px' }}
value={formData.description}
onChange={handleInputChange}
></textarea>
<div className="form-tip"></div>
</div>
{/* 引用法典输入区域 */}
<div className="mb-4">
<label className="form-label" htmlFor="law-section"></label>
<div className="mb-3" id="law-section">
<label className="text-sm text-gray-600 mb-1 block" htmlFor="law-name"></label>
<input
type="text"
className="form-input"
placeholder="例如:《中华人民共和国民法典》"
id="law-name"
value={formData.references_laws?.name || ''}
onChange={handleInputChange}
/>
</div>
<div className="mb-3">
<label className="text-sm text-gray-600 mb-1 block" htmlFor="law-articles">
<span className="text-xs text-gray-400">()</span>
</label>
<input
type="text"
className="form-input"
placeholder="例如:第五百八十五条,第五百八十六条"
id="law-articles"
value={lawArticlesText}
onChange={handleLawArticlesChange}
onBlur={handleLawArticlesBlur}
/>
<div className="form-tip"></div>
</div>
<div className="mb-4">
<label className="text-sm text-gray-600 mb-1 block" htmlFor="law-content"></label>
<textarea
className="form-textarea"
style={{ minHeight: '60px' }}
placeholder="例如:当事人应当按照约定全面履行自己的义务。"
id="law-content"
value={formData.references_laws?.content || ''}
onChange={handleInputChange}
></textarea>
</div>
<div className="p-3 bg-blue-50 border border-blue-200 rounded-md text-sm text-blue-700 mb-2">
<i className="ri-information-line mr-1"></i>
</div>
{/* 预览区域 */}
<div className="mt-3">
<div className="text-sm font-medium mb-2"></div>
<div className="law-reference">
<div className="law-reference-title" id="preview-law-name">
{formData.references_laws?.name || '《中华人民共和国民法典》'}
</div>
<div className="law-reference-articles" id="preview-law-articles">
{formData.references_laws?.articles && formData.references_laws.articles.length > 0 ?
formData.references_laws.articles.map((article, index) => (
<span key={index} className="law-article">{article}</span>
)) : (
<>
<span className="law-article"></span>
<span className="law-article"></span>
</>
)}
</div>
<div className="law-reference-content" id="preview-law-content">
{formData.references_laws?.content || '当事人应当按照约定全面履行自己的义务。'}
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
);
}