Merge pull request #628 from SuanmoSuanyangTechnology/fix/v0.2.8_zy

Fix/v0.2.8 zy
This commit is contained in:
yingzhao
2026-03-19 19:01:46 +08:00
committed by GitHub
9 changed files with 158 additions and 93 deletions

View File

@@ -2,7 +2,7 @@
* @Author: ZhaoYing * @Author: ZhaoYing
* @Date: 2026-02-03 14:00:06 * @Date: 2026-02-03 14:00:06
* @Last Modified by: ZhaoYing * @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 { request } from '@/utils/request'
import type { AxiosRequestConfig } from 'axios' 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; }) => { export const getExplicitMemoryDetails = (data: { end_user_id: string, memory_id: string; }) => {
return request.post(`/memory/explicit-memory/details`, data) return request.post(`/memory/explicit-memory/details`, data)
} }
export const getConversations = (end_user_id: string) => { export const getConversations = (end_user_id: string, page = 1, pagesize = 20) => {
return request.get(`/memory/work/${end_user_id}/conversations`) return request.get(`/memory/work/${end_user_id}/conversations`, { page, pagesize })
} }
export const getConversationMessages = (end_user_id: string, conversation_id: string) => { export const getConversationMessages = (end_user_id: string, conversation_id: string) => {
return request.get(`/memory/work/${end_user_id}/messages`, { conversation_id }) return request.get(`/memory/work/${end_user_id}/messages`, { conversation_id })

View File

@@ -2,10 +2,11 @@
* @Author: ZhaoYing * @Author: ZhaoYing
* @Date: 2025-12-10 16:46:14 * @Date: 2025-12-10 16:46:14
* @Last Modified by: ZhaoYing * @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 { 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 SendIcon from '@/assets/images/conversation/send.svg'
import SendDisabledIcon from '@/assets/images/conversation/sendDisabled.svg' import SendDisabledIcon from '@/assets/images/conversation/sendDisabled.svg'
@@ -69,6 +70,8 @@ const ChatInput: FC<ChatInputProps> = ({
onSend(values.message) onSend(values.message)
} }
console.log('previewFileList', previewFileList)
return ( return (
<div className={`rb:absolute rb:bottom-3 rb:left-0 rb:right-0 rb:w-full ${className}`}> <div className={`rb:absolute rb:bottom-3 rb:left-0 rb:right-0 rb:w-full ${className}`}>
<Flex vertical justify="space-between" className="rb:border rb:border-[#DFE4ED] rb:rounded-xl rb:min-h-30"> <Flex vertical justify="space-between" className="rb:border rb:border-[#DFE4ED] rb:rounded-xl rb:min-h-30">
@@ -76,62 +79,78 @@ const ChatInput: FC<ChatInputProps> = ({
{previewFileList.map((file) => { {previewFileList.map((file) => {
if (file.type.includes('image')) { if (file.type.includes('image')) {
return ( return (
<div key={file.url || file.uid} className="rb:inline-block rb:group rb:relative rb:rounded-lg"> <Spin key={`${file.url || file.uid}_${file.status}`} spinning={file.status === 'uploading'}>
<img src={file.url} alt={file.name} className="rb:size-12! rb:rounded-lg rb:object-cover rb:cursor-pointer" /> <div key={file.url || file.uid} className={clsx("rb:inline-block rb:group rb:relative rb:rounded-lg", {
<div 'rb:border rb:border-[#FF5D34]': file.status === 'error'
className="rb:hidden rb:group-hover:block rb:absolute rb:-right-1 rb:-top-1 rb:size-3.5 rb:cursor-pointer rb:bg-cover rb:bg-[url('@/assets/images/conversation/delete.svg')] rb:hover:bg-[url('@/assets/images/conversation/delete_hover.svg')]" })}>
onClick={() => handleDelete(file)} <img src={file.url} alt={file.name} className="rb:size-12! rb:rounded-lg rb:object-cover rb:cursor-pointer" />
></div> <div
</div> className="rb:hidden rb:group-hover:block rb:absolute rb:-right-1 rb:-top-1 rb:size-3.5 rb:cursor-pointer rb:bg-cover rb:bg-[url('@/assets/images/conversation/delete.svg')] rb:hover:bg-[url('@/assets/images/conversation/delete_hover.svg')]"
onClick={() => handleDelete(file)}
></div>
</div>
</Spin>
) )
} }
if (file.type.includes('video')) { if (file.type.includes('video')) {
return ( return (
<div key={file.url || file.uid} className="rb:w-45 rb:h-16 rb:inline-block rb:group rb:relative rb:rounded-lg"> <Spin key={`${file.url || file.uid}_${file.status}`} spinning={file.status === 'uploading'}>
<video src={file.url} controls className="rb:w-45 rb:h-16 rb:rounded-lg rb:object-cover rb:cursor-pointer" /> <div key={file.url || file.uid} className={clsx("rb:w-45 rb:h-16 rb:inline-block rb:group rb:relative rb:rounded-lg", {
<div 'rb:border rb:border-[#FF5D34]': file.status === 'error'
className="rb:hidden rb:group-hover:block rb:absolute rb:-right-1 rb:-top-1 rb:size-3.5 rb:cursor-pointer rb:bg-cover rb:bg-[url('@/assets/images/conversation/delete.svg')] rb:hover:bg-[url('@/assets/images/conversation/delete_hover.svg')]" })}>
onClick={() => handleDelete(file)} <video src={file.url} controls className="rb:w-45 rb:h-15.5 rb:rounded-lg rb:object-cover rb:cursor-pointer" />
></div> <div
</div> className="rb:hidden rb:group-hover:block rb:absolute rb:-right-1 rb:-top-1 rb:size-3.5 rb:cursor-pointer rb:bg-cover rb:bg-[url('@/assets/images/conversation/delete.svg')] rb:hover:bg-[url('@/assets/images/conversation/delete_hover.svg')]"
onClick={() => handleDelete(file)}
></div>
</div>
</Spin>
) )
} }
if (file.type.includes('audio')) { if (file.type.includes('audio')) {
return ( return (
<div key={file.url || file.uid} className="rb:w-45 rb:h-16 rb:inline-flex rb:items-center rb:group rb:relative rb:rounded-lg rb:bg-[#F0F3F8] rb:py-2 rb:px-2.5 rb:gap-2"> <Spin key={`${file.url || file.uid}_${file.status}`} spinning={file.status === 'uploading'}>
<audio src={file.url} controls className="rb:w-45 rb:h-16" /> <div key={file.url || file.uid} className={clsx("rb:w-45 rb:h-16 rb:inline-flex rb:items-center rb:group rb:relative rb:rounded-lg rb:bg-[#F0F3F8] rb:py-2 rb:px-2.5 rb:gap-2", {
'rb:border rb:border-[#FF5D34]': file.status === 'error'
})}>
<audio src={file.url} controls className="rb:w-45 rb:h-15.5" />
<div
className="rb:hidden rb:group-hover:block rb:absolute rb:-right-1 rb:-top-1 rb:size-3.5 rb:cursor-pointer rb:bg-cover rb:bg-[url('@/assets/images/conversation/delete.svg')] rb:hover:bg-[url('@/assets/images/conversation/delete_hover.svg')]"
onClick={() => handleDelete(file)}
></div>
</div>
</Spin>
)
}
return (
<Spin key={`${file.url || file.uid}_${file.status}`} spinning={file.status === 'uploading'}>
<div key={file.url || file.uid} className={clsx("rb:w-45 rb:text-[12px] rb:gap-2.5 rb:flex rb:items-center rb:group rb:relative rb:rounded-lg rb:bg-[#F0F3F8] rb:py-2 rb:px-2.5", {
'rb:border rb:border-[#FF5D34]': file.status === 'error'
})}>
{file.type.includes('pdf')
? <div
className="rb:size-5 rb:cursor-pointer rb:bg-cover rb:bg-[url('@/assets/images/conversation/pdf_disabled.svg')] rb:hover:bg-[url('@/assets/images/conversation/pdf.svg')]"
></div>
: (file.type.includes('excel') || file.type.includes('spreadsheetml.sheet') || file.type.includes('csv'))
? <div
className="rb:size-5 rb:cursor-pointer rb:bg-cover rb:bg-[url('@/assets/images/conversation/excel_disabled.svg')] rb:hover:bg-[url('@/assets/images/conversation/excel.svg')]"
></div>
: (file.type.includes('doc') || file.type.includes('docx') || file.type.includes('word') || file.type.includes('wordprocessingml.document'))
? <div
className="rb:size-5 rb:cursor-pointer rb:bg-cover rb:bg-[url('@/assets/images/conversation/word_disabled.svg')] rb:hover:bg-[url('@/assets/images/conversation/word.svg')]"
></div>
: null
}
<div className="rb:flex-1 rb:w-32.5">
<div className="rb:leading-4 rb:text-ellipsis rb:overflow-hidden rb:whitespace-nowrap">{file.name}</div>
<div className="rb:leading-3.5 rb:mt-0.5 rb:text-[#5B6167] rb:text-ellipsis rb:overflow-hidden rb:whitespace-nowrap">{file.type} · {file.size}</div>
</div>
<div <div
className="rb:hidden rb:group-hover:block rb:absolute rb:-right-1 rb:-top-1 rb:size-3.5 rb:cursor-pointer rb:bg-cover rb:bg-[url('@/assets/images/conversation/delete.svg')] rb:hover:bg-[url('@/assets/images/conversation/delete_hover.svg')]" className="rb:hidden rb:group-hover:block rb:absolute rb:-right-1 rb:-top-1 rb:size-3.5 rb:cursor-pointer rb:bg-cover rb:bg-[url('@/assets/images/conversation/delete.svg')] rb:hover:bg-[url('@/assets/images/conversation/delete_hover.svg')]"
onClick={() => handleDelete(file)} onClick={() => handleDelete(file)}
></div> ></div>
</div> </div>
) </Spin>
}
return (
<div key={file.url || file.uid} className="rb:w-45 rb:text-[12px] rb:gap-2.5 rb:flex rb:items-center rb:group rb:relative rb:rounded-lg rb:bg-[#F0F3F8] rb:py-2 rb:px-2.5">
{file.type.includes('pdf')
? <div
className="rb:size-5 rb:cursor-pointer rb:bg-cover rb:bg-[url('@/assets/images/conversation/pdf_disabled.svg')] rb:hover:bg-[url('@/assets/images/conversation/pdf.svg')]"
></div>
: (file.type.includes('excel') || file.type.includes('spreadsheetml.sheet') || file.type.includes('csv'))
? <div
className="rb:size-5 rb:cursor-pointer rb:bg-cover rb:bg-[url('@/assets/images/conversation/excel_disabled.svg')] rb:hover:bg-[url('@/assets/images/conversation/excel.svg')]"
></div>
: (file.type.includes('doc') || file.type.includes('docx') || file.type.includes('word') || file.type.includes('wordprocessingml.document'))
? <div
className="rb:size-5 rb:cursor-pointer rb:bg-cover rb:bg-[url('@/assets/images/conversation/word_disabled.svg')] rb:hover:bg-[url('@/assets/images/conversation/word.svg')]"
></div>
: null
}
<div className="rb:flex-1 rb:w-32.5">
<div className="rb:leading-4 rb:text-ellipsis rb:overflow-hidden rb:whitespace-nowrap">{file.name}</div>
<div className="rb:leading-3.5 rb:mt-0.5 rb:text-[#5B6167] rb:text-ellipsis rb:overflow-hidden rb:whitespace-nowrap">{file.type} · {file.size}</div>
</div>
<div
className="rb:hidden rb:group-hover:block rb:absolute rb:-right-1 rb:-top-1 rb:size-3.5 rb:cursor-pointer rb:bg-cover rb:bg-[url('@/assets/images/conversation/delete.svg')] rb:hover:bg-[url('@/assets/images/conversation/delete_hover.svg')]"
onClick={() => handleDelete(file)}
></div>
</div>
) )
})} })}
</Flex></div>} </Flex></div>}

View File

@@ -2,7 +2,7 @@
* @Author: ZhaoYing * @Author: ZhaoYing
* @Date: 2026-03-17 14:22:25 * @Date: 2026-03-17 14:22:25
* @Last Modified by: ZhaoYing * @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 // Toolbar component for chat input area, supporting file upload, audio recording, and variable configuration
import { useRef, forwardRef, useImperativeHandle, type ReactNode, useEffect } from 'react' import { useRef, forwardRef, useImperativeHandle, type ReactNode, useEffect } from 'react'
@@ -85,10 +85,18 @@ const ChatToolbar = forwardRef<ChatToolbarRef, ChatToolbarProps>(({
// Append newly uploaded file to the file list when upload is complete // Append newly uploaded file to the file list when upload is complete
const fileChange = (file?: any) => { const fileChange = (file?: any) => {
if (file?.status !== 'done') return console.log('file', file)
const files = [...(queryValues?.files || []), file] const lastFiles = form.getFieldValue('files') || [];
form.setFieldValue('files', files) const index = lastFiles.findIndex((item: any) => item.uid === file.uid)
onFilesChange?.(files) 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 // Append recorded audio file to the file list and notify parent

View File

@@ -183,7 +183,7 @@ const TestChat: FC<TestChatProps> = ({
const handleSend = () => { const handleSend = () => {
if (loading || !application || !message || !message?.trim()) return 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 variables = toolbarRef.current?.getVariables() || []
const { isCanSend, params } = buildVariableParams(variables) const { isCanSend, params } = buildVariableParams(variables)
if (!isCanSend) return if (!isCanSend) return
@@ -235,7 +235,7 @@ const TestChat: FC<TestChatProps> = ({
const handleWorkflowSend = () => { const handleWorkflowSend = () => {
if (loading || !application || !message || !message?.trim()) return 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 variables = toolbarRef.current?.getVariables() || []
const { isCanSend, params } = buildVariableParams(variables) const { isCanSend, params } = buildVariableParams(variables)
if (!isCanSend) return if (!isCanSend) return

View File

@@ -191,7 +191,7 @@ const Chat: FC<ChatProps> = ({
.then(() => { .then(() => {
const message = msg const message = msg
if (!message?.trim()) return 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 // Validate required variables before sending
let isCanSend = true let isCanSend = true
const params: Record<string, any> = {} const params: Record<string, any> = {}
@@ -352,7 +352,7 @@ const Chat: FC<ChatProps> = ({
.then(() => { .then(() => {
const message = msg const message = msg
if (!message || message.trim() === '') return 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) addUserMessage(message, files)
setMessage(undefined) setMessage(undefined)
toolbarRef.current?.setFiles([]) toolbarRef.current?.setFiles([])

View File

@@ -2,7 +2,7 @@
* @Author: ZhaoYing * @Author: ZhaoYing
* @Date: 2026-02-06 21:09:42 * @Date: 2026-02-06 21:09:42
* @Last Modified by: ZhaoYing * @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 * File Upload Component
@@ -23,7 +23,7 @@
import { useState, useEffect, forwardRef, useImperativeHandle, useMemo } from 'react'; import { useState, useEffect, forwardRef, useImperativeHandle, useMemo } from 'react';
import { Upload, Progress, App } from 'antd'; import { Upload, Progress, App } from 'antd';
import type { UploadProps, UploadFile } 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 { useTranslation } from 'react-i18next';
import { request } from '@/utils/request' import { request } from '@/utils/request'
@@ -221,17 +221,29 @@ const UploadFiles = forwardRef<UploadFilesRef, UploadFilesProps>(({
*/ */
const handleCustomRequest: RcUploadProps['customRequest'] = async (options) => { const handleCustomRequest: RcUploadProps['customRequest'] = async (options) => {
const { file, onSuccess, onError } = options; const { file, onSuccess, onError } = options;
if (typeof file === 'string') return;
try { const rcFile = file as RcFile;
const formData = new FormData(); const formData = new FormData();
formData.append('file', file); formData.append('file', rcFile);
const fileVo: UploadFile = {
const response = await request.uploadFile(action, formData, requestConfig); uid: rcFile.uid,
name: rcFile.name,
onSuccess?.({data: response}); status: 'uploading' as UploadFileStatus,
} catch (error) { percent: 0,
onError?.(error as Error); 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)
})
}; };
/** /**

View File

@@ -200,7 +200,7 @@ const Conversation: FC = () => {
/** Send message and handle streaming response */ /** Send message and handle streaming response */
const handleSend = () => { const handleSend = () => {
if (!token || !shareToken) return 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() || [] const variables = toolbarRef.current?.getVariables() || []
let isCanSend = true let isCanSend = true
const params: Record<string, any> = {} const params: Record<string, any> = {}

View File

@@ -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 clsx from 'clsx'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import { useParams } from 'react-router-dom' import { useParams } from 'react-router-dom'
import { Row, Col, Skeleton, Button, Divider, Tooltip } from 'antd' import { Row, Col, Skeleton, Button, Divider, Tooltip } from 'antd'
import InfiniteScroll from 'react-infinite-scroll-component'
import RbCard from '@/components/RbCard/Card' import RbCard from '@/components/RbCard/Card'
import { import {
getConversations, getConversations,
@@ -34,6 +36,8 @@ const WorkingDetail: FC = () => {
const { id } = useParams() const { id } = useParams()
const [loading, setLoading] = useState<boolean>(false) const [loading, setLoading] = useState<boolean>(false)
const [data, setData] = useState<Conversation[]>([]) const [data, setData] = useState<Conversation[]>([])
const [hasMore, setHasMore] = useState<boolean>(true)
const pageRef = useRef<number>(1)
const [messagesLoading, setMessagesLoading] = useState<boolean>(false) const [messagesLoading, setMessagesLoading] = useState<boolean>(false)
const [messages, setMessages] = useState<ChatItem[]>([]) const [messages, setMessages] = useState<ChatItem[]>([])
const [detailLoading, setDetailLoading] = useState<boolean>(false) const [detailLoading, setDetailLoading] = useState<boolean>(false)
@@ -51,16 +55,30 @@ const WorkingDetail: FC = () => {
setSelected(null) setSelected(null)
setDetail(null) setDetail(null)
setData([]) setData([])
getConversations(id).then((res) => { setHasMore(true)
const response = res as Conversation[] pageRef.current = 1
setData(response) getConversations(id, 1).then((res) => {
setSelected(response[0] || null) const response = res as { items: Conversation[], page: { hasnext: boolean } }
setData(response.items)
setSelected(response.items[0] || null)
setHasMore(response.page.hasnext)
}) })
.finally(() => { .finally(() => {
setLoading(false) 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(() => { useEffect(() => {
if (!id || !selected || !selected.id) return if (!id || !selected || !selected.id) return
getDetail(selected.id) getDetail(selected.id)
@@ -103,22 +121,30 @@ const WorkingDetail: FC = () => {
: data.length === 0 : data.length === 0
? <Empty /> ? <Empty />
:( :(
<Row gutter={16} className="rb:h-full"> <Row gutter={16}>
<Col span={5}> <Col span={5}>
<div className="rb:h-full! rb:border-r rb:border-[#EAECEE] rb:py-3 rb:px-4"> <div id="conversation-list" className="rb:h-[calc(100vh-76px)]! rb:border-r rb:border-[#EAECEE] rb:py-3 rb:px-4 rb:overflow-y-auto">
{data.map(item => ( <InfiniteScroll
<div key={item.id} className="rb:mb-3"> dataLength={data.length}
<Tooltip title={item.title}> next={loadMore}
<div className={clsx("rb:p-[8px_13px] rb:rounded-lg rb:leading-5 rb:cursor-pointer rb:hover:bg-[#F0F3F8] rb:text-ellipsis rb:overflow-hidden rb:whitespace-nowrap", { hasMore={hasMore}
'rb:bg-[#FFFFFF] rb:shadow-[0px_2px_4px_0px_rgba(0,0,0,0.15)] rb:font-medium rb:hover:bg-[#FFFFFF]!': item.id === selected?.id, loader={null}
})} scrollableTarget="conversation-list"
onClick={() => setSelected(item)} >
> {data.map(item => (
{item.title} <div key={item.id} className="rb:mb-3">
</div> <Tooltip title={item.title}>
</Tooltip> <div className={clsx("rb:p-[8px_13px] rb:rounded-lg rb:leading-5 rb:cursor-pointer rb:hover:bg-[#F0F3F8] rb:text-ellipsis rb:overflow-hidden rb:whitespace-nowrap", {
</div> 'rb:bg-[#FFFFFF] rb:shadow-[0px_2px_4px_0px_rgba(0,0,0,0.15)] rb:font-medium rb:hover:bg-[#FFFFFF]!': item.id === selected?.id,
))} })}
onClick={() => setSelected(item)}
>
{item.title}
</div>
</Tooltip>
</div>
))}
</InfiniteScroll>
</div> </div>
</Col> </Col>
{selected && <> {selected && <>

View File

@@ -2,7 +2,7 @@
* @Author: ZhaoYing * @Author: ZhaoYing
* @Date: 2026-02-06 21:10:56 * @Date: 2026-02-06 21:10:56
* @Last Modified by: ZhaoYing * @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 * Workflow Chat Component
@@ -151,7 +151,7 @@ const Chat = forwardRef<ChatRef, { appId: string; graphRef: GraphRef; data: Work
setLoading(true) setLoading(true)
const message = msg const message = msg
const files = toolbarRef.current?.getFiles() || [] const files = (toolbarRef.current?.getFiles() || []).filter(item => !['uploading', 'error'].includes(item.status))
setChatList(prev => [...prev, { setChatList(prev => [...prev, {
role: 'user', role: 'user',
content: message, content: message,