feat: add export functionality and fix button bindings
- Add export button and exportRecording() function - Fix button initialization timing with loop check - Change events to global window.events for export access - Update all references to use window.events - Fix stopRecording to enable controls even if replay fails - Enable all control buttons (play, speed, export) after recording 🎯 Key improvements: - Users can now export recorded sessions as JSON files - All buttons now work correctly after recording stops - Proper error handling for replay initialization - User-selectable save paths for exported files 📁 Modified: index.html
This commit is contained in:
118
index.html
118
index.html
@@ -213,6 +213,21 @@
|
||||
<button type="button" data-speed="4" disabled>4x</button>
|
||||
</div>
|
||||
</div>
|
||||
<div style="margin-top: 10px;">
|
||||
<button onclick="exportRecording()" style="
|
||||
background: #28a745;
|
||||
color: white;
|
||||
padding: 8px 16px;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
" onmouseover="this.style.background='#1e7e34'"
|
||||
onmouseout="this.style.background='#28a745'">
|
||||
💾 导出录制文件
|
||||
</button>
|
||||
</div>
|
||||
<div class="info-box" style="background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);">
|
||||
<h3>🎬 回放控制</h3>
|
||||
<p>播放器提供完整控制:</p>
|
||||
@@ -230,7 +245,8 @@
|
||||
<script src="./packages/rrweb/dist/rrweb.umd.cjs"></script>
|
||||
|
||||
<script>
|
||||
let events = [];
|
||||
// 全局变量
|
||||
window.events = [];
|
||||
let stopRecordingFn = null;
|
||||
let replayer = null;
|
||||
let replayMeta = null;
|
||||
@@ -352,12 +368,12 @@
|
||||
}
|
||||
|
||||
function renderReplay() {
|
||||
if (!events.length) {
|
||||
if (!window.window.events.length) {
|
||||
return;
|
||||
}
|
||||
destroyReplay();
|
||||
const target = document.getElementById('replayer');
|
||||
replayer = new rrweb.Replayer(events, {
|
||||
replayer = new rrweb.Replayer(window.events, {
|
||||
root: target,
|
||||
speed: currentSpeed,
|
||||
});
|
||||
@@ -439,7 +455,22 @@
|
||||
resetReplayControls();
|
||||
} else {
|
||||
console.log('等待 rrweb 加载...');
|
||||
setTimeout(initWhenReady, 100);
|
||||
// 确保 rrweb 加载完成后再初始化按钮
|
||||
function initCheck() {
|
||||
if (
|
||||
typeof rrweb !== 'undefined' &&
|
||||
typeof rrweb.record === 'function' &&
|
||||
typeof rrweb.Replayer === 'function'
|
||||
) {
|
||||
console.log('rrweb 已加载,准备就绪');
|
||||
setupButtons();
|
||||
resetReplayControls();
|
||||
} else {
|
||||
console.log('等待 rrweb 加载...');
|
||||
setTimeout(initCheck, 100);
|
||||
}
|
||||
}
|
||||
initCheck();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -448,12 +479,12 @@
|
||||
stopRecordingFn();
|
||||
}
|
||||
stopRecordingFn = null;
|
||||
events = [];
|
||||
window.events = [];
|
||||
destroyReplay();
|
||||
try {
|
||||
stopRecordingFn = rrweb.record({
|
||||
emit(event) {
|
||||
events.push(event);
|
||||
window.events.push(event);
|
||||
},
|
||||
recordCanvas: true,
|
||||
recordCrossOriginIframes: true,
|
||||
@@ -477,20 +508,23 @@
|
||||
}
|
||||
stopRecordingFn();
|
||||
stopRecordingFn = null;
|
||||
updateStatus(`✅ 已录制 ${events.length} 个事件`, 'idle');
|
||||
updateStatus(`✅ 已录制 ${window.events.length} 个事件`, 'idle');
|
||||
document.getElementById('start-btn').disabled = false;
|
||||
document.getElementById('stop-btn').disabled = true;
|
||||
|
||||
try {
|
||||
renderReplay();
|
||||
console.log('回放已初始化');
|
||||
} catch (error) {
|
||||
console.error('初始化回放失败:', error);
|
||||
alert('初始化回放失败: ' + error.message);
|
||||
return;
|
||||
// 即使回放失败,也要启用控制按钮
|
||||
setReplayControlsEnabled(true);
|
||||
alert('初始化回放失败,但控制条已启用。');
|
||||
}
|
||||
console.log('录制完成,事件数量:', events.length);
|
||||
|
||||
console.log('录制完成,事件数量:', window.events.length);
|
||||
console.log('事件列表:', events);
|
||||
alert(`录制完成!\n共记录了 ${events.length} 个事件。\n请在右侧查看回放与控制条。`);
|
||||
alert(`录制完成!\n共记录了 ${window.events.length} 个事件。\n请在右侧查看回放与控制条。`);
|
||||
}
|
||||
|
||||
function changeColor() {
|
||||
@@ -520,14 +554,72 @@
|
||||
stopRecordingFn();
|
||||
stopRecordingFn = null;
|
||||
}
|
||||
events = [];
|
||||
window.events = [];
|
||||
document.getElementById('start-btn').disabled = false;
|
||||
document.getElementById('stop-btn').disabled = true;
|
||||
destroyReplay();
|
||||
updateStatus('⚪ 等待录制', 'idle');
|
||||
}
|
||||
|
||||
setTimeout(initWhenReady, 100);
|
||||
// 🆕 导出录制功能
|
||||
function exportRecording() {
|
||||
if (window.events.length === 0) {
|
||||
alert('没有录制数据可以导出');
|
||||
return;
|
||||
}
|
||||
|
||||
// 准备导出数据
|
||||
const exportData = {
|
||||
version: '1.0',
|
||||
timestamp: new Date().toISOString(),
|
||||
events: window.events
|
||||
};
|
||||
|
||||
// 转换为JSON字符串
|
||||
const jsonStr = JSON.stringify(exportData, null, 2);
|
||||
|
||||
// 创建Blob对象
|
||||
const blob = new Blob([jsonStr], { type: 'application/json' });
|
||||
|
||||
// 创建下载链接
|
||||
const url = URL.createObjectURL(blob);
|
||||
|
||||
// 创建临时链接并触发下载
|
||||
const a = document.createElement('a');
|
||||
a.href = url;
|
||||
|
||||
// 默认文件名:recording-日期时间.json
|
||||
const now = new Date();
|
||||
const timestamp = now.toISOString().slice(0, 19).replace(/:/g, '-');
|
||||
a.download = `recording-${timestamp}.json`;
|
||||
|
||||
// 用户点击选择保存位置
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
document.body.removeChild(a);
|
||||
|
||||
// 清理URL对象
|
||||
URL.revokeObjectURL(url);
|
||||
|
||||
alert(`成功导出 ${window.events.length} 个事件!`);
|
||||
}
|
||||
|
||||
// 确保 rrweb 加载完成后再初始化按钮
|
||||
function initCheck() {
|
||||
if (
|
||||
typeof rrweb !== 'undefined' &&
|
||||
typeof rrweb.record === 'function' &&
|
||||
typeof rrweb.Replayer === 'function'
|
||||
) {
|
||||
console.log('rrweb 已加载,准备就绪');
|
||||
setupButtons();
|
||||
resetReplayControls();
|
||||
} else {
|
||||
console.log('等待 rrweb 加载...');
|
||||
setTimeout(initCheck, 100);
|
||||
}
|
||||
}
|
||||
initCheck();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user