From 64fb077d656b1f388fb2729451fa7e5f3c068008 Mon Sep 17 00:00:00 2001 From: AoXuan Date: Tue, 9 Sep 2025 21:10:41 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E7=82=AB=E5=BD=A9?= =?UTF-8?q?=E6=9B=B4=E6=96=B0=E6=8F=90=E7=A4=BA~?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/package.json | 2 +- frontend/src/components/TitleBar.vue | 158 ++++++++++++++++++++++++--- frontend/src/types/electron.ts | 10 +- frontend/vite.config.ts | 7 ++ 4 files changed, 158 insertions(+), 19 deletions(-) diff --git a/frontend/package.json b/frontend/package.json index f3c0b5e..23e073c 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,7 +1,7 @@ { "name": "frontend", "private": true, - "version": "0.0.1", + "version": "1.0.1", "main": "dist-electron/main.js", "scripts": { "dev": "concurrently \"vite\" \"yarn watch:main\" \"yarn electron-dev\"", diff --git a/frontend/src/components/TitleBar.vue b/frontend/src/components/TitleBar.vue index 05e0af8..ef20330 100644 --- a/frontend/src/components/TitleBar.vue +++ b/frontend/src/components/TitleBar.vue @@ -7,6 +7,12 @@ AUTO-MAS + + v{{ version }} + + 检测到更新 {{ updateInfo.latest_version }} 请尽快更新 + + @@ -16,26 +22,18 @@
- - -
@@ -47,10 +45,47 @@ import { ref, onMounted } from 'vue' import { MinusOutlined, BorderOutlined, CopyOutlined, CloseOutlined } from '@ant-design/icons-vue' import { useTheme } from '@/composables/useTheme' +import { Service } from '@/api' +import type { UpdateCheckOut } from '@/api' const { isDark } = useTheme() const isMaximized = ref(false) +// 使用 import.meta.env 或直接定义版本号,确保打包后可用 +const version = import.meta.env.VITE_APP_VERSION || '获取版本失败!' +const updateInfo = ref(null) + +// 获取是否有更新 +const getAppVersion = async () => { + try { + const ver = await Service.checkUpdateApiUpdateCheckPost({ + current_version: version, + }) + updateInfo.value = ver + return ver || '获取版本失败!' + } catch (error) { + console.error('Failed to get app version:', error) + return '获取版本失败!' + } +} + +// 生成更新提示的详细信息 +const getUpdateTooltip = () => { + if (!updateInfo.value?.update_info) return '' + + const updateDetails = [] + for (const [category, items] of Object.entries(updateInfo.value.update_info)) { + if (items && items.length > 0) { + updateDetails.push(`${category}:`) + items.forEach(item => { + updateDetails.push(`• ${item}`) + }) + updateDetails.push('') + } + } + return updateDetails.join('\n') +} + const minimizeWindow = async () => { try { await window.electronAPI?.windowMinimize() @@ -62,7 +97,7 @@ const minimizeWindow = async () => { const toggleMaximize = async () => { try { await window.electronAPI?.windowMaximize() - isMaximized.value = await window.electronAPI?.windowIsMaximized() || false + isMaximized.value = (await window.electronAPI?.windowIsMaximized()) || false } catch (error) { console.error('Failed to toggle maximize:', error) } @@ -78,10 +113,11 @@ const closeWindow = async () => { onMounted(async () => { try { - isMaximized.value = await window.electronAPI?.windowIsMaximized() || false + isMaximized.value = (await window.electronAPI?.windowIsMaximized()) || false } catch (error) { console.error('Failed to get window state:', error) } + await getAppVersion() }) @@ -124,11 +160,11 @@ onMounted(async () => { left: 55px; /* 调整:更贴近图标 */ top: 50%; transform: translate(-50%, -50%); - width: 200px; /* 缩小尺寸以适配 32px 高度 */ + width: 200px; /* 缩小尺寸以适配 32px 高度 */ height: 100px; pointer-events: none; border-radius: 50%; - background: radial-gradient(circle at 50% 50%, var(--ant-color-primary) 0%, rgba(0,0,0,0) 70%); + background: radial-gradient(circle at 50% 50%, var(--ant-color-primary) 0%, rgba(0, 0, 0, 0) 70%); filter: blur(24px); /* 降低模糊避免越界过多 */ opacity: 0.4; z-index: 0; @@ -153,10 +189,23 @@ onMounted(async () => { z-index: 1; } +.version-text { + font-size: 13px; + font-weight: 400; + opacity: 0.8; + position: relative; + z-index: 1; + margin-left: 4px; +} + .title-bar-dark .title-text { color: #fff; } +.title-bar-dark .version-text { + color: #ffffff; +} + .title-bar-center { flex: 1; height: 100%; @@ -218,4 +267,79 @@ onMounted(async () => { .title-bar-dark .maximize-button:hover { background: rgba(255, 255, 255, 0.15); } - \ No newline at end of file + +.update-hint { + font-weight: 500; + margin-left: 4px; + cursor: help; + background: linear-gradient(45deg, #ff0000, #ff7f00, #ffff00, #00ff00, #8b00ff, #ff0000); + background-size: 400% 400%; + -webkit-background-clip: text; + background-clip: text; + -webkit-text-fill-color: transparent; + animation: + rainbow-flow 3s ease-in-out infinite, + glow-pulse 2s ease-in-out infinite; + position: relative; +} + +.update-hint::before { + content: ''; + position: absolute; + top: -2px; + left: -2px; + right: -2px; + bottom: -2px; + background: linear-gradient(45deg, #ff0000, #ff7f00, #ffff00, #00ff00, #8b00ff, #ff0000); + background-size: 400% 400%; + border-radius: 4px; + z-index: -1; + opacity: 0.3; + filter: blur(8px); + animation: rainbow-flow 3s ease-in-out infinite; +} + +.title-bar-dark .update-hint::before { + opacity: 0.5; + filter: blur(10px); +} + +@keyframes rainbow-flow { + 0% { + background-position: 0% 50%; + } + 50% { + background-position: 100% 50%; + } + 100% { + background-position: 0% 50%; + } +} + +@keyframes glow-pulse { + 0% { + filter: brightness(1) saturate(1); + transform: scale(1); + } + 50% { + filter: brightness(1.2) saturate(1.3); + transform: scale(1.02); + } + 100% { + filter: brightness(1) saturate(1); + transform: scale(1); + } +} + +@keyframes pulse { + 0% { + opacity: 1; + } + 50% { + opacity: 0.7; + } + 100% { + opacity: 1; + } +} + diff --git a/frontend/src/types/electron.ts b/frontend/src/types/electron.ts index 11f134f..571c95c 100644 --- a/frontend/src/types/electron.ts +++ b/frontend/src/types/electron.ts @@ -5,9 +5,18 @@ export interface ElectronAPI { selectFolder: () => Promise selectFile: (filters?: Array<{ name: string; extensions: string[] }>) => Promise + // 窗口控制 + windowMinimize: () => Promise + windowMaximize: () => Promise + windowClose: () => Promise + windowIsMaximized: () => Promise + // 管理员权限检查 checkAdmin: () => Promise + // 重启为管理员 + restartAsAdmin: () => Promise + // 环境检查 checkEnvironment: () => Promise<{ pythonExists: boolean @@ -53,7 +62,6 @@ export interface ElectronAPI { callback: (progress: { progress: number; status: string; message: string }) => void ) => void removeDownloadProgressListener: () => void - restartAsAdmin: () => Promise } declare global { diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts index 0611bc6..aedce55 100644 --- a/frontend/vite.config.ts +++ b/frontend/vite.config.ts @@ -2,6 +2,9 @@ import { defineConfig } from 'vite' import vue from '@vitejs/plugin-vue' import path from 'path' +// 读取package.json中的版本号 +const packageJson = require('./package.json') + // https://vite.dev/config/ export default defineConfig({ plugins: [vue()], @@ -12,4 +15,8 @@ export default defineConfig({ '@': path.resolve(__dirname, './src'), }, }, + define: { + // 在编译时将版本号注入到环境变量中 + 'import.meta.env.VITE_APP_VERSION': JSON.stringify(packageJson.version) + } })