refactor: move record/playback to dedicated page, restore test mode in settings
- New '录制回放' page with sidebar nav entry - Recording: start/stop/auto, file list with play/export/delete - Playback: visual bar indicator, opens dashboards to see replayed data - Settings: restored dev tools section with test mode toggle
This commit is contained in:
@@ -0,0 +1,89 @@
|
||||
const PageRecord = {
|
||||
_el: null,
|
||||
|
||||
init() { this._el = document.getElementById('page-record'); },
|
||||
|
||||
async render() {
|
||||
this._el.innerHTML = this._template();
|
||||
this._bindEvents();
|
||||
this._loadRecordings();
|
||||
},
|
||||
|
||||
async _loadRecordings() {
|
||||
try {
|
||||
const res = await fetch('/api/recordings');
|
||||
const list = await res.json();
|
||||
const el = document.getElementById('rec-file-list');
|
||||
if (!el) return;
|
||||
if (!list.length) { el.innerHTML = '<p style="color:var(--text-tertiary);font-size:13px;padding:20px">暂无录制文件</p>'; return; }
|
||||
el.innerHTML = list.map(f => {
|
||||
const size = f.size > 1048576 ? (f.size/1048576).toFixed(1)+'MB' : (f.size/1024).toFixed(1)+'KB';
|
||||
return '<div class="glass-card" style="padding:10px 14px;margin-bottom:6px;display:flex;align-items:center;justify-content:space-between;gap:8px;flex-wrap:wrap"><span style="font-size:12px;flex:1;min-width:150px">' + f.name + '</span><span style="font-size:11px;color:var(--text-tertiary)">' + size + '</span><button class="btn btn-sm btn-primary btn-play-rec" data-file="' + f.name + '">播放</button><a class="btn btn-sm btn-secondary" href="/api/recording/' + f.name + '/export" download>导出</a><button class="btn btn-sm btn-danger btn-del-rec" data-file="' + f.name + '">删除</button></div>';
|
||||
}).join('');
|
||||
} catch (e) {}
|
||||
},
|
||||
|
||||
_bindEvents() {
|
||||
document.getElementById('btn-rec-start')?.addEventListener('click', async () => {
|
||||
await fetch('/api/record/start', { method: 'POST' });
|
||||
this._updateRecUI(true);
|
||||
});
|
||||
document.getElementById('btn-rec-stop')?.addEventListener('click', async () => {
|
||||
await fetch('/api/record/stop', { method: 'POST' });
|
||||
this._updateRecUI(false);
|
||||
this._loadRecordings();
|
||||
});
|
||||
document.getElementById('rec-auto-mode')?.addEventListener('change', async (e) => {
|
||||
await fetch('/api/record/auto', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ enabled: e.target.checked }) });
|
||||
});
|
||||
document.getElementById('btn-import-rec')?.addEventListener('click', () => {
|
||||
const input = document.createElement('input'); input.type = 'file'; input.accept = '.tsr';
|
||||
input.onchange = async (e) => {
|
||||
const file = e.target.files[0]; if (!file) return;
|
||||
const fd = new FormData(); fd.append('file', file);
|
||||
await fetch('/api/recording/import', { method: 'POST', body: fd });
|
||||
Toast.show('导入成功', 'success'); this._loadRecordings();
|
||||
}; input.click();
|
||||
});
|
||||
document.getElementById('rec-file-list')?.addEventListener('click', async (e) => {
|
||||
const playBtn = e.target.closest('.btn-play-rec');
|
||||
const delBtn = e.target.closest('.btn-del-rec');
|
||||
if (playBtn) this._playRecording(playBtn.dataset.file);
|
||||
if (delBtn && confirm('删除?')) {
|
||||
await fetch('/api/recording/' + delBtn.dataset.file + '/delete', { method: 'DELETE' });
|
||||
this._loadRecordings();
|
||||
}
|
||||
});
|
||||
document.getElementById('btn-playback-stop')?.addEventListener('click', async () => {
|
||||
await fetch('/api/playback/stop', { method: 'POST' });
|
||||
document.getElementById('playback-bar').style.display = 'none';
|
||||
});
|
||||
},
|
||||
|
||||
_updateRecUI(recording) {
|
||||
document.getElementById('btn-rec-start').style.display = recording ? 'none' : '';
|
||||
document.getElementById('btn-rec-stop').style.display = recording ? '' : 'none';
|
||||
document.getElementById('rec-status').textContent = recording ? '录制中...' : '就绪';
|
||||
},
|
||||
|
||||
async _playRecording(filename) {
|
||||
document.getElementById('playback-file').textContent = filename;
|
||||
document.getElementById('playback-bar').style.display = 'flex';
|
||||
await fetch('/api/playback/start', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ filename }) });
|
||||
Toast.show('回放已开始,打开仪表盘查看数据', 'info');
|
||||
},
|
||||
|
||||
_template() {
|
||||
return '<div class="animated"><h2 style="font-size:24px;font-weight:700;margin-bottom:8px">录制与回放</h2><p style="color:var(--text-secondary);margin-bottom:20px">录制原始 UDP 流到 .tsr 文件,回放调试仪表盘</p>' +
|
||||
|
||||
'<div class="glass-card" style="padding:20px;margin-bottom:20px"><h3 style="font-size:16px;margin-bottom:12px">录制控制</h3><div style="display:flex;align-items:center;gap:12px;flex-wrap:wrap"><button id="btn-rec-start" class="btn btn-primary">开始录制</button><button id="btn-rec-stop" class="btn btn-danger" style="display:none">停止录制</button><span id="rec-status" style="font-size:13px;color:var(--text-secondary)">就绪</span><span style="flex:1"></span><label class="switch-label"><input type="checkbox" id="rec-auto-mode"><span class="switch-track"></span> 自动检测</label></div></div>' +
|
||||
|
||||
'<div id="playback-bar" class="glass-card" style="padding:12px 20px;margin-bottom:20px;display:none;align-items:center;gap:12px;background:var(--accent-container)"><span style="font-size:13px">正在回放: <strong id="playback-file">-</strong></span><span style="flex:1"></span><span style="font-size:12px;color:var(--text-tertiary)">打开仪表盘页面即可看到数据</span><button id="btn-playback-stop" class="btn btn-sm btn-danger">停止</button></div>' +
|
||||
|
||||
'<div class="glass-card" style="padding:20px"><h3 style="font-size:16px;margin-bottom:12px">录制文件</h3><div style="display:flex;gap:8px;margin-bottom:12px"><button id="btn-import-rec" class="btn btn-sm btn-secondary">导入 .tsr</button></div><div id="rec-file-list"></div></div>' +
|
||||
|
||||
'</div>';
|
||||
}
|
||||
};
|
||||
|
||||
window.PageRecord = PageRecord;
|
||||
Reference in New Issue
Block a user