- 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
205 lines
8.8 KiB
Vue
205 lines
8.8 KiB
Vue
<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> |