Files
zn-ai/src/pages/setting/components/Version/index.vue
DEV_DSW 1aa28a7808 feat: add auto-update functionality with settings UI
- Implement electron-updater integration with IPC handlers for update operations
- Add Pinia store for managing update state and user preferences
- Create settings UI with version display, update controls, and auto-update toggles
- Update IPC constants and preload API for update-related communication
- Refactor preload invoke methods to use async IPC consistently
- Add rounded corners to settings page layout for better visual consistency
2026-04-09 15:15:23 +08:00

205 lines
8.8 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="flex-1 h-full p-[20px] select-none">
<TitleSection title="通用设置" desc="配置应用程序的外观、语言和版本信息" />
<!-- 主题设置 -->
<div
class="w-full flex items-center mt-[20px] py-[20px] box-border border-b-[1px] border-dashed border-b-[#E5E8EE] dark:border-gray-700">
<div class="label w-[64px] text-[16px] font-medium text-[#171717] dark:text-gray-100 mr-[24px]">主题设置</div>
<div class="value flex-1">
<div class="space-y-4">
<div class="flex flex-wrap gap-3">
<!-- 浅色主题 -->
<button
class="theme-button px-5 py-1.5 rounded-full border text-[14px] font-medium transition-all duration-200 flex items-center gap-2"
:class="[
currentTheme === 'light'
? 'bg-green-50 dark:bg-green-900/20 border-green-500 dark:border-green-400 text-green-700 dark:text-green-300'
: 'bg-white dark:bg-gray-800 border-gray-300 dark:border-gray-600 text-gray-700 dark:text-gray-300 hover:bg-gray-50 dark:hover:bg-gray-700'
]" @click="handleThemeChange('light')">
<RiSunLine class="w-4 h-4" />
浅色
</button>
<!-- 深色主题 -->
<button
class="theme-button px-5 py-1.5 rounded-full border text-[14px] font-medium transition-all duration-200 flex items-center gap-2"
:class="[
currentTheme === 'dark'
? 'bg-green-50 dark:bg-green-900/20 border-green-500 dark:border-green-400 text-green-700 dark:text-green-300'
: 'bg-white dark:bg-gray-800 border-gray-300 dark:border-gray-600 text-gray-700 dark:text-gray-300 hover:bg-gray-50 dark:hover:bg-gray-700'
]" @click="handleThemeChange('dark')">
<RiMoonLine class="w-4 h-4" />
深色
</button>
<!-- 跟随系统 -->
<button
class="theme-button px-5 py-1.5 rounded-full border text-[14px] font-medium transition-all duration-200 flex items-center gap-2"
:class="[
currentTheme === 'system'
? 'bg-green-50 dark:bg-green-900/20 border-green-500 dark:border-green-400 text-green-700 dark:text-green-300'
: 'bg-white dark:bg-gray-800 border-gray-300 dark:border-gray-600 text-gray-700 dark:text-gray-300 hover:bg-gray-50 dark:hover:bg-gray-700'
]" @click="handleThemeChange('system')">
<RiComputerLine class="w-4 h-4" />
跟随系统
</button>
</div>
</div>
</div>
</div>
<!-- 语言设置 -->
<div
class="w-full flex items-center mt-[20px] py-[20px] box-border border-b-[1px] border-dashed border-b-[#E5E8EE] dark:border-gray-700">
<div class="label w-[64px] text-[16px] font-medium text-[#171717] dark:text-gray-100 mr-[24px]">语言设置</div>
<div class="value flex gap-2">
<el-button v-for="lang in supportedLanguages" :key="lang.code"
:type="currentLanguage === lang.code ? 'primary' : 'text'" @click="handleLanguageChange(lang.code)"
class="px-4">
{{ lang.label }}
</el-button>
</div>
</div>
<!-- 更新部分 -->
<div class="mt-[40px]">
<div class="text-[24px] font-medium text-[#171717] dark:text-gray-100 mb-[24px]">更新</div>
<!-- 当前版本 -->
<div class="flex items-center justify-between mb-[16px]">
<div>
<div class="text-[14px] text-[#525866] dark:text-gray-400 mb-[4px]">当前版本</div>
<div class="text-[28px] font-bold text-[#171717] dark:text-gray-100">v{{ updateStore.currentVersion }}</div>
</div>
<el-button link class="!p-[8px] hover:bg-gray-100 dark:hover:bg-gray-800 rounded-full"
@click="updateStore.checkUpdate()">
<RiRefreshLine class="w-5 h-5 text-[#525866] dark:text-gray-400"
:class="{ 'animate-spin': updateStore.status === 'checking' }" />
</el-button>
</div>
<!-- 更新状态卡片 -->
<div class="bg-[#F5F7FA] dark:bg-gray-800 rounded-[8px] p-[16px] flex items-center justify-between mb-[16px]">
<div class="text-[14px] text-[#525866] dark:text-gray-300">
<span v-if="updateStore.status === 'checking'">正在检查更新...</span>
<span v-else-if="updateStore.status === 'not-available'">您已拥有最新版本</span>
<span v-else-if="updateStore.status === 'available'">发现新版本: v{{ updateStore.updateInfo?.version }}</span>
<span v-else-if="updateStore.status === 'downloading'">
正在下载新版本... {{ Math.round(updateStore.progress?.percent || 0) }}%
</span>
<span v-else-if="updateStore.status === 'downloaded'">下载完成准备安装</span>
<span v-else-if="updateStore.status === 'error'" class="text-red-500">更新出错: {{ updateStore.error }}</span>
<span v-else>检查更新以获取最新功能</span>
</div>
<div>
<el-button v-if="updateStore.status === 'available'" type="primary" @click="updateStore.downloadUpdate()">
下载更新
</el-button>
<el-button v-else-if="updateStore.status === 'downloaded'" type="success"
@click="updateStore.installUpdate()">
重启安装
</el-button>
<el-button v-else
class="!bg-white dark:!bg-gray-700 !border-[#E5E8EE] dark:!border-gray-600 !text-[#171717] dark:!text-gray-100"
@click="updateStore.checkUpdate()">
<RiRefreshLine class="w-4 h-4 mr-[4px]" :class="{ 'animate-spin': updateStore.status === 'checking' }" />
检查更新
</el-button>
</div>
</div>
<div class="text-[12px] text-[#99A0AE] dark:text-gray-500 mb-[32px]">
开启自动更新后更新将自动下载并安装
</div>
<!-- 自动检查更新 -->
<div class="flex items-center justify-between py-[16px] border-b-[1px] border-[#E5E8EE] dark:border-gray-800">
<div>
<div class="text-[16px] text-[#171717] dark:text-gray-100 mb-[4px]">自动检查更新</div>
<div class="text-[14px] text-[#99A0AE] dark:text-gray-500">启动时检查更新</div>
</div>
<el-switch v-model="updateStore.autoCheckUpdate" @change="updateStore.setAutoCheckUpdate" />
</div>
<!-- 自动更新 -->
<div class="flex items-center justify-between py-[16px]">
<div>
<div class="text-[16px] text-[#171717] dark:text-gray-100 mb-[4px]">自动更新</div>
<div class="text-[14px] text-[#99A0AE] dark:text-gray-500">自动下载并安装更新</div>
</div>
<el-switch v-model="updateStore.autoDownloadUpdate" @change="updateStore.setAutoDownloadUpdate" />
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted, computed } from 'vue'
import TitleSection from '@src/components/TitleSection/index.vue'
import { setLanguage, getLanguage, type LanguageType } from '@src/i18n'
import { useThemeStore } from '@src/stores/theme'
import { useUpdateStore } from '@src/stores/update'
import { RiSunLine, RiMoonLine, RiComputerLine, RiRefreshLine } from '@remixicon/vue'
// 更新状态管理
const updateStore = useUpdateStore()
const getStatusText = computed(() => {
if (updateStore.status === 'checking') return '正在检查...';
if (updateStore.status === 'not-available') return '已是最新版';
return '检查更新';
});
// 主题状态管理
const themeStore = useThemeStore()
const currentTheme = computed(() => themeStore.theme)
// 主题切换处理
const handleThemeChange = async (theme: 'light' | 'dark' | 'system') => {
try {
console.log('切换主题到:', theme)
await themeStore.setTheme(theme)
console.log('主题切换成功:', theme)
} catch (error) {
console.error('主题切换失败:', error)
}
}
// 语言设置
const supportedLanguages = [
{ code: 'zh', label: '中文' },
{ code: 'en', label: 'English' },
{ code: 'ja', label: '日本語' }
] as const
const currentLanguage = ref<LanguageType>('zh')
onMounted(() => {
currentLanguage.value = getLanguage() as LanguageType
updateStore.init()
})
const handleLanguageChange = async (langCode: LanguageType) => {
if (langCode === currentLanguage.value) return
await setLanguage(langCode)
currentLanguage.value = langCode
}
</script>
<style scoped>
.theme-button {
cursor: pointer;
outline: none;
transition-property: background-color, border-color, color, transform;
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
transition-duration: 150ms;
}
.theme-button:focus-visible {
outline: 2px solid var(--primary-color);
outline-offset: 2px;
}
</style>