当前位置: 首页 > web >正文

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.xdnf.cn/news/300.html

相关文章:

  • 【学习笔记】文件上传漏洞--黑白盒审计
  • 数字友好战略视域下数字安全核心要素的理论解构与实践路径
  • 2022年世界青年科学家峰会-高端装备系统动力学与智能诊断维护学术研讨会
  • Java之this关键字
  • CTF--MD5
  • 慢速率拉伸热变形工艺试验机
  • 关于模拟噪声分析的11个误区
  • Dify快速入门之基于知识库构建聊天机器人
  • 汽车免拆诊断案例 | 2019款大众途观L车鼓风机偶尔不工作
  • 在浏览器中输入 URL 到页面加载完成都做了什么
  • 【含文档+PPT+源码】基于python爬虫的豆瓣电影、音乐、图书数据分析系统
  • nginx-基础知识(二)
  • 为什么计算「网络响应时间」或「定位响应时间」时,CACurrentMediaTime() 比 Date() 更优?
  • MCP系列之架构篇:深入理解MCP的设计架构
  • DeepSeek 操作 MySQL 数据库:使用 MCP 实现数据库查询
  • 【HDFS入门】联邦机制(Federation)与扩展性:HDFS NameNode水平扩展深度解析
  • 【AI提示词】儿童看护员
  • 实验五 内存管理实验
  • 如何在PDF.js中改造viewer.html以实现PDF的动态加载
  • STM32单片机入门学习——第41节: [12-1] Unix时间戳
  • MyBatis如何配置数据库连接并实现交互?
  • YOLOv5、YOLOv6、YOLOv7、YOLOv8、YOLOv9、YOLOv10、YOLOv11、YOLOv12的网络结构图
  • 日语学习-日语知识点小记-构建基础-JLPT-N4阶段(7):(1)ながら 一边。。一边 (2)。。。し。。。し。。 又……又……
  • Vue3后代传祖先组件通讯方法
  • BGP基础实验
  • DeepSeek-R3、GPT-4o 与 Claude-3.5-Sonnet 全面对比:性能、应用场景与技术解析
  • 【信息系统项目管理师】高分论文:论信息系统项目的沟通管理(网管改造工程)
  • C++数据收发管道:构建高效的数据传输通道
  • 【android bluetooth 案例分析 03】【PTS 测试 1】【pts基本介绍】
  • 空调制冷量和功率有什么关系?