fix: use App.useApp modal + Button delete for reliable click handling
All checks were successful
Deploy Admin Frontend / build-and-deploy (push) Successful in 8s

This commit is contained in:
WangDL 2026-05-22 11:16:33 +08:00
parent 7438323e96
commit cf2dfc1351

View File

@ -1,5 +1,5 @@
import { useState, useRef, useEffect, useCallback } from 'react'
import { Input, Button, Avatar, Spin, theme, Modal, Tooltip, Typography } from 'antd'
import { Input, Button, Avatar, Spin, theme, Typography, App } from 'antd'
import {
SendOutlined, RobotOutlined, UserOutlined, PlusOutlined,
DeleteOutlined, StopOutlined, MessageOutlined,
@ -20,7 +20,8 @@ interface Message {
timestamp: number
}
export default function TaskAssistant() {
function ChatPage() {
const { modal } = App.useApp()
const [conversations, setConversations] = useState<Conversation[]>([])
const [activeId, setActiveId] = useState<string | null>(null)
const [messages, setMessages] = useState<Message[]>([])
@ -28,23 +29,21 @@ export default function TaskAssistant() {
const [loading, setLoading] = useState(false)
const [editingId, setEditingId] = useState<string | null>(null)
const [editTitle, setEditTitle] = useState('')
const [deleting, setDeleting] = useState(false)
const abortRef = useRef<AbortController | null>(null)
const messagesEndRef = useRef<HTMLDivElement>(null)
const editInputRef = useRef<any>(null)
const { token } = theme.useToken()
// Load conversations
const loadConversations = useCallback(async () => {
try { setConversations(await listConversations()) } catch { /* */ }
}, [])
useEffect(() => { loadConversations() }, [loadConversations])
// Scroll to bottom
useEffect(() => {
messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' })
}, [messages])
// Load messages when switching conversation
const switchConversation = useCallback(async (id: string) => {
if (loading) { abortRef.current?.abort(); setLoading(false) }
setActiveId(id)
@ -58,7 +57,6 @@ export default function TaskAssistant() {
} catch { /* */ }
}, [loading])
// New conversation
const handleNew = async () => {
if (loading) { abortRef.current?.abort(); setLoading(false) }
try {
@ -70,27 +68,31 @@ export default function TaskAssistant() {
} catch { /* */ }
}
// Delete conversation
const handleDelete = async (id: string) => {
Modal.confirm({
title: '删除对话', content: '确定要删除这个对话吗?',
okText: '删除', okType: 'danger', cancelText: '取消',
const handleDelete = (id: string) => {
modal.confirm({
title: '删除对话',
content: '确定要删除这个对话吗?',
okText: '删除',
okType: 'danger',
cancelText: '取消',
onOk: async () => {
await deleteConversation(id).catch(() => {})
setDeleting(true)
try {
await deleteConversation(id)
setConversations(prev => prev.filter(c => c.id !== id))
if (activeId === id) { setActiveId(null); setMessages([]) }
} catch { /* */ }
setDeleting(false)
},
})
}
// Start editing title
const startEdit = (conv: Conversation) => {
setEditingId(conv.id)
setEditTitle(conv.title)
setTimeout(() => editInputRef.current?.focus(), 50)
}
// Save title
const saveTitle = async (id: string) => {
const title = editTitle.trim()
if (title && title !== conversations.find(c => c.id === id)?.title) {
@ -100,7 +102,6 @@ export default function TaskAssistant() {
setEditingId(null)
}
// Send message
const handleSend = async () => {
const text = input.trim()
if (!text || loading) return
@ -128,11 +129,10 @@ export default function TaskAssistant() {
loadConversations()
}
const assistantMsg: Message = {
setMessages(prev => [...prev, {
id: (Date.now() + 1).toString(), role: 'assistant',
content: result.content, timestamp: Date.now(),
}
setMessages(prev => [...prev, assistantMsg])
}])
loadConversations()
} catch (err: any) {
if (err.name === 'AbortError') return
@ -155,7 +155,6 @@ export default function TaskAssistant() {
return (
<div style={{ display: 'flex', height: 'calc(100vh - 112px)', gap: 12 }}>
{/* Sidebar */}
<div style={{
width: 260, flexShrink: 0, display: 'flex', flexDirection: 'column',
background: token.colorBgContainer, borderRadius: token.borderRadiusLG,
@ -184,18 +183,18 @@ export default function TaskAssistant() {
style={{ flex: 1, fontSize: 13 }}
/>
) : (
<Text
ellipsis
style={{ flex: 1, fontSize: 13, lineHeight: '28px' }}
onDoubleClick={e => { e.stopPropagation(); startEdit(conv) }}
>{conv.title}</Text>
<Text ellipsis style={{ flex: 1, fontSize: 13, lineHeight: '28px' }}
onDoubleClick={e => { e.stopPropagation(); startEdit(conv) }}>
{conv.title}
</Text>
)}
<Tooltip title="删除">
<DeleteOutlined
<Button
type="text" size="small" danger
icon={<DeleteOutlined />}
disabled={deleting}
onClick={e => { e.stopPropagation(); handleDelete(conv.id) }}
style={{ fontSize: 12, color: token.colorTextQuaternary, marginLeft: 4, cursor: 'pointer' }}
style={{ marginLeft: 4, flexShrink: 0 }}
/>
</Tooltip>
</div>
))}
{conversations.length === 0 && (
@ -208,9 +207,7 @@ export default function TaskAssistant() {
</div>
</div>
{/* Chat area */}
<div style={{ flex: 1, display: 'flex', flexDirection: 'column', minWidth: 0 }}>
{/* Messages */}
<div style={{
flex: 1, overflowY: 'auto', background: token.colorBgContainer,
borderRadius: `${token.borderRadiusLG}px ${token.borderRadiusLG}px 0 0`,
@ -270,9 +267,8 @@ export default function TaskAssistant() {
<div ref={messagesEndRef} />
</div>
{/* Input area — redesigned */}
<div style={{
background: token.colorBgContainer, borderTop: 'none',
background: token.colorBgContainer,
border: `1px solid ${token.colorBorderSecondary}`,
borderRadius: `0 0 ${token.borderRadiusLG}px ${token.borderRadiusLG}px`,
padding: '16px 20px',
@ -293,18 +289,12 @@ export default function TaskAssistant() {
style={{ flex: 1, resize: 'none', padding: '4px 0', fontSize: 14 }}
/>
{loading ? (
<Button
danger type="primary" icon={<StopOutlined />} onClick={handleStop}
style={{ borderRadius: 8, height: 34, minWidth: 72 }}>
</Button>
<Button danger type="primary" icon={<StopOutlined />} onClick={handleStop}
style={{ borderRadius: 8, height: 34, minWidth: 72 }}></Button>
) : (
<Button
type="primary" icon={<SendOutlined />} onClick={handleSend}
<Button type="primary" icon={<SendOutlined />} onClick={handleSend}
disabled={!input.trim() || !activeId}
style={{ borderRadius: 8, height: 34, minWidth: 72 }}>
</Button>
style={{ borderRadius: 8, height: 34, minWidth: 72 }}></Button>
)}
</div>
</div>
@ -312,3 +302,11 @@ export default function TaskAssistant() {
</div>
)
}
export default function TaskAssistant() {
return (
<App>
<ChatPage />
</App>
)
}