Jelajahi Sumber

feat: 联系人列表

王晓东 1 bulan lalu
induk
melakukan
3a3f7942ea
41 mengubah file dengan 708 tambahan dan 322 penghapusan
  1. 1 1
      src/app.config.ts
  2. 0 0
      src/components/AgentPage/components/AgentQRcode/index.module.less
  3. 12 7
      src/components/AgentPage/components/AgentQRcode/index.tsx
  4. 9 19
      src/components/AgentPage/components/AgentSwap/index.tsx
  5. 42 37
      src/components/AgentPage/components/SummaryBar/index.tsx
  6. 3 2
      src/components/AgentPage/index.tsx
  7. 13 0
      src/components/ContactIcon/index.module.less
  8. 35 0
      src/components/ContactIcon/index.tsx
  9. 7 0
      src/components/icon/IconLocationBlue/index.tsx
  10. 0 0
      src/components/icon/IconLocationGray/index.tsx
  11. 7 0
      src/components/icon/IconMailBlue/index.tsx
  12. 0 0
      src/components/icon/IconMailGray/index.tsx
  13. 7 0
      src/components/icon/IconPhoneBlue/index.tsx
  14. 0 0
      src/components/icon/IconPhoneGray/index.tsx
  15. 7 0
      src/components/icon/IconQrcodeBlue/index.tsx
  16. 0 0
      src/components/icon/IconQrcodeGray/index.tsx
  17. 3 0
      src/images/svgs/icon-location-blue.svg
  18. 3 0
      src/images/svgs/icon-mail-blue.svg
  19. 3 0
      src/images/svgs/icon-phone-blue.svg
  20. 11 0
      src/images/svgs/icon-qrcode-blue.svg
  21. 5 8
      src/pages/agent/components/AgentSetting/components/AgentContactCard/index.tsx
  22. 2 0
      src/pages/chat/components/input-bar/VoiceInputBar.tsx
  23. 10 8
      src/pages/chat/components/input-bar/index.tsx
  24. 0 86
      src/pages/chat/history.ts
  25. 69 15
      src/pages/chat/index.tsx
  26. 8 8
      src/pages/contact/components/contact-card/index.tsx
  27. 28 45
      src/pages/contact/index.tsx
  28. 78 0
      src/pages/profile/components/InitView/index.tsx
  29. 59 0
      src/pages/profile/components/WelcomeTips/index.module.less
  30. 77 0
      src/pages/profile/components/WelcomeTips/index.tsx
  31. 8 0
      src/pages/profile/index.config.ts
  32. 56 0
      src/pages/profile/index.module.less
  33. 54 0
      src/pages/profile/index.tsx
  34. 16 7
      src/service/agent.ts
  35. 30 17
      src/service/contact.ts
  36. 7 3
      src/store/agentStore.ts
  37. 3 3
      src/store/textChat.ts
  38. 1 0
      src/types/agent.ts
  39. 18 0
      src/types/contact.ts
  40. 0 46
      src/utils/dataFetcher.ts
  41. 16 10
      src/utils/loadMore.ts

+ 1 - 1
src/app.config.ts

@@ -1,7 +1,7 @@
 export default defineAppConfig({
   pages: [
     'pages/index/index',
-    // 'pages/profile/index',
+    'pages/profile/index',
     'pages/login/index',
     'pages/dashboard/index',
     'pages/contact/index',

+ 0 - 0
src/components/AgentPage/components/agent-qrcode/index.module.less → src/components/AgentPage/components/AgentQRcode/index.module.less


+ 12 - 7
src/components/AgentPage/components/agent-qrcode/index.tsx → src/components/AgentPage/components/AgentQRcode/index.tsx

@@ -15,6 +15,8 @@ interface IProps {
 }
 export default ({ show, setShow }: IProps) => {
   const agentContactCard = useAgentStore((state) => state.agentContactCard);
+  const agent = useAgentStore((state) => state.agent);
+
   const { setValue, submit } = useEditContactCard(
     "qrCodeUrl",
     agentContactCard?.qrCodeUrl
@@ -39,11 +41,14 @@ export default ({ show, setShow }: IProps) => {
     if (agentContactCard?.qrCodeUrl) {
       return <Image src={agentContactCard?.qrCodeUrl} mode="widthFix"></Image>;
     }
-    return (
-      <View onClick={handleClick}>
-        <QrcodeUploadTips />
-      </View>
-    );
+    if(agent?.isMineAgent){
+      return (
+        <View onClick={handleClick}>
+          <QrcodeUploadTips />
+        </View>
+      );
+    }
+    return <></>;
   };
 
   return (
@@ -54,11 +59,11 @@ export default ({ show, setShow }: IProps) => {
             {renderQrcode()}
           </View>
 
-          <View className={style.bottomButtons}>
+          {agent?.isMineAgent && <View className={style.bottomButtons}>
             <View className="pr-16" onClick={handleClick}>替换</View>
             <View className="text-gray-25">|</View>
             <View className="pl-16" onClick={handleClear}>清空</View>
-          </View>
+          </View>}
         </View>
       </Popup>
     </View>

+ 9 - 19
src/components/AgentPage/components/AgentSwap/index.tsx

@@ -2,19 +2,17 @@ import { View } from "@tarojs/components";
 import style from "./index.module.less";
 import TagCertificated from "@/components/tag-certificated";
 
-import IconPhoneGray from "@/components/icon/icon-phone-gray";
-import IconMailGray from "@/components/icon/icon-mail-gray";
-import IconLocationGray from "@/components/icon/icon-location-gray";
-import IconQRCodeGray from "@/components/icon/icon-qrcode-gray";
+import IconPhoneGray from "@/components/icon/IconPhoneGray";
+import IconMailGray from "@/components/icon/IconMailGray";
+import IconLocationGray from "@/components/icon/IconLocationGray";
+import IconQRCodeGray from "@/components/icon/IconQrcodeGray";
 import IconPlusBlue from "@/components/icon/icon-plus-blue";
-import { useAppStore } from "@/store/appStore";
 import Popup from "@/components/popup/popup";
-import { useState } from "react";
 import WemetaTabs from "@/components/wemeta-tabs/index";
 import { useAgentStore } from "@/store/agentStore";
-import { setDefaultAgent } from "@/service/agent";
 import { TAgent } from "@/types/agent";
 import Taro from "@tarojs/taro";
+import ContactIcon from '@/components/ContactIcon'
 interface IProps {
   show: boolean;
   setShow: (show: boolean) => void;
@@ -60,18 +58,10 @@ export default ({ show, setShow }: IProps) => {
       </View>
     </View>
     <View className={style.icons}>
-      <View className={style.icon}>
-        <IconPhoneGray />
-      </View>
-      <View className={style.icon}>
-        <IconMailGray />
-      </View>
-      <View className={style.icon}>
-        <IconLocationGray />
-      </View>
-      <View className={style.icon}>
-        <IconQRCodeGray />
-      </View>
+      <ContactIcon type="phone" actived={!!item?.mobile}></ContactIcon>
+      <ContactIcon type="mail" actived={!!item?.email}></ContactIcon>
+      <ContactIcon type="location" actived={!!item?.address}></ContactIcon>
+      <ContactIcon type="qrcode" actived={!!item?.qrCodeUrl}></ContactIcon>
     </View>
   </View>
   }

+ 42 - 37
src/components/AgentPage/components/SummaryBar/index.tsx

@@ -9,35 +9,46 @@ import IconSendBlack from "@/components/icon/icon-send-black";
 import IconEditBlack from "@/components/icon/icon-edit-black";
 import IconMoreBlack from "@/components/icon/icon-more-black";
 
-import IconPhoneGray from "@/components/icon/icon-phone-gray";
-import IconMailGray from "@/components/icon/icon-mail-gray";
-import IconLocationGray from "@/components/icon/icon-location-gray";
+import ContactIcon from '@/components/ContactIcon'
 import { useAppStore } from "@/store/appStore";
 import AgentSwap from "../AgentSwap/index";
-import AgentQRCode from "../agent-qrcode/index";
+import AgentQRCode from "../AgentQRcode/index";
 import SharePopup from "@/components/custom-share/share-popup";
 import Taro from "@tarojs/taro";
 import PopupSheets from "@/components/popup/popup-sheets";
-import { useAgentStore } from "@/store/agentStore";
 
 import { useModalStore } from "@/store/modalStore";
-
-export default () => {
+import { TAgentDetail } from "@/types/agent";
+interface IProps {
+  agent: TAgentDetail,
+  isVisitor: boolean, // 是否是访问他人智能体
+}
+export default ({agent, isVisitor}: IProps) => {
   const headerHeight = useAppStore((state) => state.headerHeight);
   const [showAgentSwap, setShowAgentSwap] = useState(false);
   const [showAgentQRcode, setShowAgentQRcode] = useState(false);
   const [showPopup, setShowPopup] = useState(false);
   const [showShare, setShowShare] = useState(false);
 
-  const agent = useAgentStore((state)=> state.agent)
   
 
   const {showModal,} = useModalStore()
 
-  const test = ()=> {
-    
+  const handleClick = ()=> {
+    Taro.navigateTo({
+      url: `/pages/chat/index?agentId=${agent.agentId}`,
+    });
+  }
+
+  const renderValue = (value?:string)=> {
+    return value?.length ? value :  '--'
   }
 
+  // 允许更多操作,非企业智能体且非访问者身份
+  const enableMoreAction = !agent?.isEnt && !isVisitor
+  // 是自己的智能体或者有二维码地址
+  const enableQrcode = agent?.isMineAgent || !!agent?.qrCodeUrl
+
 
   // 自定义背景样式
   const bgImageStyle = {
@@ -45,6 +56,7 @@ export default () => {
     // background: `linear-gradient(rgba(242, 244, 245, 0) 0%, rgba(242, 244, 245, .8) 80% , rgba(242, 244, 245,1) 100%), url(${customBgImg})`,
     backgroundImage: `url(https://cdn.wehome.cn/cmn/png/53/META-H8UKWHWU-2JNUAG2BARJF55VHU9QS3-YBQGHDAM-IW.png)`,
   };
+
   return (
     <View className="relative">
       <View className={style.topBg} style={bgImageStyle}></View>
@@ -64,39 +76,37 @@ export default () => {
                   <TagCertificated />
                 </View>
               </View>
-              <View
+              {!isVisitor && <View
                 className={style.boxButton}
                 onClick={() => {
                   setShowAgentSwap(true);
                 }}
               >
                 <IconSwapBlack />
-              </View>
+              </View>}
             </View>
             <View className="flex items-center gap-8">
               {/* 和TA聊聊 */}
               <View className="flex-1">
                 <View
                   className="button-rounded button-primary"
-                  onClick={() => {
-                    Taro.navigateTo({
-                      url: "/pages/chat/index",
-                    });
-                  }}
+                  onClick={handleClick}
                 >
                   <IconChatWhite />
                   和TA聊聊
                 </View>
               </View>
+
               {/* 二维码 */}
-              <View
+              {enableQrcode && <View
                 className={style.boxButton}
                 onClick={() => {
                   setShowAgentQRcode(true);
                 }}
               >
                 <IconQRCodeBlack />
-              </View>
+              </View>}
+              
               {/* 智能体分享 */}
               <View
                 className={style.boxButton}
@@ -106,22 +116,23 @@ export default () => {
               >
                 <IconSendBlack />
               </View>
+
               {/* 编辑智能体 */}
-              <View
+              {!isVisitor && <View
                 className={style.boxButton}
                 onClick={() => {
                   Taro.navigateTo({ url: `/pages/agent/index?agentId=${agent?.agentId}` });
-                  // Taro.navigateTo({ url: "/pages/editor-contact/index" });
                 }}
               >
                 <IconEditBlack />
-              </View>
+              </View>}
+
               {/* 更多操作 */}
-              <View className={style.boxButton} onClick={()=> {
+              {enableMoreAction && <View className={style.boxButton} onClick={()=> {
                 setShowPopup(true)
               }}>
                 <IconMoreBlack />
-              </View>
+              </View>}
             </View>
           </View>
 
@@ -131,25 +142,20 @@ export default () => {
             </View>
             <View className="flex flex-col gap-20 text-12 leading-20">
               <View className="flex items-center gap-12">
-                <View className={style.icon}>
-                  <IconPhoneGray />
-                </View>
-                <View>{agent?.mobile}</View>
+                <ContactIcon type="phone" actived={!!agent?.mobile}/>
+                <View>{renderValue(agent?.mobile)}</View>
               </View>
               <View className="flex items-center gap-12">
-                <View className={style.icon}>
-                  <IconMailGray />
-                </View>
-                <View>{agent?.email}</View>
+                <ContactIcon type="mail" actived={!!agent?.email}/>
+                <View>{renderValue(agent?.email)}</View>
               </View>
               <View className="flex items-center gap-12">
-                <View className={style.icon}>
-                  <IconLocationGray />
-                </View>
-                <View>{agent?.address}</View>
+                <ContactIcon type="location" actived={!!agent?.address}/>
+                <View>{renderValue(agent?.address)}</View>
               </View>
             </View>
           </View>
+
         </View>
       </View>
 
@@ -170,7 +176,6 @@ export default () => {
               showModal({
                 content: <>确认删除该智能体?</>,
                 onConfirm() {
-                  console.log(111)
                   setShowPopup(false);
                 },
                 onCancel() {

+ 3 - 2
src/components/AgentPage/index.tsx

@@ -14,7 +14,8 @@ interface IProps {
   agentId: string;
 }
 export default function Index({ agentId }: IProps) {
-  const { fetchAgent } = useAgentStore();
+  const { fetchAgent, fetchAgents } = useAgentStore();
+  const agent = useAgentStore((state)=> state.agent);
   const { fetchMyEntList } = useUserStore();
   
   const { setComponentList } = useComponentStore()
@@ -44,7 +45,7 @@ export default function Index({ agentId }: IProps) {
   return (
     <View className="w-full">
       <NavBarNormal scrollFadeIn showBgColor leftColumn={Logo}></NavBarNormal>
-      <SummaryBar></SummaryBar>
+      {agent && <SummaryBar agent={agent}></SummaryBar>}
       <View className="flex flex-col w-full p-16 gap-12">
         <ComponentList components={components}></ComponentList>
       </View>

+ 13 - 0
src/components/ContactIcon/index.module.less

@@ -0,0 +1,13 @@
+.icon{
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  width: 24px;
+  height: 24px;
+  border-radius: 100%;
+  background-color: rgba(#317CFA, .1);
+}
+.iconActive{
+  .icon();
+  background-color: rgba(#317CFA, .1);
+}

+ 35 - 0
src/components/ContactIcon/index.tsx

@@ -0,0 +1,35 @@
+import IconPhoneGray from "@/components/icon/IconPhoneGray";
+import IconPhoneBlue from "@/components/icon/IconPhoneBlue";
+import IconMailGray from "@/components/icon/IconMailGray";
+import IconMailBlue from "@/components/icon/IconMailBlue";
+import IconLocationGray from "@/components/icon/IconLocationGray";
+import IconLocationBlue from "@/components/icon/IconLocationBlue";
+import IconQrcodeBlue from "@/components/icon/IconQrcodeBlue";
+import IconQrcodeGray from "@/components/icon/IconQrcodeGray";
+import style from './index.module.less'
+import { View } from "@tarojs/components";
+
+export interface IndexProps {
+  type: 'phone'|'mail'|'location'|'qrcode'
+  actived: boolean
+}
+
+const Index = ({ type, actived }: IndexProps) => {
+  const getIcon = ()=> {
+    if(type === 'phone'){
+      return actived ? <IconPhoneBlue/> : <IconPhoneGray />
+    }
+    if(type === 'mail'){
+      return actived ? <IconMailBlue/> : <IconMailGray />
+    }
+    if(type === 'location'){
+      return actived ? <IconLocationBlue/> : <IconLocationGray />
+    }
+    if(type === 'qrcode'){
+      return actived ? <IconQrcodeBlue/> : <IconQrcodeGray />
+    }
+  }
+  return <View className={actived ? style.iconActive : style.icon}>{getIcon()}</View>
+}
+
+export default Index;

+ 7 - 0
src/components/icon/IconLocationBlue/index.tsx

@@ -0,0 +1,7 @@
+import { Image } from '@tarojs/components'
+import Icon from '@/images/svgs/icon-location-blue.svg'
+export default () => {
+  return (
+    <Image src={Icon} mode="widthFix" style={{width: '12px', height: '12px'}}></Image>
+  )
+}

+ 0 - 0
src/components/icon/icon-location-gray/index.tsx → src/components/icon/IconLocationGray/index.tsx


+ 7 - 0
src/components/icon/IconMailBlue/index.tsx

@@ -0,0 +1,7 @@
+import { Image } from '@tarojs/components'
+import Icon from '@/images/svgs/icon-mail-blue.svg'
+export default () => {
+  return (
+    <Image src={Icon} mode="widthFix" style={{width: '12px', height: '12px'}}></Image>
+  )
+}

+ 0 - 0
src/components/icon/icon-mail-gray/index.tsx → src/components/icon/IconMailGray/index.tsx


+ 7 - 0
src/components/icon/IconPhoneBlue/index.tsx

@@ -0,0 +1,7 @@
+import { Image } from '@tarojs/components'
+import Icon from '@/images/svgs/icon-phone-blue.svg'
+export default () => {
+  return (
+    <Image src={Icon} mode="widthFix" style={{width: '12px', height: '12px'}}></Image>
+  )
+}

+ 0 - 0
src/components/icon/icon-phone-gray/index.tsx → src/components/icon/IconPhoneGray/index.tsx


+ 7 - 0
src/components/icon/IconQrcodeBlue/index.tsx

@@ -0,0 +1,7 @@
+import { Image } from '@tarojs/components'
+import Icon from '@/images/svgs/icon-qrcode-blue.svg'
+export default () => {
+  return (
+    <Image src={Icon} mode="widthFix" style={{width: '12px', height: '12px'}}></Image>
+  )
+}

+ 0 - 0
src/components/icon/icon-qrcode-gray/index.tsx → src/components/icon/IconQrcodeGray/index.tsx


+ 3 - 0
src/images/svgs/icon-location-blue.svg

@@ -0,0 +1,3 @@
+<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M1.75 5.15888C1.75 2.85895 3.67194 1 5.99672 1C8.32806 1 10.25 2.85895 10.25 5.15888C10.25 6.31785 9.8285 7.39381 9.13475 8.30579C8.3694 9.31176 7.42608 10.1882 6.36427 10.8762C6.12126 11.0352 5.90194 11.0472 5.63522 10.8762C4.56737 10.1882 3.62404 9.31176 2.86525 8.30579C2.17099 7.39381 1.75 6.31785 1.75 5.15888ZM4.59711 5.28839C4.59711 6.05886 5.22582 6.66485 5.99672 6.66485C6.76811 6.66485 7.40288 6.05886 7.40288 5.28839C7.40288 4.52391 6.76811 3.88843 5.99672 3.88843C5.22582 3.88843 4.59711 4.52391 4.59711 5.28839Z" fill="rgba(49, 124, 250, 1)"/>
+</svg>

+ 3 - 0
src/images/svgs/icon-mail-blue.svg

@@ -0,0 +1,3 @@
+<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M8.4697 1.5C9.1402 1.5 9.7852 1.765 10.2597 2.2405C10.7347 2.715 11.0002 3.355 11.0002 4.025V7.975C11.0002 9.37 9.8652 10.5 8.4697 10.5H3.5302C2.1347 10.5 1.0002 9.37 1.0002 7.975V4.025C1.0002 2.63 2.1297 1.5 3.5302 1.5H8.4697ZM9.2652 4.76998L9.3052 4.72998C9.4247 4.58498 9.4247 4.37498 9.2997 4.22998C9.2302 4.15548 9.1347 4.10998 9.0352 4.09998C8.9302 4.09448 8.8302 4.12998 8.7547 4.19998L6.5002 5.99998C6.2102 6.24048 5.7947 6.24048 5.5002 5.99998L3.2502 4.19998C3.0947 4.08498 2.8797 4.09998 2.7502 4.23498C2.6152 4.36998 2.6002 4.58498 2.7147 4.73498L2.7802 4.79998L5.0552 6.57498C5.3352 6.79498 5.6747 6.91498 6.0302 6.91498C6.3847 6.91498 6.7302 6.79498 7.0097 6.57498L9.2652 4.76998Z" fill="rgba(49, 124, 250, 1)"/>
+</svg>

+ 3 - 0
src/images/svgs/icon-phone-blue.svg

@@ -0,0 +1,3 @@
+<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M5.76587 6.23619C7.76041 8.23018 8.21289 5.92336 9.48282 7.19241C10.7071 8.41638 11.4108 8.6616 9.85962 10.2124C9.66532 10.3685 8.43081 12.2471 4.0923 7.90983C-0.246739 3.572 1.63079 2.33622 1.78698 2.14197C3.34193 0.586923 3.58293 1.29469 4.80725 2.51866C6.07718 3.78825 3.77133 4.24221 5.76587 6.23619Z" fill="rgba(49, 124, 250, 1)"/>
+</svg>

+ 11 - 0
src/images/svgs/icon-qrcode-blue.svg

@@ -0,0 +1,11 @@
+<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M2.94988 9.07016H3.69988V8.27516H2.94988V9.07016ZM1.24988 10.7502H5.40488V6.59516H1.24988V10.7502Z" fill="rgba(49, 124, 250, 1)"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M8.29988 9.07016H9.04988V8.27516H8.29988V9.07016ZM6.59488 10.7502H10.7499V6.59516H6.59488V10.7502Z" fill="rgba(49, 124, 250, 1)"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M7.34703 1.99976H8.56253V1.24976H6.59703V4.05926H7.34703V1.99976Z" fill="rgba(49, 124, 250, 1)"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M6.59738 5.40215H8.45738V4.65215H6.59738V5.40215Z" fill="rgba(49, 124, 250, 1)"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M9.14903 1.24976V1.99976H10V2.85026H10.75V1.24976H9.14903Z" fill="rgba(49, 124, 250, 1)"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M9.99988 4.65205H9.14888V5.40205H10.7499V3.43705H9.99988V4.65205Z" fill="rgba(49, 124, 250, 1)"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M7.75868 2.43211V4.29161H9.42318V3.54161H8.50868V2.43211H7.75868Z" fill="rgba(49, 124, 250, 1)"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M9.72158 2.4272L8.97158 2.4322V3.1822H9.72158V2.4272Z" fill="rgba(49, 124, 250, 1)"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M2.94988 3.72515H3.69988V2.92515H2.94988V3.72515ZM1.24988 5.40015H5.40488V1.25015H1.24988V5.40015Z" fill="rgba(49, 124, 250, 1)"/>
+</svg>

+ 5 - 8
src/pages/agent/components/AgentSetting/components/AgentContactCard/index.tsx

@@ -1,12 +1,9 @@
 import { View } from "@tarojs/components"
 import style from './index.module.less'
-import IconPhoneGray from "@/components/icon/icon-phone-gray"
-import IconMailGray from "@/components/icon/icon-mail-gray"
-import IconLocationGray from "@/components/icon/icon-location-gray"
-import IconQRCodeGray from "@/components/icon/icon-qrcode-gray"
 import IconEditGray from "@/components/icon/icon-edit-gray"
 import Taro from "@tarojs/taro";
 import { useAgentStore } from "@/store/agentStore";
+import ContactIcon from '@/components/ContactIcon'
 
 export default () => {
   const agent = useAgentStore((state)=> state.agent)
@@ -20,10 +17,10 @@ export default () => {
       <View className={style.header}>
         <View className={style.name}>{agent?.name}</View>
         <View className={style.icons}>
-          <View className={style.icon}><IconPhoneGray/></View>
-          <View className={style.icon}><IconMailGray/></View>
-          <View className={style.icon}><IconLocationGray/></View>
-          <View className={style.icon}><IconQRCodeGray/></View>
+          <ContactIcon type="phone" actived={!!agent?.mobile}></ContactIcon>
+          <ContactIcon type="mail" actived={!!agent?.email}></ContactIcon>
+          <ContactIcon type="location" actived={!!agent?.address}></ContactIcon>
+          <ContactIcon type="qrcode" actived={!!agent?.qrCodeUrl}></ContactIcon>
         </View>
       </View>
       <View className={style.items}>

+ 2 - 0
src/pages/chat/components/input-bar/VoiceInputBar.tsx

@@ -46,7 +46,9 @@ export default ({agentId,disabled, onIconClick, onSend, beforeSend, onError}:Pro
     
     // 以二进制方式读取文件
     const response = await speechToText(agentId, res.tempFilePath)
+    console.log(response,111)
     if(isSuccess(response.status)){
+      console.log(response.data)
       const msg = response.data?.text ?? ''
       if(!msg.length){
         return;

+ 10 - 8
src/pages/chat/components/input-bar/index.tsx

@@ -15,7 +15,7 @@ import { saveMessageToServer } from './message'
 interface Props {
   agent: TAgentDetail | null;
   aiModel: EAI_MODEL;
-  histories: TMessage|TRobotMessage[];
+  histories: (TMessage|TRobotMessage)[];
   setShowWelcome: (b: boolean) => void;
 }
 
@@ -33,7 +33,8 @@ export default ({ agent, setShowWelcome, histories }: Props) => {
     deleteMessage,
   } = useTextChat();
   
-  let myMessageId = "";
+  let myMsgUk = '';
+  let mySessionId = '';
 
   const handleTextInputBarSwitch = () => {
     console.log("voice input on");
@@ -137,7 +138,7 @@ export default ({ agent, setShowWelcome, histories }: Props) => {
             contentType: EContentType.TextPlain,
             isStreaming: false,
             role: currentRobotMessage.role,
-            msgUk,
+            msgUk: currentRobotMessage.msgUk,
           }],
           agentId: agent.agentId,
           sessionId,
@@ -151,8 +152,8 @@ export default ({ agent, setShowWelcome, histories }: Props) => {
     });
   };
   const handleVoiceSend = (message: string) => {
-    // updateMessage(message, myMessageId);
-    // chatWithGpt(message);
+    updateMessage(message, myMsgUk);
+    chatWithGpt(message, mySessionId, myMsgUk);
   };
   const handleOnSend = async (message: string) => {
     if(!agent?.agentId){
@@ -167,13 +168,14 @@ export default ({ agent, setShowWelcome, histories }: Props) => {
     if(!agent?.agentId){
       return
     }
-    const {msgUk} = pushMessage("");
-    myMessageId = msgUk
+    const {sessionId, msgUk} = pushMessage("");
+    myMsgUk = msgUk
+    mySessionId = sessionId
   };
 
   // 发生主意识别错误时,删除当前自己发出的气泡框
   const handleVoiceError = () => {
-    deleteMessage(myMessageId);
+    deleteMessage(myMsgUk);
   };
 
   useUnload(() => {

+ 0 - 86
src/pages/chat/history.ts

@@ -1,86 +0,0 @@
-import {
-  getMessageHistories,
-  type TGetMessageHistoriesParams,
-} from "@/service/bot";
-import { isSuccess } from "@/utils";
-
-import useSWRInfinite from "swr/infinite";
-import { EChatRole } from "@/types/bot";
-import type { TMessage, TRobotMessage } from "@/store/textChat";
-import { TAgentDetail } from "@/types/agent";
-import { useEffect, useRef } from "react";
-export const useChatHistory = (agent: TAgentDetail, setScrollTop: ()=> void)=> {
-  // 1. key 生成函数
-  const getKey = (pageIndex, previousPageData) => {
-    // 没有更多数据时停止
-    if (
-      previousPageData &&
-      (!previousPageData.nextId || loadedCount >= totalCount)
-    )
-      return null;
-    return {
-      agentId: agent.agentId,
-      startId: pageIndex === 0 ? undefined : previousPageData.nextId,
-      pageSize: 10,
-    };
-  };
-
-  // 2. fetcher
-  const fetcher = async (params) => {
-    const res = await getMessageHistories(params);
-    return res.data;
-  };
-
-  // 3. SWR Infinite
-  const { data, size, setSize, isValidating } = useSWRInfinite(
-    getKey,
-    fetcher,
-    {
-      revalidateAll: false,
-      revalidateFirstPage: false,
-    }
-  );
-
-  // 4. 处理数据
-  const messages:TMessage|TRobotMessage[] = data ? data.flatMap((page) => page.data) : [];
-  const totalCount = data?.[0]?.totalCount || 0;
-  const loadedCount = messages.length;
-  const hasMore = loadedCount < totalCount;
-
-  let histories:TMessage|TRobotMessage[] = []
-  if(messages.length){
-    histories = messages.map(message => {
-      if(message.role === EChatRole.User){
-        return {...message, saveStatus: 2}
-      }
-      if(message.role === EChatRole.Assistant){
-        return {...message, robot: {
-          avatar: agent?.avatarUrl ?? "",
-          name: agent?.name ?? "",
-          agentId: agent?.agentId ?? "",
-        }, saveStatus: 2}
-      }
-      return message
-    })
-    
-  }
-  
-  // 用 ref 记录上一次 histories 长度
-  const prevHistoriesLength = useRef(0);
-
-  useEffect(() => {
-    // 只在 histories 长度变大时(即加载更多历史)设置 scrollTop
-    if (histories.length > prevHistoriesLength.current) {
-      setScrollTop();
-      prevHistoriesLength.current = histories.length;
-    }
-  }, [histories.length, setScrollTop]);
-
-  return {
-    isValidating,
-    histories,
-    hasMore,
-    size,
-    setSize,
-  }
-}

+ 69 - 15
src/pages/chat/index.tsx

@@ -1,7 +1,7 @@
 import { View, ScrollView } from "@tarojs/components";
 import NavBarNormal from "@/components/nav-bar-normal/index";
 import PageCustom from "@/components/page-custom/index";
-import Taro, { useUnload } from "@tarojs/taro";
+import Taro, { useRouter, useUnload } from "@tarojs/taro";
 import ChatMessage from "@/components/chat-message";
 import style from "./index.module.less";
 // import { useAppStore } from "@/store/appStore";
@@ -16,9 +16,10 @@ import IconArrowLeft from "@/components/icon/icon-arrow-left";
 import PersonalCard from "./components/personal-card";
 import { useAgentStore } from "@/store/agentStore";
 
-import { useChatHistory } from './history'
-
-
+import { useLoadMore } from "@/utils/loadMore";
+import {
+  getMessageHistories,
+} from "@/service/bot";
 
 // 类型谓词函数
 function isRobotMessage(
@@ -28,11 +29,19 @@ function isRobotMessage(
 }
 
 export default function Index() {
-  const agent = useAgentStore((state) => state.agent);
-  if(!agent?.agentId){
-    return <View>...</View>
+  const router = useRouter()
+  const {agentId} = router.params
+  
+  if(!agentId){
+    return <View>没有相应的智能体</View>
   }
+  const {fetchAgent} = useAgentStore()
+  const agent = useAgentStore((state) =>  state.agent)
+
+
   const [deepThink, setDeepThink] = useState(EAI_MODEL.DeepseekChat);
+
+  const [histories, setHistories] = useState<(TMessage|TRobotMessage)[]>([]);
   
   const [keyboardHeight, setKeyboardHeight] = useState(0);
   const scrollViewRef = useRef<any>(null);
@@ -42,16 +51,61 @@ export default function Index() {
   const { destroy, setScrollTop  } = useTextChat();
   const scrollTop = useTextChat((state) => state.scrollTop);
 
-  const {setSize, hasMore, isValidating, histories, size} = useChatHistory(agent, setScrollTop)
+  const fetcher = async ([_url, nextId, page, pageSize]) => {
+    if(!agent){
+      return null
+    }
+    const _nextId = nextId ? decodeURIComponent(nextId) : nextId
+    const res = await getMessageHistories({
+      agentId,
+      startId:  _nextId,
+      pageSize,
+    });
+    return res.data;
+  };
+  const { data, loadMore, page } = useLoadMore<{
+    data?: (TMessage|TRobotMessage)[]
+    nextId?: string,
+    totalCount?: number
+  }>({
+    url: '/blue-aiagent/api/v1/my/messeagehistories',
+    fetcher,
+  });
+  
   const [showWelcome, setShowWelcome] = useState(!histories.length);
   
   
-  // 5. onScrollToUpper 加载更多
+  //  加载更多
   const onScrollToUpper = () => {
-    if (hasMore && !isValidating) {
-      setSize(size + 1);
-    }
+    console.log('onscroll')
+    loadMore()
   };
+
+  
+  useEffect(()=> {
+    agentId && fetchAgent(agentId)
+  }, [agentId])
+
+  useEffect(()=> {
+    setShowWelcome(!histories.length)
+  }, [histories])
+
+  useEffect(() => {
+    if (data?.data) {
+      setHistories([...data.data.reverse(), ...histories]);
+      if(page === 1){
+        setTimeout(()=> {
+          setScrollTop()
+        }, 300)
+      }
+    }
+  }, [data, page]);
+  
+  
+
+  
+
+  
   
 
   useUnload(() => {
@@ -68,7 +122,7 @@ export default function Index() {
 
   const renderNavLeft = () => {
     return (
-      <View className="flex items-center gap-8">
+      <View className="flex items-center gap-8" onClick={()=>Taro.navigateBack()}>
         <IconArrowLeft />
         <View className={showWelcome ? "hidden" : "block"}>
           <PersonalCard size="mini" />
@@ -127,12 +181,12 @@ export default function Index() {
           >
             深度思考(R1)
           </View>
-          <InputBar
+          {agent && <InputBar
             aiModel={deepThink}
             agent={agent}
             histories={histories}
             setShowWelcome={setShowWelcome}
-          ></InputBar>
+          ></InputBar>}
         </View>
       </View>
     </PageCustom>

+ 8 - 8
src/pages/contact/components/contact-card/index.tsx

@@ -1,16 +1,16 @@
 import { View, Image } from '@tarojs/components'
 import style from './index.module.less'
-import { IContactModel } from '@/types/index'
 import Taro from '@tarojs/taro'
 import { delContact } from "@/service/contact";
+import { TContactItem } from '@/types/contact';
 interface Props {
-  data: IContactModel
+  data: TContactItem
   deleteable?: boolean
   refresh: () => void
   fromContact?: boolean
 }
 const Index = ({data, deleteable, refresh, fromContact}: Props)=> {
-  const handleClick = (data: IContactModel)=>{
+  const handleClick = (data: TContactItem)=>{
     console.log(data, fromContact)
     // if(fromContact){
     //   Taro.navigateTo({url: '/pages/profile/index?fromContact=true&profileId='+data.contactProfileId})
@@ -36,17 +36,17 @@ const Index = ({data, deleteable, refresh, fromContact}: Props)=> {
   };
 
   return (
-    <View className={style.contactCard} onClick={()=>{handleClick(data)}} onLongPress={(e) => handleLongPress(e, data.contactProfileId)}>
+    <View className={style.contactCard} onClick={()=>{handleClick(data)}} onLongPress={(e) => handleLongPress(e, data.agentId)}>
       <View className={style.avatar}>
-      {data.avatar && <Image src={data.avatar} mode="aspectFill" className={style.avatar}></Image>}
+      {data.avatarUrl && <Image src={data.avatarUrl} mode="aspectFill" className={style.avatar}></Image>}
       </View>
       <View className={style.infoColumn}>
         <View className={style.nameRow}>
           <View className={style.nickName}>{data.name}</View>
-          {data.badges?.map(item=><View className={style.nameMarkup}>{item}</View>)}
+          {/* {data.badges?.map(item=><View className={style.nameMarkup}>{item}</View>)} */}
         </View>
-        <View className={style.subInfo}>{data.company}</View>
-          <View className={style.subInfo}>{data.job}</View>
+        <View className={style.subInfo}>{data.lastChatMsg}</View>
+          {/* <View className={style.subInfo}>{data.job}</View> */}
       </View>
     </View>
   )

+ 28 - 45
src/pages/contact/index.tsx

@@ -1,4 +1,4 @@
-import Taro, { useReachBottom } from "@tarojs/taro";
+import Taro, { useReachBottom,} from "@tarojs/taro";
 import { View, Text } from "@tarojs/components";
 
 import NavBarNormal from "@/components/nav-bar-normal/index";
@@ -7,39 +7,25 @@ import style from "./index.module.less";
 import { useEffect, useState } from "react";
 import ContactCard from "./components/contact-card/index";
 import { getContactList, searchContact } from "@/service/contact";
-import { IContactModel } from "@/types/index";
 import { useDebounceWithParams } from "@/utils/index";
-import { reportPageVisit } from "@/utils/report";
 import { useLoadMore } from "@/utils/loadMore";
 import PageCustom from "@/components/page-custom/index";
+import { TContactItem } from "@/types/contact";
 
 export default function Index() {
   const [searchValue, setSearchValue] = useState("");
-  const [list, setList] = useState<IContactModel[]>([
-    {
-      avatar:
-        "https://cdn.wehome.cn/cmn/png/53/META-H8UKWHWU-2JNUAG2BARJF55VHU9QS3-YBQGHDAM-IW.png",
-      name: "孙海涛",
-      contactProfileId: "id1",
-      pinned: true,
-    },
-    {
-      avatar:
-        "https://cdn.wehome.cn/cmn/png/53/META-H8UKWHWU-2JNUAG2BARJF55VHU9QS3-YBQGHDAM-IW.png",
-      name: "殷海清",
-      contactProfileId: "id2",
-      pinned: false,
-    },
-  ]);
+  const [list, setList] = useState<TContactItem[]>([]);
 
-  const fetcher = async ([_url, page, pageSize]) => {
-    const res = await getContactList({ page, pageSize });
-    if (res.code === 0 && res.data) {
-      return res.data;
-    }
+  const fetcher = async ([_url, nextId, page, pageSize]) => {
+    const res = await getContactList({ startId: nextId, pageSize });
+    return res.data;
   };
-  const { data, loadMore, refetch } = useLoadMore<IContactModel[]>({
-    url: "/v1/contact",
+  const { data, loadMore, refetch } = useLoadMore<{
+    data?: TContactItem[]
+    nextId?: string,
+    totalCount?: number
+  }>({
+    url: '/blue-aiagent/api/v1/my/contacts',
     fetcher,
   });
 
@@ -49,12 +35,12 @@ export default function Index() {
       console.log("searchValue is empty", searchText);
       return;
     }
-    const res = await searchContact(searchText);
-    if (res.code === 0 && res.data) {
-      setList(res.data);
-    } else {
-      setList([]);
-    }
+    // const res = await searchContact(searchText);
+    // if (res.code === 0 && res.data) {
+    //   setList(res.data);
+    // } else {
+    //   setList([]);
+    // }
   }, 500);
 
   const resetFetchList = () => {
@@ -88,18 +74,15 @@ export default function Index() {
     loadMore();
   });
 
-  // useEffect(() => {
-  //   if (data?.length) {
-  //     setList((prevdata) => {
-  //       return [...prevdata, ...data];
-  //     });
-  //   }
-  // }, [data]);
+  useEffect(() => {
+    if (data?.data) {
+      setList([...list, ...data.data]);
+    }
+  }, [data]);
 
-  reportPageVisit();
 
   const handleHello = (e: any) => {
-    console.log(e);
+    console.log(e.detail);
   };
 
   const renderContent = () => {
@@ -107,15 +90,15 @@ export default function Index() {
       return list.map((item) => (
         // @ts-ignore
         <slide-delete
-          pid={item.contactProfileId}
-          pinned={item.pinned}
+          pid={item.agentId}
+          pinned={item.isTop}
           onAction={handleHello}
         >
-          <View className={`${item.pinned ? "bg-gray" : "bg-white"}`}>
+          <View className={`${item.isTop ? "bg-gray" : "bg-white"}`}>
             <ContactCard
               refresh={resetFetchList}
               deleteable
-              key={item.contactProfileId}
+              key={item.agentId}
               data={item}
               fromContact
             ></ContactCard>

+ 78 - 0
src/pages/profile/components/InitView/index.tsx

@@ -0,0 +1,78 @@
+import { useEffect, useState } from "react";
+import { View,Image } from "@tarojs/components";
+import Taro, { useDidShow, useReady } from "@tarojs/taro";
+import NavBarNormal from "@/components/nav-bar-normal/index";
+import LogoImage from '@/images/logo.png'
+import PageCustom from "@/components/page-custom/index";
+import { UserInfoResponse } from '@/xiaolanbenlib/api/auth'
+import { onLogout, useIsLogin } from '@/xiaolanbenlib/hooks/data/useAuth'
+import refreshUserId, { clearUserInfo, getOpenIdAsync } from '@/xiaolanbenlib/utils/auth'
+
+import WelcomeTips from '../WelcomeTips/index'
+import { useAgentStore } from '@/store/agentStore'
+import { useSystemStore } from '@/store/systemStore'
+import { TAgent } from "@/types/agent";
+import { useUserStore } from "@/store/userStore";
+interface Iprops {
+  setDefault: (agent: TAgent) => void
+}
+export default function Index({setDefault}: Iprops) {
+
+  const [userInfo, setUserInfo] = useState<UserInfoResponse>()
+  const isLogin = useIsLogin()
+
+  const {fetchAgents} =  useAgentStore()
+  const { getSysCoreCnf } =  useSystemStore()
+  const { getMyInfo } = useUserStore()
+
+  async function initUserInfo() {
+    await refreshUserId()
+      .then( async (value) => {
+        setUserInfo(value)
+        getOpenIdAsync().then((openId) => {
+          console.log('🚀 ~ getOpenIdAsync ~ value:', openId)
+        })
+      })
+      .catch((error) => {
+        if (error.message === 'unauthorized' || error.code === 401) {
+          Taro.showToast({
+            title: '未登录',
+            icon: 'none',
+            duration: 2000,
+          })
+        }
+      })
+    await getSysCoreCnf()
+    await getMyInfo()
+    const agents = await fetchAgents()
+    const agent = agents.find( item=> item.isDefault)
+    if(agent){
+      setDefault(agent)
+    }
+  }
+
+  useEffect(() => {
+    if (isLogin) {
+      initUserInfo()
+    }
+  }, [isLogin])
+  
+
+  const renderLogo = ()=> {
+    return <View><Image className="w-68 h-24" src={LogoImage}></Image></View>
+  }
+
+  return (
+    <PageCustom>
+      <NavBarNormal leftColumn={renderLogo}></NavBarNormal>
+      <View className="relative w-full">
+        <WelcomeTips>
+          <View className="flex items-center">
+            <View>{userInfo?.logo && <Image className="w-24 h-24" src={userInfo?.logo}></Image>}</View>
+            <View>{isLogin ? `已登录「${userInfo?.nickName}」` : '未登录'}</View>
+          </View>      
+        </WelcomeTips>
+      </View>
+      </PageCustom>
+  );
+}

+ 59 - 0
src/pages/profile/components/WelcomeTips/index.module.less

@@ -0,0 +1,59 @@
+.container{
+  width: 100%;
+  padding: 12px;
+  position: fixed;
+  bottom: 16px;
+  left: 0;
+  right: 0;
+  // display: flex;
+  // box-sizing: border-box;
+  // flex-direction: column;
+  // justify-content: flex-end;
+}
+.hello {
+  padding-left: 12px;
+  margin-bottom: 10px;
+  font-family: PingFang SC;
+  font-weight: 500;
+  font-size: 24px;
+  line-height: 32px;
+  color: #000;
+}
+
+.welcome {
+  padding-left: 12px;
+  margin-bottom: 54px;
+  font-family: PingFang SC;
+  font-weight: 400;
+  font-size: 12px;
+  line-height: 20px;
+
+}
+
+.box {
+  margin-bottom: 20px;
+  border-radius: 12px;
+  padding: 20px;
+  background-color: white;
+}
+
+.boxInner {
+  margin-bottom: 20px;
+  padding: 12;
+  border-radius: 8px;
+  background-color: rgba(var(--primary-color, .1));
+}
+
+.headline {
+  margin-bottom: 10px;
+  font-weight: 500;
+  font-size: 16px;
+  line-height: 24px;
+}
+
+.list {
+  padding-bottom: 38px;
+  font-weight: 400;
+  font-size: 14px;
+  line-height: 22px;
+}

+ 77 - 0
src/pages/profile/components/WelcomeTips/index.tsx

@@ -0,0 +1,77 @@
+import { View } from "@tarojs/components";
+import style from "./index.module.less";
+import Taro from "@tarojs/taro";
+
+import { getAgents, getAgent, createAgent, } from "@/service/agent";
+
+import { isSuccess } from "@/utils";
+
+export default function Index({children}) {
+  
+  const saveAgent = async () => {
+    const res = await createAgent()
+    console.log(res)
+    Taro.navigateTo({url: '/pages/agent-gen/index'})
+  }
+  const test = ()=> {
+    
+  }
+  const go = async () => {
+    const response = await getAgents()
+    const res = await createAgent()
+    
+    if(isSuccess(res.status)){
+      console.log(response, 'gogogo')
+    }
+    
+    // return 
+    // saveAgent({
+    //   address: '浙江杭州余杭文一西路1700号',
+    //   avatarUrl: 'https://cdn.wehome.cn/cmn/png/53/META-H8UKWHWU-2JNUAG2BARJF55VHU9QS3-YBQGHDAM-IW.png',
+    //   email: '',
+    //   enabledChatBg: false,
+    //   enabledPersonalKb: false,
+    //   "entId": '',
+    //   "entName": '',
+    //   "greeting": '',
+    //   "isDefault": true,
+    //   "isEnt": false,
+    //   "isNewEnt": false,
+    //   "isSystemVoice": true,
+    //   "mobile": '18658870618',
+    //   "name": 'xiaodong',
+    //   "personality": '一名疯瘨的数码博主',
+    //   "position": 'CEO',
+    //   "qrCodeUrl": '',
+    //   "questionGuides": ['想要了解相机?', '想要了解手机?'],
+    //   // "voice": ''
+    // })
+    
+  }
+
+
+  
+
+  return (
+    <View className={style.container}>
+      <View className={style.hello}>你好</View>      
+      <View>{children}</View>
+      <View className={style.welcome}>欢迎你,小蓝本的第39293位用户</View>
+      <View className={style.box}>
+        <View className={style.boxInner}>
+          <View className={style.headline}>
+            <View>开启你的专属 AI 分身,</View>
+            <View>让客户随时随地了解你和你的解决方案。</View>
+          </View>
+          <View className={style.list}>
+              <View>1.企业 + 个人资料统一管理</View>
+              <View>2.客户问题,AI 自动回应</View>
+              <View>3.你的内容,一页呈现</View>
+          </View>
+          <View className="button-rounded-big" onClick={go}>创建智能体</View>
+          {/* <View className="button-rounded-big" onClick={test}>test</View> */}
+        </View>
+      </View>
+      </View>
+  );
+}

+ 8 - 0
src/pages/profile/index.config.ts

@@ -0,0 +1,8 @@
+export default definePageConfig({
+  navigationBarTitleText: '首页',
+  "usingComponents": {},
+  "navigationStyle": 'custom',
+  enableShareAppMessage: true,
+  enableShareTimeline: true,
+  enablePageMeta: true,
+})

+ 56 - 0
src/pages/profile/index.module.less

@@ -0,0 +1,56 @@
+.mySwiper{
+  width: 320px;
+  height: 573px;
+  overflow: hidden;
+  border-radius: 20px;
+}
+
+.swiperItem{
+  margin-right: 30px;
+  
+}
+
+
+/* 自定义指示点容器 */
+.indicatorContainer {
+  padding-top: 18px;
+  display: flex;
+  gap: 3px;
+  justify-content: center;
+}
+
+/* 单个指示点 */
+.indicator {
+  width: 6px;
+  height: 6px;
+  transition: all .3s;
+  border-radius: 100%;
+  background-color: rgba(#000, .15);
+  border-radius: 50%;
+}
+
+/* 当前选中的指示点 */
+.indicatorActive{
+  width: 16px;
+  border-radius: 30px;
+  background-color: rgba(#000, .35);
+}
+
+.logo{
+  margin-right: 5px;
+  width: 24px;
+  height: 24px;
+}
+
+.vipTips{
+  display: flex;
+  align-items: center;
+  flex-direction: column;
+}
+.vipTipsFigure{
+  margin-top: 48px;
+  width: 135px;
+  height: 96px;
+  background-image: url(https://cdn.wehome.cn/cmn/png/50/META-H8UK0IWU-9NNPJOLLD1MU95DE0NMA3-DAWUHO2M-AJ.png);
+  background-size: 100%;
+}

+ 54 - 0
src/pages/profile/index.tsx

@@ -0,0 +1,54 @@
+import PageCustom from "@/components/page-custom/index";
+import { View, Image } from "@tarojs/components";
+import { useEffect, useState } from "react";
+import ComponentList from "@/components/component-list";
+import { TAgentDetail } from "@/types/agent";
+import NavBarNormal from "@/components/nav-bar-normal/index";
+import { getAgent, postVisitorLog } from '@/service/agent'
+import { useRouter } from "@tarojs/taro";
+import { getLoginId, isSuccess } from "@/utils";
+
+import Logo from "@/components/logo";
+import SummaryBar from "@/components/AgentPage/components/SummaryBar"
+import { useAgentStore } from "@/store/agentStore";
+
+export default function Index() {
+  const {agent, fetchAgent} = useAgentStore()
+  const router = useRouter()
+  const { agentId } = router.params
+
+  if(!agentId){
+    return <View>loading...</View>
+  }
+
+
+
+  useEffect(()=> {
+    fetchAgent(agentId)
+  }, [agentId])
+  
+  useEffect(()=> {
+    if(agentId){
+      const loginId = getLoginId()
+      postVisitorLog({agentId, loginId})
+    }
+  }, [agentId])
+
+
+  
+  
+  return (
+    <PageCustom>
+      <View className="w-full">
+      <NavBarNormal scrollFadeIn showBgColor leftColumn={Logo}></NavBarNormal>
+      {agent && <>
+        <SummaryBar isVisitor agent={agent}></SummaryBar>
+        <View className="flex flex-col w-full p-16 gap-12">
+          <ComponentList components={agent.components ?? []}></ComponentList>
+        </View>  
+      </>}
+      
+    </View>
+    </PageCustom>
+  );
+}

+ 16 - 7
src/service/agent.ts

@@ -13,7 +13,8 @@ export const createAgent = () => {
 
 // 获取我的智能体详细信息
 // 供编辑页使用,预览页使用智能体信息接口获取
-export const getAgent = (agentId: string) => {
+export const getMyAgent = (agentId: string) => {
+  console.log(agentId,11333)
   return request.get<TAgentDetail>(`${bluebookAiAgent}api/v1/my/agent/${agentId}`)
 }
 
@@ -56,12 +57,6 @@ export const editAgentCard = (agentId: string, data: TAgentContactCard) => {
 }
 
 
-
-
-
-
-
-
 // 我的智能体列表
 export const getAgents = (): Promise<TAgentDetail[]> =>  {
   return request.get(`${bluebookAiAgent}api/v1/my/agents`)
@@ -72,4 +67,18 @@ export const deleteAgent = (agentId: string) => {
 }
 
 
+// 访问某个智能体,允许不登录状态下访问
+export const getAgent = (agentId: string) =>  {
+  return request.get<TAgentDetail>(`${bluebookAiAgent}api/v1/agent/${agentId}`)
+}
+
+// 上报访客日志--间隔 N 秒调用一次
+export const postVisitorLog = (data: {
+  agentId: string,
+  loginId: string,
+}) => {
+  return request.post<TAgentDetail>(`${bluebookAiAgent}api/v1//agent/visitor/log`, data)
+}
+
+
 

+ 30 - 17
src/service/contact.ts

@@ -1,30 +1,43 @@
 /**
  * 组件相关接口
  */
-import service from '@/xiaolanbenlib/module/axios'
-import type { IPageParams, IContactModel } from '@/types/index'
 
-export type TNewContact = {
-  alias?: string;
-  contactProfileId?: number;
-}
-export type TDeleteContact = {
-  id: number;
-  contactProfileId: number;
-}
 
-export const saveContact = (data: TNewContact) => {
-  return service.post<TNewContact, IContactModel>('/v1/contact', data)
+import {
+  bluebookAiAgent,
+} from '@/xiaolanbenlib/api/index'
+import request from '@/xiaolanbenlib/module/axios.js'
+import { TContactItem } from '@/types/contact'
+
+
+// 置顶与取消置顶
+export const setToTop = (data: {
+  contactId: number|string,
+  isTop: boolean
+}) => {
+  return request.put(`${bluebookAiAgent}api/v1/my/contact/top`, data)
 }
 
-export const getContactList = (data: IPageParams) => {
-  return service.get<IContactModel[]>('/v1/contact', {params: {
+// 搜索联系人列表--前端 需要根据已拉取的记录去重
+export const getContactList = (data: {
+  pageSize: number,
+  startId: string,
+  keyword?: string,
+}) => {
+  return request.get<{
+    data?: TContactItem[]
+    nextId?: string,
+    totalCount?: number
+  }>(`${bluebookAiAgent}api/v1/my/contacts`, {params: {
     ...data
   }})
 }
+// 
 export const searchContact = (str: string) => {
-  return service.get<IContactModel[]>(`/v1/contact/search`, {params: {s: str}})
+  return request.get(`/v1/contact/search`, {params: {s: str}})
 }
-export const delContact = (contactProfileId: string) => {
-  return service.delete(`/v1/contact/${contactProfileId}`)
+
+// 删除联系人
+export const delContact = (contactId: string) => {
+  return request.delete(`${bluebookAiAgent}api/v1/my/contact/${contactId}`)
 }

+ 7 - 3
src/store/agentStore.ts

@@ -4,6 +4,8 @@ import request from "@/xiaolanbenlib/module/axios.js";
 
 import {
   createAgent as _createAgent,
+  getMyAgent as _getMyAgent,
+  getAgent as _getAgent,
   setDefaultAgent as _setDefaultAgent,
   deleteAgent as _deleteAgent,
   editAgentCard as _editAgentCard,
@@ -65,9 +67,11 @@ export const useAgentStore = create<AgentStoreState>((set, get) => ({
     return [];
   },
   fetchAgent: async (agentId: string) => {
-    const response = await request.get<TAgentDetail>(
-      `${bluebookAiAgent}api/v1/my/agent/${agentId}`
-    );
+    const { agents } = get()
+    // 如果自己的智能体列表中有 对应的 agentId,则请求自己的 agent, 否则请求无需登录的 getAgent 接口
+    const isSelf = !!agents.find((item)=> item.agentId === agentId)
+    const req = isSelf ? _getMyAgent : _getAgent; 
+    const response = await req(agentId);
     const result = isSuccess(response.status)
     if (result && response.data) {
       const agent = response.data;

+ 3 - 3
src/store/textChat.ts

@@ -47,12 +47,12 @@ export interface TextChat {
   // 将自己发出的气泡框推入聊天框
   pushMessage: (content: string) => {msgUk: string, sessionId: string};
   // 更新自己发出的气泡框
-  updateMessage: (content: string, messageId: string) => string;
+  updateMessage: (content: string, msgUk: string) => string;
   // 更新机器人汽泡框内的内容实现 gpt 的效果
   updateRobotMessage: (content: string) => void;
-  updateRobotReasoningMessage: (reasoningContent: string, messageId: string) => void;
+  updateRobotReasoningMessage: (reasoningContent: string, msgUk: string) => void;
   getCurrentRobotMessage:() => TRobotMessage|undefined
-  deleteMessage: (messageId: string) => void;
+  deleteMessage: (msgUk: string) => void;
   // 清空
   destroy: () => void;
   getMessageHistories: (data: TGetMessageHistoriesParams) => void

+ 1 - 0
src/types/agent.ts

@@ -47,6 +47,7 @@ export type TAgentDetail = {
   qrCodeUrl?: string,
   questionGuides?: string[],
   voiceId?: string
+  isMineAgent: boolean
 }
 
 export type TAgentContactCard = {

+ 18 - 0
src/types/contact.ts

@@ -0,0 +1,18 @@
+export type TContactItem = {
+  agentId : string // 联系人智能体ID -- 如果客户的所有智能体都无效时,则可能为 null ,
+  agentStatus : string // 联系人智能体状态 -- normal 正常/ deleted 已删除 ,
+  avatarUrl : string // 联系人头像地址 ,
+  contactId : number // 联系人ID --用于置顶、删除、拉黑 ,
+  isEnt : boolean // 是否企业认证联系人 ,
+  isTop : boolean // 是否置顶 ,
+  lastChatMsg : string // 最新的对话内容 ,
+  lastChatTime : string // 最新的对话时间 ,
+  name : string // 联系人名称 -- 最后一次访问的快照信息 ,
+  tags: TContactItemTag[] // 标签集 --官方 ,
+  tipCnt : number // 提示数量--角标
+}
+
+export type TContactItemTag = {
+  tagName: string,
+  tagType: string
+}

+ 0 - 46
src/utils/dataFetcher.ts

@@ -1,46 +0,0 @@
-import useSWR, { SWRConfiguration } from 'swr';
-
-type Fetcher<Data> = () => Promise<Data>;
-
-export function useDataFetcher<Data = any, Error = any>(
-  url: string | null,
-  options?: SWRConfiguration<Data, Error>
-) {
-  const fetcher: Fetcher<Data> = async () => {
-    const res = await fetch(url!);
-    if (!res.ok) throw new Error('请求失败');
-    return res.json();
-  };
-
-  const { data, error, isLoading, mutate } = useSWR<Data, Error>(
-    url,
-    fetcher,
-    {
-      // 禁用自动请求
-      initialData: null, // 初始值设为 null
-      revalidateOnMount: false, // 挂载时不验证
-      revalidateIfStale: false, // 不自动重新验证
-      revalidateOnFocus: false, // 聚焦时不验证
-      revalidateOnReconnect: false, // 重连时不验证
-      ...options // 合并用户自定义配置
-    }
-  );
-
-  // 手动触发请求的函数
-  const triggerFetch = async (opts?: {
-    throwOnError?: boolean;
-    optimisticData?: Data;
-  }) => {
-    return mutate(undefined, {
-      revalidate: true,
-      ...opts
-    });
-  };
-
-  return {
-    data,
-    error,
-    isLoading,
-    triggerFetch
-  };
-}    

+ 16 - 10
src/utils/loadMore.ts

@@ -1,33 +1,39 @@
-import { useState } from "react";
+import { useEffect, useState } from "react";
 import useSWR from "swr";
-export const useLoadMore = <T>({url, fetcher,  pageSize = 10} : {
+export const useLoadMore = <T>({url, fetcher, startId,  pageSize = 10} : {
   url: string,
   fetcher: any,
+  startId?: number,
   pageSize?: number,
 
 }) => {
   const [page, setPage]  = useState(1)
+  const [nextId, setNextId]  = useState(startId)
+  console.log(url, nextId, page, pageSize)
   // 使用 useSWR
   const { data, error, isLoading, mutate } = useSWR<T>(
     // key 数组,当这些值变化时会重新请求
-    [url, page, pageSize],
+    [url, nextId, page, pageSize],
     fetcher
   )
-
+  
   // 加载更多数据
   const loadMore = () => {
-    if(data){
-      setPage(prev => prev + 1)
+    //@ts-ignore
+    if (data?.nextId) {
+      //@ts-ignore
+      setNextId(data.nextId);
+      setPage(prev => prev + 1);
+      // 这里可以选择是否立即 mutate,通常 setPage/setNextId 后 useSWR 会自动请求
     }
   }
 
   const refetch = async () => {
-    setPage(1)
-    mutate([], true); // false 表示不重新请求
+    setPage(1);
+    setNextId(undefined)
+    mutate(undefined, true); // 重新请求数据
   }
 
-  
-
   return {
     page,
     setPage,