123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263 |
- <template>
- <view class="container">
- <view class="header">
- <text class="title">语音对话</text>
- </view>
-
- <view class="chat-container">
- <scroll-view scroll-y class="chat-messages" :scroll-top="scrollTop">
- <view v-for="(message, index) in messages" :key="index" class="message" :class="message.role">
- <text>{{ message.content }}</text>
- </view>
- </scroll-view>
- </view>
-
- <view class="controls">
- <button class="voice-btn" :class="{ recording: isRecording }" @touchstart="startRecording" @touchend="stopRecording">
- <text>{{ isRecording ? '松开结束' : '按住说话' }}</text>
- </button>
- </view>
- </view>
- </template>
- <script>
- export default {
- data() {
- return {
- messages: [],
- isRecording: false,
- recorderManager: null,
- innerAudioContext: null,
- scrollTop: 0,
- apiUrl: 'https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions'
- }
- },
- onLoad() {
- // 初始化录音管理器
- this.recorderManager = uni.getRecorderManager();
- this.innerAudioContext = uni.createInnerAudioContext();
-
- // 监听录音结束事件
- this.recorderManager.onStop((res) => {
- this.processAudio(res.tempFilePath);
- });
- },
- methods: {
- startRecording() {
- this.isRecording = true;
- // 开始录音
- this.recorderManager.start({
- format: 'mp3',
- sampleRate: 16000,
- numberOfChannels: 1
- });
- },
- stopRecording() {
- this.isRecording = false;
- // 停止录音
- this.recorderManager.stop();
- },
- processAudio(filePath) {
- // 显示加载状态
- uni.showLoading({
- title: '处理中...'
- });
-
- // 将录音文件转换为文本(语音识别)
- // 这里需要接入语音识别服务,以下为示例
- this.speechToText(filePath).then(text => {
- // 添加用户消息
- this.addMessage('user', text);
-
- // 调用DashScope API
- this.callDashScopeAPI(text);
- }).catch(err => {
- uni.hideLoading();
- uni.showToast({
- title: '语音识别失败',
- icon: 'none'
- });
- console.error(err);
- });
- },
- speechToText(filePath) {
- // 临时解决方案:直接返回模拟的语音识别结果
- return new Promise((resolve) => {
- console.log('录音文件路径:', filePath);
- // 模拟语音识别结果
- setTimeout(() => {
- resolve('你好,我需要帮助');
- }, 500);
- });
-
- // 注释掉原来的实现,等待后续接入真实的语音识别服务
- /*
- return new Promise((resolve, reject) => {
- uni.uploadFile({
- url: '您的语音识别服务URL',
- filePath: filePath,
- name: 'file',
- success: (res) => {
- const data = JSON.parse(res.data);
- if (data && data.result) {
- resolve(data.result);
- } else {
- // 模拟语音识别结果,实际项目中请删除此行
- resolve('你好,我需要帮助');
- // reject(new Error('语音识别失败'));
- }
- },
- fail: reject
- });
- });
- */
- },
- callDashScopeAPI(userInput) {
- // 构建请求体
- const requestBody = {
- model: "qwen-plus",
- messages: [
- {
- role: "system",
- content: "You are a helpful assistant."
- },
- {
- role: "user",
- content: userInput
- }
- ]
- };
-
- // 发送请求到DashScope API
- uni.request({
- url: this.apiUrl,
- method: 'POST',
- header: {
- 'Content-Type': 'application/json',
- 'Authorization': 'Bearer YOUR_API_KEY' // 替换为您的API密钥
- },
- data: requestBody,
- success: (res) => {
- uni.hideLoading();
- if (res.statusCode === 200 && res.data && res.data.choices && res.data.choices.length > 0) {
- const assistantMessage = res.data.choices[0].message.content;
- this.addMessage('assistant', assistantMessage);
-
- // 可选:将回复转为语音(文本转语音)
- this.textToSpeech(assistantMessage);
- } else {
- uni.showToast({
- title: '获取回复失败',
- icon: 'none'
- });
- console.error('API响应错误:', res);
- }
- },
- fail: (err) => {
- uni.hideLoading();
- uni.showToast({
- title: '网络请求失败',
- icon: 'none'
- });
- console.error(err);
- }
- });
- },
- addMessage(role, content) {
- this.messages.push({ role, content });
- // 滚动到底部
- this.$nextTick(() => {
- this.scrollTop = 9999;
- });
- },
- textToSpeech(text) {
- // 这里需要接入文本转语音服务
- // 以下为示例,实际实现需要根据您使用的文本转语音服务调整
- uni.request({
- url: '您的文本转语音服务URL',
- method: 'POST',
- data: { text },
- success: (res) => {
- if (res.statusCode === 200 && res.data && res.data.audio_url) {
- // 播放语音
- this.innerAudioContext.src = res.data.audio_url;
- this.innerAudioContext.play();
- }
- }
- });
- }
- }
- }
- </script>
- <style>
- .container {
- display: flex;
- flex-direction: column;
- height: 100vh;
- background-color: #f5f5f5;
- }
- .header {
- padding: 20rpx;
- background-color: #007AFF;
- text-align: center;
- }
- .title {
- color: #ffffff;
- font-size: 36rpx;
- font-weight: bold;
- }
- .chat-container {
- flex: 1;
- padding: 20rpx;
- overflow: hidden;
- }
- .chat-messages {
- height: 100%;
- }
- .message {
- margin-bottom: 20rpx;
- padding: 20rpx;
- border-radius: 10rpx;
- max-width: 80%;
- word-break: break-word;
- }
- .message.user {
- align-self: flex-end;
- background-color: #007AFF;
- color: white;
- margin-left: auto;
- }
- .message.assistant {
- align-self: flex-start;
- background-color: #E5E5EA;
- color: #333;
- }
- .controls {
- padding: 20rpx;
- background-color: #ffffff;
- border-top: 1px solid #e0e0e0;
- }
- .voice-btn {
- width: 100%;
- height: 80rpx;
- line-height: 80rpx;
- text-align: center;
- background-color: #007AFF;
- color: white;
- border-radius: 40rpx;
- }
- .voice-btn.recording {
- background-color: #FF3B30;
- }
- </style>
|