From 27b782e12a1fa47c36febf440f7e77a38bad7553 Mon Sep 17 00:00:00 2001 From: zhaoying Date: Thu, 19 Mar 2026 18:41:33 +0800 Subject: [PATCH 1/2] feat(web): work memory support page --- web/src/api/memory.ts | 6 +- .../UserMemoryDetail/pages/WorkingDetail.tsx | 66 +++++++++++++------ 2 files changed, 49 insertions(+), 23 deletions(-) diff --git a/web/src/api/memory.ts b/web/src/api/memory.ts index 823e3d78..9a464893 100644 --- a/web/src/api/memory.ts +++ b/web/src/api/memory.ts @@ -2,7 +2,7 @@ * @Author: ZhaoYing * @Date: 2026-02-03 14:00:06 * @Last Modified by: ZhaoYing - * @Last Modified time: 2026-03-13 10:48:41 + * @Last Modified time: 2026-03-19 18:35:10 */ import { request } from '@/utils/request' import type { AxiosRequestConfig } from 'axios' @@ -218,8 +218,8 @@ export const getExplicitMemory = (end_user_id: string) => { export const getExplicitMemoryDetails = (data: { end_user_id: string, memory_id: string; }) => { return request.post(`/memory/explicit-memory/details`, data) } -export const getConversations = (end_user_id: string) => { - return request.get(`/memory/work/${end_user_id}/conversations`) +export const getConversations = (end_user_id: string, page = 1, pagesize = 20) => { + return request.get(`/memory/work/${end_user_id}/conversations`, { page, pagesize }) } export const getConversationMessages = (end_user_id: string, conversation_id: string) => { return request.get(`/memory/work/${end_user_id}/messages`, { conversation_id }) diff --git a/web/src/views/UserMemoryDetail/pages/WorkingDetail.tsx b/web/src/views/UserMemoryDetail/pages/WorkingDetail.tsx index 843a3b23..2320ee52 100644 --- a/web/src/views/UserMemoryDetail/pages/WorkingDetail.tsx +++ b/web/src/views/UserMemoryDetail/pages/WorkingDetail.tsx @@ -1,8 +1,10 @@ -import { type FC, useEffect, useState, useMemo } from 'react' +import { type FC, useEffect, useState, useMemo, useRef } from 'react' import clsx from 'clsx' import { useTranslation } from 'react-i18next' import { useParams } from 'react-router-dom' import { Row, Col, Skeleton, Button, Divider, Tooltip } from 'antd' + +import InfiniteScroll from 'react-infinite-scroll-component' import RbCard from '@/components/RbCard/Card' import { getConversations, @@ -34,6 +36,8 @@ const WorkingDetail: FC = () => { const { id } = useParams() const [loading, setLoading] = useState(false) const [data, setData] = useState([]) + const [hasMore, setHasMore] = useState(true) + const pageRef = useRef(1) const [messagesLoading, setMessagesLoading] = useState(false) const [messages, setMessages] = useState([]) const [detailLoading, setDetailLoading] = useState(false) @@ -51,16 +55,30 @@ const WorkingDetail: FC = () => { setSelected(null) setDetail(null) setData([]) - getConversations(id).then((res) => { - const response = res as Conversation[] - setData(response) - setSelected(response[0] || null) + setHasMore(true) + pageRef.current = 1 + getConversations(id, 1).then((res) => { + const response = res as { items: Conversation[], page: { hasnext: boolean } } + setData(response.items) + setSelected(response.items[0] || null) + setHasMore(response.page.hasnext) }) .finally(() => { setLoading(false) }) } + const loadMore = () => { + if (!id) return + const nextPage = pageRef.current + 1 + getConversations(id, nextPage).then((res) => { + const response = res as {items: Conversation[], page: { hasnext: boolean }} + setData(prev => [...prev, ...response.items]) + pageRef.current = nextPage + setHasMore(response.page.hasnext) + }) + } + useEffect(() => { if (!id || !selected || !selected.id) return getDetail(selected.id) @@ -103,22 +121,30 @@ const WorkingDetail: FC = () => { : data.length === 0 ? :( - + -
- {data.map(item => ( -
- -
setSelected(item)} - > - {item.title} -
-
-
- ))} +
+ + {data.map(item => ( +
+ +
setSelected(item)} + > + {item.title} +
+
+
+ ))} +
{selected && <> From b84aba71e73a1c00d5c404d8c6fe177c8567434d Mon Sep 17 00:00:00 2001 From: zhaoying Date: Thu, 19 Mar 2026 19:00:31 +0800 Subject: [PATCH 2/2] feat(web): file add status --- web/src/components/Chat/ChatInput.tsx | 111 ++++++++++-------- web/src/components/Chat/ChatToolbar.tsx | 18 ++- .../ApplicationConfig/TestChat/index.tsx | 4 +- .../ApplicationConfig/components/Chat.tsx | 4 +- .../Conversation/components/FileUpload.tsx | 36 ++++-- web/src/views/Conversation/index.tsx | 2 +- .../views/Workflow/components/Chat/Chat.tsx | 4 +- 7 files changed, 109 insertions(+), 70 deletions(-) diff --git a/web/src/components/Chat/ChatInput.tsx b/web/src/components/Chat/ChatInput.tsx index 7daf935c..aa0dd2f6 100644 --- a/web/src/components/Chat/ChatInput.tsx +++ b/web/src/components/Chat/ChatInput.tsx @@ -2,10 +2,11 @@ * @Author: ZhaoYing * @Date: 2025-12-10 16:46:14 * @Last Modified by: ZhaoYing - * @Last Modified time: 2026-03-19 16:05:56 + * @Last Modified time: 2026-03-19 18:44:51 */ import { type FC, useEffect, useMemo } from 'react' -import { Flex, Input, Form } from 'antd' +import { Flex, Input, Form, Spin } from 'antd' +import clsx from 'clsx' import SendIcon from '@/assets/images/conversation/send.svg' import SendDisabledIcon from '@/assets/images/conversation/sendDisabled.svg' @@ -69,6 +70,8 @@ const ChatInput: FC = ({ onSend(values.message) } + console.log('previewFileList', previewFileList) + return (
@@ -76,62 +79,78 @@ const ChatInput: FC = ({ {previewFileList.map((file) => { if (file.type.includes('image')) { return ( -
- {file.name} -
handleDelete(file)} - >
-
+ +
+ {file.name} +
handleDelete(file)} + >
+
+
) } if (file.type.includes('video')) { return ( -
-
+ +
+
+
) } if (file.type.includes('audio')) { return ( -
-
+ ) })}
} diff --git a/web/src/components/Chat/ChatToolbar.tsx b/web/src/components/Chat/ChatToolbar.tsx index 883ac98a..dda56977 100644 --- a/web/src/components/Chat/ChatToolbar.tsx +++ b/web/src/components/Chat/ChatToolbar.tsx @@ -2,7 +2,7 @@ * @Author: ZhaoYing * @Date: 2026-03-17 14:22:25 * @Last Modified by: ZhaoYing - * @Last Modified time: 2026-03-18 15:55:13 + * @Last Modified time: 2026-03-19 18:59:37 */ // Toolbar component for chat input area, supporting file upload, audio recording, and variable configuration import { useRef, forwardRef, useImperativeHandle, type ReactNode, useEffect } from 'react' @@ -85,10 +85,18 @@ const ChatToolbar = forwardRef(({ // Append newly uploaded file to the file list when upload is complete const fileChange = (file?: any) => { - if (file?.status !== 'done') return - const files = [...(queryValues?.files || []), file] - form.setFieldValue('files', files) - onFilesChange?.(files) + console.log('file', file) + const lastFiles = form.getFieldValue('files') || []; + const index = lastFiles.findIndex((item: any) => item.uid === file.uid) + if (index > -1) { + lastFiles[index] = file + } else { + lastFiles.push(file) + } + form.setFieldValue('files', [...lastFiles]) + onFilesChange?.([...lastFiles]) + + console.log('lastFiles', lastFiles) } // Append recorded audio file to the file list and notify parent diff --git a/web/src/views/ApplicationConfig/TestChat/index.tsx b/web/src/views/ApplicationConfig/TestChat/index.tsx index ad7931e2..c324622d 100644 --- a/web/src/views/ApplicationConfig/TestChat/index.tsx +++ b/web/src/views/ApplicationConfig/TestChat/index.tsx @@ -183,7 +183,7 @@ const TestChat: FC = ({ const handleSend = () => { if (loading || !application || !message || !message?.trim()) return - const files = toolbarRef.current?.getFiles() || [] + const files = (toolbarRef.current?.getFiles() || []).filter(item => !['uploading', 'error'].includes(item.status)) const variables = toolbarRef.current?.getVariables() || [] const { isCanSend, params } = buildVariableParams(variables) if (!isCanSend) return @@ -235,7 +235,7 @@ const TestChat: FC = ({ const handleWorkflowSend = () => { if (loading || !application || !message || !message?.trim()) return - const files = toolbarRef.current?.getFiles() || [] + const files = (toolbarRef.current?.getFiles() || []).filter(item => !['uploading', 'error'].includes(item.status)) const variables = toolbarRef.current?.getVariables() || [] const { isCanSend, params } = buildVariableParams(variables) if (!isCanSend) return diff --git a/web/src/views/ApplicationConfig/components/Chat.tsx b/web/src/views/ApplicationConfig/components/Chat.tsx index ee5bc53a..052ad872 100644 --- a/web/src/views/ApplicationConfig/components/Chat.tsx +++ b/web/src/views/ApplicationConfig/components/Chat.tsx @@ -191,7 +191,7 @@ const Chat: FC = ({ .then(() => { const message = msg if (!message?.trim()) return - const files = toolbarRef.current?.getFiles() || [] + const files = (toolbarRef.current?.getFiles() || []).filter(item => !['uploading', 'error'].includes(item.status)) // Validate required variables before sending let isCanSend = true const params: Record = {} @@ -352,7 +352,7 @@ const Chat: FC = ({ .then(() => { const message = msg if (!message || message.trim() === '') return - const files = toolbarRef.current?.getFiles() || [] + const files = (toolbarRef.current?.getFiles() || []).filter(item => !['uploading', 'error'].includes(item.status)) addUserMessage(message, files) setMessage(undefined) toolbarRef.current?.setFiles([]) diff --git a/web/src/views/Conversation/components/FileUpload.tsx b/web/src/views/Conversation/components/FileUpload.tsx index 48370fcc..23b62b14 100644 --- a/web/src/views/Conversation/components/FileUpload.tsx +++ b/web/src/views/Conversation/components/FileUpload.tsx @@ -2,7 +2,7 @@ * @Author: ZhaoYing * @Date: 2026-02-06 21:09:42 * @Last Modified by: ZhaoYing - * @Last Modified time: 2026-03-18 20:32:54 + * @Last Modified time: 2026-03-19 18:38:41 */ /** * File Upload Component @@ -23,7 +23,7 @@ import { useState, useEffect, forwardRef, useImperativeHandle, useMemo } from 'react'; import { Upload, Progress, App } from 'antd'; import type { UploadProps, UploadFile } from 'antd'; -import type { UploadProps as RcUploadProps } from 'antd/es/upload/interface'; +import type { UploadProps as RcUploadProps, RcFile, UploadFileStatus } from 'antd/es/upload/interface'; import { useTranslation } from 'react-i18next'; import { request } from '@/utils/request' @@ -221,17 +221,29 @@ const UploadFiles = forwardRef(({ */ const handleCustomRequest: RcUploadProps['customRequest'] = async (options) => { const { file, onSuccess, onError } = options; - - try { - const formData = new FormData(); - formData.append('file', file); - - const response = await request.uploadFile(action, formData, requestConfig); - - onSuccess?.({data: response}); - } catch (error) { - onError?.(error as Error); + if (typeof file === 'string') return; + const rcFile = file as RcFile; + const formData = new FormData(); + formData.append('file', rcFile); + const fileVo: UploadFile = { + uid: rcFile.uid, + name: rcFile.name, + status: 'uploading' as UploadFileStatus, + percent: 0, + type: rcFile.type, + originFileObj: rcFile, + thumbUrl: URL.createObjectURL(rcFile) } + onChange?.(fileVo) + request.uploadFile(action, formData, requestConfig) + .then(res => { + onSuccess?.({ data: res }); + }) + .catch((error) => { + onError?.(error as Error); + fileVo.status = 'error' + onChange?.(fileVo) + }) }; /** diff --git a/web/src/views/Conversation/index.tsx b/web/src/views/Conversation/index.tsx index 3a726746..e6047efd 100644 --- a/web/src/views/Conversation/index.tsx +++ b/web/src/views/Conversation/index.tsx @@ -200,7 +200,7 @@ const Conversation: FC = () => { /** Send message and handle streaming response */ const handleSend = () => { if (!token || !shareToken) return - const files = toolbarRef.current?.getFiles() || [] + const files = (toolbarRef.current?.getFiles() || []).filter(item => !['uploading', 'error'].includes(item.status)) const variables = toolbarRef.current?.getVariables() || [] let isCanSend = true const params: Record = {} diff --git a/web/src/views/Workflow/components/Chat/Chat.tsx b/web/src/views/Workflow/components/Chat/Chat.tsx index ad46d2dc..7d3e1313 100644 --- a/web/src/views/Workflow/components/Chat/Chat.tsx +++ b/web/src/views/Workflow/components/Chat/Chat.tsx @@ -2,7 +2,7 @@ * @Author: ZhaoYing * @Date: 2026-02-06 21:10:56 * @Last Modified by: ZhaoYing - * @Last Modified time: 2026-03-18 20:46:35 + * @Last Modified time: 2026-03-19 18:41:07 */ /** * Workflow Chat Component @@ -151,7 +151,7 @@ const Chat = forwardRef !['uploading', 'error'].includes(item.status)) setChatList(prev => [...prev, { role: 'user', content: message,