import { Bubble, Conversations, Prompts, Sender, Suggestion, XProvider, useXAgent, useXChat, Welcome, Attachments, AttachmentsProps, } from "@ant-design/x"; import { Card, Divider, Flex, App, Button } from "antd"; import React from "react"; import { BulbOutlined, SmileOutlined, UserOutlined, EditOutlined, DeleteOutlined, MessageOutlined, PlusOutlined, CloudUploadOutlined, LinkOutlined, } from "@ant-design/icons"; import type { GetProp, GetRef } from "antd"; import type { ConversationsProps } from "@ant-design/x"; import type { AgentItem } from "./data"; const roles: GetProp<typeof Bubble.List, "roles"> = { assient: { placement: "start", avatar: { icon: <i className="iconfont icon-AI1" />, style: { background: "#fde3cf" }, }, }, user: { placement: "end", avatar: { icon: <UserOutlined />, style: { background: "#87d068" } }, }, }; type AssistantProps = { agent?: AgentItem; }; export default (props: AssistantProps) => { const [value, setValue] = React.useState(""); const { message } = App.useApp(); const [agent] = useXAgent<{ role: string; content: string }>({ baseURL: "http://localhost:3000/ai/chat", }); const { onRequest, messages } = useXChat({ agent }); const menuConfig: ConversationsProps["menu"] = (conversation) => ({ items: [ { label: "修改对话名称", key: "operation1", icon: <EditOutlined />, }, { label: "删除对话", key: "operation3", icon: <DeleteOutlined />, danger: true, }, ], onClick: (menuInfo) => { message.info(`Click ${conversation.key} - ${menuInfo.key}`); }, }); const [openAttachment, setOpenAttachment] = React.useState(false); const attachmentsRef = React.useRef<GetRef<typeof Attachments>>(null); const senderRef = React.useRef<GetRef<typeof Sender>>(null); const [attachmentItems, setAttachmentItems] = React.useState<GetProp<AttachmentsProps, 'items'>>([]); const senderHeader = ( <Sender.Header title="附件" styles={{ content: { padding: 0, }, }} open={openAttachment} onOpenChange={setOpenAttachment} forceRender > <Attachments ref={attachmentsRef} // Mock not real upload file beforeUpload={() => false} items={attachmentItems} onChange={({ fileList }) => setAttachmentItems(fileList)} placeholder={(type) => type === "drop" ? { title: "拖拽文件到这里", } : { icon: <CloudUploadOutlined />, title: "文件列表", description: "点击或者拖拽文件到这里上传", } } getDropContainer={() => senderRef.current?.nativeElement} /> </Sender.Header> ); const submitMessage = (message: string) => { // onRequest({ role: "user", content: message }); }; const handlePromptItem = (item: any) => { // onRequest({ role: "user", content: item.data.description }); }; return ( <> <Card className="w-full h-full" styles={{ body: { height: "calc(100% - 48px)", }, }} title={ <span className="flex items-center"> <img className="w-20px h-20px rounded-8px" src={props.agent?.icon} /> <span className="ml-4px">{props.agent?.name}</span> </span> } > <XProvider direction="ltr"> <Flex style={{ height: "100%" }} gap={12}> <div className="w-200px"> <div className="w-full px-12px"> <Button type="primary" className="w-full" icon={<PlusOutlined />} > 新对话 </Button> </div> <Conversations style={{ width: 200 }} defaultActiveKey="1" menu={menuConfig} items={[ { key: "1", label: "新的对话", icon: <MessageOutlined />, }, ]} /> </div> <Divider type="vertical" style={{ height: "100%" }} /> <Flex vertical style={{ flex: 1 }} gap={8}> <div className="flex-1"> {!messages.length ? ( <> <div className="mt-20 mb-10"> <Welcome icon={ <img src={props.agent?.icon} className="rounded-8px" /> } title={`你好!我是易码工坊${props.agent?.name || "AI"}助手`} description={props.agent?.description} /> </div> <Prompts title={ props.agent?.promptsItems ? "✨ 你可以这样问我:" : "" } items={props.agent?.promptsItems || []} wrap onItemClick={handlePromptItem} /> </> ) : ( <Bubble.List autoScroll roles={roles} items={ [] // messages.map(({ id, message, status }) => ({ // key: id, // loading: status === "loading", // role: status === "user" ? "local" : "ai", // content: message, // })) } /> )} </div> <Prompts items={[ { key: "1", icon: <BulbOutlined style={{ color: "#FFD700" }} />, label: "一句话", }, { key: "2", icon: <SmileOutlined style={{ color: "#52C41A" }} />, label: "自动生成", }, ]} onItemClick={handlePromptItem} /> <Suggestion items={[{ label: "写一个应用介绍", value: "report" }]} onSelect={submitMessage} > {({ onTrigger, onKeyDown }) => { return ( <Sender ref={senderRef} header={senderHeader} prefix={ <Button type="text" icon={<LinkOutlined />} onClick={() => { setOpenAttachment(!openAttachment); }} /> } value={value} onPasteFile={(file) => { attachmentsRef.current?.upload(file); setOpenAttachment(true); }} onChange={(nextVal) => { if (nextVal === "/") { onTrigger(); } else if (!nextVal) { onTrigger(false); } setValue(nextVal); }} onKeyDown={onKeyDown} placeholder="输入/获取快捷提示" onSubmit={submitMessage} /> ); }} </Suggestion> </Flex> </Flex> </XProvider> </Card> </> ); };