Stabilize RVW editorial and methodology JSON parsing in production with layered repair and fallback handling, then publish the paired frontend task-level language selector updates. Also reset deployment checklist, record the 0316 deployment summary, and refresh the SAE runtime status with latest backend/frontend IPs. Made-with: Cursor
221 lines
7.7 KiB
TypeScript
221 lines
7.7 KiB
TypeScript
/**
|
||
* 智能体选择弹窗
|
||
*/
|
||
import { useEffect, useState } from 'react';
|
||
import { PlayCircle, X } from 'lucide-react';
|
||
import type { AgentType, EditorialBaseStandard } from '../types';
|
||
|
||
interface AgentModalProps {
|
||
visible: boolean;
|
||
taskCount: number;
|
||
onClose: () => void;
|
||
onConfirm: (agents: AgentType[], editorialBaseStandard?: EditorialBaseStandard) => void;
|
||
defaultEditorialBaseStandard?: EditorialBaseStandard;
|
||
isSubmitting?: boolean; // 🔒 防止重复提交
|
||
}
|
||
|
||
export default function AgentModal({
|
||
visible,
|
||
taskCount,
|
||
onClose,
|
||
onConfirm,
|
||
defaultEditorialBaseStandard = 'zh',
|
||
isSubmitting = false,
|
||
}: AgentModalProps) {
|
||
const [selectedAgents, setSelectedAgents] = useState<AgentType[]>(['editorial']);
|
||
const [editorialBaseStandard, setEditorialBaseStandard] = useState<EditorialBaseStandard>(defaultEditorialBaseStandard);
|
||
|
||
useEffect(() => {
|
||
if (!visible) return;
|
||
setSelectedAgents(['editorial']);
|
||
setEditorialBaseStandard(defaultEditorialBaseStandard);
|
||
}, [visible, defaultEditorialBaseStandard]);
|
||
|
||
const toggleAgent = (agent: AgentType) => {
|
||
if (selectedAgents.includes(agent)) {
|
||
// 至少保留一个
|
||
if (selectedAgents.length > 1) {
|
||
setSelectedAgents(selectedAgents.filter(a => a !== agent));
|
||
}
|
||
} else {
|
||
setSelectedAgents([...selectedAgents, agent]);
|
||
}
|
||
};
|
||
|
||
const handleConfirm = () => {
|
||
// 只调用onConfirm,让调用方控制关闭时机
|
||
onConfirm(
|
||
selectedAgents,
|
||
selectedAgents.includes('editorial') ? editorialBaseStandard : undefined
|
||
);
|
||
};
|
||
|
||
if (!visible) return null;
|
||
|
||
return (
|
||
<div className="fixed inset-0 bg-slate-900/50 z-50 flex items-center justify-center backdrop-blur-sm">
|
||
<div className="bg-white rounded-2xl shadow-2xl w-[560px] max-w-[92vw] overflow-hidden transform transition-all scale-100 fade-in">
|
||
{/* 头部 */}
|
||
<div className="bg-slate-900 p-5 text-white flex items-center justify-between">
|
||
<h3 className="font-bold text-lg flex items-center gap-2">
|
||
<PlayCircle className="w-5 h-5 text-indigo-400" />
|
||
发起智能审稿
|
||
</h3>
|
||
<button onClick={onClose} className="text-slate-400 hover:text-white transition-colors">
|
||
<X className="w-5 h-5" />
|
||
</button>
|
||
</div>
|
||
|
||
{/* 内容 */}
|
||
<div className="p-6 space-y-4">
|
||
<p className="text-sm text-slate-600 mb-4">
|
||
{taskCount > 1 ? `已选择 ${taskCount} 个稿件,请选择审稿维度:` : '请选择审稿维度:'}
|
||
</p>
|
||
|
||
{/* 规范性智能体 */}
|
||
<label
|
||
className={`flex items-start gap-3 p-4 border rounded-xl cursor-pointer transition-all ${
|
||
selectedAgents.includes('editorial')
|
||
? 'border-indigo-500 bg-indigo-50'
|
||
: 'border-gray-200 hover:border-indigo-300'
|
||
}`}
|
||
>
|
||
<input
|
||
type="checkbox"
|
||
checked={selectedAgents.includes('editorial')}
|
||
onChange={() => toggleAgent('editorial')}
|
||
className="mt-1 w-4 h-4 text-indigo-600 rounded border-gray-300 focus:ring-indigo-500"
|
||
/>
|
||
<div className="flex-1">
|
||
<span className="block font-bold text-slate-800 text-sm">稿约规范性智能体</span>
|
||
<span className="block text-xs text-slate-500 mt-0.5">
|
||
检查格式、参考文献、图片、伦理声明等11项标准
|
||
</span>
|
||
</div>
|
||
<span className="tag tag-blue">快速</span>
|
||
</label>
|
||
|
||
{selectedAgents.includes('editorial') && (
|
||
<div className="rounded-xl border border-sky-200 bg-sky-50 p-4 mt-2">
|
||
<div className="text-sm font-bold text-slate-800 mb-2">稿约基线语言</div>
|
||
<div className="flex items-center gap-8">
|
||
<label className="flex items-center gap-2 text-sm text-slate-700 cursor-pointer">
|
||
<input
|
||
type="radio"
|
||
name="editorialBaseStandard"
|
||
checked={editorialBaseStandard === 'zh'}
|
||
onChange={() => setEditorialBaseStandard('zh')}
|
||
className="w-4 h-4 text-indigo-600 border-gray-300 focus:ring-indigo-500"
|
||
/>
|
||
中文
|
||
</label>
|
||
<label className="flex items-center gap-2 text-sm text-slate-700 cursor-pointer">
|
||
<input
|
||
type="radio"
|
||
name="editorialBaseStandard"
|
||
checked={editorialBaseStandard === 'en'}
|
||
onChange={() => setEditorialBaseStandard('en')}
|
||
className="w-4 h-4 text-indigo-600 border-gray-300 focus:ring-indigo-500"
|
||
/>
|
||
英文
|
||
</label>
|
||
</div>
|
||
</div>
|
||
)}
|
||
|
||
{/* 方法学智能体 */}
|
||
<label
|
||
className={`flex items-start gap-3 p-4 border rounded-xl cursor-pointer transition-all ${
|
||
selectedAgents.includes('methodology')
|
||
? 'border-indigo-500 bg-indigo-50'
|
||
: 'border-gray-200 hover:border-indigo-300'
|
||
}`}
|
||
>
|
||
<input
|
||
type="checkbox"
|
||
checked={selectedAgents.includes('methodology')}
|
||
onChange={() => toggleAgent('methodology')}
|
||
className="mt-1 w-4 h-4 text-indigo-600 rounded border-gray-300 focus:ring-indigo-500"
|
||
/>
|
||
<div className="flex-1">
|
||
<span className="block font-bold text-slate-800 text-sm">方法学统计智能体</span>
|
||
<span className="block text-xs text-slate-500 mt-0.5">
|
||
深度推理检验方法、统计逻辑、研究设计等20项评估
|
||
</span>
|
||
</div>
|
||
<span className="tag tag-purple">深度</span>
|
||
</label>
|
||
|
||
{/* 临床专业评估智能体 */}
|
||
<label
|
||
className={`flex items-start gap-3 p-4 border rounded-xl cursor-pointer transition-all ${
|
||
selectedAgents.includes('clinical')
|
||
? 'border-indigo-500 bg-indigo-50'
|
||
: 'border-gray-200 hover:border-indigo-300'
|
||
}`}
|
||
>
|
||
<input
|
||
type="checkbox"
|
||
checked={selectedAgents.includes('clinical')}
|
||
onChange={() => toggleAgent('clinical')}
|
||
className="mt-1 w-4 h-4 text-indigo-600 rounded border-gray-300 focus:ring-indigo-500"
|
||
/>
|
||
<div className="flex-1">
|
||
<span className="block font-bold text-slate-800 text-sm">临床专业评估智能体</span>
|
||
<span className="block text-xs text-slate-500 mt-0.5">
|
||
基于 FINER 标准评估创新性、临床价值、科学性、可行性
|
||
</span>
|
||
</div>
|
||
<span className="tag tag-pink">专业</span>
|
||
</label>
|
||
|
||
</div>
|
||
|
||
{/* 底部按钮 */}
|
||
<div className="p-4 bg-slate-50 flex justify-end gap-3 border-t border-gray-100">
|
||
<button
|
||
onClick={onClose}
|
||
className="px-4 py-2 text-sm text-slate-600 hover:bg-gray-200 rounded-lg transition-colors"
|
||
>
|
||
取消
|
||
</button>
|
||
<button
|
||
onClick={handleConfirm}
|
||
disabled={selectedAgents.length === 0 || isSubmitting}
|
||
className="px-4 py-2 bg-indigo-600 hover:bg-indigo-700 text-white text-sm font-bold rounded-lg shadow-sm transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
|
||
>
|
||
{isSubmitting ? '提交中...' : '立即运行'}
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|