Browse Source

fix: 聊天输入框键盘改为发送

王晓东 3 weeks ago
parent
commit
781a1c8f61

+ 2 - 2
project.private.config.json

@@ -11,7 +11,7 @@
         {
           "name": "pages/profile/index",
           "pathName": "pages/profile/index",
-          "query": "agentId=p_2e73c9d7efaYfDo2-agent_2741&shareKey=2%241%24AAADa14w4xNqXnlJlHbkhcKEQKitchW6t5ppoSKoMgjaPhuUlUe5J1igcZ1ADnBeKNyVq2FpibXl9RBseDP67ot5OCZVTfSAQeGnpKLhWYLT%2FQ49",
+          "query": "agentId=p_2e73c9d7efaYfDo2-agent_2747&shareKey=2%241%24AAADJVaNamEqHCG%2FJ8rQloPjNu6BaGv5PqdIw081Yet%2BROuk5MwyAhuwauvV1dt3XgK7JnY2%2BAO6qDCDzQeF2qqhC4YSK%2FkewwevYKd6jPM%2FQO1u",
           "scene": null,
           "launchMode": "default"
         },
@@ -60,5 +60,5 @@
       ]
     }
   },
-  "libVersion": "3.9.0"
+  "libVersion": "2.27.3"
 }

+ 8 - 6
src/app.tsx

@@ -20,7 +20,7 @@ function App({ children }: PropsWithChildren<any>) {
 
   useLaunch((options) => {
     console.log("App launched.", options.query);
-    
+
 
     const updateManager = Taro.getUpdateManager();
 
@@ -45,12 +45,14 @@ function App({ children }: PropsWithChildren<any>) {
     updateManager.onUpdateFailed(function () {
       // 新版本下载失败
     });
+    const res = Taro.getSystemInfoSync()
+    setSystemInfo(res);
+    // console.log(a.miniProgram)
+    // Taro.getSystemInfoAsync({
+    //   success(res) {
 
-    Taro.getSystemInfoAsync({
-      success(res) {
-        setSystemInfo(res);
-      },
-    });
+    //   },
+    // });
 
     const uuid = generateRandomId()
     console.log(uuid)

+ 1 - 1
src/components/AgentPage/components/AgentActionBar/index.tsx

@@ -101,7 +101,7 @@ export default ({agent, isVisitor}: IProps) => {
               <View className="flex flex-col flex-1 gap-8">
                 <View className="flex items-end gap-8">
                   <View className="text-24 font-medium leading-22 truncate max-w-180">{agent?.name}</View>
-                  <View className="text-12 leading-20 text-black-60">{agent?.position ?? (isVisitor ? '' :'暂未填职业')}</View>
+                  <View className="text-12 leading-20 truncate max-w-100 text-black-60">{agent?.position ?? (isVisitor ? '' :'暂未填职业')}</View>
                 </View>
                 <View className="flex items-center gap-4">
                   <View className="text-12 leading-20 text-black-60 truncate max-w-[188px]">

+ 1 - 2
src/components/AgentPage/components/AgentQRcode/index.module.less

@@ -1,10 +1,9 @@
 .container{
-  padding-top: 12px;
   min-height: 346px;
   display: flex;
   align-items: center;
   flex-direction: column;
-  justify-content: center;
+  // justify-content: center;
   overflow: auto;
 }
 .card{

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

@@ -89,7 +89,7 @@ export default function Index({title = '知识库',  show, setShow, multi, types
     return (
       <View className="h-full pb-40">
         <View
-          className="flex items-center gap-2 bg-gray-3 rounded-12 p-12 mb-16"
+          className="flex items-center gap-2 bg-gray-1 rounded-12 p-12 mb-16"
           onClick={() => setShowPicker(true)}
         >
           <View className="flex-1 text-14 leading-22 text-gray-4">
@@ -110,7 +110,7 @@ export default function Index({title = '知识库',  show, setShow, multi, types
             {options.map((item) => {
               return (
                 <View
-                  className="flex items-center rounded-8 bg-gray-3 px-16 py-20"
+                  className="flex items-center rounded-8 px-16 py-20"
                   onClick={() => handleChange(item)}
                 >
                   <View className="flex-1 text-black font-medium text-14 leading-22 font-pingfangSCMedium">

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

@@ -37,13 +37,15 @@ export default ({ agent, text, message, mutate, showUser=false}: Props) => {
   const { setMessageRespeaking, setMessageStopHandle, messageStopHandle, isReacting, updateMessageReaction } = useTextChat()
   const { startSpeech, stopSpeech, onPlayerStatusChanged } = useTextToSpeech()
 
+  const hasText = text.length > 0;
+
   // 更新单个消息的函数
   const updateMessage = (messageId: number, updatedFields: {isLike: boolean, isDislike: boolean}) => {
     // 使用 mutate 更新 SWR 缓存
     // 历史消息
     mutate((pages:any) => {
       return pages.map(page => {
-        const updatedMessages = page.data.map((msg:TMessage) => 
+        const updatedMessages = page.data.map((msg:TMessage) =>
           msg.msgId === messageId ? { ...msg, ...updatedFields } : msg
         );
         return { ...page, data: updatedMessages };
@@ -59,7 +61,7 @@ export default ({ agent, text, message, mutate, showUser=false}: Props) => {
     setMessageRespeaking(isSpeaking)
     setIsSpeaking(isSpeaking)
   })
-  
+
   const handlePlayAudio = async (e:any, text: string) => {
     e.stopPropagation();
     if(isReacting){
@@ -86,7 +88,7 @@ export default ({ agent, text, message, mutate, showUser=false}: Props) => {
     })
 
     setMessageStopHandle(stopSpeech)
-    
+
     setIsSpeaking(true)
     setMessageRespeaking(true)
   }
@@ -124,7 +126,7 @@ export default ({ agent, text, message, mutate, showUser=false}: Props) => {
     if (!agent?.agentId) {
       return;
     }
-    
+
     const isLike = !message.isLike;
     const response = await likeMessage({
       agentId: agent?.agentId,
@@ -193,16 +195,16 @@ export default ({ agent, text, message, mutate, showUser=false}: Props) => {
         <View className={`${style.messageContent}`}>
 
           {
-            text.length === 0 ? <ThinkAnimation></ThinkAnimation> : <>
+            !hasText ? <ThinkAnimation></ThinkAnimation> : <>
               {/* <Text user-select>{text}</Text> */}
               {/* <MarkdownParser content='您可能想询问的是:**“汽车的核心理念”** 是什么。'></MarkdownParser> */}
               <MarkdownParser content={text}></MarkdownParser>
               {renderMessageBody()}
             </>
           }
-          
+
         </View>
-        <View className="flex gap-12">
+        {hasText ? <View className={`flex gap-12`}>
           <View onClick={(e) => handleCopy(e, text)}>
             <IconCopy />
           </View>
@@ -217,6 +219,7 @@ export default ({ agent, text, message, mutate, showUser=false}: Props) => {
             {isLike ? <IconLikeBlue /> : <IconLike />}
           </View>
         </View>
+         : <></> }
       </View>
     </View>
   );

+ 1 - 1
src/components/qrcode-upload-tips/index.tsx

@@ -4,7 +4,7 @@ export default ()=> {
   return (
     <View>
       <View className="flex-center flex-col">
-        <View className="w-36 h-36 rounded-full flex-center mb-12 bg-primary">
+        <View className="w-36 h-36 rounded-full flex-center mb-8 bg-primary">
           <IconPlusBig/>
         </View>
         <View className="pt-8 text-12 leading-22 text-primary">上传你的二维码</View>

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

@@ -37,7 +37,7 @@ export default () => {
               onInput={(e: any) => handleFieldChange('name', e.detail.value)}
               placeholderStyle="color: rgba(17,17,17,.25)"
               placeholder="输入姓名"
-              maxlength={40}
+              maxlength={18}
             />
           </View>
         </View>

+ 2 - 2
src/pages/agent/components/AgentSetting/components/AgentSettingList/index.tsx

@@ -83,7 +83,7 @@ export default forwardRef(function Index(props, ref) {
         prefix={() => <View>人设</View>}
         placeholder="示例:你是一名汽车销售人员,拥有专业的汽车相关知识,善于耐心的解答客户提出的每一个问题,并会主动邀请客户上门试驾。"
         value={getDisplayValue('personality')}
-        cursorSpacing={20}
+        cursorSpacing={40}
         autoHeight
         ref={personalityPolishRef}
         onInput={(value) => {
@@ -95,7 +95,7 @@ export default forwardRef(function Index(props, ref) {
         aiType="greeting"
         defaultAiTips={defalutPolishGreeting}
         onPolishStateChange={handlePolishStateChange}
-        cursorSpacing={20}
+        cursorSpacing={40}
         prefix={() => <View>开场白</View>}
         placeholder="开场白是你的智能体和用户说的第一句话,简单做个自我介绍吧"
         value={getDisplayValue('greeting')}

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

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

+ 8 - 0
src/pages/chat/components/InputBar/useChatInput.ts

@@ -46,6 +46,7 @@ export const useChatInput = ({
     setMessageStopHandle,
     setReacting,
     setScrollTop,
+    scrollTop,
     setAutoScroll,
   } = useTextChat();
 
@@ -268,6 +269,13 @@ export const useChatInput = ({
             agent.agentId,
             sessionId
           );
+
+          // 兼容低版本微信小程序基础库,滚动到底部不准确
+          setTimeout(async ()=> {
+            setScrollTop();
+            console.log("setScrollTop", scrollTop)
+          }, 800)
+
         }
 
       },

+ 17 - 9
src/pages/chat/components/PersonalCard/index.tsx

@@ -17,9 +17,11 @@ import style from './index.module.less'
 interface IProps {
   agent: TAgentDetail|null
   haveBg: boolean
-  onClear: ()=>void
+  contextMenuVisible: boolean
+  onClear: () => void
+  onVisibleChange: (visible: boolean) => void
 }
-export default ({haveBg, agent, onClear}:IProps) => {
+export default ({haveBg, agent, contextMenuVisible, onClear, onVisibleChange}:IProps) => {
 
   /**
    * 之前个人卡片信息有两种样式,现在统一为一种
@@ -27,17 +29,22 @@ export default ({haveBg, agent, onClear}:IProps) => {
 
   const nameStyle = haveBg ? 'text-white' : 'text-black'
   const entStyle = haveBg ? 'text-white' : 'text-black-45'
-  const [show, setShow] = useState(false)
+
   const { setIsNewChat } = useTextChat()
 
+  const changeShow = (_show: boolean) => {
+    // setShow(_show)
+    onVisibleChange(_show)
+  }
+
   const handleClick = (e:any) => {
     e.stopPropagation()
-    setShow(!show)
+    changeShow(!contextMenuVisible)
   }
   const handleClean = async (e:any) => {
     // e.stopPropagation()
     console.log('clean')
-    setShow(false)
+    changeShow(false)
     setIsNewChat(true)
     Taro.showLoading()
     await cleanContext({
@@ -48,7 +55,7 @@ export default ({haveBg, agent, onClear}:IProps) => {
     onClear()
   }
   const handleVoiceChoose = (e:any) => {
-    setShow(false)
+    changeShow(false)
     Taro.navigateTo({
       url: '/pages/voice/index'
     })
@@ -63,7 +70,8 @@ export default ({haveBg, agent, onClear}:IProps) => {
           </View>
           <View className="flex flex-col flex-1 gap-6 max-w-[158px]">
             <View className="flex items-center gap-4">
-              <View className={`text-14 font-medium leading-14 ${nameStyle}`}>{agent?.name}</View>
+              <View className={`text-14 font-medium leading-14 truncate ${nameStyle}`}>{agent?.name}</View>
+              <View className={`text-14 font-medium leading-14 truncate ${nameStyle}`}>{agent?.position}</View>
               {agent?.isEnt && <View className="text-12 leading-12"><IconCertificateColor/></View>}
             </View>
             {agent?.entName && <View className="flex items-center gap-2">
@@ -76,9 +84,9 @@ export default ({haveBg, agent, onClear}:IProps) => {
         <View className='w-16 h-16'>
           <View className='relative w-16 h-16'>
             <View>
-              <IconArrowVerticalFilled color={ haveBg ? 'white' : 'gray' } rotation={show ? 0 : 180}  />
+              <IconArrowVerticalFilled color={ haveBg ? 'white' : 'gray' } rotation={contextMenuVisible ? 0 : 180}  />
             </View>
-            {!!show && <View className={style.bubbleWrapper} onClick={(e)=>e.stopPropagation()}>
+            {!!contextMenuVisible && <View className={style.bubbleWrapper} onClick={(e)=>e.stopPropagation()}>
               <View className={`flex flex-col gap-8 ${style.bubble}`}>
                 <View className={style.bubbleArrowWrap}>
                   <View className={style.bubbleArrow}></View>

+ 5 - 4
src/pages/chat/hooks/useChatUI.tsx

@@ -2,8 +2,9 @@ import { useState, useEffect } from 'react';
 import Taro from '@tarojs/taro';
 import { View } from "@tarojs/components";
 import { useAppStore } from '@/store/appStore';
-import { useTextChat } from '@/store/textChatStore';
 
+import IconArrowLeftWhite24 from "@/components/icon/IconArrowLeftWhite24";
+import IconArrowLeft from "@/components/icon/icon-arrow-left";
 /**
  * 聊天UI状态管理 Hook
  * 负责处理UI相关的状态,如欢迎页显示、背景设置、输入框高度等
@@ -20,7 +21,7 @@ export const useChatUI = (
   inputContainerBottomOffset: number;
   setShowWelcome: (show: boolean) => void;
   getBgContent: () => string;
-  createNavLeftRenderer: (PersonalCard: any, IconArrowLeftWhite24: any, IconArrowLeft: any, onClear: ()=>void) => () => JSX.Element;
+  createNavLeftRenderer: (PersonalCard: any, contextMenuVisible:boolean, onClear: ()=>void, onVisibleChange: (visible: boolean) => void) => () => JSX.Element;
 } => {
   const [showWelcome, setShowWelcome] = useState(!historyListLength && !isNewChat);
   const [inputContainerHeight, setInputContainerHeight] = useState(0);
@@ -44,14 +45,14 @@ export const useChatUI = (
   };
 
   // 渲染导航栏左侧内容的工厂函数
-  const createNavLeftRenderer = (PersonalCard: any, IconArrowLeftWhite24: any, IconArrowLeft: any, onClear: ()=>void) => {
+  const createNavLeftRenderer = (PersonalCard: any, contextMenuVisible:boolean, onClear: ()=>void, onVisibleChange: (visible: boolean) => void) => {
     return () => {
       return (
         <View
           className="flex items-center gap-8"
         >
           <View onClick={() => Taro.navigateBack()}>{haveBg ? <IconArrowLeftWhite24 /> : <IconArrowLeft />}</View>
-          <PersonalCard agent={agent} haveBg={haveBg} onClear={onClear} />
+          <PersonalCard agent={agent} haveBg={haveBg} contextMenuVisible={contextMenuVisible} onClear={onClear} onVisibleChange={onVisibleChange} />
           {/* {scrollTop} */}
         </View>
       );

+ 14 - 4
src/pages/chat/index.tsx

@@ -9,8 +9,6 @@ import { useTextChat } from "@/store/textChatStore";
 import { formatMessageTime } from "@/utils/timeUtils";
 
 import ChatGreeting from "./components/ChatGreeting";
-import IconArrowLeftWhite24 from "@/components/icon/IconArrowLeftWhite24";
-import IconArrowLeft from "@/components/icon/icon-arrow-left";
 import PersonalCard from "./components/PersonalCard";
 import ButtonEnableStreamVoice from "./components/OptionButtons/ButtonEnableStreamVoice";
 
@@ -70,6 +68,8 @@ export default function Index() {
   const [isVoice, setIsVoice] = useState(false);
   const [disabled, setDisabled] = useState(false);
 
+  const [contextMenuVisible, setContextMenuVisible] = useState(false);
+
   // 获取当前消息列表长度
   const currentMessageListLength = useTextChat((state) => state.list.length);
   const isNewChat = useTextChat((state) => state.isNewChat);
@@ -118,19 +118,29 @@ export default function Index() {
   // 加载更多的处理函数(已经在 useChatScrollManager 中处理)
   const onScrollToUpper = handleScrollToUpper;
 
+  // const PersonalCardWrap = ()=> <PersonalCard />
+
   // 使用工厂函数创建导航栏左侧渲染器
-  const renderNavLeft = createNavLeftRenderer(PersonalCard, IconArrowLeftWhite24, IconArrowLeft, ()=> {
+  const renderNavLeft = createNavLeftRenderer(PersonalCard, contextMenuVisible, ()=> {
     console.log('clear chat')
     messageStopHandle?.();
     clearList()
     resetAndClearData()
+  }, (menuVisible: boolean) => {
+    setContextMenuVisible(menuVisible)
   });
-  // console.log('----scrollTop: ', scrollTop, '----')
+
+  // 页面点击时关闭清除上下文菜单
+  const handlePageClick = () => {
+    setContextMenuVisible(false)
+  }
+
   return (
     <PageCustom
       fullPage
       style={{ overflow: "hidden" }}
       styleBg={getBgContent()}
+      onClick={handlePageClick}
     >
       <NavBarNormal blur leftColumn={renderNavLeft}>
         {/* <>{`${scrollTop}`}--</> */}

+ 10 - 14
src/store/appStore.ts

@@ -21,7 +21,7 @@ export interface AppState {
   appConfig: TModelAppConfig | null
   keyboardHeight: number
   desktopPopupTips: boolean
-  setSystemInfo: (info: Taro.getSystemInfoAsync.Result)=> void
+  setSystemInfo: (info: Taro.getSystemInfoSync.Result)=> void
   setKeyboardHeight: (height: number) => void
   fetchAppConfig: ()=> Promise<TModelAppConfig | null>
 }
@@ -66,7 +66,7 @@ export const useAppStore = create<AppState>((set) => ({
     // 状态栏高度 - 6 偏差值为垂直居中对齐胶囊按钮
     // statusBarHeight 离顶部导航栏位置
     const statusBarHeight = (isNaN(info.statusBarHeight!) ? 24 : info.statusBarHeight || 47) - 6;
-    const system = info.system.toLowerCase() 
+    const system = info.system.toLowerCase()
     const isIos = system.includes('ios')
     let bottomSafeHeight = 0
     let desktopPopupTips = false;
@@ -76,13 +76,13 @@ export const useAppStore = create<AppState>((set) => ({
     const windowWidth = info.windowWidth
     const windowHeight = info.windowHeight // 更精确的可用高度(减去状态栏等)
     const ratio = 750 / windowWidth
-    
+
     // 导航栏高度 = 胶囊和状态栏之间的留白 * 2 胶囊高度
     let navbarHeight = (capsuleInfo.top - statusBarHeight) * 2 + capsuleInfo.height
-    
+
     // Android 导航栏高度多加 4
     navbarHeight = isIos ? navbarHeight : navbarHeight + 4
-    
+
     // 整个 header 高度
     const headerHeight = statusBarHeight + navbarHeight
 
@@ -95,21 +95,17 @@ export const useAppStore = create<AppState>((set) => ({
     if(info.safeArea?.bottom){
       bottomSafeHeight = screenHeight - info.safeArea?.bottom;
     }
-    
-    
+
+
     set({ systemInfo: info, capsuleInfo, statusBarHeight, headerHeight, isIos, desktopPopupTips, bottomSafeHeight, screenHeight, windowHeight, windowWidth, ratio})
-    
-      
+
+
   },
   fetchAppConfig: async ()=> {
 
     return null
   },
-  
-}))
 
-export const useUserPhone = ()=> {
-  return useUserStore((state)=> state.user?.phone)
-}
+}))
 
 

+ 18 - 8
src/store/textChatStore.ts

@@ -17,6 +17,8 @@ type TRobotMessageWithOptionalId = Omit<TRobotMessage, "msgUk"> & {
 
 const INIT_CURRENT_ROBOT_MSG_UK = ''
 
+const SCROLL_STEP = 1
+
 export interface TextChat {
   currentRobotMsgUk: string; // 当前正在说话的 AI 机器人 id, 可用于控制是否继续输出文本至当前 message 框内
   scrollTop: number; // 控制聊天内容变化后滚动至最底部
@@ -119,13 +121,12 @@ export const useTextChat = create<TextChat>((set, get) => ({
       return {
         list: [...state.list, newRobotMessage],
         currentRobotMsgUk: msgUk,
-        scrollTop: state.scrollTop + 1,
       };
     });
     setTimeout(()=> {
       set((state) => {
         return {
-          scrollTop: state.scrollTop + 1,
+          scrollTop: state.scrollTop + SCROLL_STEP,
         };
       });
     }, 100)
@@ -144,7 +145,7 @@ export const useTextChat = create<TextChat>((set, get) => ({
     setTimeout(()=> {
       set((state) => {
         return {
-          scrollTop: state.scrollTop + 1,
+          scrollTop: state.scrollTop + SCROLL_STEP,
         };
       });
     }, 100)
@@ -162,7 +163,7 @@ export const useTextChat = create<TextChat>((set, get) => ({
       // console.log('autoScroll: ', state.autoScroll)
       if(state.autoScroll){
         return {
-          scrollTop: state.scrollTop + 1,
+          scrollTop: state.scrollTop + SCROLL_STEP,
         };
       }
       return {scrollTop: state.scrollTop};
@@ -176,7 +177,7 @@ export const useTextChat = create<TextChat>((set, get) => ({
         }
         return message; // 返回未修改的 message
       });
-      return { list: updatedList, scrollTop: state.scrollTop + 1 }; // 返回新的状态
+      return { list: updatedList, scrollTop: state.scrollTop + SCROLL_STEP }; // 返回新的状态
     });
     return msgUk
   },
@@ -196,9 +197,18 @@ export const useTextChat = create<TextChat>((set, get) => ({
       if(body?.contentType === EContentType.AiseekQA){
         scrollTopValue = state.scrollTop + 200
       }else{
-        scrollTopValue = state.scrollTop + 1
+        scrollTopValue = state.scrollTop + SCROLL_STEP
       }
-      return { list: updatedList, scrollTop:  state.autoScroll ?  scrollTopValue : state.scrollTop}; // 返回新的状态
+
+      setTimeout(()=> {
+        set((state) => {
+          return {
+            scrollTop:  state.autoScroll ?  scrollTopValue : state.scrollTop
+          };
+        });
+      }, 100)
+
+      return { list: updatedList }; // 返回新的状态
     });
   },
   updateMessageReaction: (msgId, data)=> {
@@ -225,7 +235,7 @@ export const useTextChat = create<TextChat>((set, get) => ({
         const isEmptyContent = message.content.length <= 0
         return (message.msgUk !== msgUk && isEmptyContent)
       });
-      return { list: filtered, scrollTop: state.scrollTop + 1 }; // 返回新的状态
+      return { list: filtered, scrollTop: state.scrollTop + SCROLL_STEP }; // 返回新的状态
     });
   }
 }));