feat: Delete the files uploaded in the external dialog box #1880 (#1967)

### What problem does this PR solve?

feat: Delete the files uploaded in the external dialog box #1880

### Type of change


- [x] New Feature (non-breaking change which adds functionality)
This commit is contained in:
balibabu 2024-08-15 18:31:46 +08:00 committed by GitHub
parent d3ff1a30bf
commit 7bdd5a48c0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 111 additions and 33 deletions

View File

@ -14,6 +14,8 @@
}
.listWrapper {
padding: 0 10px;
overflow: auto;
max-height: 170px;
}
.inputWrapper {
border-radius: 8px;

View File

@ -1,13 +1,18 @@
import { Authorization } from '@/constants/authorization';
import { useTranslate } from '@/hooks/common-hooks';
import {
useDeleteDocument,
useFetchDocumentInfosByIds,
useRemoveNextDocument,
} from '@/hooks/document-hooks';
import { getAuthorization } from '@/utils/authorization-util';
import { getExtension } from '@/utils/document-util';
import { formatBytes } from '@/utils/file-util';
import { CloseCircleOutlined, LoadingOutlined } from '@ant-design/icons';
import {
CloseCircleOutlined,
InfoCircleOutlined,
LoadingOutlined,
} from '@ant-design/icons';
import type { GetProp, UploadFile } from 'antd';
import {
Button,
@ -41,6 +46,16 @@ const getFileIds = (fileList: UploadFile[]) => {
return ids;
};
const isUploadError = (file: UploadFile) => {
const retcode = get(file, 'response.retcode');
return typeof retcode === 'number' && retcode !== 0;
};
const isUploadSuccess = (file: UploadFile) => {
const retcode = get(file, 'response.retcode');
return typeof retcode === 'number' && retcode === 0;
};
interface IProps {
disabled: boolean;
value: string;
@ -50,6 +65,7 @@ interface IProps {
onInputChange: ChangeEventHandler<HTMLInputElement>;
conversationId: string;
uploadUrl?: string;
isShared?: boolean;
}
const getBase64 = (file: FileType): Promise<string> =>
@ -61,6 +77,7 @@ const getBase64 = (file: FileType): Promise<string> =>
});
const MessageInput = ({
isShared = false,
disabled,
value,
onPressEnter,
@ -72,6 +89,7 @@ const MessageInput = ({
}: IProps) => {
const { t } = useTranslate('chat');
const { removeDocument } = useRemoveNextDocument();
const { deleteDocument } = useDeleteDocument();
const { data: documentInfos, setDocumentIds } = useFetchDocumentInfosByIds();
const [fileList, setFileList] = useState<UploadFile[]>([]);
@ -89,7 +107,7 @@ const MessageInput = ({
const handlePressEnter = useCallback(async () => {
if (isUploadingFile) return;
const ids = getFileIds(fileList);
const ids = getFileIds(fileList.filter((x) => isUploadSuccess(x)));
onPressEnter(ids);
setFileList([]);
@ -98,14 +116,24 @@ const MessageInput = ({
const handleRemove = useCallback(
async (file: UploadFile) => {
const ids = get(file, 'response.data', []);
if (ids.length) {
await removeDocument(ids[0]);
// Upload Successfully
if (Array.isArray(ids) && ids.length) {
if (isShared) {
await deleteDocument(ids);
} else {
await removeDocument(ids[0]);
}
setFileList((preList) => {
return preList.filter((x) => getFileId(x) !== ids[0]);
});
} else {
// Upload failed
setFileList((preList) => {
return preList.filter((x) => x.uid !== file.uid);
});
}
},
[removeDocument],
[removeDocument, deleteDocument, isShared],
);
const getDocumentInfoById = useCallback(
@ -192,6 +220,11 @@ const MessageInput = ({
<LoadingOutlined style={{ fontSize: 24 }} spin />
}
/>
) : !getFileId(item) ? (
<InfoCircleOutlined
size={30}
// width={30}
></InfoCircleOutlined>
) : (
<FileIcon id={id} name={item.name}></FileIcon>
)}
@ -202,26 +235,33 @@ const MessageInput = ({
>
<b> {item.name}</b>
</Text>
{item.percent !== 100 ? (
t('uploading')
) : !item.response ? (
t('parsing')
{isUploadError(item) ? (
t('uploadFailed')
) : (
<Space>
<span>{fileExtension?.toUpperCase()},</span>
<span>
{formatBytes(getDocumentInfoById(id)?.size ?? 0)}
</span>
</Space>
<>
{item.percent !== 100 ? (
t('uploading')
) : !item.response ? (
t('parsing')
) : (
<Space>
<span>{fileExtension?.toUpperCase()},</span>
<span>
{formatBytes(
getDocumentInfoById(id)?.size ?? 0,
)}
</span>
</Space>
)}
</>
)}
</Flex>
</Flex>
{item.status !== 'uploading' && (
<CloseCircleOutlined
className={styles.deleteIcon}
onClick={() => handleRemove(item)}
/>
<span className={styles.deleteIcon}>
<CloseCircleOutlined onClick={() => handleRemove(item)} />
</span>
)}
</Card>
</List.Item>

View File

@ -313,3 +313,23 @@ export const useRemoveNextDocument = () => {
return { data, loading, removeDocument: mutateAsync };
};
export const useDeleteDocument = () => {
// const queryClient = useQueryClient();
const {
data,
isPending: loading,
mutateAsync,
} = useMutation({
mutationKey: ['deleteDocument'],
mutationFn: async (documentIds: string[]) => {
const data = await kbService.document_delete({ doc_ids: documentIds });
// if (data.retcode === 0) {
// queryClient.invalidateQueries({ queryKey: ['fetchFlowList'] });
// }
return data;
},
});
return { data, loading, deleteDocument: mutateAsync };
};

View File

@ -424,6 +424,7 @@ The above is the content you need to summarize.`,
searching: 'searching...',
parsing: 'Parsing',
uploading: 'Uploading',
uploadFailed: 'Upload failed',
},
setting: {
profile: 'Profile',

View File

@ -394,6 +394,7 @@ export default {
searching: '搜索中',
parsing: '解析中',
uploading: '上傳中',
uploadFailed: '上傳失敗',
},
setting: {
profile: '概述',

View File

@ -411,6 +411,7 @@ export default {
searching: '搜索中',
parsing: '解析中',
uploading: '上传中',
uploadFailed: '上传失败',
},
setting: {
profile: '概要',

View File

@ -65,6 +65,7 @@ const ChatContainer = () => {
</Flex>
<MessageInput
isShared
value={value}
disabled={false}
sendDisabled={sendDisabled}

View File

@ -5,7 +5,7 @@ import {
} from '@/hooks/chat-hooks';
import { useSendMessageWithSse } from '@/hooks/logic-hooks';
import { useOneNamespaceEffectsLoading } from '@/hooks/store-hooks';
import { IAnswer } from '@/interfaces/database/chat';
import { IAnswer, Message } from '@/interfaces/database/chat';
import api from '@/utils/api';
import omit from 'lodash/omit';
import trim from 'lodash/trim';
@ -57,7 +57,7 @@ export const useSelectCurrentSharedConversation = (conversationId: string) => {
const ref = useScrollToBottom(currentConversation);
const addNewestConversation = useCallback((message: string) => {
const addNewestConversation = useCallback((message: Partial<Message>) => {
setCurrentConversation((pre) => {
return {
...pre,
@ -65,14 +65,15 @@ export const useSelectCurrentSharedConversation = (conversationId: string) => {
...(pre.message ?? []),
{
role: MessageType.User,
content: message,
content: message.content,
doc_ids: message.doc_ids,
id: uuid(),
} as IMessage,
{
role: MessageType.Assistant,
content: '',
id: uuid(),
reference: [],
reference: {},
} as IMessage,
],
};
@ -140,7 +141,7 @@ export const useSendButtonDisabled = (value: string) => {
export const useSendSharedMessage = (
conversation: IClientConversation,
addNewestConversation: (message: string) => void,
addNewestConversation: (message: Partial<Message>, answer?: string) => void,
removeLatestMessage: () => void,
setCurrentConversation: Dispatch<SetStateAction<IClientConversation>>,
addNewestAnswer: (answer: IAnswer) => void,
@ -205,14 +206,17 @@ export const useSendSharedMessage = (
}
}, [answer, addNewestAnswer]);
const handlePressEnter = useCallback(() => {
if (trim(value) === '') return;
if (done) {
setValue('');
addNewestConversation(value);
handleSendMessage(value.trim());
}
}, [addNewestConversation, done, handleSendMessage, setValue, value]);
const handlePressEnter = useCallback(
(documentIds: string[]) => {
if (trim(value) === '') return;
if (done) {
setValue('');
addNewestConversation({ content: value, doc_ids: documentIds });
handleSendMessage(value.trim());
}
},
[addNewestConversation, done, handleSendMessage, setValue, value],
);
return {
handlePressEnter,

View File

@ -12,6 +12,7 @@ const {
get_document_list,
document_change_status,
document_rm,
document_delete,
document_create,
document_change_parser,
document_thumbnails,
@ -131,6 +132,10 @@ const methods = {
url: knowledge_graph,
method: 'get',
},
document_delete: {
url: document_delete,
method: 'delete',
},
};
const kbService = registerServer<keyof typeof methods>(methods, request);

View File

@ -41,6 +41,7 @@ export default {
get_document_list: `${api_host}/document/list`,
document_change_status: `${api_host}/document/change_status`,
document_rm: `${api_host}/document/rm`,
document_delete: `${api_host}/api/document`,
document_rename: `${api_host}/document/rename`,
document_create: `${api_host}/document/create`,
document_run: `${api_host}/document/run`,

View File

@ -6,6 +6,8 @@ type Service<T extends string> = Record<
(params?: any, urlAppendix?: string) => any
>;
const Methods = ['post', 'delete', 'put'];
const registerServer = <T extends string>(
opt: Record<T, { url: string; method: string }>,
request: RequestMethod,
@ -18,7 +20,7 @@ const registerServer = <T extends string>(
if (urlAppendix) {
url = url + '/' + urlAppendix;
}
if (opt[key].method === 'post' || opt[key].method === 'POST') {
if (Methods.some((x) => x === opt[key].method.toLowerCase())) {
return request(url, {
method: opt[key].method,
data: params,