186 lines
8.2 KiB
JavaScript
186 lines
8.2 KiB
JavaScript
const PageSettings = {
|
|
_el: null,
|
|
|
|
init() {
|
|
this._el = document.getElementById('page-settings');
|
|
},
|
|
|
|
async render() {
|
|
const cfg = await API.getConfig();
|
|
const games = await API.getGames();
|
|
this._el.innerHTML = this._template(cfg, games);
|
|
this._bindEvents();
|
|
},
|
|
|
|
_bindEvents() {
|
|
document.getElementById('settings-telemetry-port')?.addEventListener('change', async (e) => {
|
|
await API.updateConfig({ telemetry_port: parseInt(e.target.value) || 20777 });
|
|
Toast.show('遥测端口已更新,重启监听后生效', 'info');
|
|
});
|
|
|
|
document.getElementById('settings-server-port')?.addEventListener('change', async (e) => {
|
|
Toast.show('服务器端口修改后需要重启程序', 'warning');
|
|
});
|
|
|
|
document.getElementById('settings-restart-telemetry')?.addEventListener('click', async () => {
|
|
await API.stopTelemetry();
|
|
await API.startTelemetry();
|
|
Toast.show('遥测监听已重启', 'success');
|
|
});
|
|
|
|
document.getElementById('btn-import-plugin')?.addEventListener('click', () => this._importPlugin());
|
|
document.getElementById('btn-reload-plugins')?.addEventListener('click', async () => {
|
|
await API.reloadGamePlugins();
|
|
Toast.show('插件已重新加载', 'success');
|
|
this.render();
|
|
});
|
|
|
|
document.getElementById('settings-game-list')?.addEventListener('click', async (e) => {
|
|
const exportBtn = e.target.closest('.btn-export-plugin');
|
|
const removeBtn = e.target.closest('.btn-remove-plugin');
|
|
if (exportBtn) {
|
|
const pluginId = exportBtn.dataset.pluginId;
|
|
const data = await API.exportGamePlugin(pluginId);
|
|
if (data) {
|
|
this._downloadJson(`plugin_${pluginId}.json`, data);
|
|
Toast.show('插件已导出', 'success');
|
|
}
|
|
}
|
|
if (removeBtn) {
|
|
const pluginId = removeBtn.dataset.pluginId;
|
|
if (confirm('确定移除这个游戏插件?')) {
|
|
await API.removeGamePlugin(pluginId);
|
|
Toast.show('插件已移除', 'info');
|
|
this.render();
|
|
}
|
|
}
|
|
});
|
|
},
|
|
|
|
async _importPlugin() {
|
|
const input = document.createElement('input');
|
|
input.type = 'file';
|
|
input.accept = '.json';
|
|
input.onchange = async (e) => {
|
|
const file = e.target.files[0];
|
|
if (!file) return;
|
|
try {
|
|
const text = await file.text();
|
|
const data = JSON.parse(text);
|
|
if (data.type === 'game_plugin') {
|
|
await API.installGamePlugin(data);
|
|
Toast.show('插件安装成功', 'success');
|
|
this.render();
|
|
} else {
|
|
Toast.show('无效的插件文件格式', 'error');
|
|
}
|
|
} catch (err) {
|
|
Toast.show('文件解析失败: ' + err.message, 'error');
|
|
}
|
|
};
|
|
input.click();
|
|
},
|
|
|
|
_downloadJson(filename, data) {
|
|
const blob = new Blob([JSON.stringify(data, null, 2)], { type: 'application/json' });
|
|
const url = URL.createObjectURL(blob);
|
|
const a = document.createElement('a');
|
|
a.href = url;
|
|
a.download = filename;
|
|
a.click();
|
|
URL.revokeObjectURL(url);
|
|
},
|
|
|
|
_gamePluginListHtml(games) {
|
|
if (!games || games.length === 0) {
|
|
return '<p style="color:var(--text-tertiary);font-size:13px;">暂无游戏插件</p>';
|
|
}
|
|
return games.map(g => `
|
|
<div class="glass-card" style="padding:16px;margin-bottom:10px;display:flex;align-items:center;justify-content:space-between;">
|
|
<div>
|
|
<div style="font-weight:600;">${g.name}</div>
|
|
<div style="font-size:12px;color:var(--text-tertiary);">
|
|
${g.description || ''} | v${g.version} | ${g.author || ''}
|
|
<span class="badge ${g.is_builtin ? 'badge-info' : 'badge-success'}" style="margin-left:6px;">
|
|
${g.is_builtin ? '内置' : '社区'}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
<div style="display:flex;gap:6px;">
|
|
<button class="btn btn-sm btn-secondary btn-export-plugin" data-plugin-id="${g.id}">导出</button>
|
|
${!g.is_builtin ? `<button class="btn btn-sm btn-danger btn-remove-plugin" data-plugin-id="${g.id}">移除</button>` : ''}
|
|
</div>
|
|
</div>
|
|
`).join('');
|
|
},
|
|
|
|
_template(cfg, games) {
|
|
return `
|
|
<div class="animated">
|
|
<h2 style="font-size:24px;font-weight:700;margin-bottom:24px;">设置</h2>
|
|
|
|
<div class="settings-section">
|
|
<h3>连接设置</h3>
|
|
<div class="settings-row">
|
|
<div>
|
|
<div class="settings-label">遥测监听端口</div>
|
|
<div class="settings-desc">游戏内设置的数据输出端口</div>
|
|
</div>
|
|
<div class="settings-control">
|
|
<input type="number" id="settings-telemetry-port" value="${cfg.telemetry_port || 20777}" min="1024" max="65535">
|
|
</div>
|
|
</div>
|
|
<div class="settings-row">
|
|
<div>
|
|
<div class="settings-label">Web 服务器端口</div>
|
|
<div class="settings-desc">Web UI 服务的端口号</div>
|
|
</div>
|
|
<div class="settings-control">
|
|
<input type="number" id="settings-server-port" value="${cfg.server_port || 9527}" min="80" max="65535" disabled>
|
|
</div>
|
|
</div>
|
|
<div class="settings-row">
|
|
<div>
|
|
<div class="settings-label">重启遥测监听</div>
|
|
<div class="settings-desc">修改端口或切换游戏后需要重启监听</div>
|
|
</div>
|
|
<div class="settings-control">
|
|
<button id="settings-restart-telemetry" class="btn btn-secondary btn-sm">重启监听</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="settings-section">
|
|
<h3>游戏插件管理</h3>
|
|
<div style="display:flex;gap:8px;margin-bottom:16px;">
|
|
<button id="btn-import-plugin" class="btn btn-secondary btn-sm">📥 导入插件</button>
|
|
<button id="btn-reload-plugins" class="btn btn-secondary btn-sm">🔄 重新加载</button>
|
|
</div>
|
|
<div id="settings-game-list">
|
|
${this._gamePluginListHtml(games)}
|
|
</div>
|
|
<div class="glass-card" style="padding:16px;margin-top:12px;font-size:12px;color:var(--text-tertiary);line-height:1.6;">
|
|
<strong style="color:var(--text-secondary);">社区开发指南:</strong><br>
|
|
1. 创建一个包含 <code>manifest.json</code> 和 <code>parser.py</code> 的文件夹<br>
|
|
2. <code>manifest.json</code> 定义游戏元信息,<code>parser.py</code> 实现 <code>get_parser()</code> 函数<br>
|
|
3. <code>get_parser()</code> 返回对象需实现 <code>game_id()</code> 和 <code>parse(data, addr)</code> 方法<br>
|
|
4. 通过"导入插件"或放入 <code>games/user/</code> 目录安装<br>
|
|
5. 导出你的插件分享给社区!
|
|
</div>
|
|
</div>
|
|
|
|
<div class="settings-section">
|
|
<h3>关于</h3>
|
|
<div class="settings-row">
|
|
<div>
|
|
<div class="settings-label">TurboSu</div>
|
|
<div class="settings-desc">赛车遥测仪表盘 v1.0.0</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>`;
|
|
}
|
|
};
|
|
|
|
window.PageSettings = PageSettings;
|