提交 36decf64 作者: 方治民

feat: 添加版本更新相关实现

上级 d746409c
......@@ -73,8 +73,9 @@
"@stomp/stompjs": "^7.0.0",
"@vue/runtime-core": "^3.2.47",
"@vue/shared": "^3.2.47",
"@vueuse/core": "^9.13.0",
"@vueuse/shared": "^9.13.0",
"@vueuse/core": "^10.1.0",
"@vueuse/electron": "^10.1.0",
"@vueuse/shared": "^10.1.0",
"@zxcvbn-ts/core": "^2.2.1",
"ant-design-vue": "^3.2.19",
"axios": "^0.26.1",
......@@ -84,6 +85,8 @@
"dayjs": "^1.11.7",
"default-passive-events": "^2.0.0",
"echarts": "^5.4.2",
"electron-log": "^4.4.8",
"electron-updater": "^5.3.0",
"intro.js": "^7.0.1",
"js-file-download": "^0.4.12",
"lodash-es": "^4.17.21",
......@@ -150,7 +153,7 @@
"dotenv": "^16.0.3",
"electron": "^23.2.4",
"electron-builder": "^23.6.0",
"electron-vite": "1.0.21",
"electron-vite": "^1.0.22",
"eslint": "^8.39.0",
"eslint-config-prettier": "^8.8.0",
"eslint-plugin-prettier": "^4.2.1",
......
import log from 'electron-log'
import { autoUpdater } from 'electron-updater'
import type { BrowserWindow } from 'electron'
import { ipcMain } from 'electron'
autoUpdater.logger = log
log.info('App starting...')
type MessageType =
| 'checking-for-update'
| 'error'
| 'update-available'
| 'update-not-available'
| 'download-progress'
| 'update-downloaded'
export async function useUpdater(win: BrowserWindow) {
function checkForUpdate() {
autoUpdater.checkForUpdates()
}
function sendUpdateMessageToWindow(body: { type: MessageType; text: string }) {
log.info(`${body.type}: ${body.text}`)
win.webContents.send('updater-message', body)
}
autoUpdater.on('checking-for-update', () => {
sendUpdateMessageToWindow({ type: 'checking-for-update', text: '正在检查更新...' })
})
autoUpdater.on('update-available', (info) => {
sendUpdateMessageToWindow({ type: 'update-available', text: `发现新版本 v${info.version}` })
})
autoUpdater.on('update-not-available', () => {
sendUpdateMessageToWindow({ type: 'update-not-available', text: '当前已是最新版本!' })
})
autoUpdater.on('error', (err) => {
sendUpdateMessageToWindow({ type: 'error', text: err.message })
})
autoUpdater.on('download-progress', (progress) => {
sendUpdateMessageToWindow({
type: 'download-progress',
text: `正在下载新版本 ${Number(progress.percent).toFixed(2)}%`,
})
})
autoUpdater.on('update-downloaded', () => {
sendUpdateMessageToWindow({ type: 'update-downloaded', text: '下载完成' })
setTimeout(() => {
// 退出并安装更新包
autoUpdater.quitAndInstall(false, true)
win.destroy()
}, 1500)
})
// 监听界面发送的检查更新事件
ipcMain.on('checkForUpdate', () => {
checkForUpdate()
})
// 检查更新
checkForUpdate()
}
import type { IpcRenderer, IpcRendererEvent } from 'electron'
import type { UseIpcRendererReturn } from '@vueuse/electron'
import { useIpcRenderer } from '@vueuse/electron'
const ipc = useIpcRenderer()
export type IpcRendererListener = (event: IpcRendererEvent, ...args: any[]) => void
export interface UserIpcRendererRetrun extends UseIpcRendererReturn {
invokeAsync<T>(channel: string, ...args: any[]): Promise<T | null>
}
export interface UseElectronReturn {
ipcRenderer: UserIpcRendererRetrun
}
export function useElectron(ipcRenderer?: IpcRenderer): UseElectronReturn {
if (!ipcRenderer) ipcRenderer = window?.require('electron').ipcRenderer
if (!ipcRenderer) throw new Error('provide IpcRenderer module or enable nodeIntegration')
return {
ipcRenderer: {
on: ipc.on,
once: ipc.once,
removeListener: ipc.removeListener,
removeAllListeners: ipc.removeAllListeners,
send: ipc.send,
sendSync: ipc.sendSync,
sendTo: ipc.sendTo,
sendToHost: ipc.sendToHost,
postMessage: ipc.postMessage,
invoke: ipc.invoke,
invokeAsync: <T>(channel: string, ...args: any[]): Promise<T> => ipcRenderer.invoke(channel, ...args),
},
}
}
<script setup lang="tsx">
import dayjs from 'dayjs'
import { BasicModal } from '/@/components/Modal'
import { Icon } from '/@/components/Icon'
import { useElectron } from '@/hooks/electron/useElectron'
import { useMessage } from '@/hooks/web/useMessage'
import { getEnvText } from '/@/utils/env'
type MessageType =
| 'checking-for-update'
| 'error'
| 'update-available'
| 'update-not-available'
| 'download-progress'
| 'update-downloaded'
const { ipcRenderer: ipc } = useElectron()
const { createMessage } = useMessage()
const startYear = 2021
const currentYear = dayjs().year()
const yearText = ref<string>(currentYear === startYear ? `${currentYear}` : `${startYear} - ${currentYear}`)
const loading = ref<boolean>(false)
// 监听检查更新事件
let hide = null
ipc.on('updater-message', (_e, body: { type: MessageType; text: string }) => {
console.log('updater-message', body)
const { type, text } = body
if (type === 'checking-for-update' || type === 'download-progress') {
loading.value = true
hide = createMessage.loading({
key: 'updating',
content: text,
duration: 0,
})
} else if (type === 'update-available' || type === 'update-not-available' || type === 'update-downloaded') {
createMessage.success(text)
} else if (type === 'error') {
createMessage.error(text)
}
if (type === 'error' || type === 'update-downloaded' || type === 'update-not-available') {
loading.value = false
hide?.()
}
})
</script>
<template>
<BasicModal v-bind="$attrs" centered :footer="null" :canFullscreen="false">
<!-- 关于信息 -->
<template #title>
<a-space>
<Icon icon="ant-design:info-circle-outlined" :size="22" />
<div class="title-text">关于</div>
</a-space>
</template>
<div class="about-wrap">
<!-- 公司 + 版权信息 -->
<div class="about-app-info-wrap">
<div class="app-icon">
<img src="logo.png" />
</div>
<!-- <div class="app-name">{{ $app.name }}</div> -->
<div class="app-description">{{ $app.description }}</div>
<div class="app-version">
<a-tooltip>
<template #title>
<div class="flex flex-col items-center justify-center">
<div class="version-mode">{{ getEnvText() }}</div>
<div class="update-time">发布时间: {{ $app.lastBuildTime }}</div>
</div>
</template>
<a-button type="text">
<span class="version-text">{{ $app.version }}</span>
</a-button>
</a-tooltip>
</div>
<!-- 检查版本更新按钮 -->
<div class="app-check-update-action">
<a-button type="dashed" :loading="loading" @click="ipc.send('checkForUpdate')">
<Icon icon="ant-design:bulb-outlined" v-show="!loading" />
检查更新
</a-button>
</div>
</div>
<!-- 版本信息 -->
<div class="about-footer">
<div class="company">长沙壹润信息科技发展有限公司</div>
<div class="copyright">
<span>Copyright © {{ yearText }}</span>
<span class="link">
<a target="_blank" href="https://yiring.com">YiRing.com</a>
</span>
</div>
</div>
</div>
</BasicModal>
</template>
<style lang="less" scoped>
.about-wrap {
display: flex;
flex-direction: column;
justify-content: space-between;
align-items: center;
}
.about-app-info-wrap {
display: flex;
flex-direction: column;
align-items: center;
> div {
margin-top: 10px;
}
.app-icon {
width: 50px;
}
.app-description {
font-weight: bold;
}
.app-version {
display: flex;
flex-direction: column;
align-items: center;
.version-text::before {
content: 'V';
margin-right: 2px;
}
}
}
.about-footer {
height: 60px;
display: flex;
flex-direction: column;
justify-content: flex-end;
align-items: center;
color: #898989;
font-size: 13px;
user-select: none;
letter-spacing: 1px;
.company {
letter-spacing: 2px;
}
.link {
margin-left: 5px;
&:hover {
text-decoration: underline;
cursor: pointer;
}
a {
color: #898989;
margin-left: 3px;
}
}
}
</style>
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论