From a6d84a13909329a43c559b101772d48c35e7ec95 Mon Sep 17 00:00:00 2001 From: wuxichen <17301714657@163.com> Date: Wed, 15 Oct 2025 18:21:47 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=A4=B4=E5=83=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../translate-h5/src/http/axios-instance.ts | 2 +- projects/translate-h5/src/index.css | 7 +- .../translate/component/message/index.tsx | 93 ++++++++++++------ .../home/translate/component/voice/index.tsx | 94 ++++++++++--------- .../src/view/home/translate/index.tsx | 15 ++- projects/translate-h5/src/view/home/types.ts | 2 +- projects/translate-h5/vite.config.ts | 4 +- 7 files changed, 127 insertions(+), 90 deletions(-) diff --git a/projects/translate-h5/src/http/axios-instance.ts b/projects/translate-h5/src/http/axios-instance.ts index 0935e07..d73789e 100644 --- a/projects/translate-h5/src/http/axios-instance.ts +++ b/projects/translate-h5/src/http/axios-instance.ts @@ -50,5 +50,5 @@ console.log(import.meta.env.VITE_BASE_URL, "import.meta.env.VITE_BASE_URL"); // https://petshy.tashowz.com //192.168.1.231:48080 // || "http://192.168.1.231:48080" -const baseURL = import.meta.env.VITE_BASE_URL; +const baseURL = import.meta.env.VITE_BASE_URL || "https://petshy.tashowz.com"; export const axiosInstance = new AxiosInstance(baseURL).getInstance(); diff --git a/projects/translate-h5/src/index.css b/projects/translate-h5/src/index.css index 06b9df1..dbee066 100644 --- a/projects/translate-h5/src/index.css +++ b/projects/translate-h5/src/index.css @@ -91,12 +91,13 @@ img { --adm-font-size-main: var(--adm-font-size-5); */ - --adm-font-family: -apple-system, blinkmacsystemfont, "Helvetica Neue", - helvetica, segoe ui, arial, roboto, "PingFang SC", "miui", - "Hiragino Sans GB", "Microsoft Yahei", sans-serif; + --adm-font-family: -apple-system, blinkmacsystemfont, "Helvetica Neue", helvetica, segoe ui, arial, + roboto, "PingFang SC", "miui", "Hiragino Sans GB", "Microsoft Yahei", sans-serif; } .i-icon { height: 100%; + display: flex; + align-items: center; } svg { height: 100%; diff --git a/projects/translate-h5/src/view/home/translate/component/message/index.tsx b/projects/translate-h5/src/view/home/translate/component/message/index.tsx index 4c673ae..16e5780 100644 --- a/projects/translate-h5/src/view/home/translate/component/message/index.tsx +++ b/projects/translate-h5/src/view/home/translate/component/message/index.tsx @@ -1,5 +1,5 @@ import { useEffect, useRef, useState } from "react"; -import { Divider, Image, SpinLoading, Toast } from "antd-mobile"; +import { Avatar, Divider, Image, Space, SpinLoading, Toast } from "antd-mobile"; import { VoiceIcon } from "@workspace/shared"; import dogSvg from "@/assets/translate/dog.svg"; import catSvg from "@/assets/translate/cat.svg"; @@ -105,14 +105,8 @@ function Index(props: DefinedProps) { } }); }; - const renderAvatar = (type?: "pig" | "cat" | "dog" | "") => { - if (type === "pig") { - ; - } - if (type === "cat") { - return ; - } - return ; + const renderAvatar = (item: Message) => { + return ; }; const refreshMessage = async (messageId: number, e: React.MouseEvent) => { @@ -122,11 +116,49 @@ function Index(props: DefinedProps) { props.onRefresh(formData, messageId); }; + const renderTranslateResult = (item: Message) => { + if (item.isTranslating) { + return ( +
+ + 翻译中... +
+ ); + } else { + if (item.transStatus === 1) { + return item.transResult?.length ? ( + + {item.transResult} + + ) : ( + + 未知语句 + refreshMessage(item.id, e)} size="12" fill="#333" /> + + ); + } else { + return ( + { + e.stopPropagation(); + }} + > + 翻译失败,请重试 + refreshMessage(item.id, e)} size="12" fill="#333" /> + + ); + } + } + }; + return (
{data.map((item, index) => (
playAudio(item.id, item.contentText)}> - {renderAvatar(item.petType)} + {renderAvatar(item)}
@@ -141,38 +173,37 @@ function Index(props: DefinedProps) { />
{item.contentDuration}''
- {item.isTranslating ? ( + {renderTranslateResult(item)} + {/* {item.isTranslating ? (
翻译中...
) : item.transStatus === 1 ? ( -
{item.transResult ?? "暂无翻译结果"}
- ) : (
- 翻译失败,请重试 - refreshMessage(item.id, e)} size="12" fill="#333" /> + {item.transResult?.length ? ( + item.transResult + ) : ( + + 未知语句 + refreshMessage(item.id, e)} size="12" fill="#333" /> + + )}
- )} + ) : ( + + 翻译失败,请重试 + refreshMessage(item.id, e)} size="12" fill="#333" /> + + //
+ // 翻译失败,请重试 + // refreshMessage(item.id, e)} size="12" fill="#333" /> + //
+ )} */}
))} -
-
-
-
- 生无可恋喵 - - 15:00 -
-
- -
{isRecording ? "录制中..." : "轻点麦克风录制"}
-
-
-
-
); diff --git a/projects/translate-h5/src/view/home/translate/component/voice/index.tsx b/projects/translate-h5/src/view/home/translate/component/voice/index.tsx index 908b426..7f8e7b2 100644 --- a/projects/translate-h5/src/view/home/translate/component/voice/index.tsx +++ b/projects/translate-h5/src/view/home/translate/component/voice/index.tsx @@ -1,4 +1,4 @@ -import React, { useCallback, useEffect, useRef, useState } from "react"; +import React, { useCallback, useEffect, useMemo, useRef, useState } from "react"; import { AudioRecorder, useAudioRecorder } from "react-audio-voice-recorder"; import { Button, Dialog, Image, ProgressCircle, Toast } from "antd-mobile"; import microphoneSvg from "@/assets/translate/microphone.svg"; @@ -35,12 +35,12 @@ function Index(props: DefinedProps) { checkMicrophonePermission(); }, [hasPermission]); - useEffect(() => { - if (isRecording) { - recorderControls.startRecording(); - } else { - } - }, [isRecording]); + // useEffect(() => { + // if (isRecording) { + // recorderControls.startRecording(); + // } else { + // } + // }, [isRecording]); //重置状态 const onResetRecordingState = () => { @@ -66,7 +66,35 @@ function Index(props: DefinedProps) { console.error("音效初始化失败:", error); } }; - const renderBtn = useCallback(() => { + //开始录音 + const onStartRecording = () => { + isCancelledRef.current = false; + if (recordingTimerRef.current) { + clearInterval(recordingTimerRef.current); + recordingTimerRef.current = undefined; + } + props.onSetIsRecording(true); + recorderControls.startRecording(); + recordingStartTimeRef.current = Date.now(); + // 立即开始计时 + recordingTimerRef.current = setInterval(() => { + setRecordingDuration((prev) => prev + 1); + }, 1000); + }; + // 使用 useMemo 缓存 Image 组件 + const MicrophoneImage = useMemo( + () => ( + } // 添加占位符 + /> + ), + [microphoneSvg, onStartRecording] + ); + const renderBtn = () => { if (!hasPermission) { //没有权限 return ( @@ -95,10 +123,9 @@ function Index(props: DefinedProps) { ); } else { - //麦克风状态 - return ; + return MicrophoneImage; } - }, [hasPermission, isRecording, recordingDuration]); + }; const checkMicrophonePermission = useCallback(async () => { try { if (!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia) { @@ -262,21 +289,6 @@ function Index(props: DefinedProps) { } }; - //开始录音 - const onStartRecording = () => { - isCancelledRef.current = false; - if (recordingTimerRef.current) { - clearInterval(recordingTimerRef.current); - recordingTimerRef.current = undefined; - } - props.onSetIsRecording(true); - // recorderControls.startRecording(); - recordingStartTimeRef.current = Date.now(); - // 立即开始计时 - recordingTimerRef.current = setInterval(() => { - setRecordingDuration((prev) => prev + 1); - }, 1000); - }; const onStopRecording = useCallback(() => { recorderControls.stopRecording(); onResetRecordingState(); @@ -296,27 +308,26 @@ function Index(props: DefinedProps) { Toast.show("录音数据无效,请重新录音"); return; } + const arrayBuffer = await blob.arrayBuffer(); + const audioContext = new (window.AudioContext || (window as any).webkitAudioContext)(); + const audioBuffer = await audioContext.decodeAudioData(arrayBuffer); + const accurateDuration = audioBuffer.duration; + if (accurateDuration < 1) { + Toast.show("录音时间太短,请重新录音"); + return; + } // 转换为 WAV 格式以获得最佳兼容性 const wavBlob = await convertToWav(blob); const formData: FormData = new FormData(); - formData.append("file", wavBlob, new Date().getTime() + "." + new Date().getTime() + ".wav"); + formData.append("file", wavBlob, new Date().getTime() + ".wav"); formData.append("dialogId", `${dialogId}`); const audioUrl = URL.createObjectURL(blob); const audio = new Audio(); audio.src = audioUrl; - // 计算实际录音时长 - - audio.addEventListener("loadedmetadata", () => { - if (audio.duration < 1) { - Toast.show("录音时间太短,请重新录音"); - return; - } - // alert(audio.duration); - playSound(sendSoundRef); - const contentDuration = Math.floor(audio.duration); - formData.append("contentDuration", `${contentDuration}`); - props.onRecordingComplete?.(audioUrl, contentDuration, formData); - }); + playSound(sendSoundRef); + const contentDuration = Math.round(accurateDuration); + formData.append("contentDuration", `${contentDuration}`); + props.onRecordingComplete?.(audioUrl, contentDuration, formData); }, [isCancelledRef, isRecording, sendSoundRef] ); @@ -338,9 +349,6 @@ function Index(props: DefinedProps) { autoGainControl: true, }} showVisualizer={false} - mediaRecorderOptions={{ - mimeType: "audio/webm", - }} />
{renderBtn()}
diff --git a/projects/translate-h5/src/view/home/translate/index.tsx b/projects/translate-h5/src/view/home/translate/index.tsx index 987b5c1..e8a3f0b 100644 --- a/projects/translate-h5/src/view/home/translate/index.tsx +++ b/projects/translate-h5/src/view/home/translate/index.tsx @@ -10,7 +10,7 @@ import { useUploadAudio } from "@/api/translate"; import dogSvg from "@/assets/translate/dog.svg"; import catSvg from "@/assets/translate/cat.svg"; import pigSvg from "@/assets/translate/pig.svg"; -import { MoreTwo } from "@icon-park/react"; +import { MoreTwo, Petrol } from "@icon-park/react"; import SearchCom from "./component/search"; interface DefinedProps { searchVisible: boolean; @@ -41,15 +41,12 @@ function Index(props: DefinedProps) { fetchInitialMessages(); }, []); - // 添加初始化数据的逻辑 const fetchInitialMessages = async () => { try { // 这里替换为实际的API调用 // const response = await fetch('/api/messages'); const response = await getDialog(); - // console.log(response); - const initialMessages: Message[] = response.data?.data?.messages || []; setDialogId(response.data?.data?.dialogId); @@ -65,11 +62,10 @@ function Index(props: DefinedProps) { //完成录音 const onRecordingComplete = useCallback( (audioUrl: string, actualDuration: number, formData: FormData) => { - console.log(audioUrl, "audioUrl") + console.log(audioUrl, "audioUrl"); const newMessage: Message = { id: Date.now(), contentText: audioUrl, - petName: "匹配档案中。。。", contentDuration: actualDuration, isTranslating: true, }; @@ -77,7 +73,7 @@ function Index(props: DefinedProps) { setMessages((prev) => [...prev, newMessage]); setTimeout(() => { onTranslateAudio(formData, newMessage.id); - }, 1000); + }, 500); Toast.show("语音已发送"); }, @@ -90,7 +86,6 @@ function Index(props: DefinedProps) { try { const response = await uploadAudio(formData); const translatedData = response.data; - console.log(translatedData, "translatedText"); if (translatedData.data.transStatus) { setMessages((prev) => @@ -111,7 +106,9 @@ function Index(props: DefinedProps) { ? { ...msg, id: translatedData.data.id, + petName: "未知宠物", transStatus: translatedData.data.transStatus, + createTime: translatedData.data.createTime, isTranslating: false, } : msg @@ -183,7 +180,7 @@ function Index(props: DefinedProps) { isRecording={isRecording} onSetIsRecording={onSetIsRecording} /> - + {/* */}