Files
tra-app/src/pages/test/testChat.vue
T
2025-11-28 16:09:42 +08:00

224 lines
5.0 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<view class="ai-chat-container">
<scroll-view
class="chat-list"
scroll-y
scroll-bottom="{{scrollBottom}}"
:style="{ height: `calc(100vh - ${inputAreaHeight}px)` }"
>
<view class="chat-item ai">
<view class="chat-avatar ai-avatar">
<text class="iconfont icon-ai"></text>
</view>
<view class="chat-content">
<view class="content-text">{{ asrResult }}</view>
</view>
</view>
</scroll-view>
<view class="input-area" :style="{ height: inputAreaHeight + 'px' }">
<view class="send-btn-area">
<button class="hold-send-btn" @touchstart="startRecord" @touchend="stopRecord" @touchcancel="handleTouchEnd">
<text class="btn-text">{{ isHolding ? '松开发送' : '按住 说话/发送' }}</text>
</button>
</view>
</view>
</view>
</template>
<script>
// import Recorder from 'recorder-core';
export default {
data() {
return {
scrollBottom: 0,
inputAreaHeight: 80,
isHolding: false,
asrResult: '',
recorderManager: null, // 原生录音管理器
ws: null, // WebSocket连接
isRecording: false,
CHUNK_SIZE: 6400, // 200ms/块
recordFilePath: '', // 录音文件路径
fileReaderTimer: null, // 分片读取定时器
recorder: null, // 录音实例
audioStream: [], // 实时音频流缓存
sampleRate: 16000, // 采样率(常用 16000/44100
bitRate: 16, // 位深
channels: 1 // 声道(单声道)
};
},
onLoad() {
this.initRecorder()
},
onUnload() {
// 页面卸载时销毁实例
this.destroyRecorder();
},
methods: {
// 初始化录音器
initRecorder() {
console.log('plus.audio', plus.audio);
// 确保 plus.audio 存在
if (!plus.audio) {
console.error('5+ API 音频模块未加载');
uni.showToast({ title: '录音功能初始化失败', icon: 'none' });
return;
}
// 原初始化逻辑
const options = {
filename: '_doc/audio/temp_recorder.amr',
format: 'amr',
sampleRate: 16000,
bitRate: 16,
channels: 1,
bufferSize: 4096,
onRecorded: (buffer) => {
this.handleRealTimeStream(buffer);
}
};
try {
this.recorder = plus.audio.getRecorder(options);
console.log('录音器初始化成功');
} catch (e) {
console.error('创建录音实例失败:', e);
uni.showToast({ title: '录音功能初始化失败', icon: 'none' });
}
},
// 开始录音
startRecord() {
console.log('下·', this.isRecording, !this.recorder);
if (this.isRecording || !this.recorder) return;
this.isRecording = true;
this.audioStream = []; // 清空历史流
this.recorder.record(
() => {
console.log('录音开始成功');
},
(err) => {
console.error('录音启动失败:', err);
this.isRecording = false;
}
);
},
// 停止录音
stopRecord() {
if (!this.isRecording || !this.recorder) return;
this.isRecording = false;
this.recorder.stop(
(filePath) => {
console.log('录音停止,文件路径:', filePath);
// 最终完整文件(可选)
this.handleFinalFile(filePath);
},
(err) => {
console.error('录音停止失败:', err);
}
);
}
}
};
</script>
<style scoped>
.ai-chat-container {
width: 100%;
height: 100vh;
background-color: #f5f7fa;
box-sizing: border-box;
padding-bottom: env(safe-area-inset-bottom);
}
.chat-list {
width: 100%;
padding: 20rpx 16rpx;
box-sizing: border-box;
}
.chat-item {
display: flex;
margin-bottom: 30rpx;
max-width: 85%;
}
.ai-avatar {
width: 80rpx;
height: 80rpx;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
margin-right: 20rpx;
background-color: #9f7aea;
}
.icon-ai {
font-size: 40rpx;
color: #fff;
}
.chat-content .content-text {
padding: 24rpx 28rpx;
border-radius: 20rpx 20rpx 20rpx 8rpx;
line-height: 1.6;
font-size: 32rpx;
color: #333;
word-wrap: break-word;
word-break: break-all;
background-color: #fff;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.08);
}
.input-area {
width: 100%;
background-color: #fff;
padding: 16rpx 20rpx;
box-sizing: border-box;
border-top: 1rpx solid #eee;
}
.send-btn-area {
width: 100%;
display: flex;
justify-content: center;
}
.hold-send-btn {
width: 80%;
height: 90rpx;
line-height: 90rpx;
border-radius: 45rpx;
background-color: #4299e1;
color: #fff;
font-size: 34rpx;
font-weight: 500;
box-shadow: 0 4rpx 12rpx rgba(66, 153, 225, 0.3);
transition: all 0.2s ease;
padding: 0;
margin: 0;
}
.hold-send-btn:active {
background-color: #3182ce;
transform: scale(0.98);
}
.iconfont {
font-family: 'iconfont' !important;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.icon-ai::before {
content: '\e602';
}
</style>