Bläddra i källkod

fix: 修复录制声音错误

sheldon 2 veckor sedan
förälder
incheckning
a73ba72a6d

+ 3 - 2
src/components/custom-share/index.tsx

@@ -19,8 +19,9 @@ import { DEFAULT_AVATAR } from "@/config";
 
 const getCanvasSize = () => {
   // 根据屏幕宽度计算 canvas 宽度
-  const systemInfo = Taro.getWindowInfo();
-  const screenWidth = systemInfo.windowWidth;
+  const windowInfo = Taro.getWindowInfo();
+  const screenWidth = windowInfo.windowWidth;
+  console.log(windowInfo, 'WindowInfo');
   const designWidth = 375;
   const designHeight = 300;
   // 宽高放大一倍

+ 17 - 16
src/components/wemeta-input/index.tsx

@@ -9,6 +9,7 @@ interface Props {
   prefix?: () => JSX.Element | JSX.Element[] | undefined;
   suffix?: () => JSX.Element | JSX.Element[] | undefined;
   onInput: (value: string) => void;
+  onBlur?: (value: string) => void;
   confirmType?: keyof InputProps.ConfirmType;
   onConfirm?: (value: string) => void;
   cursorSpacing?: number;
@@ -28,33 +29,36 @@ const index = ({
   extraStyle,
   prefix,
   suffix,
+  onBlur,
   adjustPosition,
   maxlength
 }: Props) => {
   const [focus, setFocus] = useState(false);
-  const inputRef = useRef(null); // 创建一个 ref
+  const inputRef = useRef<HTMLInputElement>(null); // 创建一个 ref
 
-  const handleInput = (value: string) => {
-    const len = countCharacters(value);
-    if (maxlength && len > maxlength) {
-      const r = getStrByMaxLength(value, maxlength);
-      // const slicedValue = value.slice(0, value.length)
-      // setText(r);
-      onInput && onInput(r);
-      return;
-    }
-    // setText(value);
-    onInput && onInput(value);
-  };
   const handleFocus = () => {
     isInbox = false;
     setFocus(true);
   };
   const handleBlur = () => {
+    console.log("textarea blur");
     if (!isInbox) {
       setFocus(false);
+      if (onBlur && inputRef.current) {
+        onBlur(inputRef.current.value);
+      }
     }
   };
+  const handleInput = (value: string) => {
+    const len = countCharacters(value);
+    if (maxlength && len > maxlength) {
+      const r = getStrByMaxLength(value, maxlength);
+      onInput && onInput(r);
+      return;
+    }
+    onInput && onInput(value);
+  };
+  
   return (
     <View
       className={`${
@@ -64,9 +68,6 @@ const index = ({
     >
       <View
         className={style.inputAreaContainer}
-        onClick={() => {
-          setFocus(true);
-        }}
       >
         {prefix && prefix()}
         <Input

+ 1 - 16
src/components/wemeta-textarea/index.tsx

@@ -19,7 +19,6 @@ interface Props {
   autoFocus?: boolean;
   extraClass?: string;
   onConfirm?: (value: string) => void;
-  ai?: boolean;
 }
 let isInbox = false;
 const index = ({
@@ -39,21 +38,10 @@ const index = ({
   maxlength,
   extra,
   onConfirm,
-  ai,
 }: Props) => {
   const [focus, setFocus] = useState(false);
   const inputRef = useRef<HTMLInputElement>(null); // 创建一个 ref
-  // const [text, setText] = useState(value);
-
-  // const handleClear = (e: ITouchEvent) => {
-  //   isInbox = true
-  //   e.stopPropagation();
-  //   e.preventDefault();
-  //   onInput('')
-  //   setFocus(true)
-
-  //   inputRef.current && inputRef.current.focus()
-  // }
+  
   const handleFocus = () => {
     isInbox = false;
     setFocus(true);
@@ -71,12 +59,9 @@ const index = ({
     const len = countCharacters(value);
     if (maxlength && len > maxlength) {
       const r = getStrByMaxLength(value, maxlength);
-      // const slicedValue = value.slice(0, value.length)
-      // setText(r);
       onInput && onInput(r);
       return;
     }
-    // setText(value);
     onInput && onInput(value);
   };
 

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

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

+ 9 - 2
src/pages/contact/components/contact-card/index.tsx

@@ -3,6 +3,7 @@ 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';
 interface Props {
   data: TContactItem
   deleteable?: boolean
@@ -26,8 +27,14 @@ const Index = ({data, deleteable, refresh, fromContact}: Props)=> {
       content: "😭 确认删除该联系人吗?",
       async success(result) {
         if (result.confirm) {
-          await delContact(contactId);
-          refresh();
+          const response = await delContact(contactId);
+          if(isSuccess(response.status)) {
+            Taro.showToast({
+              title: "删除成功",
+              icon: 'none',
+            });
+            refresh();
+          }
         }
       },
     });

+ 2 - 2
src/pages/contact/index.tsx

@@ -98,8 +98,8 @@ export default function Index() {
         >
           <View className={`${item.isTop ? "bg-gray" : "bg-white"}`}>
             <ContactCard
-              refresh={resetFetchList}
-              deleteable={!item.isEnt}
+              refresh={mutate}
+              deleteable={true}
               key={item.contactId}
               data={item}
               fromContact

+ 15 - 3
src/pages/knowledge-item-editor/index.tsx

@@ -102,6 +102,15 @@ export default function Index() {
       return
     }
 
+    if(formData.a.length <= 0){
+      Taro.showToast({title: '请输入回答', icon: 'none'})
+      return 
+    }
+    if(formData.q.length <= 0){
+      Taro.showToast({title: '请输入问题描述', icon: 'none'})
+      return 
+    }
+
     const dataToSubmit = {
       answer: formData.a,
       questions: [formData.q],
@@ -112,7 +121,10 @@ export default function Index() {
     console.log(dataToSubmit)
     const {status} = await updateKnowledgeQa(knowledgeId, qaId, dataToSubmit)
     if(isSuccess(status)){
-      Taro.showToast({title: '保存成功', icon: 'success'})
+      Taro.showToast({title: '保存成功', icon: 'none'})
+      setTimeout(() => {
+        Taro.navigateBack()
+      }, 300)
     }
   }
   const getQaDetail = async (knowledgeId: string, qaId: string)=> {
@@ -175,7 +187,7 @@ export default function Index() {
                 <WemetaTextarea
                   value={formData.q}
                   onInput={handleInput}
-                  placeholder="描述你想要生成的画面和动作。例如:职场精英在点头微笑"
+                  placeholder="请输入问题描述"
                 />
               </View>
             </View>
@@ -192,7 +204,7 @@ export default function Index() {
                 <WemetaTextarea
                   value={formData.a}
                   onInput={handleValueAInput}
-                  placeholder="描述你想要生成的画面和动作。例如:职场精英在点头微笑"
+                  placeholder="请输入回答"
                 />
               </View>
             </View>

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

@@ -92,7 +92,7 @@ export default function Index() {
       return;
     }
     showModal({
-      content: "确定删除该问答项吗?",
+      content: "确认删除文件及其全部知识?",
       onConfirm: async () => {
         const { status } = await deleteKnowledge(detail.knowledgeId);
         if (isSuccess(status)) {

+ 26 - 3
src/pages/knowledge/components/PersonalTab/components/ScrollListChat.tsx

@@ -18,11 +18,13 @@ import { TKnowledgeItem } from "@/types/knowledge";
 import type { TKnowledgeStreamResponseData } from "@/types/knowledge";
 
 import {
+  deleteKnowledgeStream,
   getMyKnowledgeStream,
 } from "@/service/knowledge";
 
 import { useLoadMoreInfinite, createKey } from "@/utils/loadMoreInfinite";
 import { DEFAULT_AGENT } from "@/config";
+import { isSuccess } from "@/utils";
 
 interface IProps {
   assistantOnly?: boolean;
@@ -73,6 +75,24 @@ const Index = ({assistantOnly, setTotalCount}: IProps) => {
     })
   }
 
+  const handleLongPress = async (streamId: string|number)=> {
+    Taro.showModal({
+      content: "确认删除该知识?",
+      async success(result) {
+        if (result.confirm) {
+          const response = await deleteKnowledgeStream(streamId);
+          if(isSuccess(response.status)) {
+            Taro.showToast({
+              title: "删除成功",
+              icon: 'none',
+            });
+            mutate();
+          }
+        }
+      },
+    });
+  }
+
   useEffect(()=> {
     if(pageIndex === 1){
       setTimeout(()=> {
@@ -115,7 +135,8 @@ const Index = ({assistantOnly, setTotalCount}: IProps) => {
         {reversedList.map((group) => {
           // 渲染自己发送的消息
           if (group.role === "user") {
-            return group.knowledgeList.map((item) => {
+            return <View onLongPress={()=> handleLongPress(group.streamId)}>
+            {group.knowledgeList.map((item) => {
               return (
                 <MessageRich data={{
                   avatar: whoami?.avatarUrl ?? '',
@@ -132,10 +153,11 @@ const Index = ({assistantOnly, setTotalCount}: IProps) => {
                   </View>
                 </MessageRich>
               );
-            });
+            })}
+            </View>
           }
           // 渲染机器人小蓝本助手的消息
-          return (
+          return (<View onLongPress={()=> handleLongPress(group.streamId)}>
             <MessageRobotRich
               data={{...DEFAULT_AGENT, fileLen: group.knowledgeList.length}}
               analyzeStatus={group.isParsing ? "doing" : "done"}
@@ -163,6 +185,7 @@ const Index = ({assistantOnly, setTotalCount}: IProps) => {
                 })}
               </FigureList>
             </MessageRobotRich>
+            </View>
           );
         })}
       </View>

+ 51 - 23
src/pages/profile/index.tsx

@@ -19,7 +19,7 @@ import { useIsLogin } from "@/xiaolanbenlib/hooks/data/useAuth";
 export default function Profile() {
   const { agentProfile, fetchAgentProfile } = useAgentStore();
   const params = useRouter().params;
-  const isLogin = useIsLogin()
+  const isLogin = useIsLogin();
 
   // 解码
   const decodeSceneValue = async (sceneValue: string) => {
@@ -31,7 +31,7 @@ export default function Profile() {
           agentId?: string;
         };
         if (originalScene.agentId) {
-          console.log(originalScene,111)
+          console.log(originalScene, 111);
           fetchAgentProfile(originalScene.agentId);
           postLog(originalScene.agentId, originalScene.shareKey ?? "");
         }
@@ -43,16 +43,16 @@ export default function Profile() {
       }
     }
   };
-  console.log(params.scene, '场景值');
+  console.log(params.scene, "场景值");
   // 通过小程序码识别打开的页面
-  useEffect(()=> {
+  useEffect(() => {
     const scene = decodeURIComponent(params.scene ?? "");
     if (scene.length <= 32 && scene.length > 0) {
       // 有值,且小于 32 位,说明需要解码
       // 判断是否需要解压 shareKey
       decodeSceneValue(scene);
     }
-  }, [])
+  }, []);
 
   // 正常分享出来的页面
   useEffect(() => {
@@ -64,21 +64,24 @@ export default function Profile() {
 
   const renderNavBarLeft = () => {
     const pages = getCurrentPages();
+    console.log(pages, "当前页面");
+    // 有后退页,则不显示 home 按钮
+    if (pages.length > 1) {
+      return (
+        <View className={style.navButtons}>
+          <View
+            className="flex-center"
+            onClick={() => {
+              Taro.navigateBack();
+            }}
+          >
+            <IconArrowLeft></IconArrowLeft>
+          </View>
+        </View>
+      );
+    }
     return (
       <View className={style.navButtons}>
-        {pages.length > 1 && (
-          <>
-            <View
-              className="flex-center"
-              onClick={() => {
-                Taro.navigateBack();
-              }}
-            >
-              <IconArrowLeft></IconArrowLeft>
-            </View>
-            <View className={style.navGapLine}></View>
-          </>
-        )}
         <View
           className="flex-center"
           onClick={() => {
@@ -91,14 +94,40 @@ export default function Profile() {
         </View>
       </View>
     );
+    // return (
+    // <View className={style.navButtons}>
+    //   {pages.length > 1 && (
+    //     <>
+    //       <View
+    //         className="flex-center"
+    //         onClick={() => {
+    //           Taro.navigateBack();
+    //         }}
+    //       >
+    //         <IconArrowLeft></IconArrowLeft>
+    //       </View>
+    //       <View className={style.navGapLine}></View>
+    //     </>
+    //   )}
+
+    //   <View
+    //     className="flex-center"
+    //     onClick={() => {
+    //       Taro.switchTab({
+    //         url: "/pages/index/index",
+    //       });
+    //     }}
+    //   >
+    //     <IconHomeOutline></IconHomeOutline>
+    //   </View>
+    // </View>
+    // );
   };
-  
+
   return (
     <PageCustom styleBg={agentProfile?.avatarUrl}>
       <View className="w-full">
-        <NavBarNormal
-          leftColumn={renderNavBarLeft}
-        ></NavBarNormal>
+        <NavBarNormal leftColumn={renderNavBarLeft}></NavBarNormal>
         <View className="blur-rounded-container">
           {agentProfile && (
             <>
@@ -111,7 +140,6 @@ export default function Profile() {
             </>
           )}
         </View>
-        
       </View>
     </PageCustom>
   );

+ 6 - 1
src/pages/visiteor-detail/components/VisitorSummary/index.tsx

@@ -2,6 +2,7 @@ import { View, Text, Image } from "@tarojs/components";
 import DigitalCardBasic from "@/components/DigitalCard/DigitalCardBasic";
 import { TVisitorAgent } from "@/types/visitor";
 import TagCertificated from "@/components/tag-certificated";
+import Taro from "@tarojs/taro";
 export interface IndexProps {
   data: TVisitorAgent;
 }
@@ -10,7 +11,11 @@ const VisitorSummary = ({ data }: IndexProps) => {
   return (
     <View className="w-full p-12">
       <View className="flex items-center gap-12">
-        <View className="flex items-center rounded-full overflow-hidden">
+        <View className="flex items-center rounded-full overflow-hidden" onClick={() => {
+          Taro.navigateTo({
+            url: `/pages/profile/index?agentId=${data.agentId}`,
+          })
+        }}>
           <View className="w-60 h-60 bg-gray-3 rounded-full">
             {!!data.avatarUrl && (
               <Image

+ 14 - 10
src/pages/visiteor-detail/index.tsx

@@ -37,29 +37,33 @@ export default () => {
     }
   };
 
-  const fetcher = async ([_url, {nextId, pageSize}]) => {
+  const fetcher = async ([_url, {nextId, pageSize, pageIndex}]) => {
     const res = await getVisitorSessions({
       startId: nextId,
       pageSize,
       visitorId,
     });
-    const _totalCount = res.data.totalCount;
-
+    const _totalCount = res.data.totalCount || 0;
+    
     if (_totalCount && res.data.data) {
-      setTotalCount(_totalCount);
+      // setTotalCount(_totalCount);
       const data = res.data;
       data.data = data.data.map((item: TSessionItem) => {
-        return { ...item, visitTimes: _totalCount - pageIndex };
+        let  turns = _totalCount - pageIndex
+        turns = turns <= 0 ? 1 : turns;
+        return { ...item, visitTimes: turns };
       });
       return data;
     }
 
-    const data = res.data;
-    data.data = data.data.map((item: TSessionItem) => {
-      return { ...item, visitTimes: totalCount - pageIndex };
-    });
+    // const data = res.data;
+    // data.data = data.data.map((item: TSessionItem) => {
+    //   let  turns = totalCount - pageIndex
+    //   turns = turns <= 0 ? 1 : turns;
+    //   return { ...item, visitTimes: turns };
+    // });
 
-    return data;
+    // return data;
   };
 
 

+ 3 - 1
src/pages/voice/components/MyVoiceList/components/popup-recorder/index.tsx

@@ -22,6 +22,7 @@ interface Props {
   setCloneStatus: (status: ECloneStatus) => void;
   setShow: (show: boolean) => void;
   show: boolean;
+  entId: number | string; // 企业ID
   onRecordEnd: (r: string, voiceName?: string) => void;
 }
 
@@ -46,6 +47,7 @@ export default ({
   setCloneStatus,
   setShow,
   show,
+  entId
 }: Props) => {
   const [recording, setRecording] = useState(false);
   
@@ -103,7 +105,7 @@ export default ({
     console.log(response?.publicUrl, 'publicUrl');
     if (response && response.publicUrl) {
       Taro.showLoading();
-      const res = await cloneVoice({sourceUrl: response.publicUrl, voiceText: readText});
+      const res = await cloneVoice({sourceUrl: response.publicUrl, voiceText: readText, entId});
       Taro.hideLoading();
       if(isSuccess(res.status)){
         onRecordEnd(res.data.taskId, voiceName);

+ 6 - 4
src/pages/voice/components/MyVoiceList/index.tsx

@@ -44,7 +44,7 @@ export default ({ onPlay, agent }: Props) => {
   //   taskId: 'abc',
   //   status: 'processing'
   // }
-  const [cloning, setCloning] = useState<TTask>();
+  const [cloning, setCloning] = useState<TTask|null>();
 
   //获取一个默认声音
   const getDefaultVoice = ()=> {
@@ -126,7 +126,8 @@ export default ({ onPlay, agent }: Props) => {
         taskId: response.data.taskId,
       });
       stopTimer();
-      getVoices();
+      await getVoices();
+      setCloning(null);
     }
   };
 
@@ -199,7 +200,7 @@ export default ({ onPlay, agent }: Props) => {
           );
         }}
       >
-        <View className="flex items-center h-full ">{renderCloneStatus(item)}</View>
+        <View className="flex items-center h-full py-16">{renderCloneStatus(item)}</View>
       </CardListItem>;
     }
 
@@ -221,7 +222,7 @@ export default ({ onPlay, agent }: Props) => {
       >
         <View className="flex flex-col gap-4 py-16">
           <View className="flex items-center leading-22">{item.voiceName}</View>
-          <View className="text-12 text-gray-45 leading-20">{item.createTime.slice(0,7)}创建</View>
+          <View className="text-12 text-gray-45 leading-20">{item.createTime.slice(0,7)} 创建</View>
         </View>
       </CardListItem>
     );
@@ -262,6 +263,7 @@ export default ({ onPlay, agent }: Props) => {
         <PopupRecorder
           onRecordEnd={onRecordEnd}
           show={show}
+          entId={agent?.entId || 0}
           setShow={setShow}
           setCloneStatus={(status) => handleCloneStatus(status)}
         ></PopupRecorder>

+ 3 - 4
src/service/knowledge.ts

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

+ 1 - 0
src/service/voice.ts

@@ -11,6 +11,7 @@ import { TGetMyVoicesParams, TPaginatedVoiceResponse, TVoiceItem, TVoiceCloneRes
 export const cloneVoice = (data: {
   sourceUrl: string // 源语音地址 ,
   voiceText?: string // 录音文案
+  entId: number|string // 企业ID
 }) => {
   return request.post<TVoiceCloneResponse>(`${bluebookAiAgent}api/v1/my/voice/clone`, data)
 }

+ 29 - 10
src/utils/jsonChunkParser.ts

@@ -21,6 +21,10 @@ data: data:{"content":"比如学习建议、写作指导或日常咨询,我很
 
 data: data:{"content":"","contentType":"text/plain","last":true,"payload":{"usage":{"completion_tokens":101,"prompt_tokens":28,"total_tokens":129,"web_searched":false}},"role":"assistant"}
 
+data:{"content":{"answer":{"payload":{},"text":"你好,我是饭饭,很高兴你能使用QA"},"qaId":"qa_9605"},"contentType":"aiseek/qa","last":true,"payload":{},"role":"assistant"}
+
+
+
 
 
 data: [DONE]
@@ -28,9 +32,14 @@ data: [DONE]
 */
 interface ICompleteCallback {
   content: string;
+  reasoningContent?: string;
   body: Record<string,any>
 }
 
+type TContentType = "text/plain" | "aiseek/qa" | 'application/json' | 'aiseek/audio_chunk' | 'aiseek/thinking'  |  'aiseek/function_call' | 'aiseek/multimodal'
+type TJsonMessage = {contentType: TContentType, content: string | any, reasoningContent: string}
+
+
 export default class JsonChunkParser {
   private buffer: string; // 用于存储未完成的 JSON 字符串
   private complete: (data: ICompleteCallback) => void;
@@ -71,15 +80,24 @@ export default class JsonChunkParser {
           // 处理 data: data: 前缀
           if (line.startsWith("data:")) {
             const jsonStr = line.substring(5); // 移除 "data:" 前缀
-            const json = JSON.parse(jsonStr);
-            if (json.content) {
-              receivedJsonBody = json
-              combinedContent.push(json.content); // 收集 content 字段
+            const json: TJsonMessage = JSON.parse(jsonStr);
+            receivedJsonBody = json
+            // 文本回复
+            if(json.contentType === "text/plain") {
+              if (json.content) {
+                combinedContent.push(json.content as string); // 收集 content 字段
+              }
+              if (json.reasoningContent) {
+                combinedReasoningContent.push(json.reasoningContent); // 收集 content 字段
+              }  
             }
-            if (json.reasoningContent) {
-              receivedJsonBody = json
-              combinedReasoningContent.push(json.reasoningContent); // 收集 content 字段
+            // QA 回复
+            else if(json.contentType === "aiseek/qa") {
+              if (json.content.answer?.text) {
+                combinedContent.push(json.content.answer.text); // 收集 QA 的 answer 文本
+              }
             }
+            
           }
         } catch (error) {
           // 如果解析失败,说明当前行不是完整的 JSON
@@ -94,9 +112,10 @@ export default class JsonChunkParser {
     if (combinedContent.length > 0) {
       onParsed({ content: combinedContent.join(""),  body: receivedJsonBody  }); // 合并并输出
     }
-    if (combinedReasoningContent.length > 0) {
-      onParsed({ reasoningContent: combinedReasoningContent.join(""), body: receivedJsonBody  }); // 合并并输出
-    }
+
+    // if (combinedReasoningContent.length > 0) {
+    //   onParsed({ reasoningContent: combinedReasoningContent.join(""), body: receivedJsonBody ,  body: receivedJsonBody   }); // 合并并输出
+    // }
 
     // 清空 buffer
     this.buffer = "";

+ 2 - 2
src/utils/loadMoreInfinite.ts

@@ -13,7 +13,7 @@ export const createKey = (query: string, pageSize: number = 10, extra: Record<st
     if (pageIndex === 0)
       return [
         query,
-        { nextId: undefined, pageSize },
+        { nextId: undefined, pageSize, pageIndex },
         extra,
       ];
       // 如果是以 pageIndex 作为分页依据
@@ -27,7 +27,7 @@ export const createKey = (query: string, pageSize: number = 10, extra: Record<st
     if (previousPageData && previousPageData.nextId) {
       return [
         query,
-        { pageSize, nextId: previousPageData.nextId },
+        { pageSize, nextId: previousPageData.nextId, pageIndex },
         extra,
       ];
     }