From 1614f299c77cd523f60048b8541a97b198973057 Mon Sep 17 00:00:00 2001 From: stevelliu Date: Thu, 26 Mar 2026 17:35:22 +0800 Subject: [PATCH] =?UTF-8?q?feat(desktop):=20v0.1.6=20=E9=A1=B6=E6=A0=8F?= =?UTF-8?q?=E7=8B=AC=E7=AB=8B=E8=BD=AF=E4=BB=B6=E6=9B=B4=E6=96=B0=E4=B8=8E?= =?UTF-8?q?=E4=B8=8B=E8=BD=BD=E8=BF=9B=E5=BA=A6=E6=9D=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - AppUpdateModal + useAppUpdater;顶栏入口;关于页移除重复检查更新 - 版本与双 CHANGELOG、app-changelog 同步至 0.1.6 Made-with: Cursor --- CHANGELOG.md | 8 + apps/desktop/package.json | 2 +- apps/desktop/src-tauri/Cargo.toml | 2 +- apps/desktop/src-tauri/tauri.conf.json | 2 +- .../desktop/src/components/AppUpdateModal.vue | 179 ++++++++++++++++++ apps/desktop/src/components/SettingsModal.vue | 103 +--------- apps/desktop/src/components/TopBar.vue | 13 ++ apps/desktop/src/composables/useAppUpdater.ts | 139 ++++++++++++++ apps/desktop/src/data/app-changelog.md | 8 + docs/CHANGELOG.md | 9 + 10 files changed, 363 insertions(+), 102 deletions(-) create mode 100644 apps/desktop/src/components/AppUpdateModal.vue create mode 100644 apps/desktop/src/composables/useAppUpdater.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 71883ec..180f4d0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,14 @@ > 面向使用者的更新说明。实现细节见 `docs/CHANGELOG.md`。 +## 0.1.6(2026-03-25) + +### 体验改进 + +- **软件更新**:顶栏增加独立入口「检查更新」,与设置解耦;下载新版本时展示**进度条**(已知大小时显示百分比)。 + +--- + ## 0.1.5(2026-03-25) ### 体验改进 diff --git a/apps/desktop/package.json b/apps/desktop/package.json index a9bd243..a8932d3 100644 --- a/apps/desktop/package.json +++ b/apps/desktop/package.json @@ -1,7 +1,7 @@ { "name": "@xunji/desktop", "private": true, - "version": "0.1.5", + "version": "0.1.6", "type": "module", "scripts": { "dev": "vite", diff --git a/apps/desktop/src-tauri/Cargo.toml b/apps/desktop/src-tauri/Cargo.toml index e372c87..fe28351 100644 --- a/apps/desktop/src-tauri/Cargo.toml +++ b/apps/desktop/src-tauri/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "xunji-desktop" -version = "0.1.5" +version = "0.1.6" description = "寻迹 - AI 编程知识管理平台" authors = ["XunJi"] edition = "2021" diff --git a/apps/desktop/src-tauri/tauri.conf.json b/apps/desktop/src-tauri/tauri.conf.json index 28b4d4d..7982c57 100644 --- a/apps/desktop/src-tauri/tauri.conf.json +++ b/apps/desktop/src-tauri/tauri.conf.json @@ -1,7 +1,7 @@ { "$schema": "https://raw.githubusercontent.com/tauri-apps/tauri/dev/crates/tauri-cli/schema.json", "productName": "XunJi", - "version": "0.1.5", + "version": "0.1.6", "identifier": "com.xunji.app", "build": { "frontendDist": "../dist", diff --git a/apps/desktop/src/components/AppUpdateModal.vue b/apps/desktop/src/components/AppUpdateModal.vue new file mode 100644 index 0000000..2910a4f --- /dev/null +++ b/apps/desktop/src/components/AppUpdateModal.vue @@ -0,0 +1,179 @@ + + + diff --git a/apps/desktop/src/components/SettingsModal.vue b/apps/desktop/src/components/SettingsModal.vue index 08b4990..507f8fd 100644 --- a/apps/desktop/src/components/SettingsModal.vue +++ b/apps/desktop/src/components/SettingsModal.vue @@ -11,6 +11,7 @@ * 3. 关于寻迹 * - 应用名、版本号、Tauri 运行时版本、Bundle Identifier(来自 tauri.conf / 打包元数据) * - 各版本变更说明:`src/data/app-changelog.md`(与根目录 `CHANGELOG.md` 同步,Vite `?raw` 打入包) + * - 软件更新见顶栏「检查更新」,本页不再重复入口 */ import { ref, computed, watch } from 'vue' import { @@ -18,11 +19,7 @@ import { NInput, NInputNumber, NSelect, NSwitch, NButton, NSpace, NAlert, NDivider, NSpin, NTag, NDescriptions, NDescriptionsItem, - useDialog, - useMessage, } from 'naive-ui' -import { check } from '@tauri-apps/plugin-updater' -import { relaunch } from '@tauri-apps/plugin-process' import { getIdentifier, getTauriVersion, getVersion } from '@tauri-apps/api/app' import MarkdownRenderer from './MarkdownRenderer.vue' import appChangelogMd from '../data/app-changelog.md?raw' @@ -34,9 +31,6 @@ const emit = defineEmits<{ 'update:show': [v: boolean] }>() -const message = useMessage() -const dialog = useDialog() - /** 用户可见品牌名(与窗口标题、macOS 显示名一致;GitHub Release 安装包前缀仍为 tauri.conf 的 productName「XunJi」) */ const APP_DISPLAY_NAME = '寻迹' @@ -65,10 +59,6 @@ const aboutMeta = ref<{ } | null>(null) const aboutLoading = ref(false) const aboutError = ref('') -/** 检查/下载更新进行中(避免重复点击) */ -const updateBusy = ref(false) -/** 下载进度文案,供关于页展示 */ -const updateStatusText = ref('') // 工作副本:从后端加载后复制,保存时提交 const workingConfig = ref(null) @@ -234,75 +224,6 @@ function onClose() { emit('update:show', false) } -/** - * 应用内更新:调用 tauri-plugin-updater(与 tauri.conf plugins.updater 一致)。 - * 浏览器预览无 Tauri 时会抛错,提示用户在桌面端使用。 - */ -async function onCheckUpdate() { - if (updateBusy.value) return - updateBusy.value = true - updateStatusText.value = '' - try { - const update = await check() - if (!update) { - message.success('当前已是最新版本') - return - } - const notes = update.body?.trim() ? `\n\n${update.body}` : '' - dialog.warning({ - title: '发现新版本', - content: `最新版本:${update.version}${notes}`, - positiveText: '下载并安装', - negativeText: '稍后', - onPositiveClick: async () => { - updateBusy.value = true - let total = 0 - let downloaded = 0 - try { - await update.downloadAndInstall((event) => { - switch (event.event) { - case 'Started': - total = event.data.contentLength ?? 0 - updateStatusText.value = '正在下载更新…' - break - case 'Progress': - downloaded += event.data.chunkLength - if (total > 0) { - const pct = Math.min(100, Math.round((downloaded / total) * 100)) - updateStatusText.value = `正在下载 ${pct}%` - } - break - case 'Finished': - updateStatusText.value = '安装完成,即将重启…' - break - } - }) - await relaunch() - return true - } catch (e) { - const msg = e instanceof Error ? e.message : String(e) - message.error(`更新失败:${msg}`) - console.error('[updater] download/install failed', e) - updateStatusText.value = '' - return false - } finally { - updateBusy.value = false - } - }, - }) - } catch (e) { - const msg = e instanceof Error ? e.message : String(e) - message.error( - msg.includes('Tauri') || msg.includes('not allowed') - ? '无法检查更新:请在寻迹桌面客户端内使用(浏览器预览不支持)。' - : `检查更新失败:${msg}`, - ) - console.error('[updater] check failed', e) - } finally { - updateBusy.value = false - } -} - /** 与 n-switch 的受控值一致,避免仅「翻转」与 Naive 传入的新值不同步 */ function setSourceEnabled(source: SourceConfigDto, enabled: boolean) { source.enabled = enabled @@ -631,25 +552,9 @@ const extraArgsStr = computed({ - - - - - 检查更新 - - -

- {{ updateStatusText }} -

-
+

+ 软件更新请使用顶栏 检查更新。 +

diff --git a/apps/desktop/src/components/TopBar.vue b/apps/desktop/src/components/TopBar.vue index c536f28..4d8a89d 100644 --- a/apps/desktop/src/components/TopBar.vue +++ b/apps/desktop/src/components/TopBar.vue @@ -15,6 +15,7 @@ import { useSidebarStore } from '../stores/sidebar' import { api } from '../lib/tauri' import { exportAllCardsToDir } from '../lib/cardExport' import SettingsModal from './SettingsModal.vue' +import AppUpdateModal from './AppUpdateModal.vue' const ui = useUiStore() const sessions = useSessionsStore() @@ -24,6 +25,8 @@ const message = useMessage() const dialog = useDialog() const showSettings = ref(false) +/** 独立「软件更新」弹窗(与设置解耦) */ +const showAppUpdate = ref(false) /** 顶栏「导出」下拉:与知识库页能力对齐 */ const exportDropdownOptions: DropdownOption[] = [ @@ -172,6 +175,15 @@ async function onSync() { + + + 检查更新 + + diff --git a/apps/desktop/src/composables/useAppUpdater.ts b/apps/desktop/src/composables/useAppUpdater.ts new file mode 100644 index 0000000..8179bac --- /dev/null +++ b/apps/desktop/src/composables/useAppUpdater.ts @@ -0,0 +1,139 @@ +import { ref } from 'vue' +import { check } from '@tauri-apps/plugin-updater' +import { relaunch } from '@tauri-apps/plugin-process' +import { getVersion } from '@tauri-apps/api/app' + +/** 与 UI 同步的更新流程阶段(独立「软件更新」弹窗用) */ +export type AppUpdatePhase = + | 'idle' + | 'checking' + | 'uptodate' + | 'available' + | 'downloading' + | 'installing' + | 'error' + +/** + * 应用内更新:封装 tauri-plugin-updater 的检查与下载安装,并输出进度供进度条绑定。 + * 浏览器预览无 Tauri 时 check 会抛错,由调用方展示 errorText。 + */ +export function useAppUpdater() { + const phase = ref('idle') + const currentVersion = ref('') + const busy = ref(false) + /** 0–100;仅在已知总大小时递增 */ + const downloadPercent = ref(0) + /** 未返回 contentLength 时仅展示不确定文案,不强行伪造百分比 */ + const downloadIndeterminate = ref(false) + const statusText = ref('') + const errorText = ref('') + /** check() 返回的更新对象;安装前保持引用 */ + const pendingUpdate = ref>>(null) + + function reset() { + phase.value = 'idle' + downloadPercent.value = 0 + downloadIndeterminate.value = false + statusText.value = '' + errorText.value = '' + pendingUpdate.value = null + } + + async function loadCurrentVersion() { + try { + currentVersion.value = await getVersion() + } catch { + currentVersion.value = '—' + } + } + + async function runCheck() { + if (busy.value) return + busy.value = true + errorText.value = '' + pendingUpdate.value = null + phase.value = 'checking' + try { + const update = await check() + if (!update) { + phase.value = 'uptodate' + return + } + pendingUpdate.value = update + phase.value = 'available' + } catch (e) { + const msg = e instanceof Error ? e.message : String(e) + errorText.value = + msg.includes('Tauri') || msg.includes('not allowed') + ? '请在寻迹桌面客户端内使用(浏览器预览不支持检查更新)。' + : `检查更新失败:${msg}` + phase.value = 'error' + console.error('[updater] check failed', e) + } finally { + busy.value = false + } + } + + async function runInstall() { + const update = pendingUpdate.value + if (!update || busy.value) return + busy.value = true + phase.value = 'downloading' + downloadPercent.value = 0 + downloadIndeterminate.value = false + statusText.value = '' + let total = 0 + let downloaded = 0 + try { + await update.downloadAndInstall((event) => { + switch (event.event) { + case 'Started': + total = event.data.contentLength ?? 0 + downloadIndeterminate.value = total <= 0 + statusText.value = + total > 0 ? '正在下载更新…' : '正在下载更新(未获知总大小,进度以已接收数据为准)…' + break + case 'Progress': + downloaded += event.data.chunkLength + if (total > 0) { + downloadPercent.value = Math.min(100, Math.round((downloaded / total) * 100)) + statusText.value = `已下载 ${downloadPercent.value}%` + } else { + statusText.value = `已接收 ${(downloaded / 1024 / 1024).toFixed(2)} MB` + } + break + case 'Finished': + downloadPercent.value = 100 + statusText.value = '安装完成,即将重启…' + break + default: + break + } + }) + phase.value = 'installing' + await relaunch() + } catch (e) { + const msg = e instanceof Error ? e.message : String(e) + errorText.value = `更新失败:${msg}` + phase.value = 'error' + console.error('[updater] download/install failed', e) + } finally { + busy.value = false + } + } + + return { + phase, + currentVersion, + busy, + downloadPercent, + downloadIndeterminate, + statusText, + errorText, + pendingUpdate, + reset, + loadCurrentVersion, + runCheck, + runInstall, + } +} diff --git a/apps/desktop/src/data/app-changelog.md b/apps/desktop/src/data/app-changelog.md index 71883ec..180f4d0 100644 --- a/apps/desktop/src/data/app-changelog.md +++ b/apps/desktop/src/data/app-changelog.md @@ -2,6 +2,14 @@ > 面向使用者的更新说明。实现细节见 `docs/CHANGELOG.md`。 +## 0.1.6(2026-03-25) + +### 体验改进 + +- **软件更新**:顶栏增加独立入口「检查更新」,与设置解耦;下载新版本时展示**进度条**(已知大小时显示百分比)。 + +--- + ## 0.1.5(2026-03-25) ### 体验改进 diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 6f0f963..8201ef1 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -5,6 +5,15 @@ --- +## 0.1.6(2026-03-25) + +### 体验与构建 + +- **桌面**:`AppUpdateModal.vue`、`useAppUpdater.ts`;`TopBar` 顶栏入口;`SettingsModal` 关于页移除重复入口 +- **更新流程**:`downloadAndInstall` 回调驱动 `n-progress`;无 `contentLength` 时降级为转圈 + 已接收数据量 + +--- + ## 0.1.5(2026-03-25) ### 体验与构建