From 93776c7d98214cdd3b8cf6f41cd53c62c7974874 Mon Sep 17 00:00:00 2001 From: AskaEth Date: Sun, 26 Jul 2026 21:20:27 +0800 Subject: [PATCH] refactor: market as standalone sidebar page, removed from dashboard --- static/js/app.js | 1 + static/js/pages/dashboard.js | 9 -- static/js/pages/market.js | 168 +++++++++++++++++++++++++++++++++++ static/js/router.js | 1 + templates/index.html | 7 ++ 5 files changed, 177 insertions(+), 9 deletions(-) create mode 100644 static/js/pages/market.js diff --git a/static/js/app.js b/static/js/app.js index 9f256b0..c6ad110 100644 --- a/static/js/app.js +++ b/static/js/app.js @@ -9,6 +9,7 @@ PageScene.init(); PageDebug.init(); PageRecord.init(); + PageMarket.init(); PageSettings.init(); Router.init(); diff --git a/static/js/pages/dashboard.js b/static/js/pages/dashboard.js index 9f76b14..418dec4 100644 --- a/static/js/pages/dashboard.js +++ b/static/js/pages/dashboard.js @@ -313,15 +313,6 @@ const PageDashboard = {
-
- -
- - -
-
diff --git a/static/js/pages/market.js b/static/js/pages/market.js new file mode 100644 index 0000000..a3f55a8 --- /dev/null +++ b/static/js/pages/market.js @@ -0,0 +1,168 @@ +const PageMarket = { + _el: null, + _marketToken: null, + + init() { + this._el = document.getElementById('page-market'); + this._marketToken = localStorage.getItem('turbosu-market-token'); + }, + + async render() { + this._el.innerHTML = this._template(); + this._bindEvents(); + this._updateUI(); + this._loadItems(); + }, + + async _loadItems() { + const grid = document.getElementById('market-grid'); + if (!grid) return; + grid.innerHTML = '
加载中...
'; + try { + const res = await fetch('/api/market/dashboards'); + const data = await res.json(); + const items = data.items || []; + if (!items.length) { grid.innerHTML = this._emptyHtml('市场暂无仪表盘'); return; } + grid.innerHTML = items.map(d => { + const icon = d.icon || '📦'; + return `
+
${icon}
+
+
${d.name||'Unnamed'}
+
+ ${d.category||''}v${d.version||'1.0'} +
+
+ ${d.author||''}${d.downloads||0} 次 +
+ +
`; + }).join(''); + } catch (e) { grid.innerHTML = '
加载失败
'; } + }, + + _bindEvents() { + document.getElementById('btn-m-login')?.addEventListener('click', () => this._showLogin()); + document.getElementById('btn-m-upload')?.addEventListener('click', () => this._upload()); + document.getElementById('market-grid')?.addEventListener('click', async (e) => { + const btn = e.target.closest('.btn-m-install'); + if (!btn) return; + try { + const res = await fetch(`/api/market/install?id=${encodeURIComponent(btn.dataset.id)}&filename=${encodeURIComponent(btn.dataset.file)}`); + const data = await res.json(); + Toast.show(data.ok ? '安装成功' : '安装失败: ' + (data.error||''), data.ok ? 'success' : 'error'); + } catch (e) { Toast.show('安装失败', 'error'); } + }); + }, + + _updateUI() { + const loginBtn = document.getElementById('btn-m-login'); + const uploadBtn = document.getElementById('btn-m-upload'); + const statusEl = document.getElementById('m-status'); + if (!loginBtn || !uploadBtn) return; + if (this._marketToken) { + loginBtn.textContent = '已登录'; + loginBtn.className = 'btn btn-sm btn-secondary'; + uploadBtn.style.display = ''; + if (statusEl) statusEl.textContent = ' 已登录'; + } else { + loginBtn.textContent = '登录'; + loginBtn.className = 'btn btn-sm btn-primary'; + uploadBtn.style.display = 'none'; + if (statusEl) statusEl.textContent = ' 未登录'; + } + }, + + _upload() { + 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 fd = new FormData(); fd.append('file', file); + fd.append('name', file.name.replace('.tsd','')); + fd.append('category', 'community'); + fd.append('author', 'TurboSu User'); + fd.append('token', this._marketToken); + try { + const res = await fetch('/api/market/upload', { method: 'POST', body: fd }); + const data = await res.json(); + Toast.show(data.error ? '上传失败: ' + data.error : '上传成功', data.error ? 'error' : 'success'); + if (!data.error) this._loadItems(); + } catch (e) { Toast.show('上传失败', 'error'); } + }; input.click(); + }, + + _showLogin() { + const overlay = document.createElement('div'); + overlay.className = 'modal-overlay'; + overlay.innerHTML = ``; + document.body.appendChild(overlay); + + if (this._marketToken) { + overlay.querySelector('#ml-login').style.display = 'none'; + overlay.querySelector('#ml-register').style.display = 'none'; + overlay.querySelector('#ml-logout').style.display = ''; + overlay.querySelector('#ml-logout').addEventListener('click', () => { + this._marketToken = null; + localStorage.removeItem('turbosu-market-token'); + overlay.remove(); + this._updateUI(); + Toast.show('已退出', 'info'); + }); + } + + overlay.addEventListener('click', (e) => { if (e.target === overlay) overlay.remove(); }); + overlay.querySelector('#ml-login')?.addEventListener('click', async () => { + const email = document.getElementById('ml-email').value; + const pass = document.getElementById('ml-pass').value; + if (!email || !pass) { Toast.show('请输入邮箱和密码', 'error'); return; } + const res = await fetch('/api/market/auth', { method: 'POST', headers: {'Content-Type':'application/json'}, body: JSON.stringify({email, password: pass}) }); + const data = await res.json(); + if (data.token) { + this._marketToken = data.token; + localStorage.setItem('turbosu-market-token', data.token); + Toast.show('登录成功', 'success'); + overlay.remove(); + this._updateUI(); + } else { Toast.show(data.message || '登录失败', 'error'); } + }); + overlay.querySelector('#ml-register')?.addEventListener('click', async () => { + const email = document.getElementById('ml-email').value; + const pass = document.getElementById('ml-pass').value; + if (!email || !pass) { Toast.show('请输入邮箱和密码', 'error'); return; } + const res = await fetch('/api/market/register', { method: 'POST', headers: {'Content-Type':'application/json'}, body: JSON.stringify({email, password: pass, passwordConfirm: pass}) }); + const data = await res.json(); + Toast.show(data.error ? '注册失败' : '注册成功,请查收验证邮件', data.error ? 'error' : 'success'); + if (!data.error) overlay.remove(); + }); + }, + + _emptyHtml(msg) { + return `

${msg||'暂无'}

`; + }, + + _template() { + return `
+
+
+

仪表盘市场

+

浏览并安装社区分享的仪表盘 未登录

+
+
+ + +
+
+
+
`; + } +}; + +window.PageMarket = PageMarket; diff --git a/static/js/router.js b/static/js/router.js index 942e38e..03c3b44 100644 --- a/static/js/router.js +++ b/static/js/router.js @@ -5,6 +5,7 @@ const Router = { scene: PageScene, debug: PageDebug, record: PageRecord, + market: PageMarket, settings: PageSettings, }, _currentPage: 'home', diff --git a/templates/index.html b/templates/index.html index 909af37..0ebb990 100644 --- a/templates/index.html +++ b/templates/index.html @@ -58,6 +58,10 @@ 录制回放 + + + 市场 + 设置 @@ -97,6 +101,8 @@
+
+
@@ -115,6 +121,7 @@ +