1
This commit is contained in:
@@ -9,6 +9,8 @@ import { useMessageStore } from "./messageStore";
|
||||
import GuideQuestions from "./GuideQuestions";
|
||||
import { ClearIcon } from "@/components/bs-icons/clear";
|
||||
import duihua_send from "../../../assets/chat/duihua-send.png";
|
||||
import { Button } from "@/components/bs-ui/button";
|
||||
import { StopCircle } from "lucide-react";
|
||||
|
||||
export default function ChatInput({ clear, form, questions, inputForm, wsUrl, onBeforSend }) {
|
||||
const { toast } = useToast()
|
||||
@@ -22,7 +24,8 @@ export default function ChatInput({ clear, form, questions, inputForm, wsUrl, on
|
||||
const { messages, chatId, createSendMsg, createWsMsg, updateCurrentMessage, destory, setShowGuideQuestion } = useMessageStore()
|
||||
const currentChatIdRef = useRef(null)
|
||||
const inputRef = useRef(null)
|
||||
|
||||
// 停止状态
|
||||
const [isStop, setIsStop] = useState(true)
|
||||
/**
|
||||
* 记录会话切换状态,等待消息加载完成时,控制表单在新会话自动展开
|
||||
*/
|
||||
@@ -88,6 +91,7 @@ export default function ChatInput({ clear, form, questions, inputForm, wsUrl, on
|
||||
// 锁定 input
|
||||
setInputLock({ locked: true, reason: '' })
|
||||
await createWebSocket(chatId)
|
||||
console.log(wsMsg,inputKey);
|
||||
sendWsMsg(wsMsg)
|
||||
|
||||
// 滚动聊天到底
|
||||
@@ -96,7 +100,13 @@ export default function ChatInput({ clear, form, questions, inputForm, wsUrl, on
|
||||
messageDom.scrollTop = messageDom.scrollHeight;
|
||||
}
|
||||
}
|
||||
|
||||
const stop = async () => {
|
||||
const [wsMsg] = onBeforSend('', '')
|
||||
wsMsg.action = "stop"
|
||||
sendWsMsg(wsMsg)
|
||||
// console.log(wsMsg);
|
||||
// sendWsMsg(wsMsg)
|
||||
}
|
||||
const sendWsMsg = async (msg) => {
|
||||
try {
|
||||
wsRef.current.send(JSON.stringify(msg))
|
||||
@@ -158,6 +168,7 @@ export default function ChatInput({ clear, form, questions, inputForm, wsUrl, on
|
||||
ws.onerror = (ev) => {
|
||||
wsRef.current = null
|
||||
console.error('链接异常error', ev);
|
||||
setIsStop(true)
|
||||
toast({
|
||||
title: `${t('chat.networkError')}:`,
|
||||
variant: 'error',
|
||||
@@ -178,8 +189,11 @@ export default function ChatInput({ clear, form, questions, inputForm, wsUrl, on
|
||||
|
||||
// 接受 ws 消息
|
||||
const handleWsMessage = (data) => {
|
||||
console.log(data)
|
||||
if (Array.isArray(data) && data.length) return
|
||||
if (data.type === 'start') {
|
||||
if (data.type === "begin") {
|
||||
setIsStop(false)
|
||||
}else if (data.type === 'start') {
|
||||
createWsMsg(data)
|
||||
} else if (data.type === 'stream') {
|
||||
updateCurrentMessage({
|
||||
@@ -197,6 +211,7 @@ export default function ChatInput({ clear, form, questions, inputForm, wsUrl, on
|
||||
liked: 0
|
||||
}, data.type === 'end_cover')
|
||||
} else if (data.type === "close") {
|
||||
setIsStop(true)
|
||||
setInputLock({ locked: false, reason: '' })
|
||||
}
|
||||
|
||||
@@ -296,6 +311,9 @@ export default function ChatInput({ clear, form, questions, inputForm, wsUrl, on
|
||||
}
|
||||
}}
|
||||
></textarea>
|
||||
<div className=" absolute w-full flex justify-center bottom-32 pointer-events-none">
|
||||
<Button className="rounded-full pointer-events-auto" variant="outline" disabled={isStop} onClick={() => { setIsStop(true); stop(); }}><StopCircle className="mr-2" />Stop</Button>
|
||||
</div>
|
||||
{/* <p className="w-[100%] text-center text-[#666666]">内容由AI生成,仅供参考</p> */}
|
||||
</div>
|
||||
<p className="text-center text-sm pt-2 pb-4 text-gray-400">{appConfig.dialogTips}</p>
|
||||
|
||||
@@ -3,6 +3,9 @@ import { WordIcon } from "@/components/bs-icons/office";
|
||||
import { checkSassUrl } from "@/pages/ChatAppPage/components/FileView";
|
||||
import { downloadFile } from "@/util/utils";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import npcIcon from "../../../assets/npc/npcIcon.png";
|
||||
import nengliIcon from "../../../assets/npc/nengliIcon.png";
|
||||
import { TitleIconBg } from "../cardComponent";
|
||||
|
||||
// 颜色列表
|
||||
const colorList = [
|
||||
@@ -19,7 +22,7 @@ const colorList = [
|
||||
"#95A5A6"
|
||||
]
|
||||
|
||||
export default function FileBs({ data }) {
|
||||
export default function FileBs({ data,flow_type }) {
|
||||
const { t } = useTranslation()
|
||||
const avatarColor = colorList[(data.sender?.split('').reduce((num, s) => num + s.charCodeAt(), 0) || 0) % colorList.length]
|
||||
|
||||
@@ -33,15 +36,16 @@ export default function FileBs({ data }) {
|
||||
<div className="w-fit min-h-8 rounded-2xl px-6 py-4 max-w-[90%]">
|
||||
{data.sender && <p className="text-primary text-xs mb-2" style={{ background: avatarColor }}>{data.sender}</p>}
|
||||
<div className="flex gap-2 ">
|
||||
<div className="w-6 h-6 min-w-6 flex justify-center items-center rounded-full" style={{ background: avatarColor }} ><AvatarIcon /></div>
|
||||
{data.flow_id && <TitleIconBg className="w-[40px] h-[40px]" img={data.avatar_img} id={data.avatar_color ? data.avatar_color : data.flow_id} ><img src={data.avatar_img ? data.avatar_img : (flow_type == "assistant" ? npcIcon : nengliIcon)} alt="" /></TitleIconBg>}
|
||||
{/* <div className="w-6 h-6 min-w-6 flex justify-center items-center rounded-full" style={{ background: avatarColor }} ><AvatarIcon /></div> */}
|
||||
<div
|
||||
className="flex gap-2 w-52 border border-gray-200 shadow-sm bg-gray-50 px-4 py-2 rounded-sm cursor-pointer"
|
||||
className="flex gap-2 w-52 shadow-sm bg-[#1a1a1a] px-4 py-2 rounded-sm cursor-pointer"
|
||||
onClick={() => handleDownloadFile(data.files[0])}
|
||||
>
|
||||
<div className="flex items-center"><WordIcon /></div>
|
||||
<div className="flex items-center text-[#43AFD2]"><WordIcon /></div>
|
||||
<div>
|
||||
<h1 className="text-sm font-bold">{data.files[0]?.file_name}</h1>
|
||||
<p className="text-xs text-gray-400 mt-1">点击下载</p>
|
||||
<h1 className="text-sm font-bold text-[#43AFD2]">{data.files[0]?.file_name}</h1>
|
||||
<p className="text-xs text-[#666] mt-1">点击下载</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -38,7 +38,7 @@ const colorList = [
|
||||
"#95A5A6"
|
||||
]
|
||||
|
||||
export default function MessageBs({ data, onUnlike = () => { }, onSource }: { data: ChatMessageType, onUnlike?: any, onSource?: any }) {
|
||||
export default function MessageBs({ data, onUnlike = () => { }, flow_type, onSource }: { data: ChatMessageType, flow_type: string, onUnlike?: any, onSource?: any }) {
|
||||
const avatarColor = colorList[
|
||||
(data.sender?.split('').reduce((num, s) => num + s.charCodeAt(), 0) || 0) % colorList.length
|
||||
]
|
||||
@@ -87,7 +87,6 @@ export default function MessageBs({ data, onUnlike = () => { }, onSource }: { da
|
||||
}
|
||||
|
||||
const chatId = useMessageStore(state => state.chatId)
|
||||
console.log(data)
|
||||
return <div className="flex w-full py-1">
|
||||
<div className="w-fit max-w-[90%]">
|
||||
{data.sender && <p className="text-gray-600 text-xs mb-2">{data.sender}</p>}
|
||||
@@ -95,15 +94,15 @@ export default function MessageBs({ data, onUnlike = () => { }, onSource }: { da
|
||||
{/* {(data.flow_id == "06b1d374-ba97-46e6-8782-c56dec8dcc17" || data.flow_id == "ed8e21f6-9757-43d0-b076-8c6e81bb0580") && <img src={robot2} className="w-[50px]" alt=""/>}
|
||||
{data.flow_id == "ca214b41-2b73-4585-b172-bf1e546cf6ec" && <img src={robot3} className="w-[50px]" alt=""/>}
|
||||
{(data.flow_id != "06b1d374-ba97-46e6-8782-c56dec8dcc17" && data.flow_id != "ed8e21f6-9757-43d0-b076-8c6e81bb0580" && data.flow_id != "ca214b41-2b73-4585-b172-bf1e546cf6ec") && <img src={robot} className="w-[50px]" alt=""/>} */}
|
||||
{data.flow_id && <TitleIconBg className="w-[40px] h-[40px]" img={data.avatar_img} id={data.avatar_color ? data.avatar_color : data.flow_id} ><img src={data.avatar_img ? data.avatar_img : (data.flow_type == "assistant" ? npcIcon : nengliIcon)} alt="" /></TitleIconBg>}
|
||||
{data.flow_id && <TitleIconBg className="w-[40px] h-[40px]" img={data.avatar_img} id={data.avatar_color ? data.avatar_color : data.flow_id} ><img src={data.avatar_img ? data.avatar_img : (flow_type == "assistant" ? npcIcon : nengliIcon)} alt="" /></TitleIconBg>}
|
||||
|
||||
<div ref={messageRef} className={`min-h-8 min-w-[110px] max-w-[50vw] ml-[10px] ${data.id && data.end && 'pb-8'}`}>
|
||||
<div ref={messageRef} className={`min-h-8 min-w-[110px] max-w-[90%] ml-[10px] ${data.id && data.end && 'pb-8'}`}>
|
||||
<div className="chat-start-zk relative">
|
||||
{data.message.toString() ? mkdown : <span className="loading loading-ring loading-md"></span>}
|
||||
{/* @user */}
|
||||
{data.receiver && <p className="text-blue-500 text-sm">@ {data.receiver.user_name}</p>}
|
||||
{/* 光标 */}
|
||||
{data.message.toString() && !data.end && <div className="animate-cursor absolute w-2 h-5 ml-1 bg-gray-600" style={{ left: cursor.x, top: cursor.y }}></div>}
|
||||
{/* {data.message.toString() && !data.end && <div className="animate-cursor absolute w-2 h-5 ml-1 bg-gray-600" style={{ left: cursor.x, top: cursor.y }}></div>} */}
|
||||
</div>
|
||||
|
||||
{/* 赞 踩 */}
|
||||
|
||||
@@ -10,7 +10,7 @@ import RunLog from "./RunLog";
|
||||
import Separator from "./Separator";
|
||||
import { useMessageStore } from "./messageStore";
|
||||
|
||||
export default function MessagePanne({ useName, guideWord, loadMore }) {
|
||||
export default function MessagePanne({ useName, guideWord, loadMore, flow_type }) {
|
||||
const { t } = useTranslation()
|
||||
const { chatId, messages } = useMessageStore()
|
||||
|
||||
@@ -83,6 +83,7 @@ export default function MessagePanne({ useName, guideWord, loadMore }) {
|
||||
return <MessageBs
|
||||
key={msg.id}
|
||||
data={msg}
|
||||
flow_type={flow_type}
|
||||
onUnlike={(chatId) => { thumbRef.current?.openModal(chatId) }}
|
||||
onSource={(data) => { sourceRef.current?.openModal(data) }}
|
||||
/>;
|
||||
@@ -91,7 +92,7 @@ export default function MessagePanne({ useName, guideWord, loadMore }) {
|
||||
case 'separator':
|
||||
return <Separator key={msg.id} text={msg.message || t('chat.roundOver')} />;
|
||||
case 'file':
|
||||
return <FileBs key={msg.id} data={msg} />;
|
||||
return <FileBs key={msg.id} data={msg} flow_type={flow_type}/>;
|
||||
case 'runLog':
|
||||
return <RunLog key={msg.id} data={msg} />;
|
||||
default:
|
||||
|
||||
@@ -34,13 +34,12 @@ export default function MessageUser({ useName, data }: { data: ChatMessageType }
|
||||
{/* {chat.category === 'loading' && <span className="loading loading-spinner loading-xs mr-4 align-middle"></span>} */}
|
||||
{msg}
|
||||
</div>
|
||||
|
||||
<div className='chat-end-btn'>
|
||||
{!Array.isArray(data.message.data) && <div className='chat-end-btn'>
|
||||
{!running && <img src={btnEdit} onClick={() => handleResend(false)} className="w-[28px] cursor-pointer" alt=""/>}
|
||||
{!running && <img src={btnReSend} onClick={() => handleResend(true)} className="w-[28px] cursor-pointer" alt=""/>}
|
||||
{/* <img src={btnDel} className="w-[28px] cursor-pointer" alt=""/> */}
|
||||
{/* {!showSearch && <Search size={18} className="cursor-pointer hover:text-blue-600 text-blue-400" onClick={() => onSearch(chat.message[chat.chatKey])}></Search>} */}
|
||||
</div>
|
||||
</div>}
|
||||
</div>
|
||||
{/* <p className="mr-[20px] text-[14px]">{userName}</p> */}
|
||||
<img src={robotU} className="w-[50px]" alt=""/>
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
import ChatInput from "./ChatInput";
|
||||
import MessagePanne from "./MessagePanne";
|
||||
|
||||
export default function ChatComponent({ clear = false, questions = [], form = false, useName, inputForm = null, guideWord, wsUrl, onBeforSend, loadMore = () => { } }) {
|
||||
|
||||
export default function ChatComponent({ clear = false, questions = [], form = false, useName, inputForm = null, guideWord, wsUrl, onBeforSend, type, loadMore = () => { } }) {
|
||||
return <div className="relative h-full">
|
||||
<MessagePanne useName={useName} guideWord={guideWord} loadMore={loadMore}></MessagePanne>
|
||||
<MessagePanne useName={useName} guideWord={guideWord} loadMore={loadMore} flow_type={type}></MessagePanne>
|
||||
<ChatInput clear={clear} questions={questions} form={form} wsUrl={wsUrl} inputForm={inputForm} onBeforSend={onBeforSend} ></ChatInput>
|
||||
</div>
|
||||
};
|
||||
|
||||
@@ -82,7 +82,6 @@ export const useMessageStore = create<State & Actions>((set, get) => ({
|
||||
set({ showGuideQuestion: bln })
|
||||
},
|
||||
async loadHistoryMsg(flowid, chatId, flow_type) {
|
||||
console.log(111)
|
||||
const res = await getChatHistory(flowid, chatId, 30, 0, flow_type)
|
||||
const msgs = handleHistoryMsg(res)
|
||||
currentChatId = chatId
|
||||
@@ -155,6 +154,7 @@ export const useMessageStore = create<State & Actions>((set, get) => ({
|
||||
console.log('change updateCurrentMessage');
|
||||
const messages = get().messages
|
||||
const isRunLog = runLogsTypes.includes(wsdata.category);
|
||||
console.log(wsdata,isRunLog,messages)
|
||||
// run log类型存在嵌套情况,使用 extra 匹配 currentMessage; 否则取最近
|
||||
const currentMessageIndex = isRunLog ?
|
||||
messages.findLastIndex((msg) => msg.extra === wsdata.extra)
|
||||
@@ -173,11 +173,13 @@ export const useMessageStore = create<State & Actions>((set, get) => ({
|
||||
}
|
||||
|
||||
messages[currentMessageIndex] = newCurrentMessage
|
||||
console.log(newCurrentMessage)
|
||||
// 会话特殊处理,兼容后端的缺陷
|
||||
if (!isRunLog) {
|
||||
// start - end 之间没有内容删除load
|
||||
if (newCurrentMessage.end && !(newCurrentMessage.files.length || newCurrentMessage.thought || newCurrentMessage.message)) {
|
||||
messages.pop()
|
||||
// messages.pop()
|
||||
console.log("删了")
|
||||
}
|
||||
// 无 messageid 删除
|
||||
// if (newCurrentMessage.end && !newCurrentMessage.id) {
|
||||
|
||||
@@ -62,10 +62,7 @@ export default function SkillChatSheet({ children, onSelect }) {
|
||||
<span>
|
||||
<span>
|
||||
<div>
|
||||
{/* <img src={robot} className="w-[160px]" alt=""/> */}
|
||||
{(item.id == "06b1d374-ba97-46e6-8782-c56dec8dcc17" || item.id == "ed8e21f6-9757-43d0-b076-8c6e81bb0580") && <img src={robot2} className="w-[160px]" alt=""/>}
|
||||
{item.id == "ca214b41-2b73-4585-b172-bf1e546cf6ec" && <img src={robot3} className="w-[160px]" alt=""/>}
|
||||
{(item.id != "06b1d374-ba97-46e6-8782-c56dec8dcc17" && item.id != "ed8e21f6-9757-43d0-b076-8c6e81bb0580" && item.id != "ca214b41-2b73-4585-b172-bf1e546cf6ec") && <img src={robot} className="w-[160px]" alt=""/>}
|
||||
<TitleIconBg className="w-[160px] h-[160px] min-w-[160px]" img={item.avatar_img} id={item.avatar_color ? item.avatar_color : item.id} ><img src={item.avatar_img ? item.avatar_img : (item.flow_type == "assistant" ? npcIcon : nengliIcon)} alt="" /></TitleIconBg>
|
||||
</div>
|
||||
</span>
|
||||
</span>
|
||||
@@ -100,12 +97,12 @@ export default function SkillChatSheet({ children, onSelect }) {
|
||||
<div className="xinDuiHua-boxR">
|
||||
{/* <img src={borderR} className="w-[30px] h-[100%]" alt="" /> */}
|
||||
</div>
|
||||
<div className="w-fit p-6">
|
||||
<div className="w-[280px] p-6">
|
||||
<SheetTitle>选择对话</SheetTitle>
|
||||
<SheetDescription className="text-[#999999]">选择一个您想使用的上线NPC或能力</SheetDescription>
|
||||
<SearchInput value={keyword} placeholder="搜索" className="my-6" onChange={(e) => setKeyword(e.target.value)} />
|
||||
</div>
|
||||
<div className="min-w-[696px] overflow-y-auto bg-[#000000] scrollbar-hide skillSheet">
|
||||
<div className="w-[690px] overflow-y-auto bg-[#000000] scrollbar-hide skillSheet">
|
||||
{/* {
|
||||
options.length ? options.map((flow, i) => (
|
||||
<CardComponent key={i}
|
||||
|
||||
@@ -113,7 +113,7 @@ export default function SkillSheet({ select, children, onSelect }) {
|
||||
<div className="xinDuiHua-boxR">
|
||||
{/* <img src={borderR} className="w-[30px] h-[100%]" alt="" /> */}
|
||||
</div>
|
||||
<div className="p-6">
|
||||
<div className="w-[280px] p-6">
|
||||
<SheetTitle>{t("build.addSkill")}</SheetTitle>
|
||||
<SearchInput
|
||||
value={keyword}
|
||||
@@ -126,7 +126,7 @@ export default function SkillSheet({ select, children, onSelect }) {
|
||||
</Button> */}
|
||||
<div className="w-[244px] h-[34px] flex items-center justify-center bg-[#FFD025] text-[#000] cursor-pointer" style={{borderRadius:"34px"}} onClick={toCreateFlow}>{t("build.createSkill")}</div>
|
||||
</div>
|
||||
<div className="min-w-[696px] overflow-y-auto bg-[#000000] scrollbar-hide skillSheet">
|
||||
<div className="w-[690px] overflow-y-auto bg-[#000000] scrollbar-hide skillSheet">
|
||||
{onlineFlows[0] ? (
|
||||
// onlineFlows.map((flow, i) => (
|
||||
// <CardComponent
|
||||
|
||||
@@ -44,7 +44,7 @@ export default function ToolsSheet({ select, onSelect, children }) {
|
||||
<div className="xinDuiHua-boxR">
|
||||
{/* <img src={borderR} className="w-[30px] h-[100%]" alt="" /> */}
|
||||
</div>
|
||||
<div className="w-fit p-6">
|
||||
<div className="w-[280px] p-6">
|
||||
<SheetTitle>{t('build.addTool')}</SheetTitle>
|
||||
<div className="relative mt-[14px]">
|
||||
<img src={sousuo} className="absolute w-[14px] left-[14px] top-[10px]" alt="" />
|
||||
@@ -83,7 +83,7 @@ export default function ToolsSheet({ select, onSelect, children }) {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex-1 bg-[#121212] p-5 pt-12 h-full overflow-auto scrollbar-hide">
|
||||
<div className="w-[690px] flex-1 bg-[#121212] p-5 pt-12 h-full overflow-auto scrollbar-hide">
|
||||
<Accordion type="single" collapsible className="w-full">
|
||||
{
|
||||
options.length ? options.map(el => (
|
||||
|
||||
Reference in New Issue
Block a user