feat: 录音功能

master
LCJ-MinYa 3 months ago
parent 7577bcb0f9
commit f31816cf33

@ -0,0 +1,288 @@
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta
name="viewport"
content="width=device-width,initial-scale=1"
/>
<title>录音与播放功能</title>
<style>
body {
font-family: 'Segoe UI', sans-serif;
background-color: #f5f7fa;
margin: 0;
padding: 20px;
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
}
.container {
background-color: #fff;
border-radius: 12px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
padding: 30px;
width: 100%;
max-width: 500px;
text-align: center;
}
h1 {
color: #2c3e50;
margin-bottom: 20px;
}
.controls {
margin-bottom: 20px;
}
button {
background-color: #3498db;
color: #fff;
border: none;
padding: 12px 24px;
font-size: 16px;
border-radius: 8px;
cursor: pointer;
margin: 0 8px;
transition: background-color 0.3s;
}
button:disabled {
background-color: #bdc3c7;
cursor: not-allowed;
}
button:hover {
background-color: #2980b9;
}
.timer {
font-size: 18px;
color: #e74c3c;
margin: 10px 0;
font-weight: 700;
}
.file-info {
margin-top: 10px;
color: #7f8c8d;
font-size: 14px;
font-weight: 400;
}
.upload-status {
margin-top: 10px;
font-size: 14px;
font-weight: 400;
}
.audio-player {
margin-top: 20px;
border-radius: 8px;
overflow: hidden;
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1);
}
audio {
width: 100%;
height: 50px;
border: none;
background-color: #f0f0f0;
}
.status {
margin-top: 10px;
color: #7f8c8d;
font-size: 14px;
}
</style>
</head>
<body>
<div class="container">
<h1>录音与播放</h1>
<div class="controls">
<button id="startBtn">开始录音</button>
<button
id="stopBtn"
disabled="disabled"
>
结束录音
</button>
<button
id="uploadBtn"
disabled="disabled"
>
上传到服务器
</button>
</div>
<div
class="timer"
id="timer"
>
00:00
</div>
<div
class="file-info"
id="fileInfo"
>
文件大小0 KB
</div>
<div
class="upload-status"
id="uploadStatus"
></div>
<div
class="status"
id="status"
>
准备就绪
</div>
<div class="audio-player">
<audio
id="audioPlayer"
controls
></audio>
</div>
</div>
<script>
// 获取 DOM 元素
const startBtn = document.getElementById('startBtn');
const stopBtn = document.getElementById('stopBtn');
const uploadBtn = document.getElementById('uploadBtn');
const timerEl = document.getElementById('timer');
const fileInfoEl = document.getElementById('fileInfo');
const uploadStatusEl = document.getElementById('uploadStatus');
const statusEl = document.getElementById('status');
const audioPlayer = document.getElementById('audioPlayer');
let mediaRecorder;
let audioChunks = [];
let recordingTime = 0;
const MAX_RECORDING_TIME = 60; // 最大60秒
const AUTO_STOP_TIME = 10; // 超过10秒自动停止
let timerInterval;
let audioBlob = null; // 存储录音结果,用于上传
// 模拟上传接口地址(可替换为真实后端地址)
const UPLOAD_URL = 'https://httpbin.org/post'; // 测试用接口,可上传文件
// 开始录音
startBtn.addEventListener('click', async () => {
try {
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
mediaRecorder = new MediaRecorder(stream, {
mimeType: 'audio/webm;codecs=opus',
});
audioChunks = [];
mediaRecorder.ondataavailable = (event) => {
if (event.data.size > 0) {
audioChunks.push(event.data);
}
};
mediaRecorder.onstop = () => {
// 生成 Blob
audioBlob = new Blob(audioChunks, { type: 'audio/webm' });
const audioUrl = URL.createObjectURL(audioBlob);
audioPlayer.src = audioUrl;
// 计算文件大小KB
const fileSizeKB = (audioBlob.size / 1024).toFixed(2);
fileInfoEl.textContent = `文件大小:${fileSizeKB} KB`;
statusEl.textContent = `录音完成,大小:${fileSizeKB} KB可播放`;
uploadBtn.disabled = false; // 启用上传按钮
stopBtn.disabled = true;
startBtn.disabled = false;
clearInterval(timerInterval);
timerEl.textContent = '00:00';
};
mediaRecorder.start();
startBtn.disabled = true;
stopBtn.disabled = false;
statusEl.textContent = '正在录音...';
// 启动计时器
recordingTime = 0;
timerInterval = setInterval(() => {
recordingTime++;
timerEl.textContent = formatTime(recordingTime);
// ✅ 超过10秒自动停止录音
if (recordingTime >= AUTO_STOP_TIME) {
statusEl.textContent = `⚠️ 已超过 ${AUTO_STOP_TIME} 秒,自动停止录音`;
stopRecording();
}
// 也检查最大时长(防止意外)
if (recordingTime >= MAX_RECORDING_TIME) {
statusEl.textContent = `⚠️ 已达到最大录音时长 ${MAX_RECORDING_TIME} 秒,自动停止`;
stopRecording();
}
}, 1000);
} catch (err) {
alert('无法访问麦克风:' + err.message);
console.error('录音失败:', err);
}
});
// 结束录音
stopBtn.addEventListener('click', stopRecording);
function stopRecording() {
if (mediaRecorder && mediaRecorder.state !== 'inactive') {
mediaRecorder.stop();
}
if (mediaRecorder && mediaRecorder.stream) {
mediaRecorder.stream.getTracks().forEach((track) => track.stop());
}
}
// 上传录音文件
uploadBtn.addEventListener('click', async () => {
if (!audioBlob) {
alert('没有可上传的录音文件');
return;
}
uploadStatusEl.textContent = '正在上传...';
uploadBtn.disabled = true;
const formData = new FormData();
formData.append('audio', audioBlob, 'recording.webm'); // 文件名自定义
try {
const response = await fetch(UPLOAD_URL, {
method: 'POST',
body: formData,
});
if (response.ok) {
const result = await response.json();
uploadStatusEl.textContent = '✅ 上传成功!';
console.log('上传成功:', result);
} else {
throw new Error(`HTTP ${response.status}`);
}
} catch (err) {
uploadStatusEl.textContent = '❌ 上传失败:' + err.message;
console.error('上传失败:', err);
} finally {
uploadBtn.disabled = false;
}
});
// 格式化时间:秒转为 mm:ss
function formatTime(seconds) {
const mins = Math.floor(seconds / 60)
.toString()
.padStart(2, '0');
const secs = (seconds % 60).toString().padStart(2, '0');
return `${mins}:${secs}`;
}
// 页面加载完成后初始化
document.addEventListener('DOMContentLoaded', () => {
stopBtn.disabled = true;
uploadBtn.disabled = true;
statusEl.textContent = '点击“开始录音”开始';
});
</script>
</body>
</html>

@ -57,7 +57,7 @@
<script setup>
import { ref, computed } from 'vue';
import { Search, Warning, Sunny, Moon, FullScreen, Printer, Picture, Switch } from '@element-plus/icons-vue';
import { Search, Warning, Sunny, Moon, FullScreen, Printer, Picture, Switch, Microphone } from '@element-plus/icons-vue';
//
const isDarkTheme = ref(false);
@ -76,6 +76,7 @@ const navItems = ref([
{ icon: Picture, name: '图片压缩', url: '/#/demo/compressImage' },
{ icon: Picture, name: '图片标注', url: '/#/demo/tuiImageEditor' },
{ icon: Switch, name: 'SCSS转换', url: '/#/demo/scssLiveTranslate' },
{ icon: Microphone, name: '录音', url: '/demoHtml/minihtml.html' },
]);
//

Loading…
Cancel
Save