This commit is contained in:
2026-05-14 15:07:34 +08:00
parent c2a398930d
commit 10d04c4083
179 changed files with 24073 additions and 1243 deletions

View File

@@ -0,0 +1,246 @@
import React from 'react';
import { useTheme } from '../../contexts/ThemeContext';
import type { ComplianceChunk } from '../../types';
interface ChatPanelProps {
activeChunkId: number;
chunks: ComplianceChunk[];
messages: Array<{ id: number; role: 'user' | 'assistant'; content: string }>;
chatInput: string;
setChatInput: (value: string) => void;
chatLoading: boolean;
sendChatMessage: () => void;
closeChat: () => void;
quickQuestions?: string[];
}
export const ChatPanel: React.FC<ChatPanelProps> = ({
activeChunkId,
chunks,
messages,
chatInput,
setChatInput,
chatLoading,
sendChatMessage,
closeChat,
quickQuestions = [
'这个设计是否合规?',
'需要修改哪些内容?',
'法规的具体要求是什么?',
],
}) => {
const { theme } = useTheme();
const activeChunk = chunks.find(c => c.id === activeChunkId);
return (
<div
style={{
position: 'absolute',
right: 0,
top: 0,
bottom: 0,
width: 420,
background: theme.bgCard,
borderLeft: `1px solid ${theme.border}`,
display: 'flex',
flexDirection: 'column',
zIndex: 50,
animation: 'slideIn 0.3s ease-out forwards',
}}
>
{/* Chat Header */}
<div style={{
padding: '20px 24px',
borderBottom: `1px solid ${theme.border}`,
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
}}>
<div>
<div style={{ fontSize: 14, fontWeight: 600, color: theme.text }}></div>
<div className="mono" style={{ fontSize: 11, color: theme.text3 }}>
#{activeChunk?.index} · {activeChunk?.regulations.length}
</div>
</div>
<button
onClick={closeChat}
style={{
width: 32,
height: 32,
borderRadius: 8,
background: theme.bgHover,
border: 'none',
cursor: 'pointer',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
}}
>
<svg width="16" height="16" viewBox="0 0 24 24" fill="none">
<path d="M18 6L6 18M6 6L18 18" stroke={theme.text3} strokeWidth="2" strokeLinecap="round"/>
</svg>
</button>
</div>
{/* Current Chunk Info */}
<div style={{
padding: '16px 24px',
background: theme.bgHover,
borderBottom: `1px solid ${theme.border}`,
}}>
<div style={{ fontSize: 13, fontWeight: 500, marginBottom: 8, color: theme.text }}>
{activeChunk?.intent}
</div>
<div style={{
fontSize: 12,
color: theme.text2,
lineHeight: 1.5,
maxHeight: 60,
overflow: 'hidden',
}}>
{activeChunk?.content.substring(0, 100)}...
</div>
</div>
{/* Chat Messages */}
<div style={{
flex: 1,
overflowY: 'auto',
padding: '20px 24px',
display: 'flex',
flexDirection: 'column',
gap: 16,
}}>
{messages.map(msg => (
<div key={msg.id} style={{
display: 'flex',
gap: 12,
flexDirection: msg.role === 'user' ? 'row-reverse' : 'row',
}}>
{msg.role === 'assistant' && (
<div style={{
width: 32,
height: 32,
borderRadius: 8,
background: theme.gradientAccent,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
flexShrink: 0,
}}>
<svg width="16" height="16" viewBox="0 0 24 24" fill="none">
<circle cx="12" cy="12" r="6" fill="#fff"/>
</svg>
</div>
)}
<div style={{
maxWidth: '80%',
padding: '12px 16px',
background: msg.role === 'user' ? theme.gradientAccent : theme.bgElevated,
borderRadius: 12,
color: msg.role === 'user' ? '#fff' : theme.text,
fontSize: 14,
lineHeight: 1.6,
whiteSpace: 'pre-wrap',
border: msg.role === 'assistant' ? `1px solid ${theme.border}` : 'none',
}}>
{msg.content}
</div>
</div>
))}
{chatLoading && (
<div style={{ display: 'flex', gap: 12 }}>
<div style={{
width: 32,
height: 32,
borderRadius: 8,
background: theme.gradientAccent,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
}}>
<svg width="16" height="16" viewBox="0 0 24 24" fill="none">
<circle cx="12" cy="12" r="6" fill="#fff"/>
</svg>
</div>
<div style={{
padding: '12px 16px',
background: theme.bgElevated,
borderRadius: 12,
border: `1px solid ${theme.border}`,
display: 'flex',
alignItems: 'center',
gap: 8,
}}>
<div style={{ width: 6, height: 6, borderRadius: '50%', background: theme.accent, animation: 'pulse 1s infinite' }} />
<span style={{ fontSize: 13, color: theme.text2 }}>...</span>
</div>
</div>
)}
</div>
{/* Quick Questions */}
<div style={{
padding: '12px 24px',
display: 'flex',
gap: 8,
flexWrap: 'wrap',
}}>
{quickQuestions.map(q => (
<button
key={q}
onClick={() => { setChatInput(q); }}
style={{
padding: '6px 12px',
fontSize: 12,
background: theme.bgHover,
border: `1px solid ${theme.border}`,
borderRadius: 6,
color: theme.text2,
cursor: 'pointer',
}}
>{q}</button>
))}
</div>
{/* Chat Input */}
<div style={{
padding: '16px 24px',
borderTop: `1px solid ${theme.border}`,
display: 'flex',
gap: 12,
}}>
<input
value={chatInput}
onChange={e => setChatInput(e.target.value)}
onKeyDown={e => e.key === 'Enter' && sendChatMessage()}
placeholder="输入问题..."
style={{
flex: 1,
padding: 12,
fontSize: 14,
background: theme.bgHover,
border: `1px solid ${theme.border}`,
borderRadius: 8,
color: theme.text,
outline: 'none',
}}
/>
<button
onClick={sendChatMessage}
disabled={chatLoading || !chatInput.trim()}
style={{
padding: '12px 20px',
fontSize: 14,
fontWeight: 600,
background: chatLoading || !chatInput.trim() ? theme.bgHover : theme.gradientAccent,
color: chatLoading || !chatInput.trim() ? theme.text3 : '#fff',
border: 'none',
borderRadius: 8,
cursor: chatLoading || !chatInput.trim() ? 'not-allowed' : 'pointer',
}}
></button>
</div>
</div>
);
};