编辑功能
This commit is contained in:
@@ -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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user