diff --git a/web/src/assets/svg/paper-clip.svg b/web/src/assets/svg/paper-clip.svg
new file mode 100644
index 000000000..d3fdc540b
--- /dev/null
+++ b/web/src/assets/svg/paper-clip.svg
@@ -0,0 +1,6 @@
+
\ No newline at end of file
diff --git a/web/src/components/api-service/chat-overview-modal/index.tsx b/web/src/components/api-service/chat-overview-modal/index.tsx
index e14a53c01..44d271721 100644
--- a/web/src/components/api-service/chat-overview-modal/index.tsx
+++ b/web/src/components/api-service/chat-overview-modal/index.tsx
@@ -50,13 +50,8 @@ const ChatOverviewModal = ({
hideModal: hideApiKeyModal,
showModal: showApiKeyModal,
} = useSetModalState();
- const {
- embedVisible,
- hideEmbedModal,
- showEmbedModal,
- embedToken,
- errorContextHolder,
- } = useShowEmbedModal(id, idKey);
+ const { embedVisible, hideEmbedModal, showEmbedModal, embedToken } =
+ useShowEmbedModal(id, idKey);
const { pickerValue, setPickerValue } = useFetchNextStats();
@@ -64,7 +59,7 @@ const ChatOverviewModal = ({
return current && current > dayjs().endOf('day');
};
- const { handlePreview, contextHolder } = usePreviewChat(id, idKey);
+ const { handlePreview } = usePreviewChat(id, idKey);
return (
<>
@@ -138,8 +133,6 @@ const ChatOverviewModal = ({
visible={embedVisible}
hideModal={hideEmbedModal}
>
- {contextHolder}
- {errorContextHolder}
>
);
diff --git a/web/src/components/api-service/hooks.ts b/web/src/components/api-service/hooks.ts
index d93eab2db..2435f99f0 100644
--- a/web/src/components/api-service/hooks.ts
+++ b/web/src/components/api-service/hooks.ts
@@ -63,13 +63,12 @@ export const useSelectChartStatsList = (): ChartStatsType => {
};
export const useShowTokenEmptyError = () => {
- const [messageApi, contextHolder] = message.useMessage();
const { t } = useTranslate('chat');
const showTokenEmptyError = useCallback(() => {
- messageApi.error(t('tokenError'));
- }, [messageApi, t]);
- return { showTokenEmptyError, contextHolder };
+ message.error(t('tokenError'));
+ }, [t]);
+ return { showTokenEmptyError };
};
const getUrlWithToken = (token: string) => {
@@ -78,7 +77,7 @@ const getUrlWithToken = (token: string) => {
};
const useFetchTokenListBeforeOtherStep = (dialogId: string, idKey: string) => {
- const { showTokenEmptyError, contextHolder } = useShowTokenEmptyError();
+ const { showTokenEmptyError } = useShowTokenEmptyError();
const { data: tokenList, refetch } = useFetchTokenList({ [idKey]: dialogId });
@@ -98,7 +97,6 @@ const useFetchTokenListBeforeOtherStep = (dialogId: string, idKey: string) => {
return {
token,
- contextHolder,
handleOperate,
};
};
@@ -110,8 +108,10 @@ export const useShowEmbedModal = (dialogId: string, idKey: string) => {
showModal: showEmbedModal,
} = useSetModalState();
- const { handleOperate, token, contextHolder } =
- useFetchTokenListBeforeOtherStep(dialogId, idKey);
+ const { handleOperate, token } = useFetchTokenListBeforeOtherStep(
+ dialogId,
+ idKey,
+ );
const handleShowEmbedModal = useCallback(async () => {
const succeed = await handleOperate();
@@ -125,15 +125,11 @@ export const useShowEmbedModal = (dialogId: string, idKey: string) => {
hideEmbedModal,
embedVisible,
embedToken: token,
- errorContextHolder: contextHolder,
};
};
export const usePreviewChat = (dialogId: string, idKey: string) => {
- const { handleOperate, contextHolder } = useFetchTokenListBeforeOtherStep(
- dialogId,
- idKey,
- );
+ const { handleOperate } = useFetchTokenListBeforeOtherStep(dialogId, idKey);
const open = useCallback((t: string) => {
window.open(getUrlWithToken(t), '_blank');
@@ -148,6 +144,5 @@ export const usePreviewChat = (dialogId: string, idKey: string) => {
return {
handlePreview,
- contextHolder,
};
};
diff --git a/web/src/components/message-input/index.less b/web/src/components/message-input/index.less
index 5b203d69e..c4648dfca 100644
--- a/web/src/components/message-input/index.less
+++ b/web/src/components/message-input/index.less
@@ -1,11 +1,23 @@
.messageInputWrapper {
margin-right: 20px;
+ background-color: #f5f5f8;
+ border-radius: 8px;
+ :global(.ant-input-affix-wrapper) {
+ border-bottom-right-radius: 0;
+ border-bottom-left-radius: 0;
+ }
.documentCard {
:global(.ant-card-body) {
padding: 10px;
position: relative;
}
}
+ .listWrapper {
+ padding: 0 10px;
+ }
+ .inputWrapper {
+ border-radius: 8px;
+ }
.deleteIcon {
position: absolute;
right: -4px;
diff --git a/web/src/components/message-input/index.tsx b/web/src/components/message-input/index.tsx
index 2b86c50fa..61a762c47 100644
--- a/web/src/components/message-input/index.tsx
+++ b/web/src/components/message-input/index.tsx
@@ -1,14 +1,13 @@
import { Authorization } from '@/constants/authorization';
import { useTranslate } from '@/hooks/common-hooks';
-import { useRemoveNextDocument } from '@/hooks/document-hooks';
+import {
+ useFetchDocumentInfosByIds,
+ useRemoveNextDocument,
+} from '@/hooks/document-hooks';
import { getAuthorization } from '@/utils/authorization-util';
import { getExtension } from '@/utils/document-util';
-import {
- CloseCircleOutlined,
- LoadingOutlined,
- PlusOutlined,
- UploadOutlined,
-} from '@ant-design/icons';
+import { formatBytes } from '@/utils/file-util';
+import { CloseCircleOutlined, LoadingOutlined } from '@ant-design/icons';
import type { GetProp, UploadFile } from 'antd';
import {
Button,
@@ -22,10 +21,11 @@ import {
Upload,
UploadProps,
} from 'antd';
+import classNames from 'classnames';
import get from 'lodash/get';
-import { ChangeEventHandler, useCallback, useState } from 'react';
+import { ChangeEventHandler, useCallback, useEffect, useState } from 'react';
import FileIcon from '../file-icon';
-
+import SvgIcon from '../svg-icon';
import styles from './index.less';
type FileType = Parameters>[0];
@@ -33,6 +33,14 @@ const { Text } = Typography;
const getFileId = (file: UploadFile) => get(file, 'response.data.0');
+const getFileIds = (fileList: UploadFile[]) => {
+ const ids = fileList.reduce((pre, cur) => {
+ return pre.concat(get(cur, 'response.data', []));
+ }, []);
+
+ return ids;
+};
+
interface IProps {
disabled: boolean;
value: string;
@@ -41,6 +49,7 @@ interface IProps {
onPressEnter(documentIds: string[]): void;
onInputChange: ChangeEventHandler;
conversationId: string;
+ uploadUrl?: string;
}
const getBase64 = (file: FileType): Promise =>
@@ -59,9 +68,11 @@ const MessageInput = ({
sendLoading,
onInputChange,
conversationId,
+ uploadUrl = '/v1/document/upload_and_parse',
}: IProps) => {
const { t } = useTranslate('chat');
const { removeDocument } = useRemoveNextDocument();
+ const { data: documentInfos, setDocumentIds } = useFetchDocumentInfosByIds();
const [fileList, setFileList] = useState([]);
@@ -78,9 +89,7 @@ const MessageInput = ({
const handlePressEnter = useCallback(async () => {
if (isUploadingFile) return;
- const ids = fileList.reduce((pre, cur) => {
- return pre.concat(get(cur, 'response.data', []));
- }, []);
+ const ids = getFileIds(fileList);
onPressEnter(ids);
setFileList([]);
@@ -99,13 +108,18 @@ const MessageInput = ({
[removeDocument],
);
- const uploadButton = (
-
+ const getDocumentInfoById = useCallback(
+ (id: string) => {
+ return documentInfos.find((x) => x.id === id);
+ },
+ [documentInfos],
);
+ useEffect(() => {
+ const ids = getFileIds(fileList);
+ setDocumentIds(ids);
+ }, [fileList, setDocumentIds]);
+
return (
-
- }>
-
+ {conversationId && (
+
+
+
+ )}
- {/*
- {fileList.length >= 8 ? null : uploadButton}
- */}
+
{fileList.length > 0 && (
{
const fileExtension = getExtension(item.name);
+ const id = getFileId(item);
return (
- <>
-
- {item.status === 'uploading' || !item.response ? (
-
- }
- />
+
+ {item.status === 'uploading' || !item.response ? (
+
+ }
+ />
+ ) : (
+
+ )}
+
+
+ {item.name}
+
+ {item.percent !== 100 ? (
+ t('uploading')
+ ) : !item.response ? (
+ t('parsing')
) : (
-
+
+ {fileExtension?.toUpperCase()},
+
+ {formatBytes(getDocumentInfoById(id)?.size ?? 0)}
+
+
)}
-
-
- {item.name}
-
- {item.percent !== 100 ? (
- '上传中'
- ) : !item.response ? (
- '解析中'
- ) : (
-
- {fileExtension?.toUpperCase()},
-
- )}
-
- >
+
{item.status !== 'uploading' && (
{
- const { t } = useTranslate('chat');
const { conversationId } = useCreateSharedConversationOnMount();
const {
currentConversation: conversation,
@@ -65,24 +63,17 @@ const ChatContainer = () => {
-
- {t('send')}
-
- }
+ disabled={false}
+ sendDisabled={sendDisabled}
+ conversationId={conversationId}
+ onInputChange={handleInputChange}
onPressEnter={handlePressEnter}
- onChange={handleInputChange}
- />
+ sendLoading={sendLoading}
+ uploadUrl="/v1/api/document/upload_and_parse"
+ >
>
);
diff --git a/web/src/utils/file-util.ts b/web/src/utils/file-util.ts
index 3ec01f3ff..8a61217bf 100644
--- a/web/src/utils/file-util.ts
+++ b/web/src/utils/file-util.ts
@@ -85,3 +85,16 @@ export const downloadFile = ({
downloadElement.click();
document.body.removeChild(downloadElement);
};
+
+const Units = ['bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
+
+export const formatBytes = (x: string | number) => {
+ let l = 0,
+ n = (typeof x === 'string' ? parseInt(x, 10) : x) || 0;
+
+ while (n >= 1024 && ++l) {
+ n = n / 1024;
+ }
+
+ return n.toFixed(n < 10 && l > 0 ? 1 : 0) + ' ' + Units[l];
+};