mirror of
https://github.com/algerkong/AlgerMusicPlayer.git
synced 2026-04-07 00:50:50 +08:00
@@ -39,7 +39,18 @@ export default {
|
||||
downloadFailed: 'Download failed, please try again or download manually',
|
||||
startFailed: 'Start download failed, please try again or download manually',
|
||||
noDownloadUrl:
|
||||
'No suitable installation package found for the current system, please download manually'
|
||||
'No suitable installation package found for the current system, please download manually',
|
||||
installConfirmTitle: 'Install Update',
|
||||
installConfirmContent: 'Do you want to close the application and install the update?',
|
||||
manualInstallTip:
|
||||
'If the installer does not open automatically after closing the application, please find the file in your download folder and open it manually.',
|
||||
yesInstall: 'Install Now',
|
||||
noThanks: 'Later',
|
||||
fileLocation: 'File Location',
|
||||
copy: 'Copy Path',
|
||||
copySuccess: 'Path copied to clipboard',
|
||||
copyFailed: 'Copy failed',
|
||||
backgroundDownload: 'Background Download'
|
||||
},
|
||||
coffee: {
|
||||
title: 'Buy me a coffee',
|
||||
|
||||
@@ -38,7 +38,17 @@ export default {
|
||||
nowUpdate: '立即更新',
|
||||
downloadFailed: '下载失败,请重试或手动下载',
|
||||
startFailed: '启动下载失败,请重试或手动下载',
|
||||
noDownloadUrl: '未找到适合当前系统的安装包,请手动下载'
|
||||
noDownloadUrl: '未找到适合当前系统的安装包,请手动下载',
|
||||
installConfirmTitle: '安装更新',
|
||||
installConfirmContent: '是否关闭应用并安装更新?',
|
||||
manualInstallTip: '如果关闭应用后没有正常弹出安装程序,请至下载文件夹查找文件并手动打开。',
|
||||
yesInstall: '立即安装',
|
||||
noThanks: '稍后安装',
|
||||
fileLocation: '文件位置',
|
||||
copy: '复制路径',
|
||||
copySuccess: '路径已复制到剪贴板',
|
||||
copyFailed: '复制失败',
|
||||
backgroundDownload: '后台下载'
|
||||
},
|
||||
coffee: {
|
||||
title: '请我喝咖啡',
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import axios from 'axios';
|
||||
import { exec } from 'child_process';
|
||||
import { spawn } from 'child_process';
|
||||
import { app, BrowserWindow, ipcMain } from 'electron';
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
@@ -53,38 +53,49 @@ export function setupUpdateHandlers(_mainWindow: BrowserWindow) {
|
||||
|
||||
const { platform } = process;
|
||||
|
||||
// 关闭当前应用
|
||||
app.quit();
|
||||
|
||||
// 根据不同平台执行安装
|
||||
if (platform === 'win32') {
|
||||
exec(`"${filePath}"`, (error) => {
|
||||
if (error) {
|
||||
console.error('Error starting installer:', error);
|
||||
}
|
||||
});
|
||||
} else if (platform === 'darwin') {
|
||||
// 挂载 DMG 文件
|
||||
exec(`open "${filePath}"`, (error) => {
|
||||
if (error) {
|
||||
console.error('Error opening DMG:', error);
|
||||
}
|
||||
});
|
||||
} else if (platform === 'linux') {
|
||||
const ext = path.extname(filePath);
|
||||
if (ext === '.AppImage') {
|
||||
exec(`chmod +x "${filePath}" && "${filePath}"`, (error) => {
|
||||
if (error) {
|
||||
console.error('Error running AppImage:', error);
|
||||
}
|
||||
// 先启动安装程序,再退出应用
|
||||
try {
|
||||
if (platform === 'win32') {
|
||||
// 使用spawn替代exec,并使用detached选项确保子进程独立运行
|
||||
const child = spawn(filePath, [], {
|
||||
detached: true,
|
||||
stdio: 'ignore'
|
||||
});
|
||||
} else if (ext === '.deb') {
|
||||
exec(`pkexec dpkg -i "${filePath}"`, (error) => {
|
||||
if (error) {
|
||||
console.error('Error installing deb package:', error);
|
||||
}
|
||||
child.unref();
|
||||
} else if (platform === 'darwin') {
|
||||
// 挂载 DMG 文件
|
||||
const child = spawn('open', [filePath], {
|
||||
detached: true,
|
||||
stdio: 'ignore'
|
||||
});
|
||||
child.unref();
|
||||
} else if (platform === 'linux') {
|
||||
const ext = path.extname(filePath);
|
||||
if (ext === '.AppImage') {
|
||||
// 先添加执行权限
|
||||
fs.chmodSync(filePath, '755');
|
||||
const child = spawn(filePath, [], {
|
||||
detached: true,
|
||||
stdio: 'ignore'
|
||||
});
|
||||
child.unref();
|
||||
} else if (ext === '.deb') {
|
||||
const child = spawn('pkexec', ['dpkg', '-i', filePath], {
|
||||
detached: true,
|
||||
stdio: 'ignore'
|
||||
});
|
||||
child.unref();
|
||||
}
|
||||
}
|
||||
|
||||
// 给安装程序一点时间启动
|
||||
setTimeout(() => {
|
||||
app.quit();
|
||||
}, 500);
|
||||
} catch (error) {
|
||||
console.error('启动安装程序失败:', error);
|
||||
// 尽管出错,仍然尝试退出应用
|
||||
app.quit();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -35,23 +35,22 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-actions" :class="{ 'mt-6': !downloading }">
|
||||
<n-button
|
||||
class="cancel-btn"
|
||||
:disabled="downloading"
|
||||
:loading="downloading"
|
||||
@click="closeModal"
|
||||
>
|
||||
<n-button class="cancel-btn" :disabled="downloading" @click="closeModal">
|
||||
{{ t('comp.update.cancel') }}
|
||||
</n-button>
|
||||
<n-button
|
||||
v-if="!downloading"
|
||||
type="primary"
|
||||
class="update-btn"
|
||||
:loading="downloading"
|
||||
:disabled="downloading"
|
||||
@click="handleUpdate"
|
||||
>
|
||||
{{ downloadBtnText }}
|
||||
</n-button>
|
||||
<!-- 后台下载 -->
|
||||
<n-button v-else class="update-btn" type="primary" @click="closeModal">
|
||||
{{ t('comp.update.backgroundDownload') }}
|
||||
</n-button>
|
||||
</div>
|
||||
<div v-if="!downloading" class="modal-desc mt-4 text-center">
|
||||
<p class="text-xs text-gray-400">
|
||||
@@ -71,7 +70,7 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { marked } from 'marked';
|
||||
import { computed, onMounted, onUnmounted, ref } from 'vue';
|
||||
import { computed, h, onMounted, onUnmounted, ref } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
import { useSettingsStore } from '@/store/modules/settings';
|
||||
@@ -80,6 +79,8 @@ import { checkUpdate, getProxyNodes, UpdateResult } from '@/utils/update';
|
||||
import config from '../../../../package.json';
|
||||
|
||||
const { t } = useI18n();
|
||||
const dialog = useDialog();
|
||||
const message = useMessage();
|
||||
|
||||
// 配置 marked
|
||||
marked.setOptions({
|
||||
@@ -136,6 +137,11 @@ const downloadBtnText = computed(() => {
|
||||
return t('comp.update.nowUpdate');
|
||||
});
|
||||
|
||||
// 下载完成后的文件路径
|
||||
const downloadedFilePath = ref('');
|
||||
// 防止对话框重复弹出
|
||||
const isDialogShown = ref(false);
|
||||
|
||||
// 处理下载状态更新
|
||||
const handleDownloadProgress = (_event: any, progress: number, status: string) => {
|
||||
downloadProgress.value = progress;
|
||||
@@ -145,16 +151,73 @@ const handleDownloadProgress = (_event: any, progress: number, status: string) =
|
||||
// 处理下载完成
|
||||
const handleDownloadComplete = (_event: any, success: boolean, filePath: string) => {
|
||||
downloading.value = false;
|
||||
if (success) {
|
||||
window.electron.ipcRenderer.send('install-update', filePath);
|
||||
} else {
|
||||
window.$message.error(t('comp.update.downloadFailed'));
|
||||
closeModal();
|
||||
|
||||
if (success && !isDialogShown.value) {
|
||||
downloadedFilePath.value = filePath;
|
||||
isDialogShown.value = true;
|
||||
|
||||
// 复制文件路径到剪贴板
|
||||
const copyFilePath = () => {
|
||||
navigator.clipboard
|
||||
.writeText(filePath)
|
||||
.then(() => {
|
||||
message.success(t('comp.update.copySuccess'));
|
||||
})
|
||||
.catch(() => {
|
||||
message.error(t('comp.update.copyFailed'));
|
||||
});
|
||||
};
|
||||
|
||||
// 使用naive-ui的对话框询问用户是否安装
|
||||
const dialogRef = dialog.create({
|
||||
title: t('comp.update.installConfirmTitle'),
|
||||
content: () =>
|
||||
h('div', { class: 'update-dialog-content' }, [
|
||||
h('p', { class: 'content-text' }, t('comp.update.installConfirmContent')),
|
||||
h('div', { class: 'divider' }),
|
||||
h('p', { class: 'manual-tip' }, t('comp.update.manualInstallTip')),
|
||||
h('div', { class: 'file-path-container' }, [
|
||||
h('div', { class: 'file-path-box' }, [
|
||||
h('p', { class: 'file-path-label' }, t('comp.update.fileLocation')),
|
||||
h('div', { class: 'file-path-value' }, filePath)
|
||||
]),
|
||||
h(
|
||||
'div',
|
||||
{
|
||||
class: 'copy-btn',
|
||||
onClick: copyFilePath
|
||||
},
|
||||
[h('i', { class: 'ri-file-copy-line' }), h('span', t('comp.update.copy'))]
|
||||
)
|
||||
])
|
||||
]),
|
||||
positiveText: t('comp.update.yesInstall'),
|
||||
negativeText: t('comp.update.noThanks'),
|
||||
onPositiveClick: () => {
|
||||
window.electron.ipcRenderer.send('install-update', filePath);
|
||||
},
|
||||
onNegativeClick: () => {
|
||||
closeModal();
|
||||
// 关闭当前窗口
|
||||
dialogRef.destroy();
|
||||
},
|
||||
onClose: () => {
|
||||
isDialogShown.value = false;
|
||||
}
|
||||
});
|
||||
} else if (!success) {
|
||||
message.error(t('comp.update.downloadFailed'));
|
||||
}
|
||||
};
|
||||
|
||||
// 监听下载事件
|
||||
onMounted(() => {
|
||||
checkForUpdates();
|
||||
// 确保事件监听器只注册一次
|
||||
window.electron.ipcRenderer.removeListener('download-progress', handleDownloadProgress);
|
||||
window.electron.ipcRenderer.removeListener('download-complete', handleDownloadComplete);
|
||||
|
||||
window.electron.ipcRenderer.on('download-progress', handleDownloadProgress);
|
||||
window.electron.ipcRenderer.on('download-complete', handleDownloadComplete);
|
||||
});
|
||||
@@ -163,6 +226,7 @@ onMounted(() => {
|
||||
onUnmounted(() => {
|
||||
window.electron.ipcRenderer.removeListener('download-progress', handleDownloadProgress);
|
||||
window.electron.ipcRenderer.removeListener('download-complete', handleDownloadComplete);
|
||||
isDialogShown.value = false;
|
||||
});
|
||||
|
||||
const handleUpdate = async () => {
|
||||
@@ -215,6 +279,7 @@ const handleUpdate = async () => {
|
||||
try {
|
||||
downloading.value = true;
|
||||
downloadStatus.value = t('comp.update.prepareDownload');
|
||||
isDialogShown.value = false;
|
||||
|
||||
// 获取代理节点列表
|
||||
const proxyHosts = await getProxyNodes();
|
||||
@@ -224,11 +289,11 @@ const handleUpdate = async () => {
|
||||
window.electron.ipcRenderer.send('start-download', proxyDownloadUrl);
|
||||
} catch (error) {
|
||||
downloading.value = false;
|
||||
window.$message.error(t('comp.update.startFailed'));
|
||||
message.error(t('comp.update.startFailed'));
|
||||
console.error('下载失败:', error);
|
||||
}
|
||||
} else {
|
||||
window.$message.error(t('comp.update.noDownloadUrl'));
|
||||
message.error(t('comp.update.noDownloadUrl'));
|
||||
window.open('https://github.com/algerkong/AlgerMusicPlayer/releases/latest', '_blank');
|
||||
}
|
||||
};
|
||||
@@ -355,3 +420,110 @@ const handleUpdate = async () => {
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<style lang="scss">
|
||||
/* 对话框内容样式 */
|
||||
.update-dialog-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
|
||||
.content-text {
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.divider {
|
||||
width: 100%;
|
||||
height: 1px;
|
||||
background-color: #e5e7eb;
|
||||
margin: 4px 0;
|
||||
}
|
||||
|
||||
.manual-tip {
|
||||
font-size: 14px;
|
||||
color: #6b7280;
|
||||
}
|
||||
|
||||
.file-path-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
margin-top: 8px;
|
||||
|
||||
.file-path-box {
|
||||
flex: 1;
|
||||
|
||||
.file-path-label {
|
||||
font-size: 12px;
|
||||
color: #6b7280;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.file-path-value {
|
||||
padding: 8px;
|
||||
border-radius: 4px;
|
||||
background-color: #f3f4f6;
|
||||
font-size: 12px;
|
||||
font-family: monospace;
|
||||
color: #1f2937;
|
||||
word-break: break-all;
|
||||
}
|
||||
}
|
||||
|
||||
.copy-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
padding: 8px 12px;
|
||||
border-radius: 4px;
|
||||
background-color: #e5e7eb;
|
||||
color: #4b5563;
|
||||
font-size: 12px;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.2s;
|
||||
|
||||
&:hover {
|
||||
background-color: #d1d5db;
|
||||
}
|
||||
|
||||
i {
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 深色模式样式 */
|
||||
.dark .update-dialog-content {
|
||||
.divider {
|
||||
background-color: #374151;
|
||||
}
|
||||
|
||||
.manual-tip {
|
||||
color: #9ca3af;
|
||||
}
|
||||
|
||||
.file-path-container {
|
||||
.file-path-box {
|
||||
.file-path-label {
|
||||
color: #9ca3af;
|
||||
}
|
||||
|
||||
.file-path-value {
|
||||
background-color: #1f2937;
|
||||
color: #d1d5db;
|
||||
}
|
||||
}
|
||||
|
||||
.copy-btn {
|
||||
background-color: #374151;
|
||||
color: #d1d5db;
|
||||
|
||||
&:hover {
|
||||
background-color: #4b5563;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user