AI語音助手 React 組件使用js-audio-recorder實現,將獲取到的語音轉成base64發送給后端,后端接口返回文本內容

頁面效果:

js代碼:

import React, { useState, useRef, useEffect } from 'react';
import { Layout, List, Input, Button, Avatar, Space, Typography, message } from 'antd';
import { SendOutlined, UserOutlined, RobotOutlined, AudioOutlined, StopOutlined } from '@ant-design/icons';
import JsAudioRecorder from 'js-audio-recorder';
import './style.less';const { Header, Content, Footer } = Layout;
const { Text } = Typography;const ChatInterface = () => {const [messages, setMessages] = useState([{ sender: 'assistant', content: '你好!我是AI助手,有什么可以幫你的嗎?' },]);const [inputValue, setInputValue] = useState('');const [isRecording, setIsRecording] = useState(false);const messagesEndRef = useRef(null);const recorderRef = useRef(null);// 初始化錄音器useEffect(() => {recorderRef.current = new JsAudioRecorder({sampleBits: 16,sampleRate: 16000,numChannels: 1,});return () => {if (recorderRef.current) {recorderRef.current.destroy();}};}, []);// 開始/停止錄音const toggleRecording = () => {if (isRecording) {stopRecording();} else {startRecording();}};// 開始錄音const startRecording = () => {recorderRef.current.start().then(() => {setIsRecording(true);message.success('錄音中...');}).catch((err) => {message.error('錄音失敗: ' + err.message);});};// 停止錄音并發送const stopRecording = () => {try {recorderRef.current.stop();setIsRecording(false);message.success('錄音結束,處理中...');const blob = recorderRef.current.getWAVBlob();console.log(blob);const reader = new FileReader();reader.onloadend = () => {const base64Data = reader.result.split(',')[1];console.log(base64Data);sendAudioToAPI(base64Data);};reader.onerror = () => {message.error('音頻轉換失敗');};reader.readAsDataURL(blob);} catch (err) {message.error('停止錄音失敗: ' + err.message);}};// 模擬API調用const sendAudioToAPI = (base64Data) => {setTimeout(() => {const mockResponse = { text: '這是語音識別后的文本(模擬數據)' };setInputValue(mockResponse.text);message.success('語音識別完成!');}, 1500);};const handleSend = () => {if (!inputValue || inputValue.trim() === '') {message.warning('消息不能為空!');return;}// 添加用戶消息setMessages([...messages, { sender: 'user', content: inputValue }]);setInputValue('');// 模擬AI回復setTimeout(() => {setMessages(prev => [...prev, { sender: 'assistant', content: `這是對你"${inputValue}"的回復。` }]);}, 1000);};// 自動滾動到底部useEffect(() => {messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });}, [messages]);return (<Layout className="chat-layout"><Header className="chat-header"><Text strong style={{ color: 'white', fontSize: '18px' }}>AI聊天助手(支持語音輸入)</Text></Header><Content className="chat-content"><div className="message-container">{messages.map((item, index) => (<divkey={index}className={`message-wrapper ${item.sender}`}><div className={`message-bubble ${item.sender}`}><div className="message-avatar"><Avataricon={item.sender === 'user' ? <UserOutlined /> : <RobotOutlined />}style={{ backgroundColor: item.sender === 'user' ? '#1890ff' : '#52c41a' }}/></div><div className="message-content"><div className="message-sender">{item.sender === 'user' ? '你' : 'AI助手'}</div><div className="message-text">{item.content}</div></div></div></div>))}<div ref={messagesEndRef} /></div></Content><Footer className="chat-footer"><Space.Compact style={{ width: '100%' }}><Buttontype={isRecording ? 'danger' : 'default'}icon={isRecording ? <StopOutlined /> : <AudioOutlined />}onClick={toggleRecording}/><Inputplaceholder={isRecording ? '正在錄音...' : '輸入消息或語音...'}value={inputValue}onChange={(e) => setInputValue(e.target.value)}onPressEnter={handleSend}disabled={isRecording}/><Buttontype="primary"icon={<SendOutlined />}onClick={handleSend}disabled={isRecording}>發送</Button></Space.Compact></Footer></Layout>);
};export default ChatInterface;

less代碼:

/* 整體布局 */
.chat-layout {height: 100vh;display: flex;flex-direction: column;background-color: #f5f5f5;
}.chat-header {background-color: #1e88e5;padding: 0 24px;display: flex;align-items: center;height: 64px;box-shadow: 0 1px 4px rgba(0, 0, 0, 0.1);
}.chat-content {flex: 1;overflow-y: auto;padding: 16px;background-color: #eaeaea;
}.chat-footer {padding: 12px 16px;background: #f0f2f5;border-top: 1px solid #e8e8e8;
}/* 消息容器 */
.message-container {display: flex;flex-direction: column;gap: 12px;
}/* 消息包裝器 */
.message-wrapper {display: flex;
}.message-wrapper.user {justify-content: flex-end;
}.message-wrapper.assistant {justify-content: flex-start;
}/* 消息氣泡 */
.message-bubble {display: flex;max-width: 80%;gap: 8px;
}.message-bubble.user {flex-direction: row-reverse;
}/* 消息內容 */
.message-content {display: flex;flex-direction: column;max-width: calc(100% - 40px);
}.message-sender {font-size: 12px;color: #666;margin-bottom: 4px;
}.message-text {padding: 10px 14px;border-radius: 18px;line-height: 1.5;word-break: break-word;
}/* 用戶消息樣式 */
.message-wrapper.user .message-text {background-color: #1890ff;color: white;border-top-right-radius: 4px;
}/* AI消息樣式 */
.message-wrapper.assistant .message-text {background-color: white;color: #333;border-top-left-radius: 4px;box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
}/* 第一條歡迎消息全寬 */
.message-wrapper.assistant:first-child .message-bubble {max-width: 100%;
}.message-wrapper.assistant:first-child .message-text {background-color: #f6ffed;border-radius: 8px;padding: 12px 16px;
}/* 頭像樣式 */
.message-avatar {display: flex;align-items: flex-end;padding-bottom: 24px;
}/* 移動端適配 */
@media (max-width: 768px) {.message-bubble {max-width: 90%;}.chat-footer {padding: 8px;}
}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/77542.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/77542.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/77542.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

pycharm無法識別到本地python的conda環境解決方法

問題一 現象描述&#xff1a; 本地已經安裝了conda&#xff0c;但在pycharm中選擇conda環境卻識別不到&#xff0c; 解決方法&#xff1a;手動輸入conda path&#xff0c;點擊R eload environments基本就能修復&#xff0c;比如我的路徑如下 /Users/test/conda/miniconda3/b…

PDK中technology file從tf格式轉換為lef格式

在數字后端流程中需要導入technology file工藝文件&#xff0c;一般傳統的PDK中都提供.tf形式&#xff0c;能夠在Synopsys ICC中進行導入。但是由于Cadence Innovus不斷地完善&#xff0c;更多的工程采用了其進行數字后端設計。不過Cadence Innovus導入的是.lef格式的工藝文件&…

UE虛幻4虛幻5動畫藍圖調試,觸發FellOutOfWorld事件和打印輸出,繼續DeepSeek輸出

找到了一個pdf&#xff0c;本來想寫個翻譯的&#xff0c;但還是算了&#xff0c;大概看了下&#xff0c;這類文檔很全面&#xff0c;內容很多&#xff0c;但都不是我要的&#xff0c;我想要一個動畫藍圖&#xff0c;搜索Montage&#xff0c;或者Anim 只占了一行&#xff08;幾百…

【Sa-Token】學習筆記05 - 踢人下線源碼解析

目錄 前言 強制注銷 踢人下線 源碼解析 前言 所謂踢人下線&#xff0c;核心操作就是找到指定 loginId 對應的 Token&#xff0c;并設置其失效。 上圖為踢人下線后&#xff0c;前端應該用圖像給出來讓用戶重新登錄&#xff0c;而不是讓前端收到一個描述著被下線 的JSON 強…

C語言==》字符串斷行

示例代碼 #include <stdio.h>int main(void) {printf("Heres one way to print a ");printf("long string.\n");printf("Heres another way to print a \ long string.\n");printf("Heres the newest way to print a ""lo…

Linux | I.MX6ULL 文件系統

01 本節所有的測試程序需要開發板有 Qt 環境來運行。我們提供的文件系統是由 yocto 裁剪整理得來的。之后我們會整理一份單獨移植的 qt 系統。方便用戶移植第三方軟件。如果用戶的文件系統非我們的出廠版本,請參考之前燒寫章節重新燒寫出廠文件系統。開發板啟動需要輸入登錄…

網絡原理 - 應用層, 傳輸層(UDP 和 TCP) 進階, 網絡層, 數據鏈路層 [Java EE]

目錄 應用層 1. 應用層的作用 2. 自定義應用層協議 3. 應用層的 "通用協議格式" 3.1 xml 3.2 json 3.3 protobuffer (pd) 傳輸層 1. UDP 1.1 無連接 1.2 不可靠傳輸 1.3 面向數據報 1.4 全雙工 1.5 緩沖區 1.6 UDP 數據報 2. TCP 2.1 有連接 …

如何將自己封裝的組件發布到npm上:詳細教程

如何將自己封裝的組件發布到npm上&#xff1a;詳細教程 作為前端開發者&#xff0c;我們經常從npm&#xff08;Node Package Manager&#xff09;上下載并使用各種第三方庫和組件。然而&#xff0c;有時候我們可能會發現自己需要的功能在npm上并不存在&#xff0c;或者我們希望…

[OS_7] 訪問操作系統對象 | offset | FHS | Handle

實驗代碼可以看去年暑假的這篇文章&#xff1a;【Linux】進程間通信&#xff1a;詳解 VSCode使用 | 匿名管道 我們已經知道&#xff0c;進程從 execve 后的初始狀態開始&#xff0c;可以通過 mmap 改變自己的地址空間&#xff0c;通過 fork 創建新的進程&#xff0c;再通過 exe…

關于TCP三次握手和四次揮手過程中的狀態機、使用三次握手和四次揮手的原因、擁塞控制

關于傳輸層中的TCP協議&#xff0c;我們在之前的博客中對其報文格式、三次握手、四次揮手、流量控制、數據傳輸等機制進行了具體說明&#xff0c;接下來在前面所學的基礎上&#xff0c;我們再來講講TCP中三次握手和四次揮手各階段所處的狀態機以及為什么要使用三次握手和四次揮…

學習筆記二十——Rust trait

&#x1f9e9; Rust Trait 徹底搞懂版 &#x1f440; 目標讀者&#xff1a;對 Rust 完全陌生&#xff0c;但想真正明白 “Trait、Trait Bound、孤島法則” 在做什么、怎么用、為什么這樣設計。 &#x1f6e0; 方法&#xff1a; 先給“心里模型”——用生活類比把抽象概念掰開揉…

es 混合檢索多向量

在結合向量相似度檢索的同時,可以通過 bool 查詢的 filter 或 must 子句實現關鍵詞過濾。以下是一個同時包含 關鍵詞匹配 和 多向量相似度計算 的完整示例: 參考博文:ES集群多向量字段檢索及混合檢索方法-CSDN博客 示例:帶關鍵詞過濾的多向量聯合檢索 GET /my_index/_sea…

HTML5好看的水果蔬菜在線商城網站源碼系列模板4

文章目錄 1.設計來源1.1 主界面1.2 關于我們1.3 商品信息1.4 新聞資訊1.5 聯系我們1.5 登錄注冊 2.效果和源碼2.1 動態效果2.2 源代碼 源碼下載 作者&#xff1a;xcLeigh 文章地址&#xff1a;https://blog.csdn.net/weixin_43151418/article/details/147264262 HTML5好看的水果…

Kubernetes(k8s)學習筆記(二)--k8s 集群安裝

1、kubeadm kubeadm 是官方社區推出的一個用于快速部署 kubernetes 集群的工具。這個工具能通過兩條指令完成一個 kubernetes 集群的部署&#xff1a; 1.1 創建一個 Master 節點$ kubeadm init 1.2 將一個 Node 節點加入到當前集群中$ kubeadm join <Master 節點的 IP 和…

AI數據分析的優勢分析

隨著科技的飛速發展&#xff0c;人工智能&#xff08;AI&#xff09;已經深入滲透到數據分析領域&#xff0c;為各行各業帶來了前所未有的變革。AI數據分析作為一種新興的技術手段&#xff0c;通過運用機器學習、深度學習等算法對海量數據進行挖掘和分析&#xff0c;顯著提升了…

leetcode(01)森林中的兔子

今天開始記錄刷題的過程&#xff0c;每天記錄自己刷題的題目和自己的解法&#xff0c;歡迎朋友們給出更多更好的解法。 森林中的兔子 森林中有未知數量的兔子&#xff0c;提問其中若干只兔子“還有多少只兔子與你&#xff08;被提問的兔子&#xff09;顏色相同”。將答案收集到…

基于SpringBoot+Vue實現的旅游景點預約平臺功能一

一、前言介紹&#xff1a; 1.1 項目摘要 隨著人們生活水平的提高和休閑時間的增多&#xff0c;旅游已經成為人們生活中不可或缺的一部分。旅游業作為全球經濟的重要支柱&#xff0c;其發展趨勢呈現出數字化、網絡化和智能化的特點。傳統的旅游服務方式&#xff0c;如人工預約…

【支付】支付寶支付

下面為你詳細介紹使用 Spring Boot 對接支付寶支付&#xff0c;實現支付與退款功能的具體步驟和代碼示例。 添加依賴 在 pom.xml 里添加支付寶 SDK 依賴&#xff1a; <dependencies><!-- Spring Boot Web --><dependency><groupId>org.springframewo…

shell 正則表達式與文本處理器

目錄 前言 一、正則表達式 &#xff08;一&#xff09;定義與用途 &#xff08;二&#xff09;基礎正則表達式 &#xff08;三&#xff09;基礎正則表達式元字符 &#xff08;四&#xff09;擴展正則表達式 二、文本處理器&#xff1a;Shell 編程的得力助手 &#xff0…

ASP.NET Core 最小 API:極簡開發,高效構建(上)

一、概述 構建最小 API&#xff0c;以創建具有最小依賴項的 HTTP API。 它們非常適合于需要在 ASP.NET Core 中僅包括最少文件、功能和依賴項的微服務和應用。 本文介紹使用 ASP.NET Core 生成最小 API 的基礎知識&#xff0c;將創建以下 API&#xff1a; API&#xff08;應用…