编辑功能

This commit is contained in:
liamzi
2025-01-06 10:51:42 +08:00
parent 25046c5440
commit 9dcfe5b0fa

View File

@@ -1,9 +1,10 @@
import { Button, Drawer, Input, Space, message } from 'antd'; import { Button, Drawer, Input, Space, message } from 'antd';
import { useChat } from "ai/react"; import { useChat } from "ai/react";
import { CodeHighlight } from "@mantine/code-highlight"; import { CodeHighlight } from "@mantine/code-highlight";
import { DownloadOutlined } from '@ant-design/icons'; import { DownloadOutlined, EditOutlined, CheckOutlined, CloseOutlined } from '@ant-design/icons';
import { useRef, useEffect } from 'react'; import { useRef, useEffect, useState } from 'react';
import { useSessionStorage } from 'react-use'; import { useSessionStorage } from 'react-use';
import Editor from "@monaco-editor/react";
export default function ChatAIDrawer({ open, onClose, onExport }) { export default function ChatAIDrawer({ open, onClose, onExport }) {
const STORAGE_KEY = 'chat_history'; const STORAGE_KEY = 'chat_history';
@@ -35,13 +36,44 @@ export default function ChatAIDrawer({ open, onClose, onExport }) {
} }
}; };
const [editingMessageId, setEditingMessageId] = useState(null);
const [editingContent, setEditingContent] = useState('');
// 优化编辑处理函数
const handleEdit = (message) => {
if(isLoading) return;
setEditingContent(message.content);
setEditingMessageId(message.id);
};
const handleSaveEdit = () => {
try {
JSON.parse(editingContent);
setMessages(messages.map(msg =>
msg.id === editingMessageId
? { ...msg, content: editingContent }
: msg
));
setEditingMessageId(null);
message.success('编辑成功');
} catch (error) {
message.error('请输入有效的 JSON 格式');
}
};
const handleCancelEdit = () => {
setEditingMessageId(null);
setEditingContent('');
};
return ( return (
<Drawer <Drawer
title={ title={
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}> <div className="flex justify-between items-center">
<span>AI 助手</span> <span className="text-lg font-medium text-gray-800">AI 助手</span>
<Button <Button
size="small" size="small"
className="hover:bg-gray-100"
onClick={() => { onClick={() => {
setMessages([]); setMessages([]);
setStoredMessages('[]'); setStoredMessages('[]');
@@ -53,95 +85,137 @@ export default function ChatAIDrawer({ open, onClose, onExport }) {
</div> </div>
} }
placement="right" placement="right"
width={600} width={800}
open={open} open={open}
onClose={onClose} onClose={onClose}
className="rounded-l-xl"
> >
<div style={{ display: 'flex', flexDirection: 'column', height: 'calc(100vh - 108px)' }}> <div className="flex flex-col h-[calc(100vh-108px)]">
<div style={{ <div className="flex-1 overflow-y-auto px-4 space-y-6">
flex: 1,
overflowY: 'auto',
marginBottom: 16,
padding: '0 16px'
}}>
{messages.map((message) => ( {messages.map((message) => (
<div <div
key={message.id} key={message.id}
style={{ className={`rounded-lg p-4 transition-all ${
marginBottom: 16, message.role === 'assistant'
backgroundColor: message.role === 'assistant' ? '#f0f9ff' : '#f8f9fa', ? 'bg-blue-50 hover:bg-blue-100'
padding: 16, : 'bg-gray-50 hover:bg-gray-100'
borderRadius: 12, }`}
boxShadow: '0 2px 4px rgba(0,0,0,0.05)'
}}
> >
<div style={{ <div className="flex justify-between items-center mb-3">
fontWeight: 'bold', <span className={`font-medium ${
marginBottom: 8, message.role === 'assistant' ? 'text-blue-600' : 'text-gray-600'
display: 'flex', }`}>
justifyContent: 'space-between', {message.role === 'assistant' ? 'AI 助手' : '用户'}
alignItems: 'center', </span>
color: message.role === 'assistant' ? '#1677ff' : '#52525b'
}}>
<span>{message.role === 'assistant' ? 'AI 助手' : '用户'}</span>
{message.role === 'assistant' && ( {message.role === 'assistant' && (
<Space>
<Button <Button
type="link" type="text"
size="small"
icon={<EditOutlined />}
disabled={isLoading}
onClick={() => handleEdit(message)}
className="text-gray-500 hover:text-blue-600"
>
编辑
</Button>
<Button
type="text"
size="small" size="small"
icon={<DownloadOutlined />} icon={<DownloadOutlined />}
onClick={() => handleExport(message.content)} onClick={() => handleExport(message.content)}
className="text-gray-500 hover:text-blue-600"
> >
导出 导出
</Button> </Button>
</Space>
)} )}
</div> </div>
<div>
{message.role === "assistant" ? ( {message.role === "assistant" ? (
<div className="relative">
{editingMessageId === message.id ? (
<div className="rounded-lg border border-blue-200">
<Editor
height="300px"
defaultLanguage="json"
value={editingContent}
theme="vs-light"
options={{
minimap: { enabled: false },
scrollBeyondLastLine: false,
fontSize: 14,
lineNumbers: 'on',
renderLineHighlight: 'none',
roundedSelection: true,
}}
onChange={setEditingContent}
onMount={(editor) => {
editor.getModel()?.updateOptions({ tabSize: 2 });
editor.focus();
}}
/>
<div className="flex justify-end gap-2 p-2 bg-gray-50 border-t">
<Button
size="small"
icon={<CloseOutlined />}
onClick={handleCancelEdit}
className="hover:bg-gray-200"
>
取消
</Button>
<Button
type="primary"
size="small"
icon={<CheckOutlined />}
onClick={handleSaveEdit}
className="bg-blue-600 hover:bg-blue-700"
>
保存
</Button>
</div>
</div>
) : (
<CodeHighlight <CodeHighlight
code={message.content} code={message.content}
language="json" language="json"
copyLabel="复制代码" copyLabel="复制代码"
copiedLabel="已复制!" copiedLabel="已复制!"
withLineNumbers withLineNumbers
className="rounded-lg"
/> />
)}
</div>
) : ( ) : (
<div style={{ <div className="text-gray-700 whitespace-pre-wrap break-words">
whiteSpace: 'pre-wrap',
wordBreak: 'break-word'
}}>
{message.content} {message.content}
</div> </div>
)} )}
</div> </div>
</div>
))} ))}
<div ref={messagesEndRef} /> <div ref={messagesEndRef} />
</div> </div>
<form onSubmit={handleSubmit} style={{ <div className="border-t bg-white p-4">
padding: '16px', <form onSubmit={handleSubmit} className="flex gap-2">
borderTop: '1px solid #f0f0f0',
backgroundColor: '#fff'
}}>
<Space.Compact style={{ width: '100%' }}>
<Input <Input
value={input} value={input}
placeholder="请输入您的问题..." placeholder="请输入您的问题..."
onChange={handleInputChange} onChange={handleInputChange}
disabled={isLoading} disabled={isLoading}
style={{ borderRadius: '6px 0 0 6px' }} className="flex-1 rounded-lg border-gray-300 hover:border-blue-400 focus:border-blue-600 focus:shadow-blue-100"
/> />
<Button <Button
type="primary" type="primary"
htmlType="submit" htmlType="submit"
loading={isLoading} loading={isLoading}
style={{ borderRadius: '0 6px 6px 0' }} className="rounded-lg bg-blue-600 hover:bg-blue-700"
> >
发送 发送
</Button> </Button>
</Space.Compact>
</form> </form>
</div> </div>
</div>
</Drawer> </Drawer>
); );
} }