From 5071fc7dd31377e46404d40f6c0148d57533aefd Mon Sep 17 00:00:00 2001 From: OpenClaw Agent Date: Fri, 4 Sep 2026 14:03:13 +0800 Subject: [PATCH] snapshot: index.html @ 20260904_140313 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 由 index.html.bak.20260904_140313 重建的历史快照。 时间取自备份文件名(备份生成时刻,Asia/Shanghai)。 此提交只含 index.html,当时其余文件的状态没有对应备份,无法还原。 --- index.html | 107 ++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 90 insertions(+), 17 deletions(-) diff --git a/index.html b/index.html index de526cd..314c019 100644 --- a/index.html +++ b/index.html @@ -768,6 +768,7 @@

{{ sync.lastError }}

+ @@ -1027,6 +1028,67 @@ + + +
+
+ +
+

+ 本机与服务器有冲突 +

+

+ 下面这{{ mergeRows.length }}项两边内容不一样,需要你决定保留哪份。其余数据两边都会保留,不会丢。 +

+
+ +
+
+

本机

+

{{ mergeInfo ? mergeInfo.localDays : 0 }} 天

+
+ +
+

服务器

+

{{ mergeInfo ? mergeInfo.remoteDays : 0 }} 天

+
+
+ +
+
+

{{ r.label }}

+
+
+ 本机 + {{ r.local }} +
+
+ 服务器 + {{ r.remote }} +
+
+
+
+ +
+ + + +
+

合并前会自动留一份本机快照。两边独有的数据一律双向保留。

+
+
+
+
@@ -1695,7 +1757,7 @@ createApp({ // ===== 云端同步(离线优先:本地 localStorage 仍是唯一真相,同步只是额外把数据推到服务器)===== const sync = reactive({ status: 'idle', user: null, pending: 0, lastSyncAt: null, lastError: '', - serverUsers: null, apiBase: '', busy: false + serverUsers: null, apiBase: '', busy: false, hold: null }); const syncPanelOpen = ref(false); const loginForm = ref({ username: '', password: '' }); @@ -1743,21 +1805,27 @@ createApp({ return rows; }); - const syncStatusText = computed(() => ({ - idle: '未登录·仅存本机', - ready: sync.pending > 0 ? `待上传 ${sync.pending} 项` : '已同步', - syncing: '同步中…', - offline: '连不上服务器·本地照常用', - error: '同步出错' - })[sync.status] || sync.status); + const syncStatusText = computed(() => { + if (sync.hold) return '有冲突·已暂停同步'; + return ({ + idle: '未登录·仅存本机', + ready: sync.pending > 0 ? `待上传 ${sync.pending} 项` : '已同步', + syncing: '同步中…', + offline: '连不上服务器·本地照常用', + error: '同步出错' + })[sync.status] || sync.status; + }); - const syncStatusClass = computed(() => ({ - idle: 'text-slate-500 bg-slate-100', - ready: sync.pending > 0 ? 'text-amber-700 bg-amber-100' : 'text-emerald-700 bg-emerald-100', - syncing: 'text-indigo-700 bg-indigo-100', - offline: 'text-amber-700 bg-amber-100', - error: 'text-rose-700 bg-rose-100' - })[sync.status] || 'text-slate-500 bg-slate-100'); + const syncStatusClass = computed(() => { + if (sync.hold) return 'text-amber-700 bg-amber-100'; + return ({ + idle: 'text-slate-500 bg-slate-100', + ready: sync.pending > 0 ? 'text-amber-700 bg-amber-100' : 'text-emerald-700 bg-emerald-100', + syncing: 'text-indigo-700 bg-indigo-100', + offline: 'text-amber-700 bg-amber-100', + error: 'text-rose-700 bg-rose-100' + })[sync.status] || 'text-slate-500 bg-slate-100'; + }); const syncTimeText = computed(() => { if (!sync.lastSyncAt) return ''; @@ -1837,6 +1905,7 @@ createApp({ async function runMerge(diff, winner) { // 合并前留一份本机快照。远端值落地是不可逆的,出意外至少数据还在。 window.WHTSync.snapshotLocal('login-merge-' + winner); + window.WHTSync.releaseSync(); // 用户已拍板,解除同步冻结 const excludeRecords = winner === 'remote' ? diff.conflict : []; const excludeAdjust = winner === 'remote' ? diff.adjustConflict : []; @@ -1865,10 +1934,13 @@ createApp({ mergeInfo.value = null; }); - // 先不合并:保持登录但不动任何数据。同步面板会留一个入口重新进来。 + // 先不合并:保持登录但冻结自动同步。 + // 必须冻结 —— 否则下一次改动触发的 syncNow 会把远端值静默盖到本机冲突日上, + // 用户看到的就是「我选了不处理,数据却自己变了」。 const deferMerge = () => { mergeOpen.value = false; - showToast('已跳过合并,本机数据未改动'); + window.WHTSync.holdSync('login-conflict'); + showToast('已暂停自动同步,本机数据未改动'); }; const reopenMerge = () => withBusy(async () => { @@ -1946,6 +2018,7 @@ createApp({ sync.lastSyncAt = s.lastSyncAt; sync.lastError = s.lastError; sync.serverUsers = s.serverUsers; + sync.hold = s.hold; }); sync.apiBase = window.WHTSync.getApiBase() === location.origin ? '' : window.WHTSync.getApiBase(); // 探一下服务端在不在、有没有账号,决定设置页该显示「创建账号」还是「登录」