refactor: standalone dashboard folders with index.html, zip-based import/export (.tsd/.tss/.tsp)
- Each dashboard is now a self-contained folder: manifest.json + index.html - Auto-scan dashboards/ and data/dashboards/ on startup - Export: .tsd (dashboards), .tss (scenes), .tsp (game plugins) - all zip format - Dashboard index.html is complete standalone page (WS + data binding) - Aspect ratio constraints handled in each dashboard's own JS - Removed template-based dashboard rendering in favor of static serve - Import via file upload endpoints, export via direct file download
This commit is contained in:
@@ -83,7 +83,33 @@ const PageDashboard = {
|
||||
const themeId = card.dataset.themeId;
|
||||
this._openDashboard(themeId);
|
||||
});
|
||||
gridEl.addEventListener('contextmenu', (e) => {
|
||||
const card = e.target.closest('.theme-card');
|
||||
if (!card) return;
|
||||
e.preventDefault();
|
||||
const themeId = card.dataset.themeId;
|
||||
this._exportDashboard(themeId);
|
||||
});
|
||||
}
|
||||
|
||||
document.getElementById('btn-import-dashboard')?.addEventListener('click', () => {
|
||||
const input = document.createElement('input');
|
||||
input.type = 'file';
|
||||
input.accept = '.tsd';
|
||||
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/dashboards/import', { method: 'POST', body: formData });
|
||||
const data = await res.json();
|
||||
if (data.ok) { Toast.show('仪表盘导入成功', 'success'); this._loadThemes(); }
|
||||
else Toast.show('导入失败', 'error');
|
||||
} catch (err) { Toast.show('导入失败', 'error'); }
|
||||
};
|
||||
input.click();
|
||||
});
|
||||
},
|
||||
|
||||
_openDashboard(themeId) {
|
||||
@@ -96,9 +122,17 @@ const PageDashboard = {
|
||||
});
|
||||
},
|
||||
|
||||
_exportDashboard(themeId) {
|
||||
const a = document.createElement('a');
|
||||
a.href = `/api/dashboards/${themeId}/export`;
|
||||
a.download = `${themeId}.tsd`;
|
||||
a.click();
|
||||
Toast.show('正在下载 .tsd 文件...', 'info');
|
||||
},
|
||||
|
||||
_themeCardHtml(theme) {
|
||||
const icon = theme.config?.icon || '📊';
|
||||
const aspect = theme.config?.aspect_ratio || 'auto';
|
||||
const icon = theme.icon || '📊';
|
||||
const aspect = theme.aspect_ratio || 'auto';
|
||||
return `
|
||||
<div class="glass-card theme-card" data-theme-id="${theme.id}">
|
||||
<div class="theme-card-preview">${icon}</div>
|
||||
@@ -121,6 +155,9 @@ const PageDashboard = {
|
||||
<div class="dashboard-layout">
|
||||
<div class="dashboard-sub-sidebar">
|
||||
<div id="dash-category-list"></div>
|
||||
<div style="padding:8px 12px;margin-top:auto;">
|
||||
<button id="btn-import-dashboard" class="btn btn-sm btn-secondary" style="width:100%;">导入 .tsd</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="dashboard-main">
|
||||
<div id="dash-theme-grid" class="theme-grid"></div>
|
||||
|
||||
Reference in New Issue
Block a user