Преглед на файлове

fix: 微官网图片视频组件允许导入视频

王晓东 преди 2 месеца
родител
ревизия
0720ffaa8c

+ 14 - 1
src/components/KnowledgeList/index.tsx

@@ -1,4 +1,4 @@
-import { ScrollView, View, Image } from "@tarojs/components";
+import { ScrollView, View, Image, Video } from "@tarojs/components";
 
 import FigureListItem from "@/components/list/FigureListItem";
 import WemetaRadio from "@/components/WemetaRadio/index";
@@ -100,6 +100,19 @@ const Index = ({ types, multi, entId, placeholder, onChange }: IProps) => {
     if(item.picUrl){
       return <View className="w-36 h-36 overflow-hidden"><Image className="w-36 h-36" src={item.picUrl} mode="widthFix" ></Image></View>
     }
+    if(item.type === 'video' && item.ossPath){
+      return <View className="w-36 h-36 overflow-hidden">
+        <Video
+        controls={false}
+        showCenterPlayBtn={false}
+        muted={true}
+        objectFit="cover"
+        className={`overflow-hidden shrink-0 w-36 h-36`}
+        src={item.ossPath}
+      />
+      </View>
+    }
+
     return <IconFIleLink />
   }
 

+ 1 - 1
src/components/KnowledgePicker/index.tsx

@@ -58,7 +58,7 @@ export default function Index({title = '知识库',  show, setShow, multi, types
   useEffect(()=> {
     if(!ent){
       setEnt(entList[0])
-      setSelected(entList[0].entName)
+      setSelected(entList[0]?.entName)
     }
   }, [entList])
 

+ 33 - 6
src/components/chat-message/MessageRobot.tsx

@@ -27,16 +27,31 @@ interface Props {
   textReasoning: string;
   message: TMessage;
   showUser?: boolean
+  mutate: (func:any, params:any)=>void
 }
-export default ({ agent, text, message, showUser=false, textReasoning = "" }: Props) => {
+export default ({ agent, text, message, mutate, showUser=false}: Props) => {
   const [isDislike, setIsDislike] = useState(message.isDislike);
   const [isLike, setIsLike] = useState(message.isLike);
   const [isSpeaking, setIsSpeaking] = useState(false);
   const loginId = getLoginId();
-  const { setMessageRespeaking, setMessageStopHandle, messageStopHandle, isReacting } = useTextChat()
+  const { setMessageRespeaking, setMessageStopHandle, messageStopHandle, isReacting, updateMessageReaction } = useTextChat()
   const { startSpeech, stopSpeech, onPlayerStatusChanged } = useTextToSpeech()
 
-  
+  // 更新单个消息的函数
+  const updateMessage = (messageId: number, updatedFields: {isLike: boolean, isDislike: boolean}) => {
+    // 使用 mutate 更新 SWR 缓存
+    // 历史消息
+    mutate((pages:any) => {
+      return pages.map(page => {
+        const updatedMessages = page.data.map((msg:TMessage) => 
+          msg.msgId === messageId ? { ...msg, ...updatedFields } : msg
+        );
+        return { ...page, data: updatedMessages };
+      });
+    }, false); // 不重新验证,因为我们手动更新了
+    // 更新在 store 中新的 messages 数据
+    updateMessageReaction(messageId, updatedFields)
+  };
 
 
   onPlayerStatusChanged((status)=> {
@@ -92,10 +107,15 @@ export default ({ agent, text, message, showUser=false, textReasoning = "" }: Pr
         title: isDislike ? "已差评" : "已取消差评",
         icon: "none",
       });
-      message.isDislike = isDislike; // 更新本地状态
+      if(message.msgId){
+        updateMessage(message.msgId, {isLike: isLike ?? false, isDislike})
+      }
       setIsDislike(isDislike);
       if (isDislike && isLike) {
         message.isLike = false; // 取消 like
+        if(message.msgId){
+          updateMessage(message.msgId, {isLike: false, isDislike})
+        }
         setIsLike(false); // 取消 like
       }
     }
@@ -104,6 +124,7 @@ export default ({ agent, text, message, showUser=false, textReasoning = "" }: Pr
     if (!agent?.agentId) {
       return;
     }
+    
     const isLike = !message.isLike;
     const response = await likeMessage({
       agentId: agent?.agentId,
@@ -117,10 +138,16 @@ export default ({ agent, text, message, showUser=false, textReasoning = "" }: Pr
         title: isLike ? "已喜欢" : "已取消喜欢",
         icon: "none",
       });
-      message.isLike = isLike; // 更新本地状态
+      if(message.msgId){
+        updateMessage(message.msgId, {isLike, isDislike: isDislike ?? false})
+      }
+      // message.isLike = isLike; // 更新本地状态
       // 触发 mutate 更新列表
       setIsLike(isLike);
       if (isDislike && isLike) {
+        if(message.msgId){
+          updateMessage(message.msgId, {isLike, isDislike: false})
+        }
         message.isDislike = false; // 取消 dislike
         setIsDislike(false); // 取消 dislike
       }
@@ -177,7 +204,7 @@ export default ({ agent, text, message, showUser=false, textReasoning = "" }: Pr
           <View onClick={(e) => handleCopy(e, text)}>
             <IconCopy />
           </View>
-          <View className={isReacting ? 'text-opacity-60' : ''} onClick={(e) => handlePlayAudio(e, text)}>
+          <View className={isReacting ? 'opacity-60' : ''} onClick={(e) => handlePlayAudio(e, text)}>
             <IconSpeaker color={isSpeaking} />
           </View>
           {/* <IconSpeaker></IconSpeaker> */}

+ 3 - 2
src/components/chat-message/index.tsx

@@ -9,12 +9,13 @@ interface Props {
   text: string|Record<string, any>
   textReasoning?: string
   message: TMessage
+  mutate: (func:any, params: any)=>void
 }
-export default ({agent, text, role, message, textReasoning=''}:Props) => {
+export default ({agent, text, role, message, mutate, textReasoning=''}:Props) => {
   if(role === EChatRole.User ){
     return <Message text={text as string}/>
   }
-  return <MessageRobot message={message} textReasoning={textReasoning} text={text as string} agent={agent}/>
+  return <MessageRobot mutate={mutate} message={message} textReasoning={textReasoning} text={text as string} agent={agent}/>
 }
 
 

+ 1 - 1
src/components/list/FigureList/index.tsx

@@ -4,7 +4,7 @@ interface IProps {
   children: JSX.Element | JSX.Element[]
 }
 const Index = ({className='', children}:IProps) => {
-  
+  // todo: 改了好几版,需要根据新 ui 重新规划组件
   return <View>
     <View className={`rounded-8 flex flex-col gap-12 ${className}`}>
       {children}

+ 1 - 1
src/components/list/FigureListItem/index.tsx

@@ -9,7 +9,7 @@ export interface IndexProps {
   underline?: boolean;
   onClick?: () => void;
 }
-
+// todo: 改了好几版,需要根据新 ui 重新规划组件
 const Index = ({ figure, arrow, underline, rightRenderer, children, className, onClick }: IndexProps) => {
   return (
     <View className={`flex items-start gap-12 w-full ${underline ? 'border-bottom1-gray pb-12' : ''} ${className}`} onClick={ ()=> { onClick && onClick()}}>

+ 1 - 0
src/components/list/card-list-item/index.tsx

@@ -13,6 +13,7 @@ interface Props {
   onLongPress?: ()=> void;
   underline?: boolean;
 }
+// todo: 改了好几版,需要根据新 ui 重新规划组件
 export default ({
   arrow = false,
   underline = false,

+ 3 - 0
src/components/list/card-list/index.tsx

@@ -1,7 +1,10 @@
 import { View } from "@tarojs/components"
+
+// todo: 改了好几版,需要根据新 ui 重新规划组件
 interface Props {
   children?: JSX.Element[]|JSX.Element
 }
+
 export default ({children}:Props) => {
   return (
     <View className="bg-white rounded-16 overflow-hidden">

+ 2 - 2
src/pages/chat/components/InputBar/index.module.less

@@ -12,9 +12,9 @@
   width: 100%;
   height: 54px;
   justify-content: center;
-  background: url(https://cdn.wehome.cn/cmn/gif/199/META-H8UKVHWU-KIGP3BIL7M5AYC6XHNUA2-OSAK6A2M-72.gif) center center no-repeat;
+  background: url(https://cdn.wehome.cn/cmn/gif/169/META-H8UKXHWU-X0WXBKY1C0G0QA1DIH762-PGXK7IEM-OD.gif) center center no-repeat;
   background-size: 124px 27px;
-  background-color: #000;
+  background-color:  var(--color-primary);
   // background-color: var(--color-primary);
   // background-color: var(--color-primary);
 }

+ 21 - 6
src/pages/chat/components/InputBar/useChatInput.ts

@@ -1,11 +1,11 @@
-import { useEffect } from "react";
+import { useEffect, useState } from "react";
 import { requestTextToChat } from "@/service/chat";
 import { useTextChat } from "@/store/textChatStore";
 import { TAgentDetail } from "@/types/agent";
 import { delay, getLoginId, isSuccess } from "@/utils";
 import { useAudioStreamPlayer } from "@/utils/audioStreamPlayer";
 
-import { EChatRole, EContentType } from "@/types/bot";
+import { EChatRole, EContentType, TAnyMessage } from "@/types/bot";
 
 import { usePostMessage, saveAgentChatContentToServer, sendMyMessageToServer } from "./useMessage";
 
@@ -13,6 +13,7 @@ import { getRecommendPrompt } from "@/service/chat";
 
 interface Props {
   agent: TAgentDetail | null;
+  historyList: TAnyMessage[];
   enableOutputAudioStream: boolean;
   setShowWelcome?: (b: boolean) => void;
   setIsVoice?: (b: boolean) => void;
@@ -25,6 +26,7 @@ export const useChatInput = ({
   enableOutputAudioStream,
   setShowWelcome,
   setDisabled,
+  historyList,
 }: Props) => {
   let myMsgUk = "";
   let mySessionId = "";
@@ -45,6 +47,8 @@ export const useChatInput = ({
     setScrollTop,
     setAutoScroll,
   } = useTextChat();
+  const list = useTextChat(state => state.list)
+  
 
   // 聊天框内消息定时上报
   const { startTimedMessage, stopTimedMessage } =
@@ -75,6 +79,7 @@ export const useChatInput = ({
     sessionId: string,
     msgUk: string
   ) => {
+    
     if (!agent?.agentId) {
       return;
     }
@@ -114,6 +119,15 @@ export const useChatInput = ({
 
     let isFirstChunk = true;
     console.log('==== start new chat ====')
+    // 取最后两条消息带上
+    const allMessages = [...historyList.reverse(), ...list];
+    const prevMessages = allMessages
+    .slice(Math.max(0, allMessages.length - 2)) // 确保不会负数
+    .map(item => ({
+      content: item.content as string,
+      contentType: item.contentType ?? EContentType.TextPlain,
+      role: item.role,
+    }));
     // 发起文本聊天
     const request = requestTextToChat({
       params: {
@@ -122,7 +136,9 @@ export const useChatInput = ({
         isEnableSearch: false,
         isEnableThinking: false,
         loginId,
-        messages: [{
+        messages: [
+          ...prevMessages,
+        {
           content: message,
           contentType: EContentType.TextPlain,
           role: EChatRole.User,
@@ -191,7 +207,7 @@ export const useChatInput = ({
         playChunk();
       },
       onReceived: (m) => {
-        updateRobotMessage(m.content, m.body ?? {});
+        updateRobotMessage(m.content ?? '', m.body ?? {});
       },
       // 流式结束
       onFinished: async (m) => {
@@ -318,9 +334,8 @@ export const useChatInput = ({
     console.log('chat input ')
     return () => {
       clearActions();
-      setScrollTop(0);
       setAutoScroll(true);
-      
+      setScrollTop(0);
       console.log('clear chat')
     };
   }, []);

+ 4 - 3
src/pages/chat/hooks/useChatMessages.ts

@@ -17,7 +17,7 @@ export const useChatMessages = (agentId: string, isVisitor?: string): {
   messagesLength: number;
   loadMore: () => void;
   pageIndex: number;
-  mutate: () => void;
+  mutate: (data: any, params: any) => void;
 } => {
   const messageList = useTextChat((state) => state.list);
 
@@ -35,8 +35,9 @@ export const useChatMessages = (agentId: string, isVisitor?: string): {
   // 使用无限滚动加载历史消息
   const { list, loadMore, pageIndex, mutate } = useLoadMoreInfinite<
     TMessage[] | TRobotMessage[]
-  >(createKey(`messeagehistories${isVisitor}${agentId}`), fetcher);
-
+  >(createKey(`messeagehistories${isVisitor}${agentId}`), fetcher, {
+    revalidateOnMount: false
+  });
   // 解析消息体 content,并展平数组
   const parsedList = list
     .flat()

+ 1 - 1
src/pages/chat/hooks/useChatScrollManager.ts

@@ -41,7 +41,7 @@ export const useChatScrollManager = (messagesLength: number, pageIndex: number,
       setTimeout(() => {
         setScrollTop(999999);
         console.log("首次进入,滚动到底部");
-      }, 600);
+      }, 800);
     }
   }, [pageIndex, messagesLength, setScrollTop]);
 

+ 5 - 3
src/pages/chat/index.tsx

@@ -90,11 +90,12 @@ export default function Index() {
     setShowWelcome,
     setIsVoice,
     setDisabled,
+    historyList,
   });
 
   // 页面显示时刷新数据
   useDidShow(() => {
-    mutate();
+    mutate(undefined, {revalidate: true})
   });
 
   // 首次进入聊天生成 session id
@@ -112,7 +113,7 @@ export default function Index() {
 
   // 使用工厂函数创建导航栏左侧渲染器
   const renderNavLeft = createNavLeftRenderer(PersonalCard, IconArrowLeftWhite24, IconArrowLeft);
-
+  console.log('----scrollTop: ', scrollTop, '----')
   return (
     <PageCustom
       fullPage
@@ -120,7 +121,7 @@ export default function Index() {
       styleBg={getBgContent()}
     >
       <NavBarNormal blur leftColumn={renderNavLeft}>
-        {/* <>{`${scrollTop}`}--{autoScroll ? 'true': 'false'}</> */}
+        {/* <>{`${scrollTop}`}--</> */}
       </NavBarNormal>
       <View
         className="flex flex-col w-full h-full relative z-10 flex-1"
@@ -168,6 +169,7 @@ export default function Index() {
                         role={message.role}
                         text={message.content}
                         message={message}
+                        mutate={mutate as any}
                       />
                     );
                   })}

+ 3 - 2
src/pages/editor-pages/editor-media/index.tsx

@@ -109,7 +109,8 @@ export default function Index({ editMode }: Props) {
       const r = picked.map((item) => {
         return {
           size: item.fileSize,
-          url: item.picUrl,
+          // 如果是视频,则 url 是 ossPath
+          url: item.picUrl || item.ossPath || '',
           fileType: item.type,
         };
       });
@@ -136,7 +137,7 @@ export default function Index({ editMode }: Props) {
       </View>
       <KnowledgePicker
         title="知识库-图片/视频"
-        types={[EKnowlegeTypes.image]}
+        types={[EKnowlegeTypes.image, EKnowlegeTypes.video]}
         multi
         onPicked={onPicked}
         setShow={setShowPopup}

+ 2 - 2
src/pages/voice/components/MyVoiceList/components/popup-recorder/index.module.less

@@ -63,9 +63,9 @@
 }
 .actionButtonActived{
   .actionButton();
-  background: url(https://cdn.wehome.cn/cmn/gif/115/META-H8UKYHWU-E9EPKMQUBSVOJ4T4NHO63-Y5XUW92M-CI.gif) center center no-repeat;
+  background: url(https://cdn.wehome.cn/cmn/gif/169/META-H8UKXHWU-X0WXBKY1C0G0QA1DIH762-PGXK7IEM-OD.gif) center center no-repeat;
   background-size: 124px 27px;
-  background-color: #000;
+  background-color:  var(--color-primary);
   box-shadow: none;
 }
 

+ 13 - 1
src/store/textChatStore.ts

@@ -5,7 +5,6 @@
 import { create } from "zustand";
 import { generateUUID } from '@/utils/index'
 
-import { getMessageHistories, type TGetMessageHistoriesParams,  } from '@/service/chat'
 import { EChatRole, TAnyMessage, TRobotMessage, TMessage } from "@/types/bot";
 import {getFormatNow} from '@/utils/timeUtils'
 
@@ -48,6 +47,7 @@ export interface TextChat {
   setMessageRespeaking: (respeaking: boolean)=> void
   setMessageStopHandle: (func: (()=> void) | null) => void
   setReacting: (reacting: boolean)=> void
+  updateMessageReaction: (msgId: number, data: {isLike: boolean, isDislike: boolean})=> void
 }
 
 // 新messageId 为 index 加 1
@@ -179,6 +179,18 @@ export const useTextChat = create<TextChat>((set, get) => ({
       return { list: updatedList, scrollTop:  state.autoScroll ?   state.scrollTop + 1 : state.scrollTop}; // 返回新的状态
     });
   },
+  updateMessageReaction: (msgId, data)=> {
+    set((state) => {
+      const updatedList = state.list.map((message) => {
+        if (message.msgId === msgId) {
+          return { ...message, isLike: data.isLike, isDislike: data.isDislike}  // 更新 content
+        }
+        return message; // 返回未修改的 message
+      });
+      
+      return { list: updatedList}; // 返回新的状态
+    });
+  },
   getCurrentRobotMessage: ()=> {
     const state = get()
     const currentMessage = state.list.find((message)=>  message.msgUk === state.currentRobotMsgUk) as TRobotMessage

+ 2 - 1
src/types/bot.ts

@@ -59,6 +59,7 @@ export type TMessage = {
   role: TChatRole,
   body?: TMessageBody
   sessionId?: string
+  agentId?: string
 }
 
 export type TRobotMessage = {
@@ -90,7 +91,7 @@ export type TRequestBody = {
   isEnableSearch: boolean;
   isEnableThinking: boolean;
   loginId: string;
-  messages: TRequestMessage[];
+  messages: (TAnyMessage|TRequestMessage)[];
   sessionId: string;
 };
 

+ 2 - 1
src/types/knowledge.ts

@@ -22,7 +22,8 @@ export type TKnowledgeItem = {
   parseMsg: string;
   parseStatus: string;  // parseStatus (string, optional): 解析状态 unknown未知/wait_transfer待转存/unparsed待解析/parsing解析中/wait_audit待审核/audit_pass审核通过/parsed解析成功/audit_fail审核未通过/parse_fail解析失败 ,
   parseStatusDesc: string;
-  picUrl: string;
+  picUrl: string|null; // 图片地址
+  ossPath: string|null; // 视频地址
   title: string;
   type: string;
 };

+ 1 - 1
src/utils/ChunkParser.ts

@@ -60,7 +60,7 @@ export default class ChunkParser {
   public parseChunk(
       chunk: string, 
       onParsed: (json: ICompleteCallback) => void,
-      onAudioParsed: (json: IAudioPared) => void
+      onAudioParsed: (m: ICompleteCallback) => void
     ): void {
     // 将新接收到的 chunk 添加到 buffer
     this.buffer += chunk;