feat: dashboard manifest supports 'supported_games' field, frontend filters by selected game
- DashboardTheme model adds supported_games (default: 'all') - Built-in dashboards declare game compatibility: speedometer/tachometer/gear_indicator -> all lap_timer -> forza_horizon_5,forza_horizon_4,forza_motorsport,f1_24 - Dashboard browser filters themes by selected game - Card shows '全' (all) or '限' (limited) badge - Updated DASHBOARD_DEV.md with supported_games spec
This commit is contained in:
@@ -5,6 +5,7 @@
|
|||||||
"description": "醒目大圆档位显示,适合竖屏手机副屏",
|
"description": "醒目大圆档位显示,适合竖屏手机副屏",
|
||||||
"author": "Yei.J. (AskaEth)",
|
"author": "Yei.J. (AskaEth)",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
|
"supported_games": "all",
|
||||||
"config": {
|
"config": {
|
||||||
"icon": "⚙️",
|
"icon": "⚙️",
|
||||||
"aspect_ratio": "1:1",
|
"aspect_ratio": "1:1",
|
||||||
|
|||||||
@@ -2,9 +2,10 @@
|
|||||||
"id": "lap_timer",
|
"id": "lap_timer",
|
||||||
"name": "圈速计时器",
|
"name": "圈速计时器",
|
||||||
"category": "timing",
|
"category": "timing",
|
||||||
"description": "当前圈速、最佳圈和上圈时间对比显示",
|
"description": "当前圈速、最佳圈和上圈时间对比显示。仅支持 Forza 系列和 F1",
|
||||||
"author": "Yei.J. (AskaEth)",
|
"author": "Yei.J. (AskaEth)",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
|
"supported_games": "forza_horizon_5,forza_horizon_4,forza_motorsport,f1_24",
|
||||||
"config": {
|
"config": {
|
||||||
"icon": "⏱️",
|
"icon": "⏱️",
|
||||||
"aspect_ratio": "4:3",
|
"aspect_ratio": "4:3",
|
||||||
|
|||||||
@@ -5,10 +5,10 @@
|
|||||||
"description": "大字号实时速度显示,支持km/h和mph,适合横屏全屏显示",
|
"description": "大字号实时速度显示,支持km/h和mph,适合横屏全屏显示",
|
||||||
"author": "Yei.J. (AskaEth)",
|
"author": "Yei.J. (AskaEth)",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
|
"supported_games": "all",
|
||||||
"config": {
|
"config": {
|
||||||
"icon": "🏎️",
|
"icon": "🏎️",
|
||||||
"unit": "kmh",
|
"unit": "kmh",
|
||||||
"show_unit": true,
|
|
||||||
"aspect_ratio": "auto",
|
"aspect_ratio": "auto",
|
||||||
"render_mode": "contain"
|
"render_mode": "contain"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
"description": "赛车风格弧形转速表,支持红区换档提示,推荐横屏",
|
"description": "赛车风格弧形转速表,支持红区换档提示,推荐横屏",
|
||||||
"author": "Yei.J. (AskaEth)",
|
"author": "Yei.J. (AskaEth)",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
|
"supported_games": "all",
|
||||||
"config": {
|
"config": {
|
||||||
"icon": "⏱️",
|
"icon": "⏱️",
|
||||||
"max_rpm": 8000,
|
"max_rpm": 8000,
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ dashboards/my_dashboard/
|
|||||||
"description": "简短描述",
|
"description": "简短描述",
|
||||||
"author": "你的名字",
|
"author": "你的名字",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
|
"supported_games": "all",
|
||||||
"config": {
|
"config": {
|
||||||
"icon": "🏎️",
|
"icon": "🏎️",
|
||||||
"aspect_ratio": "16:9",
|
"aspect_ratio": "16:9",
|
||||||
@@ -37,6 +38,7 @@ dashboards/my_dashboard/
|
|||||||
| `description` | string | 否 | 描述文字 |
|
| `description` | string | 否 | 描述文字 |
|
||||||
| `author` | string | 否 | 作者 |
|
| `author` | string | 否 | 作者 |
|
||||||
| `version` | string | 否 | 版本号 |
|
| `version` | string | 否 | 版本号 |
|
||||||
|
| `supported_games` | string | 否 | 支持的游戏: `"all"` 或 ID 逗号分隔 (如 `"forza_horizon_5,f1_24"`) |
|
||||||
| `config.icon` | string | 否 | 预览图标 emoji |
|
| `config.icon` | string | 否 | 预览图标 emoji |
|
||||||
| `config.aspect_ratio` | string | 否 | 渲染比例: `auto` / `16:9` / `4:3` / `1:1` / `21:9` |
|
| `config.aspect_ratio` | string | 否 | 渲染比例: `auto` / `16:9` / `4:3` / `1:1` / `21:9` |
|
||||||
| `config.render_mode` | string | 否 | 渲染模式: `contain` / `cover` / `fill` / `center` |
|
| `config.render_mode` | string | 否 | 渲染模式: `contain` / `cover` / `fill` / `center` |
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ class DashboardTheme:
|
|||||||
icon: str = "📊"
|
icon: str = "📊"
|
||||||
aspect_ratio: str = "auto"
|
aspect_ratio: str = "auto"
|
||||||
render_mode: str = "contain"
|
render_mode: str = "contain"
|
||||||
|
supported_games: str = "all"
|
||||||
created_at: str = ""
|
created_at: str = ""
|
||||||
updated_at: str = ""
|
updated_at: str = ""
|
||||||
is_builtin: bool = False
|
is_builtin: bool = False
|
||||||
@@ -46,6 +47,7 @@ class DashboardTheme:
|
|||||||
"icon": self.icon,
|
"icon": self.icon,
|
||||||
"aspect_ratio": self.aspect_ratio,
|
"aspect_ratio": self.aspect_ratio,
|
||||||
"render_mode": self.render_mode,
|
"render_mode": self.render_mode,
|
||||||
|
"supported_games": self.supported_games,
|
||||||
"created_at": self.created_at,
|
"created_at": self.created_at,
|
||||||
"updated_at": self.updated_at,
|
"updated_at": self.updated_at,
|
||||||
"is_builtin": self.is_builtin,
|
"is_builtin": self.is_builtin,
|
||||||
@@ -67,6 +69,7 @@ class DashboardTheme:
|
|||||||
icon=config.get("icon", "📊"),
|
icon=config.get("icon", "📊"),
|
||||||
aspect_ratio=config.get("aspect_ratio", "auto"),
|
aspect_ratio=config.get("aspect_ratio", "auto"),
|
||||||
render_mode=config.get("render_mode", "contain"),
|
render_mode=config.get("render_mode", "contain"),
|
||||||
|
supported_games=data.get("supported_games", "all"),
|
||||||
created_at=data.get("created_at", ""),
|
created_at=data.get("created_at", ""),
|
||||||
updated_at=data.get("updated_at", ""),
|
updated_at=data.get("updated_at", ""),
|
||||||
is_builtin=is_builtin,
|
is_builtin=is_builtin,
|
||||||
@@ -137,6 +140,7 @@ class DashboardManager:
|
|||||||
"description": theme.description,
|
"description": theme.description,
|
||||||
"author": theme.author,
|
"author": theme.author,
|
||||||
"version": theme.version,
|
"version": theme.version,
|
||||||
|
"supported_games": theme.supported_games,
|
||||||
"config": {
|
"config": {
|
||||||
"icon": theme.icon,
|
"icon": theme.icon,
|
||||||
"aspect_ratio": theme.aspect_ratio,
|
"aspect_ratio": theme.aspect_ratio,
|
||||||
|
|||||||
@@ -1,12 +1,15 @@
|
|||||||
const PageDashboard = {
|
const PageDashboard = {
|
||||||
_el: null,
|
_el: null,
|
||||||
_currentCategory: 'all',
|
_currentCategory: 'all',
|
||||||
|
_selectedGameId: null,
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
this._el = document.getElementById('page-dashboard');
|
this._el = document.getElementById('page-dashboard');
|
||||||
},
|
},
|
||||||
|
|
||||||
async render() {
|
async render() {
|
||||||
|
const cfg = await API.getConfig();
|
||||||
|
this._selectedGameId = cfg.selected_game_id;
|
||||||
this._el.innerHTML = this._template();
|
this._el.innerHTML = this._template();
|
||||||
await this._loadCategories();
|
await this._loadCategories();
|
||||||
await this._loadThemes();
|
await this._loadThemes();
|
||||||
@@ -44,14 +47,18 @@ const PageDashboard = {
|
|||||||
gridEl.innerHTML = '<div style="text-align:center;padding:40px;color:var(--text-tertiary);">加载中...</div>';
|
gridEl.innerHTML = '<div style="text-align:center;padding:40px;color:var(--text-tertiary);">加载中...</div>';
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const themes = await API.getDashboards(this._currentCategory);
|
let themes = await API.getDashboards(this._currentCategory);
|
||||||
if (!themes || themes.length === 0) {
|
if (!themes || themes.length === 0) {
|
||||||
gridEl.innerHTML = `
|
gridEl.innerHTML = this._emptyHtml();
|
||||||
<div class="empty-state" style="grid-column:1/-1;">
|
return;
|
||||||
<svg viewBox="0 0 24 24" width="64" height="64"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8zm0-12c-2.21 0-4 1.79-4 4s1.79 4 4 4 4-1.79 4-4-1.79-4-4-4z" fill="currentColor"/></svg>
|
}
|
||||||
<h4>暂无仪表盘主题</h4>
|
|
||||||
<p>创建或导入你的第一个仪表盘主题</p>
|
if (this._selectedGameId) {
|
||||||
</div>`;
|
themes = themes.filter(t => this._isGameSupported(t));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (themes.length === 0) {
|
||||||
|
gridEl.innerHTML = this._emptyHtml('当前游戏没有兼容的仪表盘');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -62,6 +69,22 @@ const PageDashboard = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_isGameSupported(theme) {
|
||||||
|
const supported = theme.supported_games || 'all';
|
||||||
|
if (supported === 'all') return true;
|
||||||
|
const ids = supported.split(',').map(s => s.trim());
|
||||||
|
return ids.includes(this._selectedGameId);
|
||||||
|
},
|
||||||
|
|
||||||
|
_emptyHtml(msg) {
|
||||||
|
return `
|
||||||
|
<div class="empty-state" style="grid-column:1/-1;">
|
||||||
|
<svg viewBox="0 0 24 24" width="64" height="64"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8zm0-12c-2.21 0-4 1.79-4 4s1.79 4 4 4 4-1.79 4-4-1.79-4-4-4z" fill="currentColor"/></svg>
|
||||||
|
<h4>${msg || '暂无仪表盘主题'}</h4>
|
||||||
|
<p>创建或导入你的仪表盘主题</p>
|
||||||
|
</div>`;
|
||||||
|
},
|
||||||
|
|
||||||
_bindEvents() {
|
_bindEvents() {
|
||||||
const categoryList = document.getElementById('dash-category-list');
|
const categoryList = document.getElementById('dash-category-list');
|
||||||
if (categoryList) {
|
if (categoryList) {
|
||||||
@@ -133,6 +156,10 @@ const PageDashboard = {
|
|||||||
_themeCardHtml(theme) {
|
_themeCardHtml(theme) {
|
||||||
const icon = theme.icon || '📊';
|
const icon = theme.icon || '📊';
|
||||||
const aspect = theme.aspect_ratio || 'auto';
|
const aspect = theme.aspect_ratio || 'auto';
|
||||||
|
const supported = theme.supported_games || 'all';
|
||||||
|
const isAll = supported === 'all';
|
||||||
|
const compatLabel = isAll ? '全' : '限';
|
||||||
|
const compatClass = isAll ? 'badge-info' : 'badge-warning';
|
||||||
return `
|
return `
|
||||||
<div class="glass-card theme-card" data-theme-id="${theme.id}">
|
<div class="glass-card theme-card" data-theme-id="${theme.id}">
|
||||||
<div class="theme-card-preview">${icon}</div>
|
<div class="theme-card-preview">${icon}</div>
|
||||||
@@ -144,7 +171,7 @@ const PageDashboard = {
|
|||||||
</div>
|
</div>
|
||||||
<div class="theme-card-meta" style="margin-top:4px;">
|
<div class="theme-card-meta" style="margin-top:4px;">
|
||||||
<span>${theme.author || ''}</span>
|
<span>${theme.author || ''}</span>
|
||||||
<span class="badge ${theme.is_builtin ? 'badge-info' : 'badge-success'}">${theme.is_builtin ? '内置' : '自定义'}</span>
|
<span class="badge ${compatClass}">${compatLabel}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>`;
|
</div>`;
|
||||||
|
|||||||
Reference in New Issue
Block a user