修复: 本地存储写满静默丢数据——失败告警 + 导入图片体积闸门
This commit is contained in:
@@ -168,6 +168,11 @@ onMounted(() => {
|
|||||||
document.addEventListener('keydown', onKey)
|
document.addEventListener('keydown', onKey)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
/* 持久化失败告警 → toast(防止静默丢数据) */
|
||||||
|
watch(() => store.saveWarning.text, (txt) => {
|
||||||
|
if (txt) toast(txt)
|
||||||
|
})
|
||||||
|
|
||||||
/* 文库关闭时若在首页且从文库加载了 deck 则切入编辑 */
|
/* 文库关闭时若在首页且从文库加载了 deck 则切入编辑 */
|
||||||
watch(libraryVisible, (now, prev) => {
|
watch(libraryVisible, (now, prev) => {
|
||||||
if (prev && !now && deckLoaded.value) {
|
if (prev && !now && deckLoaded.value) {
|
||||||
|
|||||||
@@ -149,6 +149,18 @@ function doImport() {
|
|||||||
let insertedImages = 0
|
let insertedImages = 0
|
||||||
let insertedSlides = 0
|
let insertedSlides = 0
|
||||||
|
|
||||||
|
// 图片体积闸门:localStorage 约 5MB 字符上限,超限导入后刷新会丢图
|
||||||
|
if (images.length > 0) {
|
||||||
|
const imgChars = images.reduce((s, e) => s + (e.data?.length || 0), 0)
|
||||||
|
let deckChars = 0
|
||||||
|
try { deckChars = JSON.stringify(store.getDeck()).length } catch (e) { /* ignore */ }
|
||||||
|
const QUOTA_CHARS = 4_500_000
|
||||||
|
if (deckChars + imgChars > QUOTA_CHARS) {
|
||||||
|
emit('toast', '图片过大:导入后约 ' + ((deckChars + imgChars) / 1048576).toFixed(1) + 'MB,超本地存储上限(约 5MB),刷新可能丢失。请压缩图片、减少数量,或先清理文库/旧图')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 1. 图片 → 当前幻灯片
|
// 1. 图片 → 当前幻灯片
|
||||||
if (images.length > 0) {
|
if (images.length > 0) {
|
||||||
if (images.length === 1) {
|
if (images.length === 1) {
|
||||||
|
|||||||
+20
-4
@@ -100,10 +100,25 @@ let saveTimer: ReturnType<typeof setTimeout> | null = null
|
|||||||
/* ---------- 工具 ---------- */
|
/* ---------- 工具 ---------- */
|
||||||
function clone<T>(o: T): T { return JSON.parse(JSON.stringify(o)) }
|
function clone<T>(o: T): T { return JSON.parse(JSON.stringify(o)) }
|
||||||
|
|
||||||
|
/* ---------- 持久化告警(写入失败置位,供 UI 提示,避免静默丢数据) ---------- */
|
||||||
|
const saveWarning = reactive({ text: '' })
|
||||||
|
|
||||||
|
/** 安全写入 localStorage;失败时置告警(非静默),返回是否成功 */
|
||||||
|
function safeSet(key: string, val: string): boolean {
|
||||||
|
try { localStorage.setItem(key, val); saveWarning.text = ''; return true }
|
||||||
|
catch (e) {
|
||||||
|
if (!saveWarning.text) {
|
||||||
|
saveWarning.text = '本地存储空间不足,最新改动可能未保存。请导出 JSON 备份,或删除部分图片/文库释放空间'
|
||||||
|
}
|
||||||
|
console.error('[u-ppt] 保存失败:', key, e)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function scheduleSave() {
|
function scheduleSave() {
|
||||||
if (saveTimer) clearTimeout(saveTimer)
|
if (saveTimer) clearTimeout(saveTimer)
|
||||||
saveTimer = setTimeout(() => {
|
saveTimer = setTimeout(() => {
|
||||||
try { localStorage.setItem(LS_KEY, JSON.stringify(state.deck)) } catch (e) { /* 配额满等忽略 */ }
|
safeSet(LS_KEY, JSON.stringify(state.deck))
|
||||||
}, SAVE_DELAY)
|
}, SAVE_DELAY)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -475,7 +490,7 @@ function getCfg(): AiCfg {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function setCfg(cfg: AiCfg) {
|
function setCfg(cfg: AiCfg) {
|
||||||
localStorage.setItem(LS_CFG, JSON.stringify({ ...DEFAULT_CFG, ...cfg }))
|
safeSet(LS_CFG, JSON.stringify({ ...DEFAULT_CFG, ...cfg }))
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ---------- 自定义主题:AI 配色建议应用后存储 ---------- */
|
/* ---------- 自定义主题:AI 配色建议应用后存储 ---------- */
|
||||||
@@ -578,7 +593,7 @@ function getLibrary(): LibItem[] {
|
|||||||
try { return JSON.parse(localStorage.getItem(LIB_KEY) || '[]') || [] } catch (e) { return [] }
|
try { return JSON.parse(localStorage.getItem(LIB_KEY) || '[]') || [] } catch (e) { return [] }
|
||||||
}
|
}
|
||||||
function setLibrary(arr: LibItem[]) {
|
function setLibrary(arr: LibItem[]) {
|
||||||
try { localStorage.setItem(LIB_KEY, JSON.stringify(arr)) } catch (e) {}
|
safeSet(LIB_KEY, JSON.stringify(arr))
|
||||||
}
|
}
|
||||||
function getActiveLibId() { return state.activeLibId }
|
function getActiveLibId() { return state.activeLibId }
|
||||||
function setActive(id: string | null) {
|
function setActive(id: string | null) {
|
||||||
@@ -666,7 +681,7 @@ function getChat(chatId: string): ChatMessage[] {
|
|||||||
|
|
||||||
/** 写入某个 chatId 的会话 */
|
/** 写入某个 chatId 的会话 */
|
||||||
function setChat(chatId: string, msgs: ChatMessage[]) {
|
function setChat(chatId: string, msgs: ChatMessage[]) {
|
||||||
try { localStorage.setItem(CHAT_PREFIX + chatId, JSON.stringify(msgs)) } catch (e) {}
|
safeSet(CHAT_PREFIX + chatId, JSON.stringify(msgs))
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 清除某个 chatId 的会话 */
|
/** 清除某个 chatId 的会话 */
|
||||||
@@ -691,6 +706,7 @@ function recoverBackup(): boolean {
|
|||||||
export const store = {
|
export const store = {
|
||||||
// 状态(响应式)
|
// 状态(响应式)
|
||||||
state,
|
state,
|
||||||
|
saveWarning,
|
||||||
// computed
|
// computed
|
||||||
deck, slides, count, currentIndex, theme, selectedId, activeLibId,
|
deck, slides, count, currentIndex, theme, selectedId, activeLibId,
|
||||||
currentSlide, canUndo, canRedo,
|
currentSlide, canUndo, canRedo,
|
||||||
|
|||||||
Reference in New Issue
Block a user