|
@@ -24,7 +24,7 @@ import {
|
|
|
Modal,
|
|
|
Input,
|
|
|
} from "antd";
|
|
|
-import { useEffect, useRef, useState } from "react";
|
|
|
+import { useEffect, useMemo, useRef, useState } from "react";
|
|
|
import {
|
|
|
BulbOutlined,
|
|
|
SmileOutlined,
|
|
@@ -34,7 +34,6 @@ import {
|
|
|
PlusOutlined,
|
|
|
CloudUploadOutlined,
|
|
|
LinkOutlined,
|
|
|
- CopyOutlined,
|
|
|
RedoOutlined,
|
|
|
ReadOutlined,
|
|
|
} from "@ant-design/icons";
|
|
@@ -46,6 +45,8 @@ import { ChangeSessionName, DeleteSession, AppParamenters } from "@/api/ai";
|
|
|
import InfiniteScroll from "react-infinite-scroll-component";
|
|
|
import "./assistant.less";
|
|
|
import { UploadFile } from "@/api";
|
|
|
+import UserBubbleFooter from "./UserBubbleFooter";
|
|
|
+import AIBubbleFooter from "./AIBubbleFooter";
|
|
|
|
|
|
type AssistantProps = {
|
|
|
agent?: AgentItem;
|
|
@@ -89,7 +90,9 @@ const roles: GetProp<typeof Bubble.List, "roles"> = {
|
|
|
export default (props: AssistantProps) => {
|
|
|
const [senderVal, setSenderVal] = useState("");
|
|
|
const [supportFile, setSupportFile] = useState(false);
|
|
|
+ const [fileLimit, setFileLimit] = useState(0);
|
|
|
const [fileIds, setFileIds] = useState<Record<string, string>[]>([]);
|
|
|
+
|
|
|
// 聊天相关状态
|
|
|
const {
|
|
|
messages,
|
|
@@ -114,9 +117,10 @@ export default (props: AssistantProps) => {
|
|
|
const query = arr[messages.length - 2].content as string;
|
|
|
arr[messages.length - 1].status = "done";
|
|
|
arr[messages.length - 1].footer = (
|
|
|
- <BubbleFooter
|
|
|
+ <AIBubbleFooter
|
|
|
content={arr[messages.length - 1].content as string}
|
|
|
query={query}
|
|
|
+ onRedo={submitMessage}
|
|
|
/>
|
|
|
);
|
|
|
return arr;
|
|
@@ -147,20 +151,21 @@ export default (props: AssistantProps) => {
|
|
|
});
|
|
|
|
|
|
useEffect(() => {
|
|
|
- setAttachmentItems([])
|
|
|
+ setAttachmentItems([]);
|
|
|
setFileIds([]);
|
|
|
setOpenAttachment(false);
|
|
|
}, [activeConversation]);
|
|
|
|
|
|
useEffect(() => {
|
|
|
// 清除上传文件
|
|
|
- setAttachmentItems([])
|
|
|
+ setAttachmentItems([]);
|
|
|
setFileIds([]);
|
|
|
setOpenAttachment(false);
|
|
|
// 查询是否支持文件
|
|
|
props.agent?.key &&
|
|
|
AppParamenters({ app_name: props.agent.key }).then((res) => {
|
|
|
setSupportFile(!!res?.result?.file_upload.enabled);
|
|
|
+ setFileLimit(res?.result?.file_upload?.image.number_limits);
|
|
|
});
|
|
|
}, [props.agent?.key]);
|
|
|
|
|
@@ -293,10 +298,11 @@ export default (props: AssistantProps) => {
|
|
|
formData.append("file", file);
|
|
|
try {
|
|
|
const res = await UploadFile(formData);
|
|
|
- if(res.code === 1) {
|
|
|
+ if (res.code === 1) {
|
|
|
onSuccess?.(res.result);
|
|
|
const uid = (file as UploadFileType).uid || Date.now().toString();
|
|
|
- res.result?.[0] && setFileIds((ids) => [...ids, {[uid]: res.result[0].id}]);
|
|
|
+ res.result?.[0] &&
|
|
|
+ setFileIds((ids) => [...ids, { [uid]: res.result[0].id }]);
|
|
|
} else {
|
|
|
onError?.(res?.message || "上传失败");
|
|
|
}
|
|
@@ -322,9 +328,15 @@ export default (props: AssistantProps) => {
|
|
|
>
|
|
|
<Attachments
|
|
|
ref={attachmentsRef}
|
|
|
- // beforeUpload={() => false}
|
|
|
items={attachmentItems}
|
|
|
- onChange={({ fileList }) => setAttachmentItems(fileList)}
|
|
|
+ maxCount={fileLimit}
|
|
|
+ onChange={({ fileList }) => {
|
|
|
+ const uids = fileList.map((file) => file.uid);
|
|
|
+ setAttachmentItems(fileList);
|
|
|
+ setFileIds((arr) =>
|
|
|
+ arr.filter((item) => uids.includes(Object.keys(item)[0]))
|
|
|
+ );
|
|
|
+ }}
|
|
|
placeholder={(type) =>
|
|
|
type === "drop"
|
|
|
? {
|
|
@@ -338,56 +350,29 @@ export default (props: AssistantProps) => {
|
|
|
}
|
|
|
getDropContainer={() => senderRef.current?.nativeElement}
|
|
|
customRequest={uploadRequest}
|
|
|
- onRemove={(file) => {
|
|
|
- setFileIds((arr) => arr.filter((item) => {
|
|
|
- return !Object.keys(item)?.includes(file.uid)
|
|
|
- }));
|
|
|
- }}
|
|
|
/>
|
|
|
</Sender.Header>
|
|
|
);
|
|
|
|
|
|
- // 底部组件
|
|
|
- const BubbleFooter = (props: { content: string; query: string }) => {
|
|
|
- const handleCopy = () => {
|
|
|
- navigator.clipboard.writeText(props.content);
|
|
|
- message.success("复制成功");
|
|
|
- };
|
|
|
-
|
|
|
- const handleRedo = () => {
|
|
|
- submitMessage(props.query);
|
|
|
- };
|
|
|
- return (
|
|
|
- <Space>
|
|
|
- <Button
|
|
|
- type="text"
|
|
|
- size="small"
|
|
|
- icon={<CopyOutlined />}
|
|
|
- onClick={handleCopy}
|
|
|
- >
|
|
|
- 复制
|
|
|
- </Button>
|
|
|
- <Button
|
|
|
- type="text"
|
|
|
- size="small"
|
|
|
- icon={<RedoOutlined />}
|
|
|
- onClick={handleRedo}
|
|
|
- >
|
|
|
- 重新生成
|
|
|
- </Button>
|
|
|
- </Space>
|
|
|
- );
|
|
|
- };
|
|
|
-
|
|
|
// 提交消息
|
|
|
const submitMessage = (msg: string) => {
|
|
|
setSenderVal("");
|
|
|
|
|
|
+ const ids = supportFile
|
|
|
+ ? fileIds.map((item) => Object.values(item)[0]).flat(Infinity)
|
|
|
+ : undefined;
|
|
|
+
|
|
|
setMessages((arr) => {
|
|
|
const index = arr.length;
|
|
|
return [
|
|
|
...arr,
|
|
|
- { id: index + "", content: msg, status: "done", role: "user" },
|
|
|
+ {
|
|
|
+ id: index + "",
|
|
|
+ content: msg,
|
|
|
+ status: "done",
|
|
|
+ role: "user",
|
|
|
+ message_files: ids,
|
|
|
+ },
|
|
|
{
|
|
|
id: index + 1 + "",
|
|
|
content: "",
|
|
@@ -397,7 +382,7 @@ export default (props: AssistantProps) => {
|
|
|
},
|
|
|
];
|
|
|
});
|
|
|
- const ids = supportFile ? fileIds.map(item => Object.values(item)[0]).flat(Infinity) : undefined;
|
|
|
+
|
|
|
onRequest(msg, ids);
|
|
|
|
|
|
setFileIds([]);
|
|
@@ -422,7 +407,7 @@ export default (props: AssistantProps) => {
|
|
|
]);
|
|
|
onRequest(msg);
|
|
|
|
|
|
- setAttachmentItems([])
|
|
|
+ setAttachmentItems([]);
|
|
|
setFileIds([]);
|
|
|
setOpenAttachment(false);
|
|
|
};
|
|
@@ -439,9 +424,10 @@ export default (props: AssistantProps) => {
|
|
|
<div className="text-12px text-text-secondary pl-12px">
|
|
|
(已停止思考)
|
|
|
</div>
|
|
|
- <BubbleFooter
|
|
|
+ <AIBubbleFooter
|
|
|
content={arr[messages.length - 1].content as string}
|
|
|
query={arr[messages.length - 2].content as string}
|
|
|
+ onRedo={submitMessage}
|
|
|
/>
|
|
|
</div>
|
|
|
);
|
|
@@ -449,6 +435,15 @@ export default (props: AssistantProps) => {
|
|
|
});
|
|
|
};
|
|
|
|
|
|
+ const getMessages = useMemo(() => {
|
|
|
+ return messages.map((item) => {
|
|
|
+ return {
|
|
|
+ ...item,
|
|
|
+ footer: <UserBubbleFooter fieldIds={item.message_files} />,
|
|
|
+ };
|
|
|
+ });
|
|
|
+ }, [messages]);
|
|
|
+
|
|
|
return (
|
|
|
<>
|
|
|
<Card
|
|
@@ -555,7 +550,7 @@ export default (props: AssistantProps) => {
|
|
|
style={{ maxHeight: contentHeight, padding: "0 20px" }}
|
|
|
autoScroll
|
|
|
roles={roles}
|
|
|
- items={messages}
|
|
|
+ items={getMessages}
|
|
|
/>
|
|
|
)}
|
|
|
</div>
|