Web 端本地缓存、首页与会员弹窗体验优化
新增 IndexedDB 与最近文件 L1 缓存、云保存/打开链路;首页支持下载与清空本地缓存;修复清空 storage 后语言初始化报错;优化 design2 背景加载与侧栏标签样式;完善会员激活/支付弹层样式;后端补充文件缩略图字段与 thumb 接口。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,217 @@
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
var RECENT_KEY = 'soondesign_recent';
|
||||
var MIGRATED_KEY = 'soondesign_recent_migrated';
|
||||
var HISTORY_KEY = 'soondesign_history';
|
||||
var MAX_ITEMS = 20;
|
||||
|
||||
function nowTimeStr() {
|
||||
var d = new Date();
|
||||
function pad(n) { return n < 10 ? '0' + n : String(n); }
|
||||
return d.getFullYear() + '-' + pad(d.getMonth() + 1) + '-' + pad(d.getDate()) + ' ' +
|
||||
pad(d.getHours()) + ':' + pad(d.getMinutes()) + ':' + pad(d.getSeconds());
|
||||
}
|
||||
|
||||
function readRaw() {
|
||||
try {
|
||||
var raw = localStorage.getItem(RECENT_KEY);
|
||||
if (!raw) return { version: 1, items: [] };
|
||||
var j = JSON.parse(raw);
|
||||
if (!j || !Array.isArray(j.items)) return { version: 1, items: [] };
|
||||
return j;
|
||||
} catch (e) {
|
||||
return { version: 1, items: [] };
|
||||
}
|
||||
}
|
||||
|
||||
function writeRaw(data) {
|
||||
try {
|
||||
localStorage.setItem(RECENT_KEY, JSON.stringify(data));
|
||||
return true;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function soonRecentKindFromKey(key) {
|
||||
if (!key) return 'local';
|
||||
if (key.indexOf('soondesign_file:') === 0) return 'cloud';
|
||||
if (key.indexOf('soondesign_template:') === 0) return 'template';
|
||||
return 'local';
|
||||
}
|
||||
|
||||
function parseFileId(key) {
|
||||
var m = String(key).match(/^soondesign_file:(\d+)/);
|
||||
return m ? parseInt(m[1], 10) : null;
|
||||
}
|
||||
|
||||
function soonRecentList() {
|
||||
return readRaw().items.slice();
|
||||
}
|
||||
|
||||
function soonRecentRemove(key) {
|
||||
if (!key) return;
|
||||
var data = readRaw();
|
||||
data.items = data.items.filter(function (it) { return it.key !== key; });
|
||||
writeRaw(data);
|
||||
}
|
||||
|
||||
function soonRecentUpsert(item) {
|
||||
if (!item || !item.key) return;
|
||||
var key = item.key;
|
||||
var kind = item.kind || soonRecentKindFromKey(key);
|
||||
var type = item.type != null ? item.type : 1;
|
||||
var name = item.name || '';
|
||||
if (!name && typeof window.soonDisplayFileName === 'function') {
|
||||
name = window.soonDisplayFileName(key);
|
||||
}
|
||||
var entry = {
|
||||
key: key,
|
||||
name: name || 'design.soon',
|
||||
type: type,
|
||||
kind: kind,
|
||||
fileId: item.fileId != null ? item.fileId : parseFileId(key),
|
||||
thumbRef: item.thumbRef || key,
|
||||
time: item.time || nowTimeStr()
|
||||
};
|
||||
var data = readRaw();
|
||||
data.items = data.items.filter(function (it) { return it.key !== key; });
|
||||
data.items.unshift(entry);
|
||||
if (data.items.length > MAX_ITEMS) data.items = data.items.slice(0, MAX_ITEMS);
|
||||
writeRaw(data);
|
||||
}
|
||||
|
||||
function soonRecentUpsertFromOpen(fileKey, json) {
|
||||
if (!fileKey) return;
|
||||
var type = 1;
|
||||
if (json) {
|
||||
type = json.soonType ? json.soonType : (json.backBlackPic ? 2 : 1);
|
||||
}
|
||||
var name = '';
|
||||
if (typeof window.soonDisplayFileName === 'function') name = window.soonDisplayFileName(fileKey);
|
||||
var tplMeta = window._soonTemplateMeta;
|
||||
if (tplMeta && tplMeta.name && fileKey.indexOf('soondesign_template:') === 0) {
|
||||
name = tplMeta.name;
|
||||
if (tplMeta.type) type = tplMeta.type;
|
||||
}
|
||||
var fileMeta = window._soonFileMeta;
|
||||
if (fileMeta && fileMeta.name && fileKey.indexOf('soondesign_file:') === 0) {
|
||||
name = fileMeta.name;
|
||||
}
|
||||
soonRecentUpsert({
|
||||
key: fileKey,
|
||||
name: name,
|
||||
type: type,
|
||||
kind: soonRecentKindFromKey(fileKey),
|
||||
fileId: parseFileId(fileKey),
|
||||
thumbRef: fileKey,
|
||||
time: nowTimeStr(),
|
||||
updated_at: (tplMeta && tplMeta.updated_at) ? tplMeta.updated_at : undefined
|
||||
});
|
||||
}
|
||||
|
||||
function soonRecentMigrateFromHistory() {
|
||||
try {
|
||||
if (localStorage.getItem(MIGRATED_KEY) === '1') return;
|
||||
} catch (e) { return; }
|
||||
var histRaw;
|
||||
try {
|
||||
histRaw = localStorage.getItem(HISTORY_KEY);
|
||||
} catch (e) { return; }
|
||||
if (!histRaw) {
|
||||
try { localStorage.setItem(MIGRATED_KEY, '1'); } catch (e2) { /* ignore */ }
|
||||
return;
|
||||
}
|
||||
var hist;
|
||||
try {
|
||||
hist = JSON.parse(histRaw);
|
||||
} catch (e) {
|
||||
try { localStorage.setItem(MIGRATED_KEY, '1'); } catch (e2) { /* ignore */ }
|
||||
return;
|
||||
}
|
||||
var list = (hist && Array.isArray(hist.history)) ? hist.history : [];
|
||||
list.forEach(function (h) {
|
||||
if (!h || !h.path) return;
|
||||
soonRecentUpsert({
|
||||
key: h.path,
|
||||
name: typeof window.soonDisplayFileName === 'function' ? window.soonDisplayFileName(h.path) : h.path,
|
||||
type: h.type || 1,
|
||||
kind: soonRecentKindFromKey(h.path),
|
||||
fileId: parseFileId(h.path),
|
||||
thumbRef: h.path,
|
||||
time: h.time || nowTimeStr()
|
||||
});
|
||||
});
|
||||
try { localStorage.setItem(MIGRATED_KEY, '1'); } catch (e) { /* ignore */ }
|
||||
}
|
||||
|
||||
function soonRecentEnrichFromCloud(items, cloudItems) {
|
||||
if (!items || !items.length || !cloudItems || !cloudItems.length) return items;
|
||||
var byId = {};
|
||||
cloudItems.forEach(function (c) {
|
||||
if (c && c.id != null) byId[c.id] = c;
|
||||
});
|
||||
return items.map(function (it) {
|
||||
if (!it.fileId || !byId[it.fileId]) return it;
|
||||
var c = byId[it.fileId];
|
||||
var copy = Object.assign({}, it);
|
||||
if (c.name) copy.name = c.name;
|
||||
var curKey = it.filePath || it.key;
|
||||
if (c.version != null && typeof window.soonMakeFileKey === 'function') {
|
||||
var newKey = window.soonMakeFileKey(c.id, c.version);
|
||||
if (newKey !== curKey) {
|
||||
soonRecentRemove(curKey);
|
||||
copy.filePath = newKey;
|
||||
copy.key = newKey;
|
||||
copy.thumbRef = newKey;
|
||||
soonRecentUpsert({
|
||||
key: newKey,
|
||||
name: copy.name,
|
||||
type: copy.type,
|
||||
kind: soonRecentKindFromKey(newKey),
|
||||
fileId: c.id,
|
||||
thumbRef: newKey
|
||||
});
|
||||
}
|
||||
}
|
||||
return copy;
|
||||
});
|
||||
}
|
||||
|
||||
function soonRecentOnCloudSave(res, prevKey, type) {
|
||||
if (!res || !res.fileKey) return;
|
||||
var dropKeys = {};
|
||||
if (prevKey && prevKey !== res.fileKey) dropKeys[prevKey] = 1;
|
||||
if (res.fileId != null) {
|
||||
readRaw().items.forEach(function (it) {
|
||||
if (it.fileId === res.fileId && it.key !== res.fileKey) dropKeys[it.key] = 1;
|
||||
});
|
||||
}
|
||||
Object.keys(dropKeys).forEach(function (k) {
|
||||
soonRecentRemove(k);
|
||||
if (typeof window.soonLocalRemove === 'function') window.soonLocalRemove(k);
|
||||
});
|
||||
var name = res.name || '';
|
||||
if (!name && typeof window.soonDisplayFileName === 'function') {
|
||||
name = window.soonDisplayFileName(res.fileKey);
|
||||
}
|
||||
soonRecentUpsert({
|
||||
key: res.fileKey,
|
||||
name: name || 'design.soon',
|
||||
type: type != null ? type : 1,
|
||||
kind: soonRecentKindFromKey(res.fileKey),
|
||||
fileId: res.fileId,
|
||||
thumbRef: res.fileKey
|
||||
});
|
||||
}
|
||||
|
||||
window.soonRecentList = soonRecentList;
|
||||
window.soonRecentRemove = soonRecentRemove;
|
||||
window.soonRecentUpsert = soonRecentUpsert;
|
||||
window.soonRecentUpsertFromOpen = soonRecentUpsertFromOpen;
|
||||
window.soonRecentMigrateFromHistory = soonRecentMigrateFromHistory;
|
||||
window.soonRecentKindFromKey = soonRecentKindFromKey;
|
||||
window.soonRecentEnrichFromCloud = soonRecentEnrichFromCloud;
|
||||
window.soonRecentOnCloudSave = soonRecentOnCloudSave;
|
||||
})();
|
||||
Reference in New Issue
Block a user