123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272 |
- 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>
- </>
- );
- };
|