Quellcode durchsuchen

fix: 消息体适配qa图文

王晓东 vor 2 Wochen
Ursprung
Commit
f53e4a17a9
44 geänderte Dateien mit 808 neuen und 345 gelöschten Zeilen
  1. 28 0
      project.private.config.json
  2. 2 1
      src/app.config.ts
  3. 16 0
      src/components/AgentPage/components/SummaryBar/index.tsx
  4. 53 4
      src/components/ChatRecordItem/index.tsx
  5. 1 1
      src/components/GlobalModal/index.tsx
  6. 16 9
      src/components/KnowledgeList/index.tsx
  7. 0 1
      src/components/KnowledgePicker/index.module.less
  8. 6 13
      src/components/KnowledgePicker/index.tsx
  9. 3 0
      src/components/NavBarNormal/index.module.less
  10. 3 1
      src/components/NavBarNormal/index.tsx
  11. 39 0
      src/components/WeComQRcode/index.module.less
  12. 13 4
      src/components/WeComQRcode/index.tsx
  13. 4 3
      src/components/WemetaModal/index.tsx
  14. 121 78
      src/components/chat-message/MessageRobot.tsx
  15. 2 2
      src/components/chat-message/index.module.less
  16. 8 0
      src/components/popup/popup/index.module.less
  17. 65 44
      src/components/popup/popup/index.tsx
  18. 3 3
      src/pages/agent-avatars/index.tsx
  19. 47 3
      src/pages/chat-messages/index.tsx
  20. 65 24
      src/pages/chat-session-messages/index.tsx
  21. 1 1
      src/pages/chat/components/input-bar/TextInputBar.tsx
  22. 8 8
      src/pages/chat/components/input-bar/chatInput.ts
  23. 13 2
      src/pages/chat/components/input-bar/message.ts
  24. 71 0
      src/pages/chat/components/keyboard.ts
  25. 26 70
      src/pages/chat/index.tsx
  26. 48 2
      src/pages/dislike-messages/index.tsx
  27. 23 18
      src/pages/editor-contact/index.tsx
  28. 1 0
      src/pages/editor-pages/editor-address/index.tsx
  29. 5 0
      src/pages/editor-pages/editor-position/index.config.ts
  30. 2 0
      src/pages/editor-pages/editor-position/index.module.less
  31. 40 0
      src/pages/editor-pages/editor-position/index.tsx
  32. 0 5
      src/pages/knowledge/components/AsistantMessage/index.tsx
  33. 0 4
      src/pages/knowledge/components/PersonalTab/components/ScrollListChat.tsx
  34. 2 7
      src/pages/knowledge/components/PersonalTab/index.tsx
  35. 0 0
      src/pages/message-editor/index.config.ts
  36. 24 6
      src/pages/message-editor/index.tsx
  37. 2 2
      src/service/bot.ts
  38. 1 0
      src/store/modalStore.ts
  39. 6 23
      src/store/textChat.ts
  40. 3 0
      src/types/agent.ts
  41. 28 1
      src/types/bot.ts
  42. 4 1
      src/types/visitor.ts
  43. 1 1
      src/utils/jsonChunkParser.ts
  44. 4 3
      src/utils/loadMoreInfinite.ts

+ 28 - 0
project.private.config.json

@@ -8,6 +8,34 @@
   "condition": {
     "miniprogram": {
       "list": [
+        {
+          "name": "pages/dashboard/index",
+          "pathName": "pages/dashboard/index",
+          "query": "",
+          "scene": null,
+          "launchMode": "default"
+        },
+        {
+          "name": "pages/agent/index",
+          "pathName": "pages/agent/index",
+          "query": "agentId=p_2e73c9d7efaYfDo2-agent_1009",
+          "launchMode": "default",
+          "scene": null
+        },
+        {
+          "name": "pages/index/index",
+          "pathName": "pages/index/index",
+          "query": "",
+          "launchMode": "default",
+          "scene": null
+        },
+        {
+          "name": "pages/visiteor-detail/index",
+          "pathName": "pages/visiteor-detail/index",
+          "query": "visitorId=1391",
+          "launchMode": "default",
+          "scene": null
+        },
         {
           "name": "pages/profile/index",
           "pathName": "pages/profile/index",

+ 2 - 1
src/app.config.ts

@@ -6,7 +6,7 @@ export default defineAppConfig({
     'pages/dashboard/index',
     'pages/visiteor-detail/index',
     'pages/dislike-messages/index',
-    'pages/dislike-message-editor/index',
+    'pages/message-editor/index',
     'pages/chat-messages/index',
     'pages/chat-session-messages/index',
     'pages/contact/index',
@@ -37,6 +37,7 @@ export default defineAppConfig({
     'pages/editor-pages/editor-address/index',
     'pages/editor-pages/editor-qrcode/index',
     'pages/editor-pages/editor-ent-name/index',
+    'pages/editor-pages/editor-position/index',
 
 
     

+ 16 - 0
src/components/AgentPage/components/SummaryBar/index.tsx

@@ -36,6 +36,22 @@ export default ({agent, isVisitor}: IProps) => {
   const {showModal,} = useModalStore()
 
   const handleClick = ()=> {
+    
+    // 如果是已经下线的智能体,显示提示信息
+    if(agent.status === 'deleted'){
+      showModal({
+        content: <View className="w-200">{agent.deletedTip}</View>,
+        showCancelButton: false,
+        onConfirm() {
+          Taro.showTabBar();
+        },
+        onCancel() {
+          Taro.showTabBar();
+        },
+      })
+      return
+    }
+    
     Taro.navigateTo({
       url: `/pages/chat/index?agentId=${agent.agentId}&isVisitor=${isVisitor}`,
     });

+ 53 - 4
src/components/ChatRecordItem/index.tsx

@@ -1,4 +1,4 @@
-import { View,Image } from "@tarojs/components";
+import { View,Image, Text } from "@tarojs/components";
 import { useEffect, useState } from "react";
 import { isSuccess } from "@/utils";
 import { getVisitorMessages } from "@/service/visitor";
@@ -7,6 +7,7 @@ import IconEditButtonBlue from '@/images/svgs/dashboard/IconEditButtonBlue.svg'
 
 import style from "./index.module.less";
 import Taro from "@tarojs/taro";
+import { EContentType, TMessageBodyContent } from "@/types/bot";
 
 interface IProps {
   visitorId: string,
@@ -26,6 +27,16 @@ export default ({ visitorId, sessionId, showMoreButton = false }:IProps) => {
         setQuestion(q)
       }
       if(a){
+        if(a.contentType == EContentType.AiseekQA){
+          try{
+            const contentJson = JSON.parse(a.content as string) as TMessageBodyContent
+            a.content = contentJson.answer.text
+            // 把消息详情放入统一 body 中
+            a.body = {contentType: a.contentType, content: contentJson,}
+          }catch(e){
+            // console.error(e)
+          }
+        }
         setAnswer(a)
       }
     }
@@ -41,6 +52,44 @@ export default ({ visitorId, sessionId, showMoreButton = false }:IProps) => {
       url: `/pages/chat-session-messages/index?visitorId=${visitorId}&sessionId=${sessionId}`,
     });
   };
+
+  const handleEdit = (item)=> {
+    if(!answer){
+      return;
+    }
+    Taro.navigateTo({
+      url: `/pages/message-editor/index?msgId=${answer.msgId}&visitorId=${answer.visitorId}&agentId=${answer.agentId}`,
+    });
+  }
+
+  const renderAnswerContent = (item: TVisitorChat) => {
+    if(item.contentType === EContentType.AiseekQA){
+      const body = item.body
+      const payload = body?.content?.answer?.payload;
+      const links = payload?.links ?? [];
+      const pics = payload?.pics ?? [];
+      return (
+        <View>
+          <View>{item.content}</View>
+          <View className="pb-12">
+            {pics.map((pic: string) => {
+              return (
+                <View>
+                  <Image src={pic} mode="widthFix" className="w-full"></Image>
+                </View>
+              );
+            })}
+          </View>
+          <View className="pb-12">
+            {links.map((link: string) => {
+              return <Text>{link}</Text>;
+            })}
+          </View>
+        </View>
+      );
+    }
+    return <View>{item.content}</View>
+  }
   
 
   return (
@@ -49,10 +98,10 @@ export default ({ visitorId, sessionId, showMoreButton = false }:IProps) => {
         <View className="p-16">
           <View className="text-14 text-title mb-8">{question?.content}</View>
           {!!answer?.content?.length && <View className="p-16 text-title rounded-8 relative" style={{backgroundColor: '#F4F9FF'}}>
-              <View>{answer?.content}</View>
-              {/* <View className={style.editButton}>
+              <View>{renderAnswerContent(answer)}</View>
+              <View className={style.editButton} onClick={handleEdit}>
                 <Image src={IconEditButtonBlue} className="w-14 h-14" />
-              </View> */}
+              </View>
             </View>}
         </View>
         {showMoreButton && <View

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

@@ -12,7 +12,7 @@ export function GlobalModal() {
 
   return (
     <RootPortal className={`taro-portal-container taro-portal-container-ztop`}>
-      <WemetaModal show={isVisible} onConfirm={()=> onConfirm() } onCancel={()=> onCancel()} >
+      <WemetaModal show={isVisible} showCancelButton={config.showCancelButton} onConfirm={()=> onConfirm() } onCancel={()=> onCancel()} >
         <>{config.content}</>
       </WemetaModal>
       </RootPortal>

+ 16 - 9
src/components/KnowledgeList/index.tsx

@@ -18,8 +18,9 @@ export interface IProps {
   types?: EKnowlegeTypes[]; // 列表类型
   entId?: string | number; // 企业id
   onChange: (value: TKnowledgeItem[]) => void;
+  placeholder?: ()=> JSX.Element
 }
-const Index = ({ types, multi, entId, onChange }: IProps) => {
+const Index = ({ types, multi, entId, placeholder, onChange }: IProps) => {
   const { list, checkedValue, initLoad, setCheckedValue, loadMore } =
     useKnowledge({ types: types, entId });
 
@@ -84,6 +85,19 @@ const Index = ({ types, multi, entId, onChange }: IProps) => {
     );
   };
 
+  const renderPlaceHolder = ()=> {
+    if(placeholder){
+      return <>{placeholder()}</>
+    }
+    return <View className="flex flex-col gap-8 items-center pt-45"> <EmptyData type={"plane"}>
+      <View className="text-gray-45 flex flex-col items-center">
+        <View>暂无数据</View>
+        <View>可在电脑端管理知识库</View>
+      </View>
+    </EmptyData>
+    </View>
+  }
+
   return (
     <ScrollView
       scrollY
@@ -95,14 +109,7 @@ const Index = ({ types, multi, entId, onChange }: IProps) => {
     >
       <FigureList>
         <>
-          {!list.length && (
-            <EmptyData type={"plane"}>
-              <View className="text-gray-45">
-                <View>暂无数据</View>
-                <View>可在电脑端管理知识库</View>
-              </View>
-            </EmptyData>
-          )}
+          {!list.length && ( renderPlaceHolder() )}
           {list.map((item) => {
             return (
               <FigureListItem

+ 0 - 1
src/components/KnowledgePicker/index.module.less

@@ -11,5 +11,4 @@
   width: 100%;
   border-radius: 12px;
   height: 480px;
-  background-color: rgba(#000, 0.03);
 }

+ 6 - 13
src/components/KnowledgePicker/index.tsx

@@ -7,7 +7,7 @@ import KnowledgeList from "@/components/KnowledgeList";
 import { EKnowlegeTypes } from "@/consts/enum";
 import BottomBar from "@/components/BottomBar";
 import PickerSingleColumn from "@/components/Picker/PickerSingleColumn";
-
+import WeComQRcode from '@/components/WeComQRcode'
 import IconArrowDownRounded from "@/components/icon/IconArrowDownRounded";
 import Taro from "@tarojs/taro";
 import { useUserStore } from "@/store/userStore";
@@ -127,21 +127,14 @@ export default function Index({ show, setShow, multi, types, onPicked }: IProps)
       label: "个人",
       children: (
         <View className={style.tabContainer}>
-          {/* <View className={style.dataEmpty}>
-            <Image
-              showMenuByLongpress
-              src="https://nexthuman.cn/api-web/img/wechat.9d5eaf4d.jpg"
-              style={{ width: "160px", height: "160px" }}
-            />
-            <View className="pt-10 text-center">
-              <View>添加小蓝本文档助手的企业微信</View>
-              <View>随时随地上传文档</View>
-            </View>
-          </View> */}
-
           <KnowledgeList
             multi={multi}
             types={types}
+            placeholder={()=> {
+              return <View className={style.dataEmpty}>
+                <WeComQRcode/>
+              </View>
+            }}
             onChange={handleOnChange}
           ></KnowledgeList>
         </View>

+ 3 - 0
src/components/NavBarNormal/index.module.less

@@ -5,6 +5,9 @@
   top: 0;
   z-index: 999;
 }
+.backdropFilter{
+  backdrop-filter: blur(10px);
+}
 .navBarBg{
   position: absolute;
   left: 0;

+ 3 - 1
src/components/NavBarNormal/index.tsx

@@ -10,6 +10,7 @@ interface Props {
   center?: boolean;
   leftColumn?: () => JSX.Element;
   backText?: string;
+  blur?: boolean
   onNavBack?: () => Promise<boolean | void> | void;
   backButtonRound?: boolean;
   navDelta?: number;
@@ -21,6 +22,7 @@ const Index: React.FC<Props> = ({
   children,
   center = true,
   onNavBack,
+  blur = false,
   navDelta = 1,
   backText,
   scrollFadeIn = false,
@@ -88,7 +90,7 @@ const Index: React.FC<Props> = ({
   const renderNavBar = () => {
     return (
       <View
-        className={`${style.navBarContainer}`}
+        className={`${style.navBarContainer} ${blur ? style.backdropFilter : ''}`}
         id="leaf-nav-bar"
         style={{
           paddingTop: statusBarHeight,

+ 39 - 0
src/components/WeComQRcode/index.module.less

@@ -0,0 +1,39 @@
+.wrapper{
+  position: relative;
+  border: 2px solid #DBDEEC;
+  border-radius: 10px;
+  padding: 10px;
+}
+.block{
+  position: absolute;
+  z-index: 1;
+  background-color: white;
+}
+.leftBlock{
+  .block();
+  left: -2px;
+  top: 20px;
+  height: 140px;
+  width: 10px; 
+}
+.rightBlock{
+  .block();
+  right: -2px;
+  top: 20px;
+  height: 140px;
+  width: 10px; 
+}
+.topBlock{
+  .block();
+  left: 20px;
+  top: -2px;
+  height: 10px;
+  width: 140px; 
+}
+.bottomBlock{
+  .block();
+  left: 20px;
+  bottom: -2px;
+  height: 10px;
+  width: 140px; 
+}

+ 13 - 4
src/components/WeComQRcode/index.tsx

@@ -1,12 +1,21 @@
-import { Image } from "@tarojs/components";
-
-const WeComQRcode = () => {
+import { Image, View } from "@tarojs/components";
+import style from './index.module.less'
+const WeComQRcode = ({text = '长按二维码 上传文件给知识库助手'}: {text?: string}) => {
   return (
-    <Image
+    <View className="flex flex-col items-center gap-12">
+      <View className={style.wrapper}>
+        <View className={style.leftBlock}></View>
+        <View className={style.topBlock}></View>
+        <View className={style.rightBlock}></View>
+        <View className={style.bottomBlock}></View>
+        <Image
           showMenuByLongpress
           src="https://cdn.wehome.cn/cmn/jpg/141/META-H8UKWHWU-5YMWRSCR77VBOBA6AANE3-UFEPQICM-26.jpg"
           style={{ width: "160px", height: "160px" }}
         />
+      </View>
+      <View className="text-12 text-[#777E95] leading-16">{text}</View>
+    </View>
   );
 };
 

+ 4 - 3
src/components/WemetaModal/index.tsx

@@ -4,10 +4,11 @@ interface Props {
   show?: boolean
   children: React.ReactChild | React.ReactChild[]
   footer?: boolean
+  showCancelButton?: boolean
   onCancel?: () => void
   onConfirm?: () => void
 }
-const index = ({show = false, children, onConfirm, onCancel, footer = true}: Props) => {
+const index = ({show = false, children, onConfirm, onCancel, showCancelButton=true, footer = true}: Props) => {
   
   const handleConfirm = () => {
     onConfirm && onConfirm()
@@ -18,7 +19,7 @@ const index = ({show = false, children, onConfirm, onCancel, footer = true}: Pro
   const renderFooter = () => {
     return <>
       <View className={style.footer}>
-        <View className={style.footerButton} onClick={handleCancel}>取消</View>
+        {showCancelButton && <View className={style.footerButton} onClick={handleCancel}>取消</View>}
         <View className={`${style.footerButtonPrimary}`} onClick={handleConfirm}>确定</View>
       </View>
     </>
@@ -31,7 +32,7 @@ const index = ({show = false, children, onConfirm, onCancel, footer = true}: Pro
           <View className={style.content}>
             {children}
           </View>
-          {renderFooter()}
+          {footer && renderFooter()}
         </View>
       </View> 
       ) 

+ 121 - 78
src/components/chat-message/MessageRobot.tsx

@@ -1,5 +1,5 @@
 import { View, Image, Text } from "@tarojs/components";
-import style from './index.module.less'
+import style from "./index.module.less";
 import IconCopy from "@/components/icon/IconCopy";
 import IconDislike from "@/components/icon/IconDislike";
 import IconDislikeBlue from "@/components/icon/IconDislikeBlue";
@@ -7,134 +7,177 @@ import IconLike from "@/components/icon/IconLike";
 import IconLikeBlue from "@/components/icon/IconLikeBlue";
 import IconSpeaker from "@/components/icon/icon-speaker";
 import { TAgentDetail } from "@/types/agent";
+
 import Taro from "@tarojs/taro";
-import ThinkAnimation from '../think-animation/index'
+import ThinkAnimation from "../think-animation/index";
 import { dislikeMessage, likeMessage } from "@/service/bot";
-import { TMessage } from "@/types/bot";
+import { EContentType, TMessage } from "@/types/bot";
 import { getLoginId, isSuccess } from "@/utils";
 import { useState } from "react";
 interface Props {
-  agent?: TAgentDetail | null,
-  text: string,
-  textReasoning: string,
-  message: TMessage
+  agent?: TAgentDetail | null;
+  text: string;
+  textReasoning: string;
+  message: TMessage;
 }
-export default ({agent, text, message, textReasoning=''}:Props) => {
-  const [isDislike, setIsDislike] = useState(message.isDislike)
-  const [isLike, setIsLike] = useState(message.isLike)
-  const handleCopy = (e: any)=> {
+export default ({ agent, text, message, textReasoning = "" }: Props) => {
+  const [isDislike, setIsDislike] = useState(message.isDislike);
+  const [isLike, setIsLike] = useState(message.isLike);
+  // console.log('helloworld: ', message)
+  const handleCopy = (e: any, textStr: string) => {
     e.stopPropagation();
     // 手动复制并 toast 提示
-    if(text){
+    if (textStr) {
       Taro.setClipboardData({
-        data: text,
-        success(){
+        data: textStr,
+        success() {
           Taro.showToast({
-            title: '复制成功',
-            icon: 'none'
-          })
-        },fail(res) {
-          console.log(res)
+            title: "复制成功",
+            icon: "none",
+          });
+        },
+        fail(res) {
+          console.log(res);
           Taro.showToast({
-            title: '复制失败',
-            icon: 'none'
-          })
+            title: "复制失败",
+            icon: "none",
+          });
         },
-      })
+      });
     }
-  }
+  };
   const loginId = getLoginId();
   const handleDislike = async () => {
-    if(!agent?.agentId){
-      return;  
+    if (!agent?.agentId) {
+      return;
     }
-    const isDislike = !message.isDislike
+    const isDislike = !message.isDislike;
     const response = await dislikeMessage({
       agentId: agent?.agentId,
-      dislikeReason: '',
+      dislikeReason: "",
       isDislike: isDislike,
       loginId,
-      msgUk: message.msgUk
-    })
-    if(isSuccess(response.status)){
+      msgUk: message.msgUk,
+    });
+    if (isSuccess(response.status)) {
       Taro.showToast({
-        title: isDislike ? '已差评' : '已取消差评',
-        icon: 'none'
-      })
+        title: isDislike ? "已差评" : "已取消差评",
+        icon: "none",
+      });
       message.isDislike = isDislike; // 更新本地状态
-      setIsDislike(isDislike)
-      if(isDislike && isLike) {
+      setIsDislike(isDislike);
+      if (isDislike && isLike) {
         message.isLike = false; // 取消 like
-        setIsLike(false) // 取消 like
+        setIsLike(false); // 取消 like
       }
     }
-  }
+  };
   const handleLike = async () => {
-    if(!agent?.agentId){
-      return;  
+    if (!agent?.agentId) {
+      return;
     }
-    const isLike = !message.isLike
+    const isLike = !message.isLike;
     const response = await likeMessage({
       agentId: agent?.agentId,
       isLike: isLike,
       loginId,
-      msgUk: message.msgUk
-    })
-    
-    if(isSuccess(response.status)){
+      msgUk: message.msgUk,
+    });
+
+    if (isSuccess(response.status)) {
       Taro.showToast({
-        title: isLike ? '已喜欢' : '已取消喜欢',
-        icon: 'none'
-      })
+        title: isLike ? "已喜欢" : "已取消喜欢",
+        icon: "none",
+      });
       message.isLike = isLike; // 更新本地状态
       // 触发 mutate 更新列表
-      setIsLike(isLike)
-      if(isDislike && isLike) {
+      setIsLike(isLike);
+      if (isDislike && isLike) {
         message.isDislike = false; // 取消 dislike
-        setIsDislike(false) // 取消 dislike
+        setIsDislike(false); // 取消 dislike
       }
-      
     }
-  }
+  };
 
-  return <View>
-      {agent && <View className="flex gap-8 mb-10">
-        <View className={style.avatarContainer}>
-          {agent?.avatarUrl && (
-            <Image
-              mode="aspectFill"
-              className={style.avatar}
-              src={agent.avatarUrl}
-            />
-          )}
+  // 渲染消息主体
+  const renderMessageBody = () => {
+    const body = message.body;
+    console.log(body?.contentType, body)
+    // 渲染 QA 回答
+    if (body?.contentType === EContentType.AiseekQA) {
+      const payload = body?.content?.answer?.payload ?? {};
+      const links = payload.links ?? [];
+      const pics = payload.pics ?? [];
+      console.log(body)
+      return (
+        <View>
+          <View className="pb-12">
+            {pics.map((pic: string) => {
+              return (
+                <View>
+                  <Image src={pic} mode="widthFix" className="w-full"></Image>
+                </View>
+              );
+            })}
+          </View>
+          <View className="pb-12">
+            {links.map((link: string) => {
+              return (
+                <View onClick={(e) => handleCopy(e, link)}>
+                  <Text className="text-primary">复制链接</Text> <Text>{link}</Text>
+                </View>
+              );
+            })}
+          </View>
         </View>
-        <View className="font-medium text-16 leading-24">{agent?.name}</View>
-      </View>}
+      );
+    }
+    return <></>;
+  };
 
-      <View className={`${style.message } ${style.messageRobot} gap-10`}>
+  return (
+    <View>
+      {agent && (
+        <View className="flex gap-8 mb-10">
+          <View className={style.avatarContainer}>
+            {agent?.avatarUrl && (
+              <Image
+                mode="aspectFill"
+                className={style.avatar}
+                src={agent.avatarUrl}
+              />
+            )}
+          </View>
+          <View className="font-medium text-16 leading-24">{agent?.name}</View>
+        </View>
+      )}
+
+      <View className={`${style.message} ${style.messageRobot} gap-10`}>
         <View className={style.messageContent}>
-        {textReasoning && <View className={style.deepThinkContainer}>
+          {/* {textReasoning && <View className={style.deepThinkContainer}>
             <View className="font-bold">深度思考:</View>
             <Text>
               {textReasoning}
             </Text>
-          </View>}
+          </View>} */}
 
-        {text.length === 0 && <ThinkAnimation></ThinkAnimation>}
+          {text.length === 0 && <ThinkAnimation></ThinkAnimation>}
           <Text>{text}</Text>
+          {renderMessageBody()}
         </View>
         <View className="flex gap-8">
-          <View onClick={handleCopy}><IconCopy /></View>
+          <View onClick={(e) => handleCopy(e, text)}>
+            <IconCopy />
+          </View>
           {/* <IconSpeaker></IconSpeaker> */}
-          <View onClick={()=> handleDislike()}>
-            {isDislike ? <IconDislikeBlue/> : <IconDislike/>}
+          <View onClick={() => handleDislike()}>
+            {isDislike ? <IconDislikeBlue /> : <IconDislike />}
           </View>
-          <View onClick={()=> handleLike()}>
-            {isLike ? <IconLikeBlue/> : <IconLike/>}
+          <View onClick={() => handleLike()}>
+            {isLike ? <IconLikeBlue /> : <IconLike />}
           </View>
         </View>
+      </View>
     </View>
-  </View>
-}
-
-
+  );
+};

+ 2 - 2
src/components/chat-message/index.module.less

@@ -44,8 +44,8 @@
 
 .messageContent{
   font-size: 15px;
-  line-height: 26px;
-  color: #000;
+  line-height: 28px;
+  color: #111A34;
   word-break: break-all;
 }
 .deepThinkContainer{

+ 8 - 0
src/components/popup/popup/index.module.less

@@ -37,6 +37,14 @@
   padding: 0 16px;
 }
 
+.closeButtonWrapper{
+  margin-right: -12px;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  width: 48px;
+  height: 48px;
+}
 .closeButton{
   display: flex;
   align-items: center;

+ 65 - 44
src/components/popup/popup/index.tsx

@@ -1,53 +1,74 @@
-import { View, Image, RootPortal } from "@tarojs/components"
-import style from './index.module.less'
-import closeIcon from '@/images/icon-close-8.png';
+import { View, Image, RootPortal } from "@tarojs/components";
+import style from "./index.module.less";
+import closeIcon from "@/images/icon-close-8.png";
 import { useEffect, useState } from "react";
 import Taro from "@tarojs/taro";
 interface IProps {
-  title?: string
-  setShow: (show: boolean)=>void
-  show: boolean,
-  showCloseButton?: boolean
-  children?: JSX.Element|JSX.Element[]
+  title?: string;
+  setShow: (show: boolean) => void;
+  show: boolean;
+  showCloseButton?: boolean;
+  maskClosable?: boolean
+  children?: JSX.Element | JSX.Element[];
 }
-export default ({title = '', showCloseButton = true, show, setShow, children}: IProps)=> {
-  const [slideUp, setSlideUp] = useState(false)
-  const handleClose = (b: boolean) => {
-    setSlideUp(false)
-    setTimeout(()=> {
-      setShow(b)
-      Taro.showTabBar()
-    }, 100)
-    
+export default ({
+  title = "",
+  showCloseButton = true,
+  show,
+  setShow,
+  children,
+  maskClosable = true,
+}: IProps) => {
+  const [slideUp, setSlideUp] = useState(false);
+  const handleMaskClick = ()=> {
+    maskClosable && handleClose(false)
   }
+  const handleClose = (b: boolean) => {
+    setSlideUp(false);
+    setTimeout(() => {
+      setShow(b);
+      Taro.showTabBar();
+    }, 100);
+  };
 
-  useEffect(()=> {
-    if(show){
-      setTimeout(()=> {
-        setSlideUp(true)
-      }, 100)  
+  useEffect(() => {
+    if (show) {
+      setTimeout(() => {
+        setSlideUp(true);
+      }, 100);
       Taro.hideTabBar();
-    }else{
+    } else {
       Taro.showTabBar();
     }
-    
-  }, [show])
-  
-  return <>
-    {show && <RootPortal><View
-      className={`taro-portal-container ${style.popup}`}
-    >
-      <View className={`${style.container} ${slideUp ? style.show : ''}`}>
-        <View className={style.titleBar}>
-          <View className={style.title}>{title}</View>
-          {showCloseButton && <View className={style.closeButton} onClick={()=>handleClose(false)}>
-            <Image src={closeIcon} mode="widthFix" className={style.closeIcon} />
-          </View>}
-        </View>
-        <View className={style.content}>
-          {children}
-        </View>
-      </View>
-   </View></RootPortal>}
-  </>
-}
+  }, [show]);
+
+  return (
+    <>
+      {show && (
+        <RootPortal>
+          <View className={`taro-portal-container ${style.popup}`} onClick={handleMaskClick}>
+            <View className={`${style.container} ${slideUp ? style.show : ""}`} onClick={(e)=> {e.stopPropagation()}}>
+              <View className={style.titleBar}>
+                <View className={style.title}>{title}</View>
+                {showCloseButton && (
+                  <View className={style.closeButtonWrapper} onClick={() => handleClose(false)}>
+                    <View
+                      className={style.closeButton}
+                    >
+                      <Image
+                        src={closeIcon}
+                        mode="widthFix"
+                        className={style.closeIcon}
+                      />
+                    </View>
+                  </View>
+                )}
+              </View>
+              <View className={style.content}>{children}</View>
+            </View>
+          </View>
+        </RootPortal>
+      )}
+    </>
+  );
+};

+ 3 - 3
src/pages/agent-avatars/index.tsx

@@ -29,7 +29,7 @@ export default function Index() {
   const { showModal } = useModalStore();
 
   const fetcher = async ([_url, { pageIndex, pageSize }]) => {
-    const res = await fetchMyAvatars({ pageIndex, pageSize });
+    const res = await fetchMyAvatars({ pageIndex: pageIndex, pageSize });
     return res.data;
   };
 
@@ -47,7 +47,7 @@ export default function Index() {
     }
 
     Taro.showLoading();
-    const result = await editAgentAvatar(agent.agentId, item.avatarId, false);
+    const result = await editAgentAvatar(agent.agentId, item.avatarId, true);
     await fetchAgent(agent.agentId);
     Taro.hideLoading();
     setCurrent(item);
@@ -103,7 +103,7 @@ export default function Index() {
     if (!list.length) {
       return (
         <>
-          <EmptyData type={"search"}></EmptyData>
+          {/* <EmptyData type={"search"}></EmptyData> */}
         </>
       );
     }

+ 47 - 3
src/pages/chat-messages/index.tsx

@@ -11,6 +11,8 @@ import { getVisitorMessagesByMsgId } from "@/service/visitor";
 import { TVisitorAgent, TVisitorChat } from "@/types/visitor";
 import IconEditButtonBlue from "@/images/svgs/dashboard/IconEditButtonBlue.svg";
 import style from "./index.module.less";
+import { EComponentType } from "@/consts/enum";
+import { EContentType, TMessageBodyContent } from "@/types/bot";
 
 export default () => {
   const router = useRouter();
@@ -35,11 +37,53 @@ export default () => {
       size: 10,
     });
     if (isSuccess(response.status)) {
-      setList(response.data)
+      const data = response.data.map((item)=> {
+        
+        if(item.contentType == EContentType.AiseekQA){
+          try{
+            const contentJson = JSON.parse(item.content as string) as TMessageBodyContent
+            item.content = contentJson.answer.text
+            // 把消息详情放入统一 body 中
+            item.body = {contentType: item.contentType, content: contentJson,}
+          }catch(e){
+            // console.error(e)
+          }
+        }
+        return item
+      })
+      setList(data)
     }
   };
 
-  
+
+  const renderAsistentContent = (item: TVisitorChat) => {
+    if(item.contentType === EContentType.AiseekQA){
+      const body = item.body
+      const payload = body?.content?.answer?.payload;
+      const links = payload?.links ?? [];
+      const pics = payload?.pics ?? [];
+      return (
+        <View>
+          <View>{item.content}</View>
+          <View className="pb-12">
+            {pics.map((pic: string) => {
+              return (
+                <View>
+                  <Image src={pic} mode="widthFix" className="w-full"></Image>
+                </View>
+              );
+            })}
+          </View>
+          <View className="pb-12">
+            {links.map((link: string) => {
+              return <Text>{link}</Text>;
+            })}
+          </View>
+        </View>
+      );
+    }
+    return <View>{item.content}</View>
+  }
 
   
   useEffect(() => {
@@ -60,7 +104,7 @@ export default () => {
         className="p-16 text-title rounded-8 relative"
         style={{ backgroundColor: "#F4F9FF" }}
       >
-        <View>{item.content}</View>
+        <View>{renderAsistentContent(item)}</View>
       </View>
     );
   };

+ 65 - 24
src/pages/chat-session-messages/index.tsx

@@ -8,10 +8,11 @@ import { getVisitorInfo } from "@/service/visitor";
 import VisitorSummary from "./components/VisitorSummary";
 
 import { getVisitorMessages } from "@/service/visitor";
-import { TSessionItem, TVisitorAgent, TVisitorChat } from "@/types/visitor";
-import { useLoadMore } from "@/utils/loadMore";
+import { TVisitorAgent, TVisitorChat } from "@/types/visitor";
+import { createKey, useLoadMoreInfinite } from "@/utils/loadMoreInfinite";
 import IconEditButtonBlue from "@/images/svgs/dashboard/IconEditButtonBlue.svg";
 import style from "./index.module.less";
+import { EContentType } from "@/types/bot";
 
 export default () => {
   const router = useRouter();
@@ -21,7 +22,7 @@ export default () => {
   }
 
   const [visitor, setVisitor] = useState<TVisitorAgent>();
-  const [list, setList] = useState<TVisitorChat[]>([]);
+
 
   const fetchData = async () => {
     if (visitorId) {
@@ -32,7 +33,7 @@ export default () => {
     }
   };
 
-  const fetcher = async ([_url, nextId, page, pageSize, sessionId]) => {
+  const fetcher = async ([_url, {nextId, pageSize}, [sessionId]]) => {
     const res = await getVisitorMessages({
       startId: nextId,
       pageSize,
@@ -42,31 +43,71 @@ export default () => {
     return res.data;
   };
 
-  const { data, loadMore } = useLoadMore<{
-    data?: TVisitorChat[];
-    nextId?: string;
-    totalCount?: number;
-  }>({
-    url: `api/v1/my/visitor/sessions${visitorId}`,
+  const { list, loadMore } = useLoadMoreInfinite<TVisitorChat[]>(
+    createKey(`api/v1/my/visitor/sessions${visitorId}`, 10, [sessionId]),
     fetcher,
-    params: [sessionId],
-  });
-
-  useEffect(() => {
-    fetchData();
-  }, []);
+    );
+  
+  const parsedList = list.map((item: TMessage|TRobotMessage) => {
+      if(item.contentType == EContentType.AiseekQA){
+        try{
+          const contentJson = JSON.parse(item.content as string)
+          item.content = contentJson.answer.text
+          // 把消息详情放入统一 body 中
+          item.body = {...item, content: contentJson,}
+        }catch(e){
+          // console.error(e)
+        }
+      }
+      return item
+    })
 
-  useEffect(() => {
-    if (data?.data) {
-      //@ts-ignore
-      setList([...list, ...data.data]);
+    const handleEdit = (item: TVisitorChat)=> {
+      if(!item){
+        return;
+      }
+      Taro.navigateTo({
+        url: `/pages/message-editor/index?msgId=${item.msgId}&visitorId=${item.visitorId}&agentId=${item.agentId}`,
+      });
     }
-  }, [data]);
 
   useReachBottom(() => {
     loadMore();
   });
 
+  useEffect(()=> {
+    fetchData()
+  }, [visitorId])
+
+  const renderAsistentContent = (item: TVisitorChat) => {
+    if(item.contentType === EContentType.AiseekQA){
+      const body = item.body
+      const payload = body?.content?.answer?.payload;
+      const links = payload?.links ?? [];
+      const pics = payload?.pics ?? [];
+      return (
+        <View>
+          <View>{item.content}</View>
+          <View className="pb-12">
+            {pics.map((pic: string) => {
+              return (
+                <View>
+                  <Image src={pic} mode="widthFix" className="w-full"></Image>
+                </View>
+              );
+            })}
+          </View>
+          <View className="pb-12">
+            {links.map((link: string) => {
+              return <Text>{link}</Text>;
+            })}
+          </View>
+        </View>
+      );
+    }
+    return <View>{item.content}</View>
+  }
+
   const renderContent = (item: TVisitorChat) => {
     if (item.role === "user") {
       return <View className="text-14 text-title mb-8">{item?.content}</View>;
@@ -77,8 +118,8 @@ export default () => {
         className="p-16 text-title rounded-8 relative"
         style={{ backgroundColor: "#F4F9FF" }}
       >
-        <View>{item.content}</View>
-        <View className={style.editButton}>
+        <View>{renderAsistentContent(item)}</View>
+        <View className={style.editButton} onClick={()=> handleEdit(item)}>
           <Image src={IconEditButtonBlue} className="w-14 h-14" />
         </View>
       </View>
@@ -91,7 +132,7 @@ export default () => {
       {visitor ? <VisitorSummary data={visitor} /> : <></>}
       <View className="flex-1 overflow-hidden w-full mb-100">
         <View className="flex flex-col gap-16 px-16 w-full">
-          {list.map((item) => {
+          {parsedList.map((item) => {
             return (
               <View className={` bg-white rounded-12 overflow-hidden`}>
                 <View className="p-16">{renderContent(item)}</View>

+ 1 - 1
src/pages/chat/components/input-bar/TextInputBar.tsx

@@ -35,7 +35,7 @@ export default ({disabled, onIconClick, onSend}:Props) => {
         placeholder="有问题尽管问我..." 
         value={value} 
         cursorSpacing={400}
-        // onConfirm={handleConfirm}
+        onConfirm={handleConfirm}
         onInput={(value: any) => handleInput(value)}
         >
       </WemetaInput>

+ 8 - 8
src/pages/chat/components/input-bar/chatInput.ts

@@ -2,7 +2,7 @@ import { useState } from "react";
 import TextInputBar from "./TextInputBar";
 import VoiceInputBar from "./VoiceInputBar";
 import { textChat } from "@/service/bot";
-import { TMessage,TRobotMessage, useTextChat } from "@/store/textChat";
+import { useTextChat } from "@/store/textChat";
 import { TAgentDetail } from "@/types/agent";
 import { delay, getLoginId, isSuccess } from "@/utils";
 import { EAI_MODEL } from "@/consts/enum";
@@ -104,17 +104,17 @@ export const useChatInput = ({ agent, setShowWelcome, setDisabled, }: Props) =>
         console.log("received:", m);
         if (m.reasoningContent) {
           updateRobotReasoningMessage(
+            currentRobotMsgUk,
             m.reasoningContent,
-            currentRobotMsgUk
+            m.body,
           );
         } else {
-          updateRobotMessage(m.content);
+          updateRobotMessage(m.content, m.body);
         }
       },
       onFinished: async () => {
-        console.log("回复完毕 ok");
         const currentRobotMessage = getCurrentRobotMessage();
-        console.log(currentRobotMessage,333)
+        console.log("回复完毕 ok, 当前robotmessage: ", currentRobotMessage);
         if(!agent.agentId){
           return
         }
@@ -124,13 +124,13 @@ export const useChatInput = ({ agent, setShowWelcome, setDisabled, }: Props) =>
           updateRobotMessage("服务器繁忙...");
           return 
         }
-
+        
         // 将智能体的回答保存至服务器
         await saveMessageToServer({
           loginId,
           messages: [{
-            content: currentRobotMessage.content,
-            contentType: EContentType.TextPlain,
+            content: currentRobotMessage?.body?.content ?? currentRobotMessage.content,
+            contentType: currentRobotMessage?.body?.contentType ?? EContentType.TextPlain,
             isStreaming: false,
             role: currentRobotMessage.role,
             msgUk: currentRobotMessage.msgUk,

+ 13 - 2
src/pages/chat/components/input-bar/message.ts

@@ -1,5 +1,16 @@
 import { appendMessages } from "@/service/bot"
-import type { TMessageHistories, TRequestBody, TAppendMessages } from "@/types/bot";
+import { type TMessageHistories, type TRequestBody, type TAppendMessages, EContentType } from "@/types/bot";
 export const saveMessageToServer = async(data: TAppendMessages) => {
-  await appendMessages(data)
+  const postData = {
+    ...data,
+    messages: data.messages.map((message)=> {
+      
+      if(message.contentType === EContentType.AiseekQA){
+        message.content = JSON.stringify(message.content)
+        return message
+      }
+      return message
+    })
+  }
+  await appendMessages(postData)
 }

+ 71 - 0
src/pages/chat/components/keyboard.ts

@@ -0,0 +1,71 @@
+import Taro from "@tarojs/taro";
+import { useEffect, useState } from "react";
+
+export const useKeyboard = (scrollViewRef: React.MutableRefObject<any>, messageList:any[])=> {
+  
+  const [keyboardHeight, setKeyboardHeight] = useState(0);
+  const [contentHeight, setContentHeight] = useState(0);
+  const [scrollViewHeight, setScrollViewHeight] = useState(0);
+  // 计算 marginTopOffset 偏移的距离
+  const marginTopOffset = (() => {
+    if (keyboardHeight <= 0) return 0;
+    // 如果内容超过滚动容器,取键盘弹起高度
+    if (contentHeight > scrollViewHeight) {
+      return -keyboardHeight;
+    }
+    // 如果内容+键盘弹起高度超过滚动容器, 则取其差值
+    if (contentHeight + keyboardHeight > scrollViewHeight) {
+      // 内容+键盘弹起高度 - 滚动容器高度
+      return -(contentHeight + keyboardHeight - scrollViewHeight);
+    }
+  })();
+
+  useEffect(() => {
+    // 监听键盘高度变化
+    Taro.onKeyboardHeightChange((res) => {
+      if (res.height <= 0) {
+        return setKeyboardHeight(0);
+      }
+
+      setKeyboardHeight(res.height - 24);
+    });
+
+    return () => {
+      // 清理监听器
+      Taro.offKeyboardHeightChange();
+    };
+  }, []);
+
+  // 监听内容高度和 ScrollView 高度变化
+  useEffect(() => {
+    if (scrollViewRef.current) {
+      const query = Taro.createSelectorQuery();
+      // 获取聊天内容高度
+      query
+        .select("#message-list")
+        .boundingClientRect((rect: any) => {
+          if (rect) {
+            setContentHeight(rect.height);
+          }
+        })
+        .exec();
+      // 获取滚动容器高度
+      query
+        .select("#scroll-view")
+        .boundingClientRect((rect: any) => {
+          if (rect) {
+            setScrollViewHeight(rect.height);
+          }
+        })
+        .exec();
+    }
+  }, [messageList]);
+
+  return {
+    marginTopOffset,
+    keyboardHeight,
+    setKeyboardHeight,
+    setContentHeight,
+    setScrollViewHeight,
+  }
+}

+ 26 - 70
src/pages/chat/index.tsx

@@ -8,8 +8,7 @@ import InputBar from "./components/input-bar";
 import { useEffect, useState, useRef } from "react";
 import { useTextChat } from "@/store/textChat";
 import { EAI_MODEL } from "@/consts/enum";
-import type { TRobotMessage } from "@/store/textChat";
-import { EChatRole, TChatRole, TMessage } from "@/types/bot";
+import { EChatRole, TRobotMessage, TMessage, EContentType } from "@/types/bot";
 
 import ChatWelcome from "./components/chat-welcome";
 import IconArrowLeft from "@/components/icon/icon-arrow-left";
@@ -21,7 +20,7 @@ import { getMessageHistories } from "@/service/bot";
 import { useAppStore } from "@/store/appStore";
 
 import RecommendQuestions from './components/RecommendQuestions'
-
+import {useKeyboard} from './components/keyboard'
 
 
 export default function Index() {
@@ -30,7 +29,7 @@ export default function Index() {
   if (!agentId) {
     return <View>没有相应的智能体</View>;
   }
-  const headerHeight = useAppStore((state) => state.headerHeight);
+  
   const { fetchAgent, fetchAgentProfile, } = useAgentStore()
   
 const agent = useAgentStore((state) => {
@@ -39,18 +38,16 @@ const agent = useAgentStore((state) => {
     }
     return state.agent
   });
-
+  const scrollViewRef = useRef<any>(null);
   const [deepThink, setDeepThink] = useState(EAI_MODEL.DeepseekChat);
-
+  const messageList = useTextChat((state) => state.list);
+  
+  const {keyboardHeight, marginTopOffset} = useKeyboard(scrollViewRef, messageList)
   
 
-  const [keyboardHeight, setKeyboardHeight] = useState(0);
-  const [contentHeight, setContentHeight] = useState(0);
-  const [scrollViewHeight, setScrollViewHeight] = useState(0);
-
-  const scrollViewRef = useRef<any>(null);
+  
 
-  const messageList = useTextChat((state) => state.list);
+  
   const { destroy, setScrollTop, genSessionId } = useTextChat();
   const scrollTop = useTextChat((state) => state.scrollTop);
 
@@ -66,11 +63,25 @@ const agent = useAgentStore((state) => {
     });
     return res.data;
   };
-  const { list, loadMore, pageIndex, mutate } = useLoadMoreInfinite<(TMessage | TRobotMessage)[]>(
+  const { list, loadMore, pageIndex, mutate } = useLoadMoreInfinite<TMessage[]|TRobotMessage[]>(
     createKey(`messeagehistories${isVisitor}${agentId}`),
     fetcher,
   );
 
+  const parsedList = list.map((item: TMessage|TRobotMessage) => {
+    if(item.contentType == EContentType.AiseekQA){
+      try{
+        const contentJson = JSON.parse(item.content as string)
+        item.content = contentJson.answer.text
+        // 把消息详情放入统一 body 中
+        item.body = {...item, content: contentJson,}
+      }catch(e){
+        // console.error(e)
+      }
+    }
+    return item
+  })
+
   const [showWelcome, setShowWelcome] = useState(!list.length);
 
   //  加载更多
@@ -104,61 +115,6 @@ const agent = useAgentStore((state) => {
     }
   }, [pageIndex]);
 
-  // 计算 marginTopOffset 偏移的距离
-  const marginTopOffset = (() => {
-    if (keyboardHeight <= 0) return 0;
-    // 如果内容超过滚动容器,取键盘弹起高度
-    if (contentHeight > scrollViewHeight) {
-      return -keyboardHeight;
-    }
-    // 如果内容+键盘弹起高度超过滚动容器, 则取其差值
-    if (contentHeight + keyboardHeight > scrollViewHeight) {
-      // 内容+键盘弹起高度 - 滚动容器高度
-      return -(contentHeight + keyboardHeight - scrollViewHeight);
-    }
-  })();
-
-  useEffect(() => {
-    // 监听键盘高度变化
-    Taro.onKeyboardHeightChange((res) => {
-      if (res.height <= 0) {
-        return setKeyboardHeight(0);
-      }
-
-      setKeyboardHeight(res.height - 24);
-    });
-
-    return () => {
-      // 清理监听器
-      Taro.offKeyboardHeightChange();
-    };
-  }, []);
-
-  // 监听内容高度和 ScrollView 高度变化
-  useEffect(() => {
-    if (scrollViewRef.current) {
-      const query = Taro.createSelectorQuery();
-      // 获取聊天内容高度
-      query
-        .select("#message-list")
-        .boundingClientRect((rect: any) => {
-          if (rect) {
-            setContentHeight(rect.height);
-          }
-        })
-        .exec();
-      // 获取滚动容器高度
-      query
-        .select("#scroll-view")
-        .boundingClientRect((rect: any) => {
-          if (rect) {
-            setScrollViewHeight(rect.height);
-          }
-        })
-        .exec();
-    }
-  }, [messageList]);
-
   useEffect(() => {
     genSessionId();
   }, []);
@@ -216,7 +172,7 @@ const agent = useAgentStore((state) => {
 
   return (
     <PageCustom fullPage style={{ overflow: "hidden" }}>
-      <NavBarNormal leftColumn={renderNavLeft}></NavBarNormal>
+      <NavBarNormal blur leftColumn={renderNavLeft}></NavBarNormal>
       {renderTopBg()}
       <View
         className="flex flex-col w-full h-screen relative z-10"
@@ -237,7 +193,7 @@ const agent = useAgentStore((state) => {
           {showWelcome && <ChatWelcome agent={agent} />}
           <View id="messageList" className="flex flex-col gap-8 px-18">
             {/* 复制 histories 再 reverse 否则会影响 state */}
-            {[...[...list].reverse(), ...messageList].map((message) => {
+            {[...[...parsedList].reverse(), ...messageList].map((message) => {
               return (
                 <ChatMessage
                   key={message.msgUk}

+ 48 - 2
src/pages/dislike-messages/index.tsx

@@ -21,6 +21,7 @@ import { useModalStore } from "@/store/modalStore";
 import { isSuccess } from "@/utils";
 import { useLoadMoreInfinite, createKey } from "@/utils/loadMoreInfinite";
 import EmptyData from "@/components/empty-data";
+import { EContentType } from "@/types/bot";
 
 export default function Index() {
   const router = useRouter();
@@ -52,6 +53,16 @@ export default function Index() {
       item.user = list[i];
       if (list[i + 1]?.role === "assistant") {
         item.assistant = list[i + 1];
+        if(item.assistant.contentType == EContentType.AiseekQA){
+          try{
+            const contentJson = JSON.parse(item.assistant.content as string)
+            item.assistant.content = contentJson.answer.text
+            // 把消息详情放入统一 body 中
+            item.assistant.body = {...item.assistant, content: contentJson, contentType: item.assistant.contentType}
+          }catch(e){
+            // console.error(e)
+          }
+        }
         qaList.push(item);
         i++; // 跳过下一个元素,因为它已经被配对了
       }else{
@@ -66,7 +77,7 @@ export default function Index() {
 
   const handleEdit = (item: TVisitorChat) => {
     Taro.navigateTo({
-      url: `/pages/dislike-message-editor/index?msgId=${item.msgId}&visitorId=${item.visitorId}&agentId=${item.agentId}`,
+      url: `/pages/message-editor/index?msgId=${item.msgId}&visitorId=${item.visitorId}&agentId=${item.agentId}`,
     });
   };
 
@@ -127,6 +138,41 @@ export default function Index() {
     ];
   };
 
+  const renderContent = (item:TVisitorChat)=> {
+    if(item.contentType === EContentType.TextPlain){
+      return item.content
+    }
+    if(item.contentType === EContentType.AiseekQA){
+      try{
+        const body = item.body
+        const payload = body?.content?.answer?.payload;
+        const links = payload?.links ?? [];
+        const pics = payload?.pics ?? [];
+        return (
+          <View>
+            <View>{item.content}</View>
+            <View className="pb-12">
+              {pics.map((pic: string) => {
+                return (
+                  <View>
+                    <Image src={pic} mode="widthFix" className="w-full"></Image>
+                  </View>
+                );
+              })}
+            </View>
+            <View className="pb-12">
+              {links.map((link: string) => {
+                return <Text>{link}</Text>;
+              })}
+            </View>
+          </View>
+        );
+      }catch(e){}
+      return <View>解析出错...</View>;
+    }
+    return <></>
+  }
+
   return (
     <PageCustom fullPage>
       <NavBarNormal scrollFadeIn>待处理差评</NavBarNormal>
@@ -176,7 +222,7 @@ export default function Index() {
                       </View>
                       <View className="flex-1 text-12 leading-20 text-gray-45 truncate">
                         <View className="truncate">
-                          {item.assistant.content}
+                          {renderContent(item.assistant)}
                         </View>
 
                         {!!item.assistant?.correctionLinks?.length && (

+ 23 - 18
src/pages/editor-contact/index.tsx

@@ -17,19 +17,19 @@ const RenderEntCard = (
   navToUrl: (url: string) => void
 ) => {
   const agentContactCard = useAgentStore((state)=> state.agentContactCard)
-  const { submit } = useEditContactCard('position', agentContactCard?.position)
-  // 当前选中的值
-  const options = ['销售人员', '客服与售后支持', '市场与商务合作人员', '新员工 / 培训岗位', '管理者 / 内容运营者']
-  // 是否显示选择器
-  const [showPicker, setShowPicker] = useState(false)
+  // const { submit } = useEditContactCard('position', agentContactCard?.position)
+  // // 当前选中的值
+  // const options = ['销售人员', '客服与售后支持', '市场与商务合作人员', '新员工 / 培训岗位', '管理者 / 内容运营者']
+  // // 是否显示选择器
+  // const [showPicker, setShowPicker] = useState(false)
 
-  // 当前选中的值
-  const [selected, setSelected] = useState(options[0])
+  // // 当前选中的值
+  // const [selected, setSelected] = useState(options[0])
 
-  const handlePicked = (value: string) => {
-    setSelected(value)
-    submit(value, false)
-  }
+  // const handlePicked = (value: string) => {
+  //   setSelected(value)
+  //   submit(value, false)
+  // }
 
   
   if (!agent?.isEnt) {
@@ -49,14 +49,15 @@ const RenderEntCard = (
             </ListRow>
             <ListRow
               arrow
+              onClick={()=> {navToUrl('/pages/editor-pages/editor-position/index')}}
             >
-              
-              <PickerSingleColumn headerTitle="您的岗位" options={options} selected={selected} onPicked={handlePicked} showPicker={showPicker} setShowPicker={setShowPicker}>
-                <View className="flex items-center font-normal" onClick={() => setShowPicker(true)}>
+              <View className="flex items-center font-normal">
                   <View className="flex-1 font-normal">职位</View>
                   <View className="text-gray-65 mr-8">{agent?.position}</View>
                 </View>
-              </PickerSingleColumn>
+              {/* <PickerSingleColumn headerTitle="您的岗位" options={options} selected={selected} onPicked={handlePicked} showPicker={showPicker} setShowPicker={setShowPicker}>
+                
+              </PickerSingleColumn> */}
             </ListRow>
         </ListWrapper>
       </View>;
@@ -82,14 +83,18 @@ const RenderEntCard = (
           </ListRow>
           <ListRow
             arrow
+            onClick={()=> {navToUrl('/pages/editor-pages/editor-position/index')}}
           >
-            
-            <PickerSingleColumn headerTitle="您的岗位" options={options} selected={selected} onPicked={handlePicked} showPicker={showPicker} setShowPicker={setShowPicker}>
+            <View className="flex items-center font-normal">
+              <View className="flex-1 font-normal">职位</View>
+              <View className="text-gray-65 mr-8">{agent?.position}</View>
+            </View>
+            {/* <PickerSingleColumn headerTitle="您的岗位" options={options} selected={selected} onPicked={handlePicked} showPicker={showPicker} setShowPicker={setShowPicker}>
               <View className="flex items-center font-normal" onClick={() => setShowPicker(true)}>
                 <View className="flex-1 font-normal">职位</View>
                 <View className="text-gray-65 mr-8">{agent?.position}</View>
               </View>
-            </PickerSingleColumn>
+            </PickerSingleColumn> */}
           </ListRow>
       </ListWrapper>
     </View>

+ 1 - 0
src/pages/editor-pages/editor-address/index.tsx

@@ -41,6 +41,7 @@ export default function Index() {
             onInput={(value: string) => onChange(value)}
             placeholder="填写联系地址"
             autoFocus
+            maxlength={100}
           />
           <View className="text-primary" onClick={handleEditAddress}>
             选择地址

+ 5 - 0
src/pages/editor-pages/editor-position/index.config.ts

@@ -0,0 +1,5 @@
+export default definePageConfig({
+  navigationBarTitleText: '章节标题',
+  "usingComponents": {},
+  navigationStyle: 'custom'
+})

+ 2 - 0
src/pages/editor-pages/editor-position/index.module.less

@@ -0,0 +1,2 @@
+
+

+ 40 - 0
src/pages/editor-pages/editor-position/index.tsx

@@ -0,0 +1,40 @@
+import { useState } from "react";
+import { View } from "@tarojs/components";
+
+import PageCustom from "@/components/page-custom/index";
+import NavBarNormal from "@/components/NavBarNormal/index";
+import ButtonMain from "@/components/buttons/ButtonMain";
+import BottomBar from "@/components/BottomBar";
+
+import WemetaTextarea from "@/components/wemeta-textarea/index";
+import { useAgentStore } from "@/store/agentStore";
+import useEditContactCard from "@/hooks/useEditContactCard";
+export default function Index() {
+  const agentContactCard = useAgentStore((state)=> state.agentContactCard)
+  const {value, onChange, handleSubmit} = useEditContactCard('position', agentContactCard?.position)
+  
+  
+
+  return (
+    <PageCustom>
+      <NavBarNormal>职位</NavBarNormal>
+      <View className="flex flex-col items-center w-full">
+        <View
+          className="w-full p-16"
+        >
+          <WemetaTextarea
+            value={value}
+            onBlur={(value: string) => onChange(value)}
+            onInput={(value: string) => onChange(value)}
+            placeholder="填写职位名称"
+            autoFocus
+            maxlength={40}
+          />
+        </View>
+        <BottomBar>
+          <ButtonMain className="flex-1" disabled={!value.length} onClick={handleSubmit}>保存</ButtonMain>
+        </BottomBar>
+      </View>
+    </PageCustom>
+  );
+}

+ 0 - 5
src/pages/knowledge/components/AsistantMessage/index.tsx

@@ -7,11 +7,6 @@ export const WelcomeCard = () => {
         Hi~我是你的 AI 小助手!
         请将资料发送到我的企业微信,我会自动为你解析内容,并同步到这里。
       </View>
-      <Text>
-        {` · 无限制,图片/文档/链接等多种格式;
-         · 无需小程序,直接在微信内操作;
-         · 智能解析处理,生成高质量的QA集。`}
-      </Text>
       <WeComQRcode />
     </View>
   );

+ 0 - 4
src/pages/knowledge/components/PersonalTab/components/ScrollListChat.tsx

@@ -127,10 +127,6 @@ const Index = ({assistantOnly, setTotalCount}: IProps) => {
         {/* 欢迎语 */}
         {(reversedList.length <= 0) && <MessageRobotRich data={DEFAULT_AGENT}><WelcomeCard /></MessageRobotRich>}
 
-        {/* 添加小蓝本助手成功 */}
-        {/* <MessageRobotRich data={avatarData}>
-          <AddSuccessfulTips />
-        </MessageRobotRich> */}
 
         {reversedList.map((group) => {
           // 渲染自己发送的消息

+ 2 - 7
src/pages/knowledge/components/PersonalTab/index.tsx

@@ -42,13 +42,8 @@ const Index = () => {
 
       <Popup title="添加AI小助理的企业微信" setShow={setShowAiAsistant} show={showAiAsistant}>
         <View className="p-20">
-          <View>将资料发送到我的企业微信,我会自动为你解析内容,并同步到这里。</View>
-          <Text>
-            {` · 无限制,图片/文档/链接等多种格式;;
-            · 无需小程序,直接在微信内操作;;
-            · 智能解析处理,生成高质量的QA集。`}
-          </Text>
-          <WeComQRcode />
+          <View className="text-12 leading-24 text-[#414A64]">请将资料发送到我的企业微信,我会自动为你解析内容,并同步到这里。</View>
+          <WeComQRcode text="长按二维码添加" />
         </View>
       </Popup>
     </View>

+ 0 - 0
src/pages/dislike-message-editor/index.config.ts → src/pages/message-editor/index.config.ts


+ 24 - 6
src/pages/dislike-message-editor/index.tsx → src/pages/message-editor/index.tsx

@@ -29,6 +29,7 @@ import { isSuccess } from "@/utils";
 import { editVisitorDislikeAnswer, getVisitorMessagesByMsgId } from "@/service/visitor";
 
 import { TVisitorChat } from "@/types/visitor";
+import { EContentType, TMessageBodyContent } from "@/types/bot";
 
 type TFormdata = {content:string, correctionAnswer: string, links: string[], mediaList: TMediaType[]}
 
@@ -37,10 +38,11 @@ export default function Index() {
   const router = useRouter()
   const { msgId, visitorId, agentId } = router.params as {msgId: string, visitorId: string, agentId: string}
 
-  console.log(msgId, visitorId, agentId, 8888)
+  
   
   const [visitorQ, setQVisitor] = useState<TVisitorChat|null>(null)
   const [visitor, setVisitor] = useState<TVisitorChat|null>(null)
+  const [isDislike, setIsDislike] = useState(false)
   const [formData, setFormData] = useState<TFormdata>(
     {
       content: '',
@@ -134,11 +136,27 @@ export default function Index() {
       if(data[1]){
         setQVisitor(data[1])
       }
+
+      setIsDislike(_visitor.isDislike)
+      
+      let content = _visitor.content
+      let links = _visitor.correctionLinks ?? []
+      let pics = _visitor.correctionPics ?? []
+      // 特殊处理消息内容
+      if(_visitor.contentType === EContentType.AiseekQA){
+        try{
+          const json = JSON.parse(_visitor.content) as TMessageBodyContent
+          content = json.answer.text
+          links = json.answer.payload.links
+          pics = json.answer.payload.pics
+        }catch(e){}
+      }
+
       setFormData({
-        content: _visitor.content,
+        content: content,
         correctionAnswer: _visitor.correctionAnswer,
-        links: _visitor.correctionLinks ?? [],
-        mediaList: _visitor.correctionPics?.map( pic => {
+        links: links,
+        mediaList: pics?.map( pic => {
           return {fileType: 'image', url: pic}
         }) ?? [],
       })
@@ -176,10 +194,10 @@ export default function Index() {
 
   return (
     <PageCustom>
-      <NavBarNormal scrollFadeIn backText={'待处理差评'}></NavBarNormal>
+      <NavBarNormal scrollFadeIn>{isDislike ? '纠错' : '编辑'}</NavBarNormal>
       <View className="w-full pb-120">
         <View className="p-16">
-          <View className="text-gray-45 text-14 text-center py-8">修改后的问答将存入知识库,持续训练提升您的智能体</View>
+          {isDislike && <View className="text-gray-45 text-14 text-center py-8">修改后的问答将存入知识库,持续训练提升您的智能体</View>}
           <View className="flex flex-col gap-16">
             <View className="flex flex-col">
               <View className="flex items-start gap-8 mb-6">

+ 2 - 2
src/service/bot.ts

@@ -1,7 +1,7 @@
 import { bluebookAiAgent } from "@/xiaolanbenlib/api/index";
 import Taro from "@tarojs/taro";
 import { getSimpleHeader } from "@/xiaolanbenlib/module/axios.js";
-import JsonChunkParser from "@/utils/jsonChunkParser";
+import JsonChunkParser, { ICompleteCallback } from "@/utils/jsonChunkParser";
 import request from "@/xiaolanbenlib/module/axios.js";
 import type { TMessageHistories, TRequestBody, TAppendMessages } from "@/types/bot";
 import { TextDecoder } from "text-encoding-shim";
@@ -79,7 +79,7 @@ export const speechToText = async (agentId: string, tempFilePath: string) => {
 export type TTextChatParams = {
   params: TRequestBody;
   onStart: () => void;
-  onReceived: (m: { content: string; reasoningContent: string }) => void;
+  onReceived: (m: ICompleteCallback) => void;
   onFinished: () => void;
   onError: () => void;
 };

+ 1 - 0
src/store/modalStore.ts

@@ -6,6 +6,7 @@ type ModalConfig ={
   content: React.ReactNode;
   beforeClose?: () => Promise<void> | void; // 用户自定义关闭前回调
   onCancel?: () => Promise<void> | void; // 用户自定义关闭前回调
+  showCancelButton?: boolean
   onConfirm?: () => Promise<void> | void; // 用户自定义关闭前回调
 }
 type ModalStore = {

+ 6 - 23
src/store/textChat.ts

@@ -10,23 +10,6 @@ import { EChatRole, EContentType, TChatRole } from "@/types/bot";
 
 
 
-export type TMessage = {
-  msgUk: string;
-  content: string;
-  role: TChatRole
-  isStreaming?: boolean
-  saveStatus: 0|1|2,
-};
-
-export type TRobotMessage = {
-  reasoningContent: string
-  robot: {
-    avatar: string;
-    agentId: string;
-    name: string;
-  };
-} & TMessage;
-
 type TRobotMessageWithOptionalId = Omit<TRobotMessage, "msgUk"> & {
   msgUk?: string; // 将 messageId 设置为可选
   reasoningContent?: string; // 添加 reasoningContent
@@ -52,8 +35,8 @@ export interface TextChat {
   // 更新自己发出的气泡框
   updateMessage: (content: string, msgUk: string) => string;
   // 更新机器人汽泡框内的内容实现 gpt 的效果
-  updateRobotMessage: (content: string) => void;
-  updateRobotReasoningMessage: (reasoningContent: string, msgUk: string) => void;
+  updateRobotMessage: (content: string, body?: Record<string,any>) => void;
+  updateRobotReasoningMessage: (msgUk: string, reasoningContent: string, body?:Record<string,any>) => void;
   getCurrentRobotMessage:() => TRobotMessage|undefined
   deleteMessage: (msgUk: string) => void;
   // 清空
@@ -147,11 +130,11 @@ export const useTextChat = create<TextChat>((set, get) => ({
     });
     return msgUk
   },
-  updateRobotMessage: (content) => {
+  updateRobotMessage: (content, body={}) => {
     set((state) => {
       const updatedList = state.list.map((message) => {
         if (message.msgUk === state.currentRobotMsgUk) {
-          return { ...message, content: message.content + content }; // 更新 content
+          return { ...message, content: message.content + content, body }; // 更新 content
         }
         return message; // 返回未修改的 message
       });
@@ -163,14 +146,14 @@ export const useTextChat = create<TextChat>((set, get) => ({
     const currentMessage = state.list.find((message)=>  message.msgUk === state.currentRobotMsgUk) as TRobotMessage
     return currentMessage
   },
-  updateRobotReasoningMessage: (reasoningContent, msgUk) => {
+  updateRobotReasoningMessage: (msgUk, reasoningContent, body={}) => {
     set((state) => {
       // 由于 currentRobotMsgUk 可能发生变化, 所以需要传递 msgUk
       // console.log(state.currentRobotMsgUk, msgUk)
       const updatedList = state.list.map((message) => {
         if (message.msgUk === msgUk) {
           //@ts-ignore
-          return { ...message, reasoningContent: message.reasoningContent + reasoningContent }; // 更新 reasoningContent
+          return { ...message, body, reasoningContent: message.reasoningContent + reasoningContent }; // 更新 reasoningContent
         }
         return message; // 返回未修改的 message
       });

+ 3 - 0
src/types/agent.ts

@@ -64,6 +64,9 @@ export type TAgentDetail = {
   questionGuides?: string[],
   voiceId?: string
   isMineAgent: boolean
+  deletedTip?: string // 如果是已删除状态时的提示词 ,
+  status?:  'normal' | 'deleted' //  智能体状态:normal 正常/ deleted 已删除 ,
+  toAgentId?: string  //  当是删除状态时,提示跳转至新的智能体 ID
 }
 
 export type TAgentContactCard = {

+ 28 - 1
src/types/bot.ts

@@ -1,7 +1,23 @@
 
+// contentType === "aiseek/qa" 消息体内容
+export type TMessageBodyContent = {
+  "answer": {
+      "payload": {
+          "links": string[],
+          "pics": string[]
+      },
+      "text": string
+  },
+  "qaId": string
+}
+export type TMessageBody = {
+  content: TMessageBodyContent,
+  contentType: EContentType,
+}
 
 export type TMessage = {
-  content: string,
+  content: string|Record<string, any>,
+  contentType: EContentType
   dislikeReason: string,
   isDislike?: boolean,
   isLike?: boolean,
@@ -11,8 +27,19 @@ export type TMessage = {
   msgUk: string,
   originalAgentId: string,
   role: string
+  body?: TMessageBody
 }
 
+export type TRobotMessage = {
+  reasoningContent: string
+  body?: Record<string,any>
+  robot: {
+    avatar: string;
+    agentId: string;
+    name: string;
+  };
+} & TMessage;
+
 
 
 export enum EContentType {

+ 4 - 1
src/types/visitor.ts

@@ -1,12 +1,14 @@
 // TVisitorAgent 访客详情
 // agentId (string, optional): 
 
+import { TContentType, TMessageBody } from "./bot"
+
 
 // 访客的会话详情 
 export type TVisitorChat = {
   agentId: string ,
   content: string  // 对方消息内容 ,
-  contentType: string // 消息类型:text/plain; application/json; aiseek/audio_chunk; aiseek/thinking; aiseek/function_call; aiseek/multimodal; aiseek/qa; ,
+  contentType: TContentType // 消息类型:text/plain; application/json; aiseek/audio_chunk; aiseek/thinking; aiseek/function_call; aiseek/multimodal; aiseek/qa; ,
   correctionAnswer: string // 纠错回答内容 ,
   correctionId :number //  纠错记录ID ,
   correctionLinks: string[] // 纠错链接 ,
@@ -21,6 +23,7 @@ export type TVisitorChat = {
   msgUk :string  // 消息唯一值,由前端生成的UUID ,
   role :string  // 角色:system/user/assistant/function/tool ,
   visitorId: number//  会话用户对应的访客记录ID
+  body?: TMessageBody
 }
 
 

+ 1 - 1
src/utils/jsonChunkParser.ts

@@ -30,7 +30,7 @@ data:{"content":{"answer":{"payload":{},"text":"你好,我是饭饭,很高
 data: [DONE]
 
 */
-interface ICompleteCallback {
+export interface ICompleteCallback {
   content: string;
   reasoningContent?: string;
   body: Record<string,any>

+ 4 - 3
src/utils/loadMoreInfinite.ts

@@ -10,24 +10,25 @@ export type TResponseData<D> = {
 
 export const createKey = (query: string, pageSize: number = 10, extra: Record<string, any> = []) => {
   return (pageIndex: number, previousPageData) => {
+    // pageIndex 后端要求是从 1 开始
     if (pageIndex === 0)
       return [
         query,
-        { nextId: undefined, pageSize, pageIndex },
+        { nextId: undefined, pageSize, pageIndex: pageIndex + 1 },
         extra,
       ];
       // 如果是以 pageIndex 作为分页依据
     if (previousPageData && previousPageData.pageIndex) {
       return [
         query,
-        { pageSize, pageIndex: previousPageData.pageIndex+1 },
+        { pageSize, pageIndex: previousPageData.pageIndex + 1 },
         extra,
       ];
     }
     if (previousPageData && previousPageData.nextId) {
       return [
         query,
-        { pageSize, nextId: previousPageData.nextId, pageIndex },
+        { pageSize, nextId: previousPageData.nextId, pageIndex: pageIndex + 1 },
         extra,
       ];
     }