Files
TurboSu/static/js/pages/market.js
T

169 lines
8.6 KiB
JavaScript

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 = '<div style="text-align:center;padding:40px;color:var(--text-tertiary);">加载中...</div>';
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 `<div class="glass-card theme-card">
<div class="theme-card-preview">${icon}</div>
<div class="theme-card-info">
<div class="theme-card-name">${d.name||'Unnamed'}</div>
<div class="theme-card-meta">
<span>${d.category||''}</span><span>v${d.version||'1.0'}</span>
</div>
<div class="theme-card-meta" style="margin-top:4px">
<span>${d.author||''}</span><span style="color:var(--text-tertiary)">${d.downloads||0} 次</span>
</div>
<button class="btn btn-sm btn-primary btn-m-install" data-id="${d.id}" data-file="${d.file||''}" style="margin-top:8px;width:100%">安装</button>
</div></div>`;
}).join('');
} catch (e) { grid.innerHTML = '<div style="text-align:center;padding:40px;color:var(--danger);">加载失败</div>'; }
},
_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 = `<div class="modal" style="min-width:360px;max-width:400px">
<h3>市场账户</h3>
<div class="form-group"><label>邮箱</label><input id="ml-email" type="email" placeholder="your@email.com"></div>
<div class="form-group"><label>密码</label><input id="ml-pass" type="password"></div>
<div class="modal-actions">
<button class="btn btn-secondary btn-sm" id="ml-logout" style="display:none;margin-right:auto">退出</button>
<button class="btn btn-secondary btn-sm" id="ml-register">注册</button>
<button class="btn btn-primary btn-sm" id="ml-login">登录</button>
</div></div>`;
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 `<div class="empty-state" style="grid-column:1/-1"><h4>${msg||'暂无'}</h4></div>`;
},
_template() {
return `<div class="animated">
<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:20px">
<div>
<h2 style="font-size:24px;font-weight:700">仪表盘市场</h2>
<p style="color:var(--text-secondary);font-size:14px;margin-top:4px">浏览并安装社区分享的仪表盘 <span id="m-status" style="color:var(--text-tertiary)"> 未登录</span></p>
</div>
<div style="display:flex;gap:8px">
<button id="btn-m-upload" class="btn btn-sm btn-secondary" style="display:none">发布到市场</button>
<button id="btn-m-login" class="btn btn-sm btn-primary">登录</button>
</div>
</div>
<div id="market-grid" class="theme-grid"></div>
</div>`;
}
};
window.PageMarket = PageMarket;