Selaa lähdekoodia

fix: 联系人列表滑动删除

王晓东 1 viikko sitten
vanhempi
commit
8687087d1d
42 muutettua tiedostoa jossa 672 lisäystä ja 278 poistoa
  1. 14 0
      project.private.config.json
  2. 14 9
      src/components/AvatarMedia/index.tsx
  3. 29 23
      src/components/DigitalCard/DigitalCardBasic.tsx
  4. 2 7
      src/components/chat-message/MessageRobot.tsx
  5. 3 2
      src/components/chat-message/MessageRobotPlain.tsx
  6. 1 1
      src/components/component-list/components/card-channels/index.tsx
  7. 0 2
      src/components/component-list/index.tsx
  8. 9 7
      src/components/custom-share/index.tsx
  9. 159 0
      src/components/slide-contact/index.js
  10. 4 0
      src/components/slide-contact/index.json
  11. 21 0
      src/components/slide-contact/index.wxml
  12. 54 0
      src/components/slide-contact/index.wxss
  13. 2 2
      src/components/slide-delete/index.js
  14. 2 3
      src/components/slide-delete/index.wxml
  15. 10 2
      src/components/slide-delete/index.wxss
  16. 2 2
      src/components/widgets/widget-media/index.tsx
  17. 0 1
      src/config/index.ts
  18. 10 1
      src/pages/chat/components/chat-welcome/index.tsx
  19. 1 2
      src/pages/chat/components/input-bar/TextInputBar.tsx
  20. 2 2
      src/pages/chat/components/input-bar/VoiceInputBar.tsx
  21. 4 3
      src/pages/chat/components/personal-card/index.tsx
  22. 9 7
      src/pages/chat/index.tsx
  23. 2 0
      src/pages/contact/components/contact-card/index.module.less
  24. 7 26
      src/pages/contact/components/contact-card/index.tsx
  25. 1 1
      src/pages/contact/index.config.ts
  26. 2 1
      src/pages/contact/index.module.less
  27. 64 24
      src/pages/contact/index.tsx
  28. 76 0
      src/pages/dashboard/components/AgentList/index.tsx
  29. 7 17
      src/pages/dashboard/components/VisitorCard/index.tsx
  30. 15 13
      src/pages/dashboard/components/VisitorList/index.tsx
  31. 37 39
      src/pages/dashboard/index.tsx
  32. 2 2
      src/pages/knowledge-item/index.tsx
  33. 7 14
      src/pages/knowledge/components/CompanyTab/components/ScrollList.tsx
  34. 59 21
      src/pages/knowledge/components/PersonalTab/components/ScrollList.tsx
  35. 2 2
      src/pages/knowledge/components/PersonalTab/components/ScrollListChat.tsx
  36. 4 2
      src/pages/knowledge/index.config.ts
  37. 4 8
      src/pages/visiteor-detail/components/VisitorSummary/index.tsx
  38. 3 3
      src/service/knowledge.ts
  39. 11 25
      src/types/agent.ts
  40. 1 1
      src/types/contact.ts
  41. 10 3
      src/types/visitor.ts
  42. 6 0
      src/utils/EventBus.ts

+ 14 - 0
project.private.config.json

@@ -15,6 +15,20 @@
           "scene": null,
           "launchMode": "default"
         },
+        {
+          "name": "pages/contact/index",
+          "pathName": "pages/contact/index",
+          "query": "",
+          "launchMode": "default",
+          "scene": null
+        },
+        {
+          "name": "pages/dashboard/index",
+          "pathName": "pages/dashboard/index",
+          "query": "",
+          "launchMode": "default",
+          "scene": null
+        },
         {
           "name": "pages/agent/index",
           "pathName": "pages/agent/index",

+ 14 - 9
src/components/AvatarMedia/index.tsx

@@ -1,14 +1,15 @@
-import { Image, Video, VideoProps } from "@tarojs/components";
-// import style from "../index.module.less";
+import { Image, Video, VideoProps, ImageProps, View } from "@tarojs/components";
+import avatarDefaultSource from '@/images/avatar-default.png'
 import { useEffect, useRef } from "react";
 import Taro from "@tarojs/taro";
 
 interface Props {
   source: string;
   className: string;
+  mode?: keyof ImageProps.Mode | undefined
 }
 
-export const AvatarMedia = ({ source, className }: Props) => {
+export const AvatarMedia = ({ source, className, mode = 'widthFix' }: Props) => {
   
   const videoRef = useRef<React.ComponentType<VideoProps>|null>(null);
   const videoContext = useRef<Taro.VideoContext|null>(null);
@@ -34,7 +35,8 @@ export const AvatarMedia = ({ source, className }: Props) => {
     }
   }, []);
 
-  if (source.lastIndexOf('.mp4') > -1) {
+  // 以 .mp4 结尾则认为是 视频
+  if (source.slice(-4) === '.mp4') {
     return (
       <Video
         id={videoId.current}
@@ -54,11 +56,14 @@ export const AvatarMedia = ({ source, className }: Props) => {
     );  
   }
 
+  const _source = source.length ? source : avatarDefaultSource
   return (
-    <Image
-      mode="widthFix"
-      className={className}
-      src={source}
-    />
+    <View className={`overflow-hidden rounded-full ${className}`}>
+      <Image
+        mode={mode}
+        className={`${className}`}
+        src={_source}
+      />
+    </View>
   );
 };

+ 29 - 23
src/components/DigitalCard/DigitalCardBasic.tsx

@@ -1,28 +1,34 @@
 import { View } from "@tarojs/components";
-import TagCertificated from "@/components/tag-certificated";
+import IconCertificateColor from "@/components/icon/icon-certificate-color";
 export interface DigitalCardBasicProps {
-  certificated?: boolean
-  name: string
-  position: string
-  company: string
+  certificated?: boolean;
+  name: string;
+  position: string;
+  entName?: string|null;
 }
 
-const DigitalCardBasic = ({name, position, company, certificated }: DigitalCardBasicProps) => {
-  
-  return <View className="flex items-start">
-              <View className="flex flex-col flex-1">
-                <View className="flex items-end gap-8">
-                  <View className="text-16 font-medium leading-32">{name}</View>
-                  <View className="text-12 leading-20">{position}</View>
-                </View>
-                <View className="flex items-center gap-2">
-                  <View className="text-12 leading-20 truncate max-w-[188px]">
-                    {company}
-                  </View>
-                  {certificated && <TagCertificated />}
-                </View>
-              </View>
-            </View>
-}
+const DigitalCardBasic = ({
+  name,
+  position,
+  entName,
+  certificated,
+}: DigitalCardBasicProps) => {
+  return (
+    <View className="flex items-start">
+      <View className="flex flex-col flex-1">
+        <View className="flex items-end gap-8">
+          <View className="text-16 font-medium leading-32">{name}</View>
+          <View className="text-12 leading-20">{position || ''}</View>
+        </View>
+        <View className="flex items-center gap-2">
+          <View className="text-12 leading-20 truncate max-w-[188px]">
+            {entName}
+            {/* {entName}{certificated && <IconCertificateColor />} */}
+          </View>
+        </View>
+      </View>
+    </View>
+  );
+};
 
-export default DigitalCardBasic;
+export default DigitalCardBasic;

+ 2 - 7
src/components/chat-message/MessageRobot.tsx

@@ -14,6 +14,7 @@ import { dislikeMessage, likeMessage } from "@/service/bot";
 import { EContentType, TMessage } from "@/types/bot";
 import { getLoginId, isSuccess } from "@/utils";
 import { useState } from "react";
+import { AvatarMedia } from "../AvatarMedia";
 interface Props {
   agent?: TAgentDetail | null;
   text: string;
@@ -140,13 +141,7 @@ export default ({ agent, text, message, textReasoning = "" }: Props) => {
       {agent && (
         <View className="flex gap-8 mb-10">
           <View className={style.avatarContainer}>
-            {agent?.avatarUrl && (
-              <Image
-                mode="aspectFill"
-                className={style.avatar}
-                src={agent.avatarUrl}
-              />
-            )}
+            <AvatarMedia source={agent?.avatarLogo || ''} className={style.avatar} mode="aspectFill"></AvatarMedia>
           </View>
           <View className="font-medium text-16 leading-24">{agent?.name}</View>
         </View>

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

@@ -2,9 +2,10 @@ import { View, Text } from "@tarojs/components";
 import style from './index.module.less'
 interface Props {
   text: string,
+  onClick?: (str: string) => void
 }
-export default ({text}: Props) => {
-  return <View>
+export default ({text, onClick}: Props) => {
+  return <View onClick={()=> onClick && onClick(text)}>
       <View className={`${style.message } ${style.messageRobot} gap-10`}>
         <View className={style.messageContent}>
           <Text>{text}</Text>

+ 1 - 1
src/components/component-list/components/card-channels/index.tsx

@@ -60,7 +60,7 @@ export default ({
   const hasValue = component.data.value?.videoId && component.data.value?.accountId
 
   const renderEmpty = ()=> {
-    return <View className="px-16"><View className="component-card-empty">
+    return <View className="px-16"><View className="component-card-empty mb-16">
       <View className="component-card-content">
         <View className="component-card-empty-figure"></View>
         <View className="component-card-empty-tips">点击配置图片/视频</View>

+ 0 - 2
src/components/component-list/index.tsx

@@ -4,8 +4,6 @@ import { useState } from "react";
 import WidgetCard from "@/components/widgets/widget-card/index";
 import WidgetContent from "@/components/widgets/widget-content/index";
 import WidgetMap from "@/components/widgets/widget-map/index";
-import WidgetTypeRow from "@/components/widgets/widget-type-row/index";
-import WidgetTitleRow from "@/components/widgets/widget-title-row/index";
 import PopupLinkCopyer from "@/components/popup-link-copyer";
 import type {
   TEntityComponent,

+ 9 - 7
src/components/custom-share/index.tsx

@@ -120,7 +120,7 @@ export default (props: Props) => {
     rect.y = -140 * ratio;
     rect.direction = "column";
     rect.justifyContent = "space-around";
-    rect.width = 210 * ratio;
+    rect.width = card.width * ratio;
     rect.height = 200 * ratio;
     rect.borderRadius = 0;
     card.addChild(rect);
@@ -156,9 +156,8 @@ export default (props: Props) => {
     infoList.y = 110 * ratio;
     infoList.direction = "column";
     infoList.gap = 4 * ratio;
-    infoList.width = 216 * ratio;
+    infoList.width = card.width * ratio;
     infoList.height = 42 * ratio;
-
     // 用户名
     const name = new RichText({
       text: agent?.name ?? '',
@@ -173,8 +172,6 @@ export default (props: Props) => {
 
     // 单位名
     const companyName = new RichText();
-    companyName.x = 0;
-    companyName.y = 0;
     companyName.lineClamp = 1;
     companyName.text = agent?.entName ?? '';
     companyName.wrapWidth = 160 * ratio;
@@ -195,11 +192,16 @@ export default (props: Props) => {
   //   initCanvas();
   // });
 
-  const shareTitle = '小蓝本' // '想了解我?快和我的AI聊聊吧',
+  const shareTitle = '快和我的智能体聊聊吧' // '想了解我?快和我的AI聊聊吧',
   useShareAppMessage(async () => {
     const agentId = agent?.agentId;
     console.log(agent)
-    
+    if(!agentId){
+      return {
+        title: shareTitle,
+        path: `/pages/profile/index?agentId=${agentId}`,
+      }
+    }
     Taro.showLoading()
     await initCanvas();
     let tmpImage = ''

+ 159 - 0
src/components/slide-contact/index.js

@@ -0,0 +1,159 @@
+Component({
+  /**
+   * 组件的属性列表
+   */
+  properties: {
+    pid: {
+      type: String,
+      value: '',
+      observer(newVal) {
+        console.log("on pid:", newVal, typeof newVal);
+        if (newVal) {
+          this.setData(
+            {
+              animate: true,
+            },
+            () => {
+              this.setData({
+                translateX: 0,
+              });
+            }
+          );
+        }
+      },
+    },
+    pinned: {
+      type: Boolean,
+      value: false,
+      observer(newVal) {
+        console.log("onTop changed:", newVal, typeof newVal);
+      },
+    },
+  },
+
+  /**
+   * 组件的初始数据
+   */
+  data: {
+    translateX: 0,
+    animate: false,
+  },
+
+  /**
+   * 组件的方法列表
+   */
+  methods: {
+    /**
+     * 处理touchstart事件
+     */
+    handleTouchStart(e) {
+      // touch事件初始时,组件禁掉transition动画
+      this.setData(
+        {
+          animate: false,
+        },
+        () => {
+          this.touchStartX = e.touches[0].pageX;
+          this.touchStartY = e.touches[0].pageY;
+          this.startX = this.data.translateX; // 组件初始位置
+          this.direction = null; // 记录手指滑动方向 X:左右滑动; Y:上下滑动
+        }
+      );
+    },
+
+    /**
+     * 处理touchmove事件
+     */
+    handleTouchMove: function (e) {
+      this.touchMoveX = e.touches[0].pageX;
+      this.touchMoveY = e.touches[0].pageY;
+      this.moveX = this.touchMoveX - this.touchStartX;
+
+      // 竖直移动距离超过了左右移动距离
+      if (Math.abs(this.touchMoveY - this.touchStartY) > Math.abs(this.moveX)) {
+        this.direction = "Y";
+        return;
+      }
+      this.direction = "X";
+
+      // 以下两种情况不进行移动:1. 在最右边时向右滑动; 2. 在最左边时向左滑动
+      if (
+        (this.startX === 0 && this.moveX > 0) ||
+        (this.startX === -this.actionWidth && this.moveX < 0)
+      ) {
+        return;
+      } else if (Math.abs(this.moveX) >= this.actionWidth) {
+        // 移动超出删除按钮的宽度时取按钮宽度作为移动距离
+        this.moveX = this.moveX < 0 ? -this.actionWidth : this.actionWidth;
+        this.setData({
+          translateX: this.moveX,
+        });
+      } else {
+        // 其他情况:手指滑动多少就位移多少
+        this.setData({
+          translateX: this.touchMoveX - this.touchStartX + this.startX,
+        });
+      }
+    },
+
+    /**
+     * 处理touchend事件
+     */
+    handleTouchEnd: function (e) {
+      // 非左右滑动时不进行任何操作
+      if (this.direction !== "X") {
+        return;
+      }
+      let translateX = 0;
+      // 移动超出右滑最大位移
+      if (this.moveX + this.startX >= 0) {
+        translateX = 0;
+      } else if (this.moveX + this.startX <= -this.actionWidth) {
+        // 移动超出左滑最大位移
+        translateX = -this.actionWidth;
+      } else if (
+        (this.startX === 0 && Math.abs(this.moveX) < this.actionWidth / 2) ||
+        (this.startX === -this.actionWidth &&
+          Math.abs(this.moveX) > this.actionWidth / 2)
+      ) {
+        // 以下两种情况都滑动到右边起点(即删除按钮隐藏的状态):
+        // 1. 从右边起点左滑但未超过最大位移的一半,回退到右边起点
+        // 2. 从左边起点右滑且超过最大位移的一半,继续滑到到右边起点
+        translateX = 0;
+      } else {
+        translateX = -this.actionWidth;
+      }
+      this.setData(
+        {
+          animate: true,
+        },
+        () => {
+          if (translateX === -this.actionWidth) {
+            this.triggerEvent("action", {
+              type: e.currentTarget.dataset.type,
+              id: this.data.pid,
+            });
+          }
+          this.setData({
+            translateX,
+          });
+        }
+      );
+    },
+
+    /**
+     * 组件操作事件(此示例只有删除事件,可根据需要增加其他事件)
+     */
+    handleAction({ currentTarget: { dataset: data } }) {
+      this.triggerEvent("action", {
+        type: data.type,
+        id: this.data.pid,
+      });
+    },
+  },
+
+  ready() {
+    this.actionWidth = 120;
+    console.log('i am ready')
+  },
+});

+ 4 - 0
src/components/slide-contact/index.json

@@ -0,0 +1,4 @@
+{
+  "component": true,
+  "usingComponents": {}
+}

+ 21 - 0
src/components/slide-contact/index.wxml

@@ -0,0 +1,21 @@
+<view class="wrap">
+  <view class="content {{animate ? 'animate' : ''}}"
+        bindtouchstart="handleTouchStart"
+        bindtouchmove="handleTouchMove"
+        bindtouchend="handleTouchEnd"
+        data-type="slide"
+        style="transform: translateX({{translateX * 2}}rpx)"
+  >
+      <slot/>
+  </view>
+  <view class="action-wrap">
+    <view class="action delete" bindtap="handleAction" data-type="delete">
+      <text>删除</text>
+    </view>
+    <view wx:if="{{pinned}}" class="action cancel" bindtap="handleAction" data-type="unpin">
+      <view>取消</view>
+      <view>置顶</view>
+    </view>
+    <view wx:else class="action pinned" bindtap="handleAction" data-type="pin"><text>置顶</text></view>
+  </view>
+</view>

+ 54 - 0
src/components/slide-contact/index.wxss

@@ -0,0 +1,54 @@
+.wrap {
+  position: relative;
+}
+
+.wrap .content {
+  position: relative;
+  z-index: 1;
+  border-radius: 24rpx;
+  overflow: hidden;
+}
+
+.wrap .content.animate {
+  transition: transform 0.3s;
+  background: #F6F8FB;
+}
+
+.wrap .action-wrap {
+  position: absolute;
+  display: flex;
+  width: 240rpx;
+  top: 0;
+  bottom: 0;
+  right: 0;
+  border-radius: 0 24rpx 24rpx 0;
+  overflow: hidden;
+}
+
+.wrap .action {
+  display: flex;
+  width: 120rpx;
+  height: 100%;
+  justify-content: center;
+  align-items: center;
+  color: white;
+  background: #327BF9;
+}
+.wrap .delete {
+  background: #FF8200;
+}
+.wrap .action.cancel {
+  display: flex;
+  flex-direction: column;
+  gap: 0;
+  width: 120rpx;
+  height: 100%;
+  justify-content: center;
+  align-items: center;
+  background: #FF4747;
+}
+
+.wrap .action text {
+  font-size: 24rpx;
+  color: #fff;
+}

+ 2 - 2
src/components/slide-delete/index.js

@@ -47,6 +47,7 @@ Component({
      * 处理touchstart事件
      */
     handleTouchStart(e) {
+      console.log(33334)
       // touch事件初始时,组件禁掉transition动画
       this.setData(
         {
@@ -128,8 +129,7 @@ Component({
           animate: true,
         },
         () => {
-          console.log(translateX, 3333);
-          if (translateX === -60) {
+          if (translateX === -this.actionWidth) {
             this.triggerEvent("action", {
               type: e.currentTarget.dataset.type,
               id: this.data.pid,

+ 2 - 3
src/components/slide-delete/index.wxml

@@ -9,9 +9,8 @@
       <slot/>
   </view>
   <view class="action-wrap">
-    <view wx:if="{{pinned}}" class="action cancel" bindtap="handleAction" data-type="unpin">
-        <text>取消置顶</text>
+    <view class="action delete" bindtap="handleAction" data-type="delete">
+      <text>删除</text>
     </view>
-    <view wx:else class="action pinned" bindtap="handleAction" data-type="pin"><text>置顶</text></view>
   </view>
 </view>

+ 10 - 2
src/components/slide-delete/index.wxss

@@ -5,7 +5,8 @@
 .wrap .content {
   position: relative;
   z-index: 1;
-  background: #fff;
+  border-radius: 24rpx;
+  overflow: hidden;
 }
 
 .wrap .content.animate {
@@ -16,10 +17,12 @@
 .wrap .action-wrap {
   position: absolute;
   display: flex;
-  width: 120rpx;
+  width: 240rpx;
   top: 0;
   bottom: 0;
   right: 0;
+  border-radius: 0 24rpx 24rpx 0;
+  overflow: hidden;
 }
 
 .wrap .action {
@@ -31,8 +34,13 @@
   color: white;
   background: #327BF9;
 }
+.wrap .delete {
+  background: #FF8200;
+}
 .wrap .action.cancel {
   display: flex;
+  flex-direction: column;
+  gap: 0;
   width: 120rpx;
   height: 100%;
   justify-content: center;

+ 2 - 2
src/components/widgets/widget-media/index.tsx

@@ -27,7 +27,7 @@ const Index: React.FC<Props> = ({ layout, mediaList, editMode=false }) => {
   };
 
   const renderEmpty = ()=> {
-    return <View className="px-16"><View className="component-card-empty">
+    return <View className={editMode ? 'px-16': ''}><View className="component-card-empty">
       <View className="component-card-content">
         <View className="component-card-empty-figure"></View>
         <View className="component-card-empty-tips">点击配置图片/视频</View>
@@ -45,7 +45,7 @@ const Index: React.FC<Props> = ({ layout, mediaList, editMode=false }) => {
     if (mediaList.length === 1) {
       const item = mediaList[0];
       return (
-        <View className={editMode ? "px-20": ''}>
+        <View className={editMode ? "px-16": ''}>
           <View
             className={editMode ? style.figureSingle: style.figureSingleLarge}
             onClick={(e) => handlePreview(e, 0, item)}

Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 0 - 1
src/config/index.ts


+ 10 - 1
src/pages/chat/components/chat-welcome/index.tsx

@@ -4,11 +4,20 @@ import style from './index.module.less'
 import PersonalCard from '../personal-card'
 import MessageRobotPlain from '@/components/chat-message/MessageRobotPlain'
 import { TAgentDetail } from '@/types/agent';
+import { useChatInput } from '../input-bar/chatInput';
 interface IProps {
   agent: TAgentDetail | null;
 }
 export default ({agent}: IProps)=> {
+  const { handleOnSend } = useChatInput({
+    agent,
+  });
 
+  const handleClick = (q: string) => {
+    handleOnSend(q);
+  };
+
+  
   if(!agent){
     return <></>
   }
@@ -34,7 +43,7 @@ export default ({agent}: IProps)=> {
         <View className='flex flex-col gap-8'>
           <MessageRobotPlain text={greeting}/>
           {questions.map(q => {
-            return <MessageRobotPlain text={q} />
+            return <MessageRobotPlain text={q} onClick={handleClick} />
           })}
         </View>
     </View> 

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

@@ -25,7 +25,7 @@ export default ({disabled, onIconClick, onSend}:Props) => {
     return <View onClick={onIconClick}><IconMic /></View>
   }
   return <>
-    <View className="px-16 py-12">
+    
       <WemetaInput 
       	adjustPosition={false}
         disabled={disabled}
@@ -39,6 +39,5 @@ export default ({disabled, onIconClick, onSend}:Props) => {
         onInput={(value: any) => handleInput(value)}
         >
       </WemetaInput>
-    </View>  
   </>
 }

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

@@ -64,7 +64,7 @@ export default ({agentId,disabled, onIconClick, onSend, beforeSend, onError}:Pro
 
   return <>
     {/* <TextInputBar onIconClick={handleTextInputBarSwitch}></TextInputBar> */}
-    <View className="px-16 py-12">
+    
       <View className={`flex bg-white gap-6 items-center p-16 rounded-10 ${style.speechButton} ${ idle ? '': style.speechButtonActive}`}>
         {idle && <View className="flex items-center" onClick={onIconClick}><IconKeyboard/></View>}
         <View 
@@ -76,6 +76,6 @@ export default ({agentId,disabled, onIconClick, onSend, beforeSend, onError}:Pro
           <Text className={`text-16 leading-22 font-medium ${idle ? '':'hidden'}`}>按住说话</Text>
         </View>
       </View>
-    </View>  
+    
   </>
 }

+ 4 - 3
src/pages/chat/components/personal-card/index.tsx

@@ -4,6 +4,7 @@ import { Image, View } from '@tarojs/components'
 import TagCertificated from "@/components/tag-certificated";
 import IconCertificateColor from "@/components/icon/icon-certificate-color";
 import { TAgentDetail } from '@/types/agent';
+import { AvatarMedia } from '@/components/AvatarMedia';
 interface IProps {
   size?: 'mini'|'large'
   agent: TAgentDetail|null
@@ -14,10 +15,10 @@ export default ({size='large', agent}:IProps) => {
     if(size === 'mini'){
     return <>
         <View className="rounded-full overflow-hidden w-28 h-28">
-          <Image src={agent?.avatarLogo} mode='widthFix' className='w-28 h-28'></Image>
+          <AvatarMedia source={agent?.avatarLogo || ''} className='w-28 h-28'></AvatarMedia>
         </View>
         <View className="flex flex-col flex-1 gap-4">
-          <View className="flex items-end gap-4">
+          <View className="flex items-center gap-4">
             <View className="text-12 font-medium leading-12">{agent?.name}</View>
             {agent?.isEnt && <View className="text-12 leading-12"><IconCertificateColor/></View>}
           </View>
@@ -31,7 +32,7 @@ export default ({size='large', agent}:IProps) => {
     }
     return <>
         <View className="rounded-full overflow-hidden w-60 h-60">
-          <Image src={agent?.avatarLogo} mode='widthFix' className='w-60 h-60'></Image>
+          <AvatarMedia source={agent?.avatarLogo || ''} className='w-60 h-60'></AvatarMedia>
         </View>
         <View className="flex flex-col flex-1">
           <View className="flex items-end gap-8">

+ 9 - 7
src/pages/chat/index.tsx

@@ -212,7 +212,7 @@ const agent = useAgentStore((state) => {
         </ScrollView>
         <View className="h-140 w-10"></View>
         <View
-          className="fixed left-0 right-0 bottom-0 min-h-130 z-50"
+          className="bottom-bar px-16 pt-12 z-50"
           style={{
             bottom: `${keyboardHeight}px`,
           }}
@@ -228,12 +228,14 @@ const agent = useAgentStore((state) => {
             深度思考(R1)
           </View> */}
           {agent && (
-            <InputBar
-              aiModel={deepThink}
-              agent={agent}
-              histories={list}
-              setShowWelcome={setShowWelcome}
-            ></InputBar>
+            <View className="bg-[#F5FAFF]">
+              <InputBar
+                aiModel={deepThink}
+                agent={agent}
+                histories={list}
+                setShowWelcome={setShowWelcome}
+              ></InputBar>
+            </View>
           )}
         </View>
       </View>

+ 2 - 0
src/pages/contact/components/contact-card/index.module.less

@@ -3,8 +3,10 @@
   padding: 16px;
   align-items: center;
   gap: 8px;
+  border-radius: 12px;
   color: #262626;
   font-family: "PingFang SC";
+  background-color: white;
 }
 
 .avatar{

+ 7 - 26
src/pages/contact/components/contact-card/index.tsx

@@ -1,9 +1,10 @@
 import { View, Image } from '@tarojs/components'
 import style from './index.module.less'
 import Taro from '@tarojs/taro'
-import { delContact } from "@/service/contact";
 import { TContactItem } from '@/types/contact';
-import { isSuccess } from '@/utils';
+
+import { AvatarMedia } from '@/components/AvatarMedia';
+import IconCertificateColor from "@/components/icon/icon-certificate-color";
 interface Props {
   data: TContactItem
   deleteable?: boolean
@@ -17,37 +18,17 @@ const Index = ({data, deleteable, refresh, fromContact}: Props)=> {
       url: "/pages/profile/index?agentId=" + data.agentId,
     });
   }
-  const handleLongPress = (e, contactId: string|number) => {
-    // console.log("longpress");
-    if(!deleteable){
-      return;
-    }
-    e.stopPropagation();
-    Taro.showModal({
-      content: "😭 确认删除该联系人吗?",
-      async success(result) {
-        if (result.confirm) {
-          const response = await delContact(contactId);
-          if(isSuccess(response.status)) {
-            Taro.showToast({
-              title: "删除成功",
-              icon: 'none',
-            });
-            refresh();
-          }
-        }
-      },
-    });
-  };
+  
 
   return (
-    <View className={style.contactCard} onClick={()=>{handleClick(data)}} onLongPress={(e) => handleLongPress(e, data.contactId)}>
+    <View className={style.contactCard} onClick={()=>{handleClick(data)}}>
       <View className={style.avatar}>
-      {data.avatarUrl && <Image src={data.avatarUrl} mode="aspectFill" className={style.avatar}></Image>}
+        <AvatarMedia source={data.avatarUrl || ''} mode="aspectFill" className={style.avatar}></AvatarMedia>
       </View>
       <View className={style.infoColumn}>
         <View className={style.nameRow}>
           <View className={style.nickName}>{data.name}</View>
+          {data.isEnt && <IconCertificateColor></IconCertificateColor>}
         </View>
         <View className='flex items-center w-full'>
           <View className={`flex-1 ${style.subInfo}`}>{data.lastChatMsg}</View>

+ 1 - 1
src/pages/contact/index.config.ts

@@ -3,6 +3,6 @@ export default definePageConfig({
   navigationStyle: 'custom',
   onReachBottomDistance: 20,
   usingComponents: {
-    'slide-delete': '../../components/slide-delete/index',
+    'slide-contact': '../../components/slide-contact/index',
   }
 })

+ 2 - 1
src/pages/contact/index.module.less

@@ -5,6 +5,7 @@
   display: flex;
   flex-direction: column;
   width: 100%;
+  padding: 0 16px;
+  gap: 8px;
   overflow: hidden;
-  border-top: 1px solid #EAECF5;
 }

+ 64 - 24
src/pages/contact/index.tsx

@@ -1,5 +1,5 @@
 import Taro, { useDidShow, useReachBottom } from "@tarojs/taro";
-import { View, Text } from "@tarojs/components";
+import { View, Text, ScrollView } from "@tarojs/components";
 import EmptyData from "@/components/empty-data";
 import NavBarNormal from "@/components/NavBarNormal/index";
 import SearchBar from "@/components/search-bar/index";
@@ -11,7 +11,7 @@ import { useLoadMoreInfinite, type TResponseData } from "@/utils/loadMoreInfinit
 import PageCustom from "@/components/page-custom/index";
 import { TContactItem } from "@/types/contact";
 import { isSuccess } from "@/utils";
-
+import { delContact } from "@/service/contact";
 export default function Index() {
   const [searchValue, setSearchValue] = useState("");
 
@@ -57,14 +57,14 @@ export default function Index() {
     setSearchValue("");
   };
 
-  useReachBottom(() => {
+  const onScrollToUpper = () => {
     // 加载更多数据
     // 如果搜索框中有数据由不加载更多数据
     if (searchValue.length) {
       return;
     }
     setSize((prevSize) => prevSize + 1);
-  });
+  };
 
 
   const handleHello = async (e: any) => {
@@ -81,52 +81,92 @@ export default function Index() {
         return;
       }
     }
+    if(detail.type === 'delete'){
+      handleDelete(detail.id)
+    }
   };
 
   useDidShow(() => {
     mutate(undefined, {revalidate:true})
   })
 
+  const handleDelete = (contactId: string|number) => {
+    
+    Taro.showModal({
+      content: "😭 确认删除该联系人吗?",
+      async success(result) {
+        if (result.confirm) {
+          const response = await delContact(contactId);
+          if(isSuccess(response.status)) {
+            Taro.showToast({
+              title: "删除成功",
+              icon: 'none',
+            });
+            mutate()
+          }
+        }
+      },
+    });
+  };
+
+  const renderItem = (item)=> {
+    // @ts-ignore
+  return <slide-contact
+    pid={item.contactId}
+      pinned={item.isTop}
+      onAction={handleHello}
+    >
+    <View className={`rounded-12 overflow-hidden ${item.isTop ? "bg-[#EDF1FF]" : "bg-white"}`}>
+        <ContactCard
+          refresh={mutate}
+          deleteable={true}
+          key={item.contactId}
+          data={item}
+          fromContact
+        ></ContactCard>
+      </View>
+    </slide-contact>
+  }
+
   const renderContent = () => {
     if (list?.length) {
       return list.map((item) => (
-        // @ts-ignore
-        <slide-delete
-          pid={item.contactId}
-          pinned={item.isTop}
-          onAction={handleHello}
-        >
-          <View className={`${item.isTop ? "bg-gray" : "bg-white"}`}>
-            <ContactCard
-              refresh={mutate}
-              deleteable={true}
-              key={item.contactId}
-              data={item}
-              fromContact
-            ></ContactCard>
-          </View>
-        </slide-delete>
+        <View className="rounded-24 bg-white">
+          {renderItem(item)}
+        </View>
       ));
     }
     return <EmptyData type={'search'} />;
   };
 
   return (
-    <PageCustom>
+    <PageCustom fullPage>
       <NavBarNormal
         scrollFadeIn
         leftColumn={() => <Text className="text-16 font-medium">联系人</Text>}
       ></NavBarNormal>
       <View className="flex flex-col w-full">
-        <View className="px-16 pb-12">
+        <View className="px-16 pb-14">
           <SearchBar
             value={searchValue}
             onChange={(e) => handleSearchBarChanged(e)}
             onClear={() => handleClear()}
           ></SearchBar>
         </View>
-
-        <View className={style.contactContent}>{renderContent()}</View>
+        <View className="rounded-container-header"></View>
+        <View className="px-16 text-gray-45 text-12 leading-20 mb-20">
+          共 4 个联系人
+        </View>
+        <ScrollView
+          scrollY
+          onScrollToUpper={onScrollToUpper}
+          style={{
+            flex: 1,
+            height: "100%", // 高度自适应
+          }}
+        >
+          <View className={style.contactContent}>{renderContent()}</View>  
+        </ScrollView>
       </View>
     </PageCustom>
   );

+ 76 - 0
src/pages/dashboard/components/AgentList/index.tsx

@@ -0,0 +1,76 @@
+import { View, Text } from "@tarojs/components";
+import Taro, { useDidShow } from "@tarojs/taro";
+import Popup from "@/components/popup/popup";
+
+import { useEffect, useState } from "react";
+import { useAgentStore } from "@/store/agentStore";
+import WemetaRadio from "@/components/WemetaRadio/index";
+import IconCertificateColor from "@/components/icon/icon-certificate-color";
+import { TAgent } from "@/types/agent";
+import { AvatarMedia } from "@/components/AvatarMedia";
+
+interface IProps {
+  currentAgent: TAgent|null
+  show: boolean
+  setShow: (show: boolean)=> void
+  setCurrentAgent: (picked: TAgent|null) => void;
+}
+
+export default ({currentAgent,  setCurrentAgent, show, setShow }: IProps) => {
+  const { agents, fetchAgents } = useAgentStore();
+
+  // 是否显示选择器
+  
+
+
+  useEffect(() => {}, []);
+
+  const fetchInitData = async () => {
+    await fetchAgents();
+  };
+
+  const handleClick = (item: TAgent|null) => {
+    setCurrentAgent(item)
+    setShow(false)
+  }
+
+  useDidShow(() => {
+    fetchInitData();
+  });
+
+  if (!agents) {
+    return <View>...</View>;
+  }
+
+  return (
+    <Popup title="选择智能体" show={show} setShow={setShow}>
+      <View className="flex flex-col gap-12 w-full">
+        {agents.map((item) => {
+          return (
+            <View className="flex items-center gap-8 p-12 rounded-8 overflow-hidden bg-[#F8F8F8]" onClick={()=> handleClick(item)}>
+              <View className="flex items-start w-40 shrink-0">
+                <AvatarMedia
+                  source={item.avatarLogo || ""}
+                  className="w-40 h-40 overflow-hidden"
+                ></AvatarMedia>
+              </View>
+              <View className="flex flex-col flex-1 overflow-hidden">
+                <View className="text-24 text-black leading-24 truncate">{item.name}</View>
+                <View className="flex items-center gap-4">
+                  <View className="text-12 text-gray-65 leading-20 truncate">{item?.entName || '-'}</View>
+                  {item?.isEnt && <View><IconCertificateColor/></View>}
+                </View>
+              </View>
+              <View className="shrink-0">
+                <WemetaRadio
+                  checkbox
+                  checked={currentAgent?.agentId == item.agentId}
+                 />
+              </View>
+            </View>
+          );
+        })}
+      </View>
+    </Popup>
+  );
+};

+ 7 - 17
src/pages/dashboard/components/VisitorCard/index.tsx

@@ -2,15 +2,10 @@ import { View, Text, Image } from "@tarojs/components";
 import DigitalCardBasic from "@/components/DigitalCard/DigitalCardBasic";
 import Taro from "@tarojs/taro";
 import { TVisitorAgent } from "@/types/visitor";
+import { AvatarMedia } from "@/components/AvatarMedia";
 
 export interface IndexProps {
-  data: {
-    certificated?: boolean
-    name: string
-    company: string
-    agentName: string
-    
-  }&TVisitorAgent;
+  data:TVisitorAgent;
 }
 
 const Index = ({ data }: IndexProps) => {
@@ -24,24 +19,19 @@ const Index = ({ data }: IndexProps) => {
       <View className="flex items-start gap-12">
         <View className="flex items-star rounded-full overflow-hidden">
           <View className="w-56 h-56 bg-gray-3 rounded-full">
-            {!!data.avatarUrl && (
-              <Image
-                src={data.avatarUrl}
-                mode="aspectFill"
-                className="w-56 h-56 bg-gray-3 rounded-full"
-              ></Image>
-            )}
+            <AvatarMedia source={data.avatarUrl || ''} mode="aspectFill" className="w-56 h-56 bg-gray-3 rounded-full"></AvatarMedia>
           </View>
         </View>
-        <View className="flex flex-col gap-8">
+        <View className="flex flex-col gap-8 flex-1">
           <DigitalCardBasic
             name={data.name}
             position={data.position ?? ""}
-            company={data.company}
+            entName={data.entName}
+            certificated={data.isEnt}
           />
           <View className="text-14 font-medium leading-22">
             第 <Text className="text-yellow">{data.visitTimes}</Text>{" "}
-            次访问了你的智能体【{data.agentName}】
+            次访问了你的智能体【{data.myAgentName}】
           </View>
           <View className="flex-center text-12 leading-20">
             <View className="flex-1">

+ 15 - 13
src/pages/dashboard/components/VisitorList/index.tsx

@@ -1,10 +1,11 @@
 import { View, Text } from "@tarojs/components";
 import Taro, { useDidShow, useReachBottom } from "@tarojs/taro";
 import VisitorCard from "../VisitorCard/index";
-
+import eventBus, {BUS_EVENTS} from '@/utils/EventBus'
 import { TVisitorAgent } from "@/types/visitor";
 import { getVisitorList } from "@/service/visitor";
 import { useLoadMoreInfinite, createKey } from "@/utils/loadMoreInfinite";
+import { useEffect } from "react";
 interface IProps {
   agentId?: string | number;
 }
@@ -16,12 +17,20 @@ export default ({ agentId }: IProps) => {
     return res.data;
   };
   const { list, loadMore, mutate } = useLoadMoreInfinite<TVisitorAgent[]>(
-    createKey(`getVisitorList${agentId}`, 10, [agentId]),
+    createKey(`getVisitorList${agentId}`, 20, [agentId]),
     fetcher);
 
-  useReachBottom(() => {
-    loadMore();
-  });
+  useEffect(() => {
+    const handler = () => {
+      loadMore();
+    }
+    
+    eventBus.on(BUS_EVENTS.REACH_BOTTOM, handler)
+    
+    return () => {
+      eventBus.off(BUS_EVENTS.REACH_BOTTOM, handler)
+    }
+  }, [])
 
   useDidShow(()=> {
     mutate(undefined, {revalidate: true})
@@ -40,14 +49,7 @@ export default ({ agentId }: IProps) => {
         {list.map((item) => {
           return (
             <VisitorCard
-              data={{
-                ...item,
-                name: item.name ?? "",
-                company: "",
-                avatarUrl: item.avatarUrl,
-                position: item.position ?? "",
-                agentName: item.myAgentName ?? "",
-              }}
+              data={item}
             ></VisitorCard>
           );
         })}

+ 37 - 39
src/pages/dashboard/index.tsx

@@ -1,65 +1,57 @@
 import { View, Text, } from "@tarojs/components";
-import Taro, { useDidShow } from "@tarojs/taro";
+import Taro, { useDidShow, useReachBottom } from "@tarojs/taro";
 import NavBarNormal from "@/components/NavBarNormal/index";
 import PageCustom from "@/components/page-custom/index";
 import IconArrowDownRounded from '@/components/icon/IconArrowDownRounded';
 import DataCard from './components/DataCard'
 import VisitorList from "./components/VisitorList";
-import  PickerSingleColumn from "@/components/Picker/PickerSingleColumn";
+import  AgentList from "./components/AgentList";
 import { useEffect, useState } from "react";
 import { useAgentStore } from "@/store/agentStore";
 import { getVisitorSummary, type TVisitorSummary } from '@/service/visitor'
 import { isSuccess } from "@/utils";
+import { TAgent } from "@/types/agent";
+import eventBus, {BUS_EVENTS} from '@/utils/EventBus'
 
 export default () => {
   
   const {agents, fetchAgents} = useAgentStore()
   
-  
+  const [show, setShow] = useState(false);
   // 是否显示选择器
-  const [showPicker, setShowPicker] = useState(false)
+  const [currentAgent, setCurrentAgent] = useState<TAgent|null>(null)
   const [summary, setSummary] = useState<TVisitorSummary>()
 
-  // 当前选中的值
-  const [selected, setSelected] = useState<string>();
-  const currentAgent = agents.find( _agent => _agent.name === selected)
 
-  const onPicked = (value: string) => {
-    setSelected(value)
+  const fetchSummary = async () => {
+    const response = await getVisitorSummary(currentAgent?.agentId)
+    if(isSuccess(response.status)){
+      setSummary(response.data)
+    }
   }
 
-  useDidShow(()=> {
+  const fetchInitData = async () => {
+    await fetchAgents();
+    fetchSummary()
     
-    fetchInitData()
-  })
-
+  }
 
-  
-  useEffect(() => {
-    setSelected('全部智能体')
-  }, [])
   useEffect(() => {
-    
-  }, [])
-
+    fetchSummary()
+  }, [currentAgent])
 
-  const fetchInitData = async () => {
-    await fetchAgents();
-    const currentAgent = agents.find( _agent => _agent.name === selected)
-    const response = await getVisitorSummary(currentAgent?.agentId)
-    if(isSuccess(response.status)){
-      setSummary(response.data)
-    }
-  }
+  useDidShow(()=> {
+    fetchInitData()
+  })
 
 
 
-  if(!agents || !selected) {
+  if(!agents) {
     return <View>...</View>
   }
-
-  const options = ['全部智能体', ...agents.map(_agent => _agent.name)]
-
+  useReachBottom(() => {
+    eventBus.trigger(BUS_EVENTS.REACH_BOTTOM)
+  })
   const unprocessedDislikeCnt = summary?.unprocessedDislikeCnt ?? 0
 
   return (
@@ -67,16 +59,20 @@ export default () => {
       <NavBarNormal scrollFadeIn leftColumn={()=> <Text className="font-medium text-dark leading-22 text-16">数据分析</Text>}>
       </NavBarNormal>
       <View className="w-full pt-8 px-16 pb-40">
-        {(!!options.length && !!selected) && 
-          <PickerSingleColumn options={options} selected={selected} onPicked={onPicked} showPicker={showPicker} setShowPicker={setShowPicker}>
-            <View className="flex items-center gap-2 bg-white rounded-12 p-12 mb-16" onClick={() => setShowPicker(true)}>
-              <View className="flex-1 text-14 leading-22 text-gray-45">{selected}</View>
+        <View className="flex items-center gap-8 mb-16">
+          <View className="bg-white rounded-12 py-12 px-16" onClick={()=> setCurrentAgent(null)}>
+            <View className={`${currentAgent ? 'text-gray-45' : 'text-black' }`}>全部</View>
+          </View>
+          <View className="flex flex-1 items-center gap-2 bg-white rounded-12 p-12" onClick={()=> setShow(true)}>
+              <View className="flex-1 text-14 leading-22">
+                {currentAgent ? <View className="text-black">{currentAgent?.name}</View> : <View className="text-gray-45">请选择智能体</View> }
+              </View>
+              
               <View className="flex-center">
                 <IconArrowDownRounded/>
               </View>
             </View>
-          </PickerSingleColumn>
-        }
+        </View>
 
         <View className="grid grid-cols-2 gap-8 bg-white rounded-12 p-12 mb-12">
           <DataCard text="今日访问次数" unitText="次" value={summary?.todayPv ?? 0} />
@@ -97,7 +93,9 @@ export default () => {
         </View>
 
         <VisitorList agentId={currentAgent?.agentId}></VisitorList>
-
+        
+        <AgentList show={show} setShow={setShow} currentAgent={currentAgent} setCurrentAgent={setCurrentAgent}></AgentList>
+        
       </View>
       
     </PageCustom>

+ 2 - 2
src/pages/knowledge-item/index.tsx

@@ -22,7 +22,7 @@ import {
   getEntKnowledgeDetail,
   deleteKnowledgeQa,
   updateExactAnswer,
-  deleteKnowledge,
+  deleteMyKnowledge,
 } from "@/service/knowledge";
 import { TKnowledgeDetail,TQAListItem } from "@/types/knowledge";
 import { isSuccess } from "@/utils";
@@ -94,7 +94,7 @@ export default function Index() {
     showModal({
       content: "确认删除文件及其全部知识?",
       onConfirm: async () => {
-        const { status } = await deleteKnowledge(detail.knowledgeId);
+        const { status } = await deleteMyKnowledge(detail.knowledgeId);
         if (isSuccess(status)) {
           await Taro.showToast({ title: "删除成功", icon: "success" });
           setTimeout(() => {

+ 7 - 14
src/pages/knowledge/components/CompanyTab/components/ScrollList.tsx

@@ -20,29 +20,23 @@ export interface Iprops {
   setTotalCount: (count: number) => void;
 }
 const Index = ({entId, setTotalCount}:Iprops) => {
-  
-  
-  const [scrollTop, setScrollTop] = useState(9999)
+
 
   const fetcher = async ([_url, {nextId, pageSize},  [entId]]) => {
     const res = await getEntKnowledgeList({ startId: nextId, pageSize, entId });
     return res.data;
   };
-  const { list, loadMore, pageIndex, mutate, data } = useLoadMoreInfinite<TKnowledgeItem[]>(createKey(`getEntKnowledgeList ${entId}`, 10, [entId]),
+  const { list, loadMore, pageIndex, mutate, data } = useLoadMoreInfinite<TKnowledgeItem[]>(createKey(`getEntKnowledgeList ${entId}`, 30, [entId]),
     fetcher);
 
-  const onScrollToUpper = async () => {
-    console.log('toUpper')
+  const onScrollToLower = async () => {
+    console.log('toLower')
     loadMore()
   }
   useDidShow(()=> {
     mutate(undefined, { revalidate: true });
   })
-  useEffect(() => {
-    if(pageIndex === 1){
-      setScrollTop(prev => prev + 1)
-    }
-  }, [pageIndex]);
+  
 
   useEffect(() => {
     if(data &&  pageIndex === 1) {
@@ -60,7 +54,7 @@ const Index = ({entId, setTotalCount}:Iprops) => {
     })
   }
   const rightRenderer = (item: TKnowledgeItem) => {
-    if(item.parseStatus !== 'parsed'){
+    if(!item.isParsing){
       return <></>
     }
     return (
@@ -74,8 +68,7 @@ const Index = ({entId, setTotalCount}:Iprops) => {
   return (
     <ScrollView
       scrollY
-      onScrollToUpper={onScrollToUpper}
-      scrollTop={scrollTop}
+      onScrollToLower={onScrollToLower}
       style={{
         flex: 1,
         height: "100%", // 高度自适应

+ 59 - 21
src/pages/knowledge/components/PersonalTab/components/ScrollList.tsx

@@ -17,10 +17,12 @@ import EmptyData from "@/components/empty-data";
 import type { TKnowledgeItem } from "@/types/knowledge";
 
 import {
+  deleteMyKnowledge,
   getKnowledgeList,
 } from "@/service/knowledge";
 
 import { useLoadMoreInfinite, createKey } from "@/utils/loadMoreInfinite";
+import { isSuccess } from "@/utils";
 
 export interface IProps {
   types?: EKnowlegeTypes[],
@@ -28,8 +30,6 @@ export interface IProps {
 }
 
 const Index = ({types, setTotalCount}: IProps) => {
-  
-  const [scrollTop, setScrollTop] = useState(9999)
 
   const fetcher = async ([_url, {nextId, pageSize}, [types]]) => {
     const res = await getKnowledgeList({ startId: nextId, pageSize, types });
@@ -37,8 +37,7 @@ const Index = ({types, setTotalCount}: IProps) => {
   };
   const { list, loadMore, mutate, data, pageIndex } = useLoadMoreInfinite<TKnowledgeItem[]>( createKey('getKnowledgeList', 2, [types]), fetcher,);
 
-  const onScrollToUpper = async () => {
-    console.log('toUpper')
+  const onScrollToLower = async () => {
     loadMore()
   }
 
@@ -62,10 +61,63 @@ const Index = ({types, setTotalCount}: IProps) => {
       url: `/pages/knowledge-item/index?knowledgeId=${item.knowledgeId}`
     })
   }
+
+  const handleDelete = async (id: string|number)=> {
+    Taro.showModal({
+      content: "确认删除该知识?",
+      async success(result) {
+        if (result.confirm) {
+          const response = await deleteMyKnowledge(id);
+          if(isSuccess(response.status)) {
+            Taro.showToast({
+              title: "删除成功",
+              icon: 'none',
+            });
+            mutate();
+          }
+        }
+      },
+    });
+  }
+
+  const handleHello = async (e: any) => {
+    const detail = e.detail as { type: string; id: string };
+    console.log(detail);
+    // 置顶与取消置顶
+    if (detail.type === "delete") {
+      handleDelete(detail.id);
+    }
+  };
+
+  const renderItem = (item: TKnowledgeItem)=> {
+      // @ts-ignore
+    return <slide-delete
+        pid={item.knowledgeId}
+        onAction={handleHello}
+      >
+        111
+        <FigureListItem
+              figure={()=> <KnowledgeIcon data={item}/>}
+              underline
+              arrow={item.parseStatus === 'parsed'}
+              onClick={()=> handleEdit(item)}
+              rightRenderer={()=> rightRenderer(item)}
+            >
+              <View className="flex flex-col flex-1 gap-2 w-full">
+                <View className="text-14 leading-22">{item.title}</View>
+                <View className="text-12 leading-20 text-gray-45">
+                {item.createTime} | {item.fileSize}
+                </View>
+              </View>
+            </FigureListItem>
+      </slide-delete>
+    }
+
   const rightRenderer = (item: TKnowledgeItem) => {
-    if(item.parseStatus !== 'parsed'){
+    if(!item.isParsing){
       return <></>
     }
+
     return (
       <View className="flex items-center gap-4">
         <RotateLoading />
@@ -77,8 +129,7 @@ const Index = ({types, setTotalCount}: IProps) => {
   return (
     <ScrollView
       scrollY
-      onScrollToUpper={onScrollToUpper}
-      scrollTop={scrollTop}
+      onScrollToLower={onScrollToLower}
       style={{
         flex: 1,
         height: "100%", // 高度自适应
@@ -87,20 +138,7 @@ const Index = ({types, setTotalCount}: IProps) => {
       {list.length<=0 ? <View className="pt-46"><EmptyData type={'plane'}/></View> : <></>}
       <FigureList>
       {list.map(item => {
-        return <FigureListItem
-            figure={()=> <KnowledgeIcon data={item}/>}
-            underline
-            arrow={item.parseStatus === 'parsed'}
-            onClick={()=> handleEdit(item)}
-            rightRenderer={()=> rightRenderer(item)}
-          >
-            <View className="flex flex-col flex-1 gap-2 w-full">
-              <View className="text-14 leading-22">{item.title}</View>
-              <View className="text-12 leading-20 text-gray-45">
-              {item.createTime} | {item.fileSize}
-              </View>
-            </View>
-          </FigureListItem>
+        return renderItem(item)
       })}
       </FigureList>
     </ScrollView>

+ 2 - 2
src/pages/knowledge/components/PersonalTab/components/ScrollListChat.tsx

@@ -18,7 +18,7 @@ import { TKnowledgeItem } from "@/types/knowledge";
 import type { TKnowledgeStreamResponseData } from "@/types/knowledge";
 
 import {
-  deleteKnowledgeStream,
+  deleteMyKnowledgeStream,
   getMyKnowledgeStream,
 } from "@/service/knowledge";
 
@@ -80,7 +80,7 @@ const Index = ({assistantOnly, setTotalCount}: IProps) => {
       content: "确认删除该知识?",
       async success(result) {
         if (result.confirm) {
-          const response = await deleteKnowledgeStream(streamId);
+          const response = await deleteMyKnowledgeStream(streamId);
           if(isSuccess(response.status)) {
             Taro.showToast({
               title: "删除成功",

+ 4 - 2
src/pages/knowledge/index.config.ts

@@ -1,5 +1,7 @@
 export default definePageConfig({
   navigationBarTitleText: '知识库',
-  "usingComponents": {},
-  navigationStyle: 'custom'
+  navigationStyle: 'custom',
+  usingComponents: {
+    'slide-delete': '../../components/slide-delete/index',
+  }
 })

+ 4 - 8
src/pages/visiteor-detail/components/VisitorSummary/index.tsx

@@ -3,6 +3,7 @@ import DigitalCardBasic from "@/components/DigitalCard/DigitalCardBasic";
 import { TVisitorAgent } from "@/types/visitor";
 import TagCertificated from "@/components/tag-certificated";
 import Taro from "@tarojs/taro";
+import { AvatarMedia } from '@/components/AvatarMedia';
 export interface IndexProps {
   data: TVisitorAgent;
 }
@@ -17,13 +18,8 @@ const VisitorSummary = ({ data }: IndexProps) => {
           })
         }}>
           <View className="w-60 h-60 bg-gray-3 rounded-full">
-            {!!data.avatarUrl && (
-              <Image
-                src={data.avatarUrl}
-                mode="aspectFill"
-                className="w-60 h-60 bg-gray-3 rounded-full"
-              ></Image>
-            )}
+            <AvatarMedia source={data.avatarUrl || ''} mode="aspectFill"
+                className="w-60 h-60 bg-gray-3 rounded-full"></AvatarMedia>
           </View>
         </View>
         <View className="flex flex-col gap-8">
@@ -34,7 +30,7 @@ const VisitorSummary = ({ data }: IndexProps) => {
                 <View className="text-12 leading-20">{data.position ?? ""}</View>
               </View>
               <View className="flex items-center gap-2">
-                <View className="text-12 leading-20 truncate max-w-[188px]">{}</View>
+                {/* <View className="text-12 leading-20 truncate max-w-[188px]">{data.}</View> */}
                 {data.isEnt && <TagCertificated />}
               </View>
             </View>

+ 3 - 3
src/service/knowledge.ts

@@ -95,11 +95,11 @@ export const updateExactAnswer = (knowledgeId: number, enable: boolean) => {
 }
 
 // 删除知识库 信息流记录;流删除会同步删除相关的记录项
-export const deleteKnowledgeStream = (streamId: string|number) => {
+export const deleteMyKnowledgeStream = (streamId: string|number) => {
   return request.delete(`${bluebookAiAgent}api/v1/my/knowledge/stream/${streamId}`,)
 }
-// 删除知识库 记录项
-export const deleteKnowledge = (knowledgeId: string|number) => {
+// 删除个人知识库 记录项
+export const deleteMyKnowledge = (knowledgeId: string|number) => {
   return request.delete(`${bluebookAiAgent}api/v1/my/knowledge/${knowledgeId}`,)
 }
 // 删除指定知识库的 QA 项

+ 11 - 25
src/types/agent.ts

@@ -41,16 +41,16 @@ export type TComponentItem = {
   type: EComponentType
 }
 export type TAgentDetail = {
-  address?: string,
+  address?: string|null,
   agentId?: string,
-  avatarUrl?: string,
-  avatarLogo?: string,
+  avatarUrl?: string|null,
+  avatarLogo?: string|null,
   components?: TComponentItem[],
   email?: string,
   enabledChatBg?: boolean,
   enabledPersonalKb?: boolean,
-  entId?: string,
-  entName?: string,
+  entId?: string|number|null,
+  entName?: string|null,
   greeting?: string,
   isDefault?: boolean,
   isEnt?: boolean,
@@ -59,30 +59,16 @@ export type TAgentDetail = {
   mobile?: string,
   name?: string,
   personality?: string,
-  position?: string,
-  qrCodeUrl?: string,
+  position?: string|null,
+  qrCodeUrl?: string|null,
   questionGuides?: string[],
   voiceId?: string
   isMineAgent: boolean
-  deletedTip?: string // 如果是已删除状态时的提示词 ,
+  deletedTip?: string|null // 如果是已删除状态时的提示词 ,
   status?:  'normal' | 'deleted' //  智能体状态:normal 正常/ deleted 已删除 ,
-  toAgentId?: string  //  当是删除状态时,提示跳转至新的智能体 ID
+  toAgentId?: string|null  //  当是删除状态时,提示跳转至新的智能体 ID
 }
 
-export type TAgentContactCard = {
-  address?: string,
-  email?: string,
-  entName?: string,
-  mobile?: string,
-  name?: string,
-  position?: string,
-  qrCodeUrl?: string
-}
+export type TAgentContactCard = Pick<TAgentDetail, 'address'|'email'|'entName'|'mobile'|'name' | 'position' | 'qrCodeUrl'>
 
-export type TEditAgentCharacter = {
-  enabledPersonalKb: boolean,
-  greeting: string,
-  personality: string,
-  questionGuides: string[],
-  voiceId: string
-}
+export type TEditAgentCharacter = Pick<TAgentDetail, 'enabledChatBg' | 'greeting' | 'personality' | 'questionGuides'| 'voiceId'>

+ 1 - 1
src/types/contact.ts

@@ -1,7 +1,7 @@
 export type TContactItem = {
   agentId : string // 联系人智能体ID -- 如果客户的所有智能体都无效时,则可能为 null ,
   agentStatus : string // 联系人智能体状态 -- normal 正常/ deleted 已删除 ,
-  avatarUrl : string // 联系人头像地址 ,
+  avatarUrl? : string|null // 联系人头像地址 ,
   contactId : number // 联系人ID --用于置顶、删除、拉黑 ,
   isEnt : boolean // 是否企业认证联系人 ,
   isTop : boolean // 是否置顶 ,

+ 10 - 3
src/types/visitor.ts

@@ -45,19 +45,26 @@ export type TVisitorMessage = {
 
 
 
-
 export type TVisitorAgent = {
+  address?: string|null
+  email?: string
   agentId: string, // 访客智能体ID -- 如果客户的所有智能体都无效时,则可能为 null ,
   agentStatus: string, // agentStatus (string, optional): 访客智能体状态 -- normal 正常/ deleted 已删除 ,
-  avatarUrl: string, // avatarUrl (string, optional): 访客头像地址 ,
+  avatarLogo?: string|null, // avatarUrl (string, optional): 访客头像地址 ,
+  avatarUrl?: string|null, // avatarUrl (string, optional): 访客头像地址 ,
   chatRound: number, // chatRound (integer, optional): 会话轮数 ,
   dislikeCnt: number,// dislikeCnt (integer, optional): 差评数 ,
   isEnt: false, // isEnt (boolean, optional): 是否企业认证联系人 ,
   lastChatTime: string,// lastChatTime (string, optional): 最新的对话时间 ,
   myAgentId: string, // myAgentId (string, optional): 访问我的智能体ID ,
   myAgentName: string,// myAgentName (string, optional): 访问我的智能体名称 ,
+  isNewEnt: boolean
+  isDefault: boolean
   name: string, // name (string, optional): 访客名称 -- 最后一次访问的快照信息 ,
-  position: string,// position (string, optional): 访客职位 ,
+  entName?: string|null, // name (string, optional): 访客名称 -- 最后一次访问的快照信息 ,
+  entId?: string|null, // name (string, optional): 访客名称 -- 最后一次访问的快照信息 ,
+  position?: string|null,// position (string, optional): 访客职位 ,
+  qrCodeUrl?: string|null,// position (string, optional): 访客职位 ,
   visitTimes: number,// visitTimes (integer, optional): 访问访问我的指定智能体次数 ,
   visitorId: number// visitorId (integer, optional): 访客ID
 }

+ 6 - 0
src/utils/EventBus.ts

@@ -0,0 +1,6 @@
+import Taro from '@tarojs/taro'
+export default new Taro.Events()
+
+export const BUS_EVENTS = {
+  REACH_BOTTOM: 'REACH_BOTTOM'
+}

Kaikkia tiedostoja ei voida näyttää, sillä liian monta tiedostoa muuttui tässä diffissä