fix: 打卡后即时推送 + 设置页展示自动同步失败原因

This commit is contained in:
root
2026-10-16 15:09:03 +08:00
parent aaf8ea602c
commit 04ae06ec1c
8 changed files with 50 additions and 34 deletions
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,2 +1,2 @@
const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["./web-B9VoRh8-.js","./index-WHahwity.js","./index-_AL8y0Nb.css"])))=>i.map(i=>d[i]); const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["./web-B_APDX-X.js","./index-BGM1Hwum.js","./index-8NYf16v1.css"])))=>i.map(i=>d[i]);
import{r as s,_ as i}from"./index-WHahwity.js";var t;(function(e){e.Documents="DOCUMENTS",e.Data="DATA",e.Library="LIBRARY",e.Cache="CACHE",e.External="EXTERNAL",e.ExternalStorage="EXTERNAL_STORAGE"})(t||(t={}));var a;(function(e){e.UTF8="utf8",e.ASCII="ascii",e.UTF16="utf16"})(a||(a={}));const E=s("Filesystem",{web:()=>i(()=>import("./web-B9VoRh8-.js"),__vite__mapDeps([0,1,2]),import.meta.url).then(e=>new e.FilesystemWeb)});export{t as Directory,a as Encoding,E as Filesystem}; import{r as s,_ as i}from"./index-BGM1Hwum.js";var t;(function(e){e.Documents="DOCUMENTS",e.Data="DATA",e.Library="LIBRARY",e.Cache="CACHE",e.External="EXTERNAL",e.ExternalStorage="EXTERNAL_STORAGE"})(t||(t={}));var a;(function(e){e.UTF8="utf8",e.ASCII="ascii",e.UTF16="utf16"})(a||(a={}));const E=s("Filesystem",{web:()=>i(()=>import("./web-B_APDX-X.js"),__vite__mapDeps([0,1,2]),import.meta.url).then(e=>new e.FilesystemWeb)});export{t as Directory,a as Encoding,E as Filesystem};
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+2 -2
View File
@@ -8,8 +8,8 @@
<link rel="icon" href="./icons/icon.svg" type="image/svg+xml" /> <link rel="icon" href="./icons/icon.svg" type="image/svg+xml" />
<link rel="apple-touch-icon" href="./icons/icon-192.png" /> <link rel="apple-touch-icon" href="./icons/icon-192.png" />
<title>实习工作日志</title> <title>实习工作日志</title>
<script type="module" crossorigin src="./assets/index-WHahwity.js"></script> <script type="module" crossorigin src="./assets/index-BGM1Hwum.js"></script>
<link rel="stylesheet" crossorigin href="./assets/index-_AL8y0Nb.css"> <link rel="stylesheet" crossorigin href="./assets/index-8NYf16v1.css">
</head> </head>
<body> <body>
<div id="app"></div> <div id="app"></div>
+8 -1
View File
@@ -1,5 +1,6 @@
// 多端同步引擎:全量差异对比 + 记录级新者胜 + 自然键去重 + 外键重映射 + 软删除墓碑 // 多端同步引擎:全量差异对比 + 记录级新者胜 + 自然键去重 + 外键重映射 + 软删除墓碑
// 实时性:本地变更 3 秒内推送;服务端变更通过 SSE 秒级通知其他端;60 秒轮询兜底 // 实时性:本地变更 3 秒内推送;服务端变更通过 SSE 秒级通知其他端;60 秒轮询兜底
import { ref } from 'vue'
import Dexie, { liveQuery } from 'dexie' import Dexie, { liveQuery } from 'dexie'
import { db } from '../db/schema' import { db } from '../db/schema'
import { getSetting, setSetting } from '../db/repo' import { getSetting, setSetting } from '../db/repo'
@@ -7,6 +8,9 @@ import { getCloudCreds } from '../db/auth'
import { nowLocalISO } from '../lib/date' import { nowLocalISO } from '../lib/date'
import { notifyAttendanceChanged } from '../lib/bus' import { notifyAttendanceChanged } from '../lib/bus'
// 最近一次自动同步的错误(设置页展示用)
export const syncLastError = ref('')
const TABLES = ['project', 'work_log', 'log_item', 'attendance', 'weekly_note', 'setting', 'memo', 'credential', 'transaction'] const TABLES = ['project', 'work_log', 'log_item', 'attendance', 'weekly_note', 'setting', 'memo', 'credential', 'transaction']
// 本地敏感或临时性的设置不参与同步 // 本地敏感或临时性的设置不参与同步
@@ -150,7 +154,10 @@ export async function autoSyncNow(): Promise<void> {
try { try {
if (!(await isSyncConfigured())) return if (!(await isSyncConfigured())) return
await runSync(await loadSyncConfig()) await runSync(await loadSyncConfig())
} catch { /* 自动同步静默失败,等待下次触发 */ } syncLastError.value = ''
} catch (e) {
syncLastError.value = e instanceof Error ? e.message : String(e)
}
} }
// 实时触发:本地任何数据变更 3 秒后自动推送(Dexie 4 核心 liveQuery,跨全部业务表) // 实时触发:本地任何数据变更 3 秒后自动推送(Dexie 4 核心 liveQuery,跨全部业务表)
+10 -1
View File
@@ -13,7 +13,7 @@ import { downloadCsv } from '../lib/csv'
import { hhmm, parseDateStr, todayStr } from '../lib/date' import { hhmm, parseDateStr, todayStr } from '../lib/date'
import { computeDay } from '../domain/attendance' import { computeDay } from '../domain/attendance'
import { changeLoginPassword, deleteCurrentUser, getSession } from '../db/auth' import { changeLoginPassword, deleteCurrentUser, getSession } from '../db/auth'
import { ensureCloudBinding, getSyncBase, getSyncLastTime, syncWithStoredConfig, type SyncResult } from '../sync/engine' import { ensureCloudBinding, getSyncBase, getSyncLastTime, syncWithStoredConfig, syncLastError, enableRealtimeSync, stopRealtime, type SyncResult } from '../sync/engine'
import { getCloudCreds } from '../db/auth' import { getCloudCreds } from '../db/auth'
import { showToast } from '../lib/toast' import { showToast } from '../lib/toast'
@@ -41,6 +41,7 @@ const syncBase = ref('')
const cloudUser = ref('') const cloudUser = ref('')
const syncStatus = ref('') const syncStatus = ref('')
const syncError = ref('') const syncError = ref('')
const autoSyncErr = ref('')
const syncing = ref(false) const syncing = ref(false)
const lastSync = ref('') const lastSync = ref('')
@@ -48,8 +49,12 @@ async function loadSyncInfo() {
syncBase.value = await getSyncBase() syncBase.value = await getSyncBase()
cloudUser.value = getCloudCreds()?.username || '' cloudUser.value = getCloudCreds()?.username || ''
lastSync.value = await getSyncLastTime() lastSync.value = await getSyncLastTime()
loadError.value = syncLastError.value
const conf = await isSyncConfigured()
autoOn.value = conf
} }
async function saveSyncConfig() { async function saveSyncConfig() {
await setSetting('sync_base_url', syncBase.value.trim()) await setSetting('sync_base_url', syncBase.value.trim())
showToast('同步地址已保存') showToast('同步地址已保存')
@@ -68,12 +73,14 @@ async function rebindCloud() {
async function doSyncNow() { async function doSyncNow() {
if (syncing.value) return if (syncing.value) return
syncError.value = '' syncError.value = ''
autoSyncErr.value = syncLastError.value
syncing.value = true syncing.value = true
try { try {
await saveSyncConfig() await saveSyncConfig()
const result: SyncResult = await syncWithStoredConfig() const result: SyncResult = await syncWithStoredConfig()
lastSync.value = result.at lastSync.value = result.at
syncStatus.value = '推送 ' + result.pushed + ' 条 · 拉取 ' + result.pulled + ' 条 · 删除 ' + result.removed + ' 条' syncStatus.value = '推送 ' + result.pushed + ' 条 · 拉取 ' + result.pulled + ' 条 · 删除 ' + result.removed + ' 条'
autoSyncErr.value = ''
showToast('同步完成') showToast('同步完成')
} catch (e) { } catch (e) {
syncError.value = e instanceof Error ? e.message : '同步失败' syncError.value = e instanceof Error ? e.message : '同步失败'
@@ -357,6 +364,8 @@ async function doImport() {
</div> </div>
<div v-if="syncStatus" class="muted acc-line">上次同步:{{ lastSync || '--' }} · {{ syncStatus }}</div> <div v-if="syncStatus" class="muted acc-line">上次同步:{{ lastSync || '--' }} · {{ syncStatus }}</div>
<div v-if="syncError" class="form-error">{{ syncError }}</div> <div v-if="syncError" class="form-error">{{ syncError }}</div>
<div v-if="autoSyncErr" class="form-error">最近自动同步失败:{{ autoSyncErr }}</div>
<div v-if="autoSyncErr" class="form-error">最近自动同步失败:{{ autoSyncErr }}(可点「立即同步」重试)</div>
</div> </div>
<div class="card section"> <div class="card section">