const PageScene = {
_el: null,
_currentGameId: null,
_currentGameName: '',
_editorSceneId: null,
_editorData: null,
init() {
this._el = document.getElementById('page-scene');
},
async render() {
const cfg = await API.getConfig();
this._currentGameId = cfg.selected_game_id;
if (this._currentGameId) {
const games = await API.getGames();
const game = games.find(g => g.id === this._currentGameId);
this._currentGameName = game ? game.name : this._currentGameId;
}
this._el.innerHTML = this._template();
await this._loadScenes();
this._bindEvents();
},
async _loadScenes() {
const grid = document.getElementById('scene-grid');
if (!grid) return;
try {
const scenes = await API.getScenes(this._currentGameId);
if (!scenes || scenes.length === 0) {
grid.innerHTML = `
`;
return;
}
grid.innerHTML = scenes.map(s => this._sceneCardHtml(s)).join('');
} catch (e) {
console.error('Failed to load scenes:', e);
}
},
_bindEvents() {
document.getElementById('btn-new-scene')?.addEventListener('click', () => this._showEditor());
document.getElementById('btn-import-scene')?.addEventListener('click', () => {
const input = document.createElement('input');
input.type = 'file';
input.accept = '.tss';
input.onchange = async (e) => {
const file = e.target.files[0];
if (!file) return;
const formData = new FormData();
formData.append('file', file);
try {
const res = await fetch('/api/scenes/import', { method: 'POST', body: formData });
const data = await res.json();
if (data.ok) { Toast.show('场景导入成功', 'success'); this._loadScenes(); }
else Toast.show('导入失败', 'error');
} catch (err) { Toast.show('导入失败', 'error'); }
};
input.click();
});
const grid = document.getElementById('scene-grid');
if (grid) {
grid.addEventListener('click', (e) => {
const card = e.target.closest('[data-scene-id]');
if (!card) return;
const action = card.dataset.action;
const sceneId = card.dataset.sceneId;
if (action === 'render') {
this._renderScene(sceneId);
} else if (action === 'edit') {
e.stopPropagation();
this._showEditor(sceneId);
} else if (action === 'delete') {
e.stopPropagation();
this._deleteScene(sceneId);
} else if (action === 'export') {
e.stopPropagation();
this._exportScene(sceneId);
}
});
}
},
async _showEditor(sceneId) {
let scene = null;
if (sceneId) {
scene = await API.getScene(sceneId);
if (!scene) return;
}
this._editorSceneId = sceneId;
const modal = document.createElement('div');
modal.className = 'modal-overlay';
modal.innerHTML = this._editorTemplate(scene);
document.body.appendChild(modal);
modal.querySelector('.modal-overlay, .modal-actions .btn-secondary')
?.addEventListener('click', (e) => {
if (e.target === modal || e.target.matches('.btn-secondary')) {
modal.remove();
}
});
modal.querySelector('.btn-primary')?.addEventListener('click', async () => {
const name = modal.querySelector('#edit-scene-name').value || 'New Scene';
const desc = modal.querySelector('#edit-scene-desc').value || '';
const gameId = this._currentGameId || '';
const data = { name, description: desc, game_id: gameId };
if (this._editorSceneId) {
await API.updateScene(this._editorSceneId, data);
} else {
await API.createScene(data);
}
modal.remove();
Toast.show('场景保存成功', 'success');
this.render();
});
},
async _renderScene(sceneId) {
const url = `${location.origin}/scene/${sceneId}`;
window.open(url, '_blank');
navigator.clipboard.writeText(url).then(() => {
Toast.show('场景链接已复制,可在局域网设备访问', 'success');
}).catch(() => {});
},
async _deleteScene(sceneId) {
if (!confirm('确定删除此场景?')) return;
await API.deleteScene(sceneId);
Toast.show('场景已删除', 'info');
this._loadScenes();
},
_exportScene(sceneId) {
const a = document.createElement('a');
a.href = `/api/scenes/${sceneId}/export`;
a.download = `${sceneId}.tss`;
a.click();
Toast.show('正在下载 .tss 文件...', 'info');
},
_sceneCardHtml(scene) {
const canvasCount = (scene.canvases || []).length;
return `
${scene.name}
${this._currentGameName || '未关联游戏'}
${canvasCount} 个画布
${scene.updated_at ? new Date(scene.updated_at).toLocaleDateString() : ''}
${(scene.canvases || []).map(c => `${c.label || c.width + 'x' + c.height}`).join('')}
`;
},
_editorTemplate(scene) {
const name = scene ? scene.name : '';
const desc = scene ? scene.description : '';
return `
${scene ? '编辑场景' : '新建场景'}
提示:保存后可在场景编辑器中添加仪表盘、调整布局和画布比例。
`;
},
_template() {
return `
`;
}
};
window.PageScene = PageScene;