mirror of
https://github.com/algerkong/AlgerMusicPlayer.git
synced 2026-04-14 06:30:49 +08:00
@@ -39,7 +39,18 @@ export default {
|
|||||||
downloadFailed: 'Download failed, please try again or download manually',
|
downloadFailed: 'Download failed, please try again or download manually',
|
||||||
startFailed: 'Start download failed, please try again or download manually',
|
startFailed: 'Start download failed, please try again or download manually',
|
||||||
noDownloadUrl:
|
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: {
|
coffee: {
|
||||||
title: 'Buy me a coffee',
|
title: 'Buy me a coffee',
|
||||||
|
|||||||
@@ -38,7 +38,17 @@ export default {
|
|||||||
nowUpdate: '立即更新',
|
nowUpdate: '立即更新',
|
||||||
downloadFailed: '下载失败,请重试或手动下载',
|
downloadFailed: '下载失败,请重试或手动下载',
|
||||||
startFailed: '启动下载失败,请重试或手动下载',
|
startFailed: '启动下载失败,请重试或手动下载',
|
||||||
noDownloadUrl: '未找到适合当前系统的安装包,请手动下载'
|
noDownloadUrl: '未找到适合当前系统的安装包,请手动下载',
|
||||||
|
installConfirmTitle: '安装更新',
|
||||||
|
installConfirmContent: '是否关闭应用并安装更新?',
|
||||||
|
manualInstallTip: '如果关闭应用后没有正常弹出安装程序,请至下载文件夹查找文件并手动打开。',
|
||||||
|
yesInstall: '立即安装',
|
||||||
|
noThanks: '稍后安装',
|
||||||
|
fileLocation: '文件位置',
|
||||||
|
copy: '复制路径',
|
||||||
|
copySuccess: '路径已复制到剪贴板',
|
||||||
|
copyFailed: '复制失败',
|
||||||
|
backgroundDownload: '后台下载'
|
||||||
},
|
},
|
||||||
coffee: {
|
coffee: {
|
||||||
title: '请我喝咖啡',
|
title: '请我喝咖啡',
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import { exec } from 'child_process';
|
import { spawn } from 'child_process';
|
||||||
import { app, BrowserWindow, ipcMain } from 'electron';
|
import { app, BrowserWindow, ipcMain } from 'electron';
|
||||||
import * as fs from 'fs';
|
import * as fs from 'fs';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
@@ -53,38 +53,49 @@ export function setupUpdateHandlers(_mainWindow: BrowserWindow) {
|
|||||||
|
|
||||||
const { platform } = process;
|
const { platform } = process;
|
||||||
|
|
||||||
// 关闭当前应用
|
// 先启动安装程序,再退出应用
|
||||||
app.quit();
|
try {
|
||||||
|
if (platform === 'win32') {
|
||||||
// 根据不同平台执行安装
|
// 使用spawn替代exec,并使用detached选项确保子进程独立运行
|
||||||
if (platform === 'win32') {
|
const child = spawn(filePath, [], {
|
||||||
exec(`"${filePath}"`, (error) => {
|
detached: true,
|
||||||
if (error) {
|
stdio: 'ignore'
|
||||||
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);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
} else if (ext === '.deb') {
|
child.unref();
|
||||||
exec(`pkexec dpkg -i "${filePath}"`, (error) => {
|
} else if (platform === 'darwin') {
|
||||||
if (error) {
|
// 挂载 DMG 文件
|
||||||
console.error('Error installing deb package:', error);
|
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>
|
</div>
|
||||||
<div class="modal-actions" :class="{ 'mt-6': !downloading }">
|
<div class="modal-actions" :class="{ 'mt-6': !downloading }">
|
||||||
<n-button
|
<n-button class="cancel-btn" :disabled="downloading" @click="closeModal">
|
||||||
class="cancel-btn"
|
|
||||||
:disabled="downloading"
|
|
||||||
:loading="downloading"
|
|
||||||
@click="closeModal"
|
|
||||||
>
|
|
||||||
{{ t('comp.update.cancel') }}
|
{{ t('comp.update.cancel') }}
|
||||||
</n-button>
|
</n-button>
|
||||||
<n-button
|
<n-button
|
||||||
|
v-if="!downloading"
|
||||||
type="primary"
|
type="primary"
|
||||||
class="update-btn"
|
class="update-btn"
|
||||||
:loading="downloading"
|
|
||||||
:disabled="downloading"
|
:disabled="downloading"
|
||||||
@click="handleUpdate"
|
@click="handleUpdate"
|
||||||
>
|
>
|
||||||
{{ downloadBtnText }}
|
{{ downloadBtnText }}
|
||||||
</n-button>
|
</n-button>
|
||||||
|
<!-- 后台下载 -->
|
||||||
|
<n-button v-else class="update-btn" type="primary" @click="closeModal">
|
||||||
|
{{ t('comp.update.backgroundDownload') }}
|
||||||
|
</n-button>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="!downloading" class="modal-desc mt-4 text-center">
|
<div v-if="!downloading" class="modal-desc mt-4 text-center">
|
||||||
<p class="text-xs text-gray-400">
|
<p class="text-xs text-gray-400">
|
||||||
@@ -71,7 +70,7 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { marked } from 'marked';
|
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 { useI18n } from 'vue-i18n';
|
||||||
|
|
||||||
import { useSettingsStore } from '@/store/modules/settings';
|
import { useSettingsStore } from '@/store/modules/settings';
|
||||||
@@ -80,6 +79,8 @@ import { checkUpdate, getProxyNodes, UpdateResult } from '@/utils/update';
|
|||||||
import config from '../../../../package.json';
|
import config from '../../../../package.json';
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
const dialog = useDialog();
|
||||||
|
const message = useMessage();
|
||||||
|
|
||||||
// 配置 marked
|
// 配置 marked
|
||||||
marked.setOptions({
|
marked.setOptions({
|
||||||
@@ -136,6 +137,11 @@ const downloadBtnText = computed(() => {
|
|||||||
return t('comp.update.nowUpdate');
|
return t('comp.update.nowUpdate');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 下载完成后的文件路径
|
||||||
|
const downloadedFilePath = ref('');
|
||||||
|
// 防止对话框重复弹出
|
||||||
|
const isDialogShown = ref(false);
|
||||||
|
|
||||||
// 处理下载状态更新
|
// 处理下载状态更新
|
||||||
const handleDownloadProgress = (_event: any, progress: number, status: string) => {
|
const handleDownloadProgress = (_event: any, progress: number, status: string) => {
|
||||||
downloadProgress.value = progress;
|
downloadProgress.value = progress;
|
||||||
@@ -145,16 +151,73 @@ const handleDownloadProgress = (_event: any, progress: number, status: string) =
|
|||||||
// 处理下载完成
|
// 处理下载完成
|
||||||
const handleDownloadComplete = (_event: any, success: boolean, filePath: string) => {
|
const handleDownloadComplete = (_event: any, success: boolean, filePath: string) => {
|
||||||
downloading.value = false;
|
downloading.value = false;
|
||||||
if (success) {
|
closeModal();
|
||||||
window.electron.ipcRenderer.send('install-update', filePath);
|
|
||||||
} else {
|
if (success && !isDialogShown.value) {
|
||||||
window.$message.error(t('comp.update.downloadFailed'));
|
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(() => {
|
onMounted(() => {
|
||||||
checkForUpdates();
|
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-progress', handleDownloadProgress);
|
||||||
window.electron.ipcRenderer.on('download-complete', handleDownloadComplete);
|
window.electron.ipcRenderer.on('download-complete', handleDownloadComplete);
|
||||||
});
|
});
|
||||||
@@ -163,6 +226,7 @@ onMounted(() => {
|
|||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
window.electron.ipcRenderer.removeListener('download-progress', handleDownloadProgress);
|
window.electron.ipcRenderer.removeListener('download-progress', handleDownloadProgress);
|
||||||
window.electron.ipcRenderer.removeListener('download-complete', handleDownloadComplete);
|
window.electron.ipcRenderer.removeListener('download-complete', handleDownloadComplete);
|
||||||
|
isDialogShown.value = false;
|
||||||
});
|
});
|
||||||
|
|
||||||
const handleUpdate = async () => {
|
const handleUpdate = async () => {
|
||||||
@@ -215,6 +279,7 @@ const handleUpdate = async () => {
|
|||||||
try {
|
try {
|
||||||
downloading.value = true;
|
downloading.value = true;
|
||||||
downloadStatus.value = t('comp.update.prepareDownload');
|
downloadStatus.value = t('comp.update.prepareDownload');
|
||||||
|
isDialogShown.value = false;
|
||||||
|
|
||||||
// 获取代理节点列表
|
// 获取代理节点列表
|
||||||
const proxyHosts = await getProxyNodes();
|
const proxyHosts = await getProxyNodes();
|
||||||
@@ -224,11 +289,11 @@ const handleUpdate = async () => {
|
|||||||
window.electron.ipcRenderer.send('start-download', proxyDownloadUrl);
|
window.electron.ipcRenderer.send('start-download', proxyDownloadUrl);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
downloading.value = false;
|
downloading.value = false;
|
||||||
window.$message.error(t('comp.update.startFailed'));
|
message.error(t('comp.update.startFailed'));
|
||||||
console.error('下载失败:', error);
|
console.error('下载失败:', error);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
window.$message.error(t('comp.update.noDownloadUrl'));
|
message.error(t('comp.update.noDownloadUrl'));
|
||||||
window.open('https://github.com/algerkong/AlgerMusicPlayer/releases/latest', '_blank');
|
window.open('https://github.com/algerkong/AlgerMusicPlayer/releases/latest', '_blank');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -355,3 +420,110 @@ const handleUpdate = async () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</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