fix: 同步后刷新考勤视图 + 全局错误捕获层(白屏转错误报告)

This commit is contained in:
root
2026-10-16 14:10:21 +08:00
parent 8c9f2f0c8b
commit 8a0fde507f
7 changed files with 58 additions and 16 deletions
@@ -1,2 +1,2 @@
const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["./web-C1N1mu_I.js","./index-CocrpELo.js","./index-vDZJrZrZ.css"])))=>i.map(i=>d[i]); const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["./web-BiiYhJWj.js","./index-DJcQg2vq.js","./index-vDZJrZrZ.css"])))=>i.map(i=>d[i]);
import{r as s,_ as i}from"./index-CocrpELo.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-C1N1mu_I.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-DJcQg2vq.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-BiiYhJWj.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
+1 -1
View File
@@ -8,7 +8,7 @@
<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-CocrpELo.js"></script> <script type="module" crossorigin src="./assets/index-DJcQg2vq.js"></script>
<link rel="stylesheet" crossorigin href="./assets/index-vDZJrZrZ.css"> <link rel="stylesheet" crossorigin href="./assets/index-vDZJrZrZ.css">
</head> </head>
<body> <body>
+20
View File
@@ -0,0 +1,20 @@
// 全局错误浮层:捕获会导致白屏的致命错误并显示在屏幕上,
// 便于远程定位(点击浮层空白处关闭,可叠加多条)
export function showFatalError(title: string, detail: string): void {
try {
let el = document.getElementById('fatal-overlay')
if (!el) {
el = document.createElement('div')
el.id = 'fatal-overlay'
el.style.cssText = 'position:fixed;inset:0;z-index:99999;background:#FFFFFF;color:#1D2129;padding:20px;overflow:auto;font:13px/1.7 -apple-system,PingFang SC,Microsoft YaHei,sans-serif;'
el.addEventListener('click', e => {
if (e.target === el && el.parentElement) el.parentElement.removeChild(el)
})
document.body.appendChild(el)
}
const box = document.createElement('div')
box.style.cssText = 'border:1px solid #E5484D;border-radius:8px;padding:12px;margin-bottom:12px;word-break:break-all;white-space:pre-wrap;'
box.textContent = title + '\n' + detail
el.appendChild(box)
} catch { /* 浮层失败时保持静默 */ }
}
+19 -1
View File
@@ -1,8 +1,26 @@
import { createApp } from 'vue' import { createApp } from 'vue'
import App from './App.vue' import App from './App.vue'
import './styles/tokens.css' import './styles/tokens.css'
import { showFatalError } from './lib/errOverlay'
createApp(App).mount('#app') const app = createApp(App)
// 捕获所有会导致白屏的渲染与运行错误,转为屏幕上的错误报告
app.config.errorHandler = (err, _inst, info) => {
const e = err as Error
showFatalError('应用内部错误(点击空白处关闭,请将以下内容发送给开发者)', (e && (e.stack || e.message)) || String(err) + '\n触发位置: ' + info)
}
window.addEventListener('unhandledrejection', e => {
const reason = e.reason as Error
showFatalError('未处理的异步错误(点击空白处关闭)', (reason && (reason.stack || reason.message)) || String(e.reason))
})
window.addEventListener('error', e => {
if (e.message && e.message !== 'ResizeObserver loop limit exceeded') {
showFatalError('运行错误(点击空白处关闭)', e.message)
}
})
app.mount('#app')
// PWA:注册 Service Worker,安装为应用后可离线使用 // PWA:注册 Service Worker,安装为应用后可离线使用
if ('serviceWorker' in navigator && location.protocol.startsWith('http')) { if ('serviceWorker' in navigator && location.protocol.startsWith('http')) {
+3 -1
View File
@@ -5,6 +5,7 @@ import { db } from '../db/schema'
import { getSetting, setSetting } from '../db/repo' import { getSetting, setSetting } from '../db/repo'
import { getCloudCreds } from '../db/auth' import { getCloudCreds } from '../db/auth'
import { nowLocalISO } from '../lib/date' import { nowLocalISO } from '../lib/date'
import { notifyAttendanceChanged } from '../lib/bus'
export const DEFAULT_SYNC_BASE = 'https://daywk.linyux.cn' export const DEFAULT_SYNC_BASE = 'https://daywk.linyux.cn'
@@ -344,11 +345,12 @@ async function doRunSync(config: SyncConfig): Promise<SyncResult> {
} }
} }
// 4. 重建镜像 + 建立 SSE 实时通道 // 4. 重建镜像 + 建立 SSE 实时通道 + 广播考勤变更
await db.sync_mirror.clear() await db.sync_mirror.clear()
if (finalMirror.length) await db.sync_mirror.bulkAdd(finalMirror.map(m => ({ ...m }))) if (finalMirror.length) await db.sync_mirror.bulkAdd(finalMirror.map(m => ({ ...m })))
ensureSSE(base, token) ensureSSE(base, token)
const at = nowLocalISO() const at = nowLocalISO()
await setSetting('sync_last_time', at) await setSetting('sync_last_time', at)
if (pulled > 0 || removed > 0) notifyAttendanceChanged()
return { pushed, pulled, removed, at } return { pushed, pulled, removed, at }
} }