feat: add Status and RagChat pages with corresponding components and styles
- Created StatusPage component with system stats, configuration, and indexed documents overview. - Added RagChatPage component for chat functionality. - Introduced global CSS styles for light and dark themes, including utility classes and animations. - Defined TypeScript types for compliance, documents, and themes. - Configured Tailwind CSS for dynamic theming and custom animations. - Set up TypeScript configuration for app and node environments. - Initialized Vite configuration for React project.
This commit is contained in:
246
src/pages/Compliance/ChatPanel.tsx
Normal file
246
src/pages/Compliance/ChatPanel.tsx
Normal 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>
|
||||
);
|
||||
};
|
||||
1816
src/pages/Compliance/CompliancePage.tsx
Normal file
1816
src/pages/Compliance/CompliancePage.tsx
Normal file
File diff suppressed because it is too large
Load Diff
2
src/pages/Compliance/index.ts
Normal file
2
src/pages/Compliance/index.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export { CompliancePage } from './CompliancePage';
|
||||
export { ChatPanel } from './ChatPanel';
|
||||
Reference in New Issue
Block a user