feat: Add version update detection

This commit is contained in:
putyy
2025-07-07 14:40:41 +08:00
parent 6c21e37ce4
commit 405d0bbdb2
5 changed files with 64 additions and 17 deletions

View File

@@ -9,8 +9,6 @@ declare module 'vue' {
export interface GlobalComponents { export interface GlobalComponents {
Action: typeof import('./src/components/Action.vue')['default'] Action: typeof import('./src/components/Action.vue')['default']
ActionDesc: typeof import('./src/components/ActionDesc.vue')['default'] ActionDesc: typeof import('./src/components/ActionDesc.vue')['default']
DescriptionHeader: typeof import('./src/components/DescriptionHeader.vue')['default']
DescriptionSearch: typeof import('./src/components/DescriptionSearch.vue')['default']
Footer: typeof import('./src/components/Footer.vue')['default'] Footer: typeof import('./src/components/Footer.vue')['default']
ImportJson: typeof import('./src/components/ImportJson.vue')['default'] ImportJson: typeof import('./src/components/ImportJson.vue')['default']
Index: typeof import('./src/components/layout/Index.vue')['default'] Index: typeof import('./src/components/layout/Index.vue')['default']

View File

@@ -2,7 +2,12 @@
<div class="flex pb-2 flex-col h-full min-w-[80px] border-r border-slate-100 dark:border-slate-900"> <div class="flex pb-2 flex-col h-full min-w-[80px] border-r border-slate-100 dark:border-slate-900">
<Screen v-if="envInfo.platform!=='darwin'"></Screen> <Screen v-if="envInfo.platform!=='darwin'"></Screen>
<div class="w-full flex flex-row items-center justify-center pt-5" :class="envInfo.platform==='darwin' ? 'pt-8' : 'pt-2'"> <div class="w-full flex flex-row items-center justify-center pt-5" :class="envInfo.platform==='darwin' ? 'pt-8' : 'pt-2'">
<img class="w-12 h-12 cursor-pointer" src="@/assets/image/logo.png" alt="res-downloader logo" @click="handleFooterUpdate('github')"/> <div class="relative flex items-center justify-center cursor-pointer" @click="handleFooterUpdate('github')">
<img class="w-12 h-12 rounded-full transition-transform duration-300 hover:scale-105 dark" src="@/assets/image/logo.png" alt="res-downloader logo"/>
<span class="absolute right-[-25px] top-0 font-semibold rounded-full bg-red-500 text-white dark:bg-red-600 dark:text-gray-100 text-[10px] px-1.5 py-0.5 animate-pulse" v-if="showUpdate">
New
</span>
</div>
</div> </div>
<main class="flex-1 flex-grow-1 mb-5 overflow-auto flex flex-col pt-1 items-center h-full" v-if="is"> <main class="flex-1 flex-grow-1 mb-5 overflow-auto flex flex-col pt-1 items-center h-full" v-if="is">
<NScrollbar :size="1"> <NScrollbar :size="1">
@@ -66,6 +71,8 @@ import Footer from "@/components/Footer.vue"
import Screen from "@/components/Screen.vue" import Screen from "@/components/Screen.vue"
import {BrowserOpenURL} from "../../../wailsjs/runtime" import {BrowserOpenURL} from "../../../wailsjs/runtime"
import {useI18n} from "vue-i18n" import {useI18n} from "vue-i18n"
import request from "@/api/request"
import {compareVersions} from "@/func"
const {t} = useI18n() const {t} = useI18n()
const route = useRoute() const route = useRoute()
@@ -77,6 +84,7 @@ const showAppInfo = ref(false)
const menuValue = ref(route.fullPath.substring(1)) const menuValue = ref(route.fullPath.substring(1))
const store = useIndexStore() const store = useIndexStore()
const is = ref(false) const is = ref(false)
const showUpdate = ref(false)
const envInfo = store.envInfo const envInfo = store.envInfo
@@ -98,6 +106,13 @@ onMounted(()=>{
collapsed.value = JSON.parse(collapsedCache).collapsed collapsed.value = JSON.parse(collapsedCache).collapsed
} }
is.value = true is.value = true
request({
url: 'https://res.putyy.com/version.json',
method: 'get',
}).then((res)=>{
showUpdate.value = compareVersions(res.version, store.appInfo.Version) === 1
})
}) })
const renderIcon = (icon: any) => { const renderIcon = (icon: any) => {
@@ -182,4 +197,21 @@ const collapsedChange = (value: boolean)=>{
collapsed.value = value collapsed.value = value
localStorage.setItem("collapsed", JSON.stringify({collapsed: value})) localStorage.setItem("collapsed", JSON.stringify({collapsed: value}))
} }
</script> </script>
<style scoped>
@keyframes pulse {
0% {
transform: scale(0.9);
}
50% {
transform: scale(1);
}
100% {
transform: scale(0.9);
}
}
.animate-pulse {
animation: pulse 2s infinite;
}
</style>

29
frontend/src/func.ts Normal file
View File

@@ -0,0 +1,29 @@
const ipv4Regex = /^(25[0-5]|2[0-4]\d|1\d{2}|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d{2}|[1-9]?\d)){3}$/
const domainRegex = /^(?!:\/\/)([a-zA-Z0-9-_]+\.)*[a-zA-Z0-9][a-zA-Z0-9-_]+\.[a-zA-Z]{2,11}?$/
const localhostRegex = /^localhost$/
export const compareVersions = (v1: string, v2: string) => {
const parts1 = v1.split('.').map(Number)
const parts2 = v2.split('.').map(Number)
const maxLength = Math.max(parts1.length, parts2.length)
for (let i = 0; i < maxLength; i++) {
const num1 = parts1[i] || 0
const num2 = parts2[i] || 0
if (num1 < num2) return -1
if (num1 > num2) return 1
}
return 0
}
export const isValidHost = (host: string) => {
return ipv4Regex.test(host) || domainRegex.test(host) || localhostRegex.test(host)
}
export const isValidPort = (port: number) => {
const portNumber = Number(port)
return Number.isInteger(portNumber) && portNumber > 1024 && portNumber < 65535
}

View File

@@ -97,7 +97,6 @@ import {
DownloadOutline, DownloadOutline,
ArrowRedoCircleOutline, ArrowRedoCircleOutline,
ServerOutline, ServerOutline,
HelpCircleOutline,
SearchOutline, SearchOutline,
TrashOutline TrashOutline
} from "@vicons/ionicons5" } from "@vicons/ionicons5"

View File

@@ -204,6 +204,7 @@ import type {appType} from "@/types/app"
import appApi from "@/api/app" import appApi from "@/api/app"
import {computed} from "vue" import {computed} from "vue"
import {useI18n} from 'vue-i18n' import {useI18n} from 'vue-i18n'
import {isValidHost, isValidPort} from '@/func'
const {t} = useI18n() const {t} = useI18n()
const store = useIndexStore() const store = useIndexStore()
@@ -221,9 +222,6 @@ const renderKey = ref(999)
const hostValidationFeedback = ref("") const hostValidationFeedback = ref("")
const portValidationFeedback = ref("") const portValidationFeedback = ref("")
const ipv4Regex = /^(25[0-5]|2[0-4]\d|1\d{2}|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d{2}|[1-9]?\d)){3}$/
const domainRegex = /^(?!:\/\/)([a-zA-Z0-9-_]+\.)*[a-zA-Z0-9][a-zA-Z0-9-_]+\.[a-zA-Z]{2,11}?$/
const localhostRegex = /^localhost$/
watch(formValue.value, () => { watch(formValue.value, () => {
formValue.value.Port = formValue.value.Port.trim() formValue.value.Port = formValue.value.Port.trim()
@@ -262,15 +260,6 @@ watch(() => store.globalConfig.Locale, () => {
renderKey.value++ renderKey.value++
}) })
const isValidPort = (port: number) => {
const portNumber = Number(port)
return Number.isInteger(portNumber) && portNumber > 1024 && portNumber < 65535
}
const isValidHost = (host: string) => {
return ipv4Regex.test(host) || domainRegex.test(host) || localhostRegex.test(host)
}
const selectDir = () => { const selectDir = () => {
appApi.openDirectoryDialog().then((res: any) => { appApi.openDirectoryDialog().then((res: any) => {
if (res.code === 1) { if (res.code === 1) {