Merge branch 'qianpw' of http://gitea.tashowz.com/tashow/tashow-h5 into qianpw

This commit is contained in:
2025-10-16 15:37:15 +08:00
4 changed files with 87 additions and 23 deletions

View File

@@ -46,6 +46,9 @@ class AxiosInstance {
return this.instance; return this.instance;
} }
} }
console.log(import.meta.env.VITE_BASE_URL, "import.meta.env.VITE_BASE_URL");
const baseURL = import.meta.env.VITE_BASE_URL || "https://petshy.tashowz.com"; // https://petshy.tashowz.com
//192.168.1.231:48080
// || "http://192.168.1.231:48080"
const baseURL = import.meta.env.VITE_BASE_URL;
export const axiosInstance = new AxiosInstance(baseURL).getInstance(); export const axiosInstance = new AxiosInstance(baseURL).getInstance();

View File

@@ -74,7 +74,7 @@ function Index(props: DefinedProps) {
break; break;
default: default:
console.warn("未知错误"); console.warn("未知错误");
}; }
Toast.show("音频播放失败"); Toast.show("音频播放失败");
setIsPlating(false); setIsPlating(false);
}; };
@@ -127,6 +127,7 @@ function Index(props: DefinedProps) {
{data.map((item, index) => ( {data.map((item, index) => (
<div className="item" key={index} onClick={() => playAudio(item.id, item.contentText)}> <div className="item" key={index} onClick={() => playAudio(item.id, item.contentText)}>
{renderAvatar(item.petType)} {renderAvatar(item.petType)}
<div className="rig"> <div className="rig">
<div> <div>
<span className="name">{item.petName}</span> <span className="name">{item.petName}</span>

View File

@@ -4,9 +4,8 @@ import { Button, Dialog, Image, ProgressCircle, Toast } from "antd-mobile";
import microphoneSvg from "@/assets/translate/microphone.svg"; import microphoneSvg from "@/assets/translate/microphone.svg";
import microphoneDisabledSvg from "@/assets/translate/microphoneDisabledSvg.svg"; import microphoneDisabledSvg from "@/assets/translate/microphoneDisabledSvg.svg";
import { createStartRecordSound, createSendSound } from "@/utils/voice"; import { createStartRecordSound, createSendSound } from "@/utils/voice";
import "./index.less";
import VConsole from "vconsole"; import VConsole from "vconsole";
import "./index.less";
interface DefinedProps { interface DefinedProps {
onRecordingComplete: (url: string, finalDuration: number, formData: FormData) => void; onRecordingComplete: (url: string, finalDuration: number, formData: FormData) => void;
isRecording: boolean; isRecording: boolean;
@@ -195,6 +194,74 @@ function Index(props: DefinedProps) {
} }
}; };
// 添加音频转换函数
const convertToWav = async (blob: Blob): Promise<Blob> => {
const arrayBuffer = await blob.arrayBuffer();
const audioContext = new (window.AudioContext || (window as any).webkitAudioContext)();
const audioBuffer = await audioContext.decodeAudioData(arrayBuffer);
// 转换为 WAV 格式
const wavBuffer = audioBufferToWav(audioBuffer);
return new Blob([wavBuffer], { type: "audio/wav" });
};
// WAV 转换辅助函数
const audioBufferToWav = (buffer: AudioBuffer): ArrayBuffer => {
const length = buffer.length;
const numberOfChannels = buffer.numberOfChannels;
const sampleRate = buffer.sampleRate;
const bitsPerSample = 16;
const byteRate = (sampleRate * numberOfChannels * bitsPerSample) / 8;
const blockAlign = (numberOfChannels * bitsPerSample) / 8;
const dataSize = length * numberOfChannels * (bitsPerSample / 8);
const bufferLength = 44 + dataSize;
const arrayBuffer = new ArrayBuffer(bufferLength);
const view = new DataView(arrayBuffer);
// RIFF identifier
writeString(view, 0, "RIFF");
// file length
view.setUint32(4, 36 + dataSize, true);
// RIFF type
writeString(view, 8, "WAVE");
// format chunk identifier
writeString(view, 12, "fmt ");
// format chunk length
view.setUint32(16, 16, true);
// sample format (raw)
view.setUint16(20, 1, true);
// channel count
view.setUint16(22, numberOfChannels, true);
// sample rate
view.setUint32(24, sampleRate, true);
// byte rate (sample rate * block align)
view.setUint32(28, byteRate, true);
// block align (channel count * bytes per sample)
view.setUint16(32, blockAlign, true);
// bits per sample
view.setUint16(34, bitsPerSample, true);
// data chunk identifier
writeString(view, 36, "data");
// data chunk length
view.setUint32(40, dataSize, true);
// write the PCM samples
let offset = 44;
for (let i = 0; i < length; i++) {
for (let channel = 0; channel < numberOfChannels; channel++) {
const sample = Math.max(-1, Math.min(1, buffer.getChannelData(channel)[i]));
view.setInt16(offset, sample < 0 ? sample * 0x8000 : sample * 0x7fff, true);
offset += 2;
}
}
return arrayBuffer;
};
const writeString = (view: DataView, offset: number, string: string) => {
for (let i = 0; i < string.length; i++) {
view.setUint8(offset + i, string.charCodeAt(i));
}
};
//开始录音 //开始录音
const onStartRecording = () => { const onStartRecording = () => {
isCancelledRef.current = false; isCancelledRef.current = false;
@@ -214,6 +281,7 @@ function Index(props: DefinedProps) {
recorderControls.stopRecording(); recorderControls.stopRecording();
onResetRecordingState(); onResetRecordingState();
}, [recorderControls, recordingDuration]); }, [recorderControls, recordingDuration]);
//录音完成 //录音完成
// 在发送时检查录音时长 // 在发送时检查录音时长
const onRecordingComplete = useCallback( const onRecordingComplete = useCallback(
@@ -222,30 +290,17 @@ function Index(props: DefinedProps) {
Toast.show("已取消"); Toast.show("已取消");
return; return;
} }
// 检查blob有效性 // 检查blob有效性
if (!blob || blob.size === 0) { if (!blob || blob.size === 0) {
Toast.show("录音数据无效,请重新录音"); Toast.show("录音数据无效,请重新录音");
return; return;
} }
// 转换为 WAV 格式以获得最佳兼容性
const wavBlob = await convertToWav(blob);
const formData: FormData = new FormData(); const formData: FormData = new FormData();
formData.append( formData.append("file", wavBlob, new Date().getTime() + "." + new Date().getTime() + ".wav");
"file",
blob,
new Date().getTime() + "." + blob.type.split("/").pop()?.split(";")[0]
);
// 把demoWAV替换成blob
// const response = await fetch(demoWAV);
// const arrayBuffer = await response.arrayBuffer();
// const demoWAVBlob = new Blob([arrayBuffer], { type: "audio/wav" });
// formData.append("file", demoWAVBlob);
formData.append("dialogId", `${dialogId}`); formData.append("dialogId", `${dialogId}`);
// await handleUploadAudio(formData);
// const audioUrl = URL.createObjectURL(demoWAVBlob);
const audioUrl = URL.createObjectURL(blob); const audioUrl = URL.createObjectURL(blob);
const audio = new Audio(); const audio = new Audio();
audio.src = audioUrl; audio.src = audioUrl;
@@ -283,6 +338,9 @@ function Index(props: DefinedProps) {
autoGainControl: true, autoGainControl: true,
}} }}
showVisualizer={false} showVisualizer={false}
mediaRecorderOptions={{
mimeType: "audio/webm",
}}
/> />
<div className={` voice-record`}>{renderBtn()}</div> <div className={` voice-record`}>{renderBtn()}</div>
</> </>

View File

@@ -5,6 +5,8 @@ import { inspectorServer } from "@react-dev-inspector/vite-plugin";
import basicSsl from "@vitejs/plugin-basic-ssl"; import basicSsl from "@vitejs/plugin-basic-ssl";
export default defineConfig({ export default defineConfig({
// basicSsl() // basicSsl()
// target: "https://petshy.tashowz.com",
// http://192.168.1.231:48080
plugins: [react()], plugins: [react()],
server: { server: {
port: 3000, port: 3000,
@@ -12,7 +14,7 @@ export default defineConfig({
open: true, open: true,
proxy: { proxy: {
"/app-api": { "/app-api": {
target: "https://petshy.tashowz.com", target: "http://192.168.1.231:48080",
changeOrigin: true, changeOrigin: true,
}, },
}, },