Files
24kycj 4ce4486b26 Web 端本地缓存、首页与会员弹窗体验优化
新增 IndexedDB 与最近文件 L1 缓存、云保存/打开链路;首页支持下载与清空本地缓存;修复清空 storage 后语言初始化报错;优化 design2 背景加载与侧栏标签样式;完善会员激活/支付弹层样式;后端补充文件缩略图字段与 thumb 接口。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-11 11:05:44 +08:00

106 lines
4.6 KiB
JavaScript

(function () {
'use strict';
var STANDARD_LINKS = [
{ id: 'home', href: 'index.web.html', label: '设计首页', show: 'always' },
{ id: 'admin', href: 'admin/index.html', label: '管理', show: 'admin', elId: 'auth_admin' },
];
function esc(s) {
if (s == null) return '';
return String(s).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/"/g, '&quot;');
}
function authBlock() {
return '<div class="soon-portal-topbar__guest" id="auth_guest">' +
'<a href="login.web.html" id="auth_login" class="soon-btn soon-btn--sm">登录</a>' +
'<a href="register.web.html" id="auth_register" class="soon-btn soon-btn--sm soon-btn--ghost">注册</a>' +
'</div>' +
'<div class="soon-portal-topbar__account" id="auth_account" style="display:none">' +
'<span id="auth_avatar" class="soon-portal-avatar" style="display:none"></span>' +
'<span id="auth_identity" class="soon-portal-identity" style="display:none"></span>' +
'</div>' +
'<nav class="soon-portal-topbar__session" id="auth_session" style="display:none" aria-label="账户">' +
'<a href="admin/index.html" id="auth_admin" class="soon-portal-topbar__link" style="display:none">管理</a>' +
'<a href="#" id="auth_logout" class="soon-portal-topbar__link" style="display:none">退出</a>' +
'</nav>';
}
function renderLinks(links, active) {
return links.map(function (lnk) {
if (lnk.id === 'admin') return '';
var cls = 'soon-portal-topbar__link' + (active === lnk.id ? ' is-active' : '');
return '<a href="' + esc(lnk.href) + '" class="' + cls + '">' + esc(lnk.label) + '</a>';
}).join('');
}
function toolsBlock(lang, linksHtml) {
return '<div class="soon-portal-topbar__tools">' +
lang +
(linksHtml ? '<nav class="soon-portal-topbar__links" aria-label="导航">' + linksHtml +
'<a href="#" id="auth_clear_cache" class="soon-portal-topbar__link">清除缓存</a></nav>' : '') +
'</div>';
}
/**
* @param {object} opts
* @param {string} [opts.variant] standard | auth | design
* @param {string} [opts.active] home
* @param {string} [opts.extraClass]
*/
function html(opts) {
opts = opts || {};
var base = opts.assetBase || '../';
var variant = opts.variant || 'standard';
var extraCls = opts.extraClass ? ' ' + opts.extraClass : '';
if (variant === 'auth') {
return '<header class="soon-portal-topbar soon-auth-topbar' + extraCls + '">' +
'<div class="soon-portal-topbar__brand"><a href="index.web.html">' +
'<img src="' + esc(base) + 'assets/images/logo.png" alt=""><span>SoonDesign</span></a></div>' +
'<div class="soon-portal-topbar__actions">' +
toolsBlock('', '<a href="index.web.html" class="soon-portal-topbar__link">返回首页</a>') +
'</div></header>';
}
if (variant === 'design') {
return '<header class="soon-portal-topbar soon-design-portal-bar' + extraCls + '">' +
'<div class="soon-portal-topbar__brand"><a href="index.web.html">' +
'<img src="' + esc(base) + 'assets/images/logo.png" alt=""><span>SoonDesign</span></a></div>' +
'<div class="soon-portal-topbar__actions">' +
toolsBlock('', '<a href="index.web.html" class="soon-portal-topbar__link">返回首页</a>') +
authBlock() + '</div></header>';
}
var active = opts.active || '';
var showLang = opts.showLanguage !== false;
var links = opts.links || STANDARD_LINKS;
var lang = showLang
? '<select id="language_select" class="soon-portal-topbar__lang" aria-label="语言">' +
'<option value="zh">中文-简体</option><option value="ozh">中文-繁體</option><option value="en">English</option></select>'
: '';
return '<header class="soon-portal-topbar' + extraCls + '">' +
'<div class="soon-portal-topbar__brand"><a href="' + esc(opts.brandHref || 'index.web.html') + '">' +
'<img src="' + esc(base) + 'assets/images/logo.png" alt=""><span>SoonDesign</span></a></div>' +
'<div class="soon-portal-topbar__actions">' +
toolsBlock(lang, renderLinks(links, active)) +
authBlock() + '</div></header>';
}
function mount(selector, opts) {
var el = typeof selector === 'string' ? document.querySelector(selector) : selector;
if (!el) return;
el.innerHTML = html(opts);
if (opts && opts.initLang !== false) {
var sel = document.getElementById('language_select');
if (sel) {
sel.value = localStorage.getItem('lang') || 'zh';
sel.onchange = function () { localStorage.setItem('lang', sel.value); };
}
}
}
window.SoonPortalTopbar = { html: html, mount: mount };
})();