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" />
+ //
+ )} */}
))}
-
-
-
-
-
-
-
{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}
/>
-
+ {/* */}