feat(app-shell): 桌面端集成自动更新(electron-updater)

- 集成 electron-updater:检查更新/下载/安装/忽略此版本,并推送下载进度到前端

- 打包版启动后自动检查更新;托盘菜单支持手动检查

- preload 暴露 updater IPC + __brand 标记;前端新增更新弹窗与设置页版本/检查更新入口

- 补全发布配置:artifactName/publish;release workflow 增加上传 latest.yml
This commit is contained in:
2977094657
2026-02-18 16:53:50 +08:00
parent bcd9da4301
commit a14f8de6d0
9 changed files with 1010 additions and 39 deletions

View File

@@ -8,6 +8,22 @@
<NuxtPage />
</div>
</div>
<ClientOnly v-if="isDesktopUpdater">
<DesktopUpdateDialog
:open="desktopUpdate.open"
:info="desktopUpdate.info"
:is-downloading="desktopUpdate.isDownloading"
:ready-to-install="desktopUpdate.readyToInstall"
:progress="desktopUpdate.progress"
:error="desktopUpdate.error"
:has-ignore="true"
@close="desktopUpdate.dismiss"
@update="desktopUpdate.startUpdate"
@install="desktopUpdate.installUpdate"
@ignore="desktopUpdate.ignore"
/>
</ClientOnly>
</div>
</template>
@@ -16,12 +32,14 @@ import { useChatAccountsStore } from '~/stores/chatAccounts'
import { usePrivacyStore } from '~/stores/privacy'
const route = useRoute()
const desktopUpdate = useDesktopUpdate()
// In Electron the server/pre-render doesn't know about `window.wechatDesktop`.
// If we render different DOM on server vs client, Vue hydration will keep the
// server HTML (no patch) and the layout/CSS fixes won't apply reliably.
// So we detect desktop onMounted and update reactively.
const isDesktop = ref(false)
const isDesktopUpdater = ref(false)
const updateDprVar = () => {
const dpr = window.devicePixelRatio || 1
@@ -29,10 +47,22 @@ const updateDprVar = () => {
}
onMounted(() => {
isDesktop.value = !!window?.wechatDesktop
const isElectron = /electron/i.test(String(navigator.userAgent || ''))
const api = window?.wechatDesktop
isDesktop.value = isElectron && !!api
const brandOk = !api?.__brand || api.__brand === 'WeChatDataAnalysisDesktop'
isDesktopUpdater.value =
isDesktop.value &&
brandOk &&
typeof api?.checkForUpdates === 'function' &&
typeof api?.downloadAndInstall === 'function'
updateDprVar()
window.addEventListener('resize', updateDprVar)
if (isDesktopUpdater.value) {
void desktopUpdate.initListeners()
}
// Init global UI state.
const chatAccounts = useChatAccountsStore()
const privacy = usePrivacyStore()