mirror of
https://github.com/atdunbg/Nekosonic-Music.git
synced 2026-06-22 10:48:05 +08:00
添加自动更新功能
This commit is contained in:
@ -146,7 +146,7 @@
|
||||
<section class="mb-8">
|
||||
<h2 class="text-sm text-content-2 uppercase tracking-wider mb-4">关于</h2>
|
||||
<div class="space-y-4">
|
||||
<a @click.prevent="openUrl('https://gitea.atdunbg.xyz/atdunbg/Nekosonic-Music')"
|
||||
<a @click.prevent="openUrl('https://github.com/atdunbg/Nekosonic-Music')"
|
||||
class="flex items-center gap-4 p-4 bg-subtle rounded-xl hover:bg-muted transition cursor-pointer">
|
||||
<img src="../assets/app-icon.png" class="w-12 h-12 rounded-xl flex-shrink-0" alt="Nekosonic" />
|
||||
<div>
|
||||
@ -158,15 +158,15 @@
|
||||
Nekosonic 是一款高颜值的跨平台第三方网易云音乐桌面客户端,基于 Tauri 2 + Vue 3 构建,提供轻量流畅的音乐播放体验。
|
||||
</p>
|
||||
<button
|
||||
@click="checkUpdate"
|
||||
:disabled="checkingUpdate"
|
||||
@click="handleCheckUpdate"
|
||||
:disabled="updater.checking.value"
|
||||
class="flex items-center gap-2 px-4 py-2 bg-subtle hover:bg-muted rounded-lg text-sm transition"
|
||||
>
|
||||
<svg v-if="!checkingUpdate" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M14 2H6a2 2 0 00-2 2v16a2 2 0 002 2h12a2 2 0 002-2V8z"/><polyline points="14 2 14 8 20 8"/><line x1="16" y1="13" x2="8" y2="13"/><line x1="16" y1="17" x2="8" y2="17"/><polyline points="10 9 9 9 8 9"/></svg>
|
||||
<svg v-if="!updater.checking.value" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M14 2H6a2 2 0 00-2 2v16a2 2 0 002 2h12a2 2 0 002-2V8z"/><polyline points="14 2 14 8 20 8"/><line x1="16" y1="13" x2="8" y2="13"/><line x1="16" y1="17" x2="8" y2="17"/><polyline points="10 9 9 9 8 9"/></svg>
|
||||
<svg v-else class="animate-spin" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 12a9 9 0 11-6.22-8.56"/></svg>
|
||||
{{ checkingUpdate ? '获取中...' : '查看最新版日志' }}
|
||||
{{ updater.checking.value ? '检查中...' : '检查更新' }}
|
||||
</button>
|
||||
<p v-if="updateMessage && !latestRelease" class="text-xs" :class="updateMessageClass">{{ updateMessage }}</p>
|
||||
<p v-if="updater.error.value" class="text-xs text-content-3">{{ updater.error.value }}</p>
|
||||
</div>
|
||||
</section>
|
||||
<Transition name="fade">
|
||||
@ -188,35 +188,6 @@
|
||||
</div>
|
||||
</Transition>
|
||||
|
||||
<Transition name="fade">
|
||||
<div v-if="showUpdateModal" class="fixed inset-0 z-50 flex items-center justify-center bg-black/60 backdrop-blur-sm" @click.self="showUpdateModal = false">
|
||||
<div class="bg-surface border border-line rounded-2xl shadow-2xl w-[420px] max-h-[80vh] flex flex-col select-auto">
|
||||
<div class="p-6 pb-4">
|
||||
<div class="flex items-center justify-between mb-1">
|
||||
<h2 class="text-lg font-semibold text-content">最新版本日志</h2>
|
||||
<span v-if="latestRelease" class="text-xs font-medium px-2 py-0.5 rounded-full bg-accent/15 text-accent-text">v{{ latestRelease.tag_name?.replace('v', '') }}</span>
|
||||
</div>
|
||||
<p v-if="latestRelease?.published_at" class="text-xs text-content-3 mt-1">{{ formatDate(latestRelease.published_at) }}</p>
|
||||
</div>
|
||||
<div v-if="latestRelease?.body" class="px-6 pb-4 flex-1 overflow-y-auto max-h-60">
|
||||
<div class="text-sm text-content-2 leading-relaxed whitespace-pre-wrap">{{ latestRelease.body }}</div>
|
||||
</div>
|
||||
<div v-else class="px-6 pb-4">
|
||||
<p class="text-sm text-content-3">暂无更新日志</p>
|
||||
</div>
|
||||
<div class="p-4 border-t border-line flex gap-3">
|
||||
<button @click="showUpdateModal = false"
|
||||
class="flex-1 py-2 rounded-lg bg-muted hover:bg-emphasis text-sm text-content-2 transition">
|
||||
关闭
|
||||
</button>
|
||||
<button v-if="latestRelease?.html_url" @click="openUrl(latestRelease.html_url)"
|
||||
class="flex-1 py-2 rounded-lg bg-accent/20 hover:bg-accent/30 text-accent-text text-sm font-medium transition">
|
||||
在 Gitea 中查看
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Transition>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -224,6 +195,7 @@
|
||||
import { ref, computed, onMounted, onBeforeUnmount } from 'vue';
|
||||
import { useSettingsStore, qualityLabels, closeActionLabels, defaultShortcuts, type CloseAction } from '../stores/settings';
|
||||
import { useToast } from '../composables/useToast';
|
||||
import { useUpdater } from '../composables/useUpdater';
|
||||
import { invoke } from '@tauri-apps/api/core';
|
||||
import { getVersion } from '@tauri-apps/api/app';
|
||||
import { openUrl } from '@tauri-apps/plugin-opener';
|
||||
@ -232,6 +204,7 @@ import CustomSelect from '../components/CustomSelect.vue';
|
||||
|
||||
const settings = useSettingsStore();
|
||||
const { showToast } = useToast();
|
||||
const updater = useUpdater();
|
||||
|
||||
const appVersion = ref('');
|
||||
const defaultDownloadPath = ref('');
|
||||
@ -264,45 +237,15 @@ function clearDownloadPath() {
|
||||
showToast('已重置为默认路径', 'success');
|
||||
}
|
||||
|
||||
const checkingUpdate = ref(false);
|
||||
const updateMessage = ref('');
|
||||
const updateMessageClass = ref('text-content-2');
|
||||
const latestRelease = ref<any>(null);
|
||||
const showUpdateModal = ref(false);
|
||||
|
||||
const themeOptions = [
|
||||
{ label: '深色', value: 'dark' as const },
|
||||
{ label: '浅色', value: 'light' as const },
|
||||
];
|
||||
|
||||
async function checkUpdate() {
|
||||
checkingUpdate.value = true;
|
||||
updateMessage.value = '';
|
||||
try {
|
||||
const resp = await fetch('https://gitea.atdunbg.xyz/api/v1/repos/atdunbg/Nekosonic-Music/releases?limit=1&draft=false');
|
||||
if (!resp.ok) throw new Error(`HTTP ${resp.status}`);
|
||||
const releases = await resp.json();
|
||||
if (releases && releases.length > 0) {
|
||||
latestRelease.value = releases[0];
|
||||
showUpdateModal.value = true;
|
||||
} else {
|
||||
updateMessage.value = '暂无发布版本';
|
||||
updateMessageClass.value = 'text-content-3';
|
||||
}
|
||||
} catch (e: any) {
|
||||
updateMessage.value = `获取失败: ${e}`;
|
||||
updateMessageClass.value = 'text-danger';
|
||||
} finally {
|
||||
checkingUpdate.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
function formatDate(dateStr: string) {
|
||||
try {
|
||||
const d = new Date(dateStr);
|
||||
return d.toLocaleDateString('zh-CN', { year: 'numeric', month: 'long', day: 'numeric' });
|
||||
} catch {
|
||||
return dateStr;
|
||||
async function handleCheckUpdate() {
|
||||
const result = await updater.checkForUpdate(false);
|
||||
if (!result) {
|
||||
showToast(updater.error.value || '当前已是最新版本', 'info');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user