ソースを参照

feat: add voice input button to AI chat page

Xiaogang Liao 4 週間 前
コミット
f92b0966ba
1 ファイル変更70 行追加15 行削除
  1. 70 15
      cfc-frontend/pages/ai/chat.vue

+ 70 - 15
cfc-frontend/pages/ai/chat.vue

@@ -136,23 +136,27 @@
       </view>
     </scroll-view>
 
-    <!-- 输入区 -->
-    <view class="chat-input-area">
-      <view class="chat-input-wrap">
-        <input class="chat-input" v-model="inputText" placeholder="问我关于家庭和孩子的问题..."
-          :disabled="sending" @confirm="sendMessage" confirm-type="send" />
-      </view>
-      <view class="chat-send-btn" :class="{ 'chat-send-active': inputText.trim() }"
-        @click="sendMessage">
-        <text class="send-icon">➤</text>
-      </view>
-    </view>
-    <view class="chat-disclaimer">本服务为生成内容,结果仅供参考</view>
+<!-- 输入区 -->
+     <view class="chat-input-area">
+       <view class="chat-input-wrap">
+         <input class="chat-input" v-model="inputText" placeholder="问我关于家庭和孩子的问题..."
+           :disabled="sending" @confirm="sendMessage" confirm-type="send" />
+       </view>
+       <view class="chat-voice-btn" :class="{ 'voice-recording': isRecording }" @touchstart="startVoiceRecord" @touchend="stopVoiceRecord">
+         <text class="voice-icon">{{ isRecording ? '🔴' : '🎤' }}</text>
+       </view>
+       <view class="chat-send-btn" :class="{ 'chat-send-active': inputText.trim() }"
+         @click="sendMessage">
+         <text class="send-icon">➤</text>
+       </view>
+     </view>
+     <view class="chat-disclaimer">本服务为生成内容,结果仅供参考</view>
   </view>
 </template>
 
 <script>
 import { aiSendMessage, aiGetConversations, aiGetMessages, aiDeleteConversation, aiSendNutritionMessage, aiSendHealthCoachMessage, aiSendHealthButlerMessage, getMyMembership } from '../../utils/api.js'
+import { sttTranscribe } from '../../utils/api.js'
 import BaseEmpty from '../../components/BaseEmpty.vue'
 import TaskCard from '../../components/AITaskCard.vue'
 import config from '@/config.js'
@@ -176,7 +180,9 @@ export default {
       recommendations: [],
       mascotCode: '',
       mascotName: '',
-      mascotIcon: ''
+      mascotIcon: '',
+      isRecording: false,
+      recorderManager: null
     }
   },
   computed: {
@@ -273,7 +279,36 @@ export default {
     goBack() {
       uni.navigateBack()
     },
-    
+
+    startVoiceRecord() {
+      if (this.isRecording) return
+      this.recorderManager = uni.getRecorderManager()
+      var self = this
+      this.recorderManager.onStop(function(res) {
+        self.isRecording = false
+        uni.showLoading({ title: '语音识别中...', mask: true })
+        sttTranscribe(res.tempFilePath).then(function(text) {
+          uni.hideLoading()
+          self.inputText = text
+        }).catch(function(err) {
+          uni.hideLoading()
+          uni.showToast({ title: '语音识别失败', icon: 'none' })
+        })
+      })
+      this.recorderManager.onError(function() {
+        self.isRecording = false
+        uni.showToast({ title: '录音失败', icon: 'none' })
+      })
+      this.recorderManager.start({ duration: 60000, format: 'mp3' })
+      this.isRecording = true
+    },
+
+    stopVoiceRecord() {
+      if (this.recorderManager) {
+        this.recorderManager.stop()
+      }
+    },
+
     onTaskAccepted() {
       this.loadConversations()
     },
@@ -716,7 +751,27 @@ export default {
   background: var(--bg, #FFF7ED);
   border-radius: 40rpx;
   padding: 0 24rpx;
-  margin-right: 16rpx;
+  margin-right: 12rpx;
+}
+
+.chat-voice-btn {
+  width: 60rpx;
+  height: 60rpx;
+  border-radius: 50%;
+  background: var(--border-light, #E2E8F0);
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  flex-shrink: 0;
+  margin-right: 12rpx;
+}
+
+.chat-voice-btn.voice-recording {
+  background: #FF6B6B;
+}
+
+.voice-icon {
+  font-size: 28rpx;
 }
 
 .chat-input {