6124d6060c
Co-authored-by: Cursor <cursoragent@cursor.com>
1467 lines
52 KiB
HTML
1467 lines
52 KiB
HTML
<!DOCTYPE html>
|
|
<html>
|
|
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<title>R0对战平台</title>
|
|
<style>
|
|
* {
|
|
margin: 0;
|
|
padding: 0;
|
|
box-sizing: border-box;
|
|
}
|
|
|
|
html,
|
|
body {
|
|
font-family: 'Segoe UI', 'Microsoft Yahei', 'Helvetica Neue', Helvetica, Arial, sans-serif;
|
|
-webkit-user-select: none;
|
|
user-select: none;
|
|
width: 640px;
|
|
height: 480px;
|
|
overflow: hidden;
|
|
background: #0a0a0a;
|
|
}
|
|
|
|
/* 主容器 */
|
|
.box {
|
|
width: 640px;
|
|
height: 480px;
|
|
position: relative;
|
|
overflow: hidden;
|
|
background: linear-gradient(135deg, #0f0f0f 0%, #1a1a1a 100%);
|
|
}
|
|
|
|
/* 视频背景容器 */
|
|
.video-container {
|
|
position: absolute;
|
|
top: 0;
|
|
left: 0;
|
|
width: 100%;
|
|
height: 100%;
|
|
z-index: 1;
|
|
}
|
|
|
|
.background-video {
|
|
position: absolute;
|
|
top: 50%;
|
|
left: 50%;
|
|
transform: translate(-50%, -50%);
|
|
min-width: 100%;
|
|
min-height: 100%;
|
|
width: auto;
|
|
height: auto;
|
|
object-fit: cover;
|
|
filter: brightness(0.4) contrast(1.1) saturate(0.85) blur(1.2px);
|
|
opacity: 0.65;
|
|
animation: videoFadeIn 1.5s ease-out;
|
|
}
|
|
|
|
@keyframes videoFadeIn {
|
|
0% {
|
|
opacity: 0;
|
|
filter: brightness(0.2) contrast(1.1) saturate(0.85) blur(4px);
|
|
}
|
|
100% {
|
|
opacity: 0.65;
|
|
filter: brightness(0.4) contrast(1.1) saturate(0.85) blur(1.2px);
|
|
}
|
|
}
|
|
|
|
/* 渐变遮罩层 */
|
|
.overlay-gradient {
|
|
position: absolute;
|
|
top: 0;
|
|
left: 0;
|
|
width: 100%;
|
|
height: 100%;
|
|
background: radial-gradient(ellipse at center, transparent 0%, rgba(0, 0, 0, 0.4) 40%, rgba(0, 0, 0, 0.8) 100%);
|
|
z-index: 2;
|
|
}
|
|
|
|
/* 扫描线效果 */
|
|
.scanlines {
|
|
position: absolute;
|
|
top: 0;
|
|
left: 0;
|
|
width: 100%;
|
|
height: 100%;
|
|
z-index: 3;
|
|
pointer-events: none;
|
|
background: repeating-linear-gradient(
|
|
0deg,
|
|
transparent,
|
|
transparent 2px,
|
|
rgba(255, 255, 255, 0.01) 2px,
|
|
rgba(255, 255, 255, 0.01) 4px
|
|
);
|
|
animation: scanlines 8s linear infinite;
|
|
}
|
|
|
|
@keyframes scanlines {
|
|
0% {
|
|
transform: translateY(0);
|
|
}
|
|
100% {
|
|
transform: translateY(10px);
|
|
}
|
|
}
|
|
|
|
/* 主内容区域 */
|
|
.main-box {
|
|
position: absolute;
|
|
top: 50%;
|
|
left: 50%;
|
|
transform: translate(-50%, -50%);
|
|
width: 480px;
|
|
height: 360px;
|
|
z-index: 10;
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
justify-content: center;
|
|
pointer-events: none; /* 禁用鼠标交互,保持界面独立 */
|
|
}
|
|
|
|
/* Logo 容器 - 玻璃态效果 */
|
|
.logo-container {
|
|
position: relative;
|
|
width: 320px;
|
|
height: 180px;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
margin-bottom: 40px;
|
|
background: linear-gradient(135deg, rgba(255, 255, 255, 0.05) 0%, rgba(255, 255, 255, 0.02) 100%);
|
|
backdrop-filter: blur(10px);
|
|
-webkit-backdrop-filter: blur(10px);
|
|
border: 1px solid rgba(255, 255, 255, 0.08);
|
|
border-radius: 20px;
|
|
box-shadow:
|
|
0 8px 32px rgba(0, 0, 0, 0.3),
|
|
inset 0 1px 0 rgba(255, 255, 255, 0.1),
|
|
0 0 80px rgba(76, 175, 80, 0.15);
|
|
animation: logoContainerFadeIn 0.8s ease-out;
|
|
pointer-events: none; /* 禁用鼠标交互 */
|
|
cursor: default; /* 确保鼠标指针不会改变 */
|
|
}
|
|
|
|
@keyframes logoContainerFadeIn {
|
|
0% {
|
|
opacity: 0;
|
|
transform: scale(0.9) translateY(-20px);
|
|
}
|
|
100% {
|
|
opacity: 1;
|
|
transform: scale(1) translateY(0);
|
|
}
|
|
}
|
|
|
|
/* Logo 主体 */
|
|
.logo-main {
|
|
position: relative;
|
|
z-index: 2;
|
|
}
|
|
|
|
.logo-rel {
|
|
width: 200px;
|
|
height: auto;
|
|
filter: drop-shadow(0 4px 20px rgba(76, 175, 80, 0.3));
|
|
animation: logoFloat 3s ease-in-out infinite;
|
|
}
|
|
|
|
@keyframes logoFloat {
|
|
0%, 100% {
|
|
transform: translateY(0);
|
|
}
|
|
50% {
|
|
transform: translateY(-5px);
|
|
}
|
|
}
|
|
|
|
/* Logo 光效 */
|
|
.logo-glow {
|
|
position: absolute;
|
|
top: 50%;
|
|
left: 50%;
|
|
transform: translate(-50%, -50%);
|
|
width: 240px;
|
|
height: 240px;
|
|
background: radial-gradient(circle, rgba(76, 175, 80, 0.2) 0%, transparent 70%);
|
|
filter: blur(20px);
|
|
animation: glowPulse 2s ease-in-out infinite;
|
|
z-index: 1;
|
|
}
|
|
|
|
@keyframes glowPulse {
|
|
0%, 100% {
|
|
opacity: 0.3;
|
|
transform: translate(-50%, -50%) scale(1);
|
|
}
|
|
50% {
|
|
opacity: 0.6;
|
|
transform: translate(-50%, -50%) scale(1.1);
|
|
}
|
|
}
|
|
|
|
/* 进度条容器 */
|
|
.progress-container {
|
|
width: 280px;
|
|
margin-top: 20px;
|
|
animation: fadeInUp 0.6s ease-out 0.3s both;
|
|
}
|
|
|
|
@keyframes fadeInUp {
|
|
0% {
|
|
opacity: 0;
|
|
transform: translateY(20px);
|
|
}
|
|
100% {
|
|
opacity: 1;
|
|
transform: translateY(0);
|
|
}
|
|
}
|
|
|
|
/* 进度条背景 */
|
|
.progress-box {
|
|
position: relative;
|
|
width: 100%;
|
|
height: 4px;
|
|
background: rgba(255, 255, 255, 0.05);
|
|
border-radius: 2px;
|
|
overflow: hidden;
|
|
box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.3);
|
|
}
|
|
|
|
/* 进度条填充 */
|
|
#progress {
|
|
position: absolute;
|
|
top: 0;
|
|
left: 0;
|
|
height: 100%;
|
|
width: 0%;
|
|
background: linear-gradient(90deg, #4CAF50 0%, #6FFF63 100%);
|
|
border-radius: 2px;
|
|
transition: width 0.3s ease;
|
|
box-shadow: 0 0 10px rgba(76, 175, 80, 0.5);
|
|
}
|
|
|
|
/* 进度条光效 */
|
|
#progress::after {
|
|
content: '';
|
|
position: absolute;
|
|
top: 0;
|
|
right: 0;
|
|
width: 20px;
|
|
height: 100%;
|
|
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.4));
|
|
animation: shimmer 1s linear infinite;
|
|
}
|
|
|
|
@keyframes shimmer {
|
|
0% {
|
|
transform: translateX(-20px);
|
|
}
|
|
100% {
|
|
transform: translateX(20px);
|
|
}
|
|
}
|
|
|
|
/* 状态文本 */
|
|
#text-tip {
|
|
display: block;
|
|
margin-top: 15px;
|
|
text-align: center;
|
|
color: rgba(255, 255, 255, 0.7);
|
|
font-size: 13px;
|
|
letter-spacing: 0.5px;
|
|
text-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
|
|
}
|
|
|
|
/* 进度百分比 */
|
|
#text-progress {
|
|
display: inline-block;
|
|
margin-left: 10px;
|
|
color: #6FFF63;
|
|
font-weight: 500;
|
|
text-shadow: 0 0 10px rgba(111, 255, 99, 0.5);
|
|
}
|
|
|
|
/* 慢速提示 */
|
|
#slow {
|
|
position: absolute;
|
|
bottom: 40px;
|
|
left: 50%;
|
|
transform: translateX(-50%);
|
|
background: linear-gradient(135deg, rgba(15, 15, 15, 0.95) 0%, rgba(25, 25, 25, 0.95) 100%);
|
|
backdrop-filter: blur(10px);
|
|
-webkit-backdrop-filter: blur(10px);
|
|
border: 1px solid rgba(255, 255, 255, 0.1);
|
|
border-radius: 10px;
|
|
padding: 12px 20px;
|
|
color: rgba(255, 255, 255, 0.8);
|
|
font-size: 12px;
|
|
opacity: 0;
|
|
transition: opacity 0.3s ease;
|
|
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);
|
|
z-index: 20;
|
|
pointer-events: auto; /* 确保慢速提示框可以交互 */
|
|
}
|
|
|
|
#slow a {
|
|
color: #6FFF63;
|
|
text-decoration: none;
|
|
font-weight: 500;
|
|
margin-left: 5px;
|
|
transition: color 0.2s ease;
|
|
pointer-events: auto; /* 保持链接可点击 */
|
|
cursor: pointer;
|
|
}
|
|
|
|
#slow a:hover {
|
|
color: #4CAF50;
|
|
text-decoration: underline;
|
|
}
|
|
|
|
/* 装饰性元素 */
|
|
.decoration-dots {
|
|
position: absolute;
|
|
bottom: 20px;
|
|
left: 50%;
|
|
transform: translateX(-50%);
|
|
display: flex;
|
|
gap: 8px;
|
|
z-index: 5;
|
|
}
|
|
|
|
.dot {
|
|
width: 6px;
|
|
height: 6px;
|
|
background: rgba(255, 255, 255, 0.2);
|
|
border-radius: 50%;
|
|
animation: dotPulse 1.5s ease-in-out infinite;
|
|
}
|
|
|
|
.dot:nth-child(1) { animation-delay: 0s; }
|
|
.dot:nth-child(2) { animation-delay: 0.2s; }
|
|
.dot:nth-child(3) { animation-delay: 0.4s; }
|
|
|
|
@keyframes dotPulse {
|
|
0%, 100% {
|
|
opacity: 0.2;
|
|
transform: scale(1);
|
|
}
|
|
50% {
|
|
opacity: 0.6;
|
|
transform: scale(1.2);
|
|
}
|
|
}
|
|
|
|
/* 角落装饰线 */
|
|
.corner-decoration {
|
|
position: absolute;
|
|
width: 40px;
|
|
height: 40px;
|
|
opacity: 0.3;
|
|
z-index: 4;
|
|
}
|
|
|
|
.corner-decoration::before,
|
|
.corner-decoration::after {
|
|
content: '';
|
|
position: absolute;
|
|
background: #4CAF50;
|
|
}
|
|
|
|
.corner-decoration.top-left {
|
|
top: 20px;
|
|
left: 20px;
|
|
}
|
|
|
|
.corner-decoration.top-left::before {
|
|
top: 0;
|
|
left: 0;
|
|
width: 100%;
|
|
height: 1px;
|
|
}
|
|
|
|
.corner-decoration.top-left::after {
|
|
top: 0;
|
|
left: 0;
|
|
width: 1px;
|
|
height: 100%;
|
|
}
|
|
|
|
.corner-decoration.bottom-right {
|
|
bottom: 20px;
|
|
right: 20px;
|
|
}
|
|
|
|
.corner-decoration.bottom-right::before {
|
|
bottom: 0;
|
|
right: 0;
|
|
width: 100%;
|
|
height: 1px;
|
|
}
|
|
|
|
.corner-decoration.bottom-right::after {
|
|
bottom: 0;
|
|
right: 0;
|
|
width: 1px;
|
|
height: 100%;
|
|
}
|
|
|
|
/* 隐藏原有的不需要的元素 */
|
|
.left-5, .right-e, .shadow, .text-img {
|
|
display: none;
|
|
}
|
|
|
|
/* 版本信息 */
|
|
.version-info {
|
|
position: absolute;
|
|
bottom: 10px;
|
|
right: 15px;
|
|
color: rgba(255, 255, 255, 0.3);
|
|
font-size: 10px;
|
|
z-index: 5;
|
|
letter-spacing: 0.5px;
|
|
}
|
|
|
|
/* 添加粒子效果 */
|
|
.particles {
|
|
position: absolute;
|
|
width: 100%;
|
|
height: 100%;
|
|
overflow: hidden;
|
|
z-index: 4;
|
|
pointer-events: none;
|
|
}
|
|
|
|
.particle {
|
|
position: absolute;
|
|
width: 2px;
|
|
height: 2px;
|
|
background: rgba(111, 255, 99, 0.4);
|
|
border-radius: 50%;
|
|
animation: floatUp 10s linear infinite;
|
|
}
|
|
|
|
@keyframes floatUp {
|
|
0% {
|
|
transform: translateY(100vh) translateX(0);
|
|
opacity: 0;
|
|
}
|
|
10% {
|
|
opacity: 0.4;
|
|
}
|
|
90% {
|
|
opacity: 0.4;
|
|
}
|
|
100% {
|
|
transform: translateY(-10vh) translateX(100px);
|
|
opacity: 0;
|
|
}
|
|
}
|
|
|
|
.particle:nth-child(1) { left: 10%; animation-delay: 0s; animation-duration: 8s; }
|
|
.particle:nth-child(2) { left: 20%; animation-delay: 1s; animation-duration: 10s; }
|
|
.particle:nth-child(3) { left: 30%; animation-delay: 2s; animation-duration: 9s; }
|
|
.particle:nth-child(4) { left: 40%; animation-delay: 1.5s; animation-duration: 11s; }
|
|
.particle:nth-child(5) { left: 50%; animation-delay: 3s; animation-duration: 8.5s; }
|
|
.particle:nth-child(6) { left: 60%; animation-delay: 2.5s; animation-duration: 9.5s; }
|
|
.particle:nth-child(7) { left: 70%; animation-delay: 1.8s; animation-duration: 10.5s; }
|
|
.particle:nth-child(8) { left: 80%; animation-delay: 3.5s; animation-duration: 8s; }
|
|
.particle:nth-child(9) { left: 90%; animation-delay: 4s; animation-duration: 9s; }
|
|
.particle:nth-child(10) { left: 85%; animation-delay: 0.5s; animation-duration: 11s; }
|
|
|
|
/* 移除鼠标交互效果 - 保持界面独立于鼠标操作 */
|
|
/* Logo容器不再响应鼠标悬浮 */
|
|
|
|
/* 添加加载完成时的过渡效果 */
|
|
.fade-out {
|
|
animation: fadeOut 0.5s ease-out forwards;
|
|
}
|
|
|
|
@keyframes fadeOut {
|
|
0% {
|
|
opacity: 1;
|
|
}
|
|
100% {
|
|
opacity: 0;
|
|
}
|
|
}
|
|
|
|
/* 增强进度条完成时的效果 */
|
|
#progress.complete {
|
|
background: linear-gradient(90deg, #4CAF50 0%, #8BC34A 50%, #6FFF63 100%);
|
|
box-shadow: 0 0 20px rgba(76, 175, 80, 0.8);
|
|
}
|
|
|
|
/* 添加加载提示文字的动画 */
|
|
@keyframes textPulse {
|
|
0%, 100% {
|
|
opacity: 0.7;
|
|
}
|
|
50% {
|
|
opacity: 1;
|
|
}
|
|
}
|
|
|
|
#text-tip.loading {
|
|
animation: textPulse 1.5s ease-in-out infinite;
|
|
}
|
|
</style>
|
|
</head>
|
|
|
|
<body>
|
|
<div class="box">
|
|
<!-- 视频背景层 -->
|
|
<div class="video-container">
|
|
<video class="background-video" autoplay loop muted>
|
|
<source src="./images/loading.mp4" type="video/mp4">
|
|
</video>
|
|
</div>
|
|
|
|
<!-- 渐变遮罩层 -->
|
|
<div class="overlay-gradient"></div>
|
|
|
|
<!-- 扫描线效果 -->
|
|
<div class="scanlines"></div>
|
|
|
|
<!-- 粒子效果 -->
|
|
<div class="particles">
|
|
<span class="particle"></span>
|
|
<span class="particle"></span>
|
|
<span class="particle"></span>
|
|
<span class="particle"></span>
|
|
<span class="particle"></span>
|
|
<span class="particle"></span>
|
|
<span class="particle"></span>
|
|
<span class="particle"></span>
|
|
<span class="particle"></span>
|
|
<span class="particle"></span>
|
|
</div>
|
|
|
|
<!-- 角落装饰 -->
|
|
<div class="corner-decoration top-left"></div>
|
|
<div class="corner-decoration bottom-right"></div>
|
|
|
|
<!-- 主内容区域 -->
|
|
<div class="main-box">
|
|
<!-- Logo 容器 -->
|
|
<!-- <div class="logo-container"> -->
|
|
<div class="logo-glow"></div>
|
|
<div class="logo-main">
|
|
<img src="./images/bg.png" class="logo-rel" />
|
|
</div>
|
|
<!-- </div> -->
|
|
|
|
<!-- 进度条容器 -->
|
|
<div class="progress-container">
|
|
<div class="progress-box">
|
|
<div id="progress"></div>
|
|
</div>
|
|
<div id="text-tip">正在检测升级</div>
|
|
<span id="text-progress"></span>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 装饰性点 -->
|
|
<div class="decoration-dots">
|
|
<span class="dot"></span>
|
|
<span class="dot"></span>
|
|
<span class="dot"></span>
|
|
</div>
|
|
|
|
<!-- 慢速提示 -->
|
|
<div id="slow">
|
|
<span>⚠️ 当前网速较慢,建议下载最新安装包</span>
|
|
<a onclick="openExternal('https://r0csgo.com/', true);" href="javascript:;">点击下载</a>
|
|
</div>
|
|
|
|
<!-- 版本信息 -->
|
|
<div class="version-info">v2.0</div>
|
|
</div>
|
|
</body>
|
|
<script>
|
|
const { ipcRenderer } = require('electron');
|
|
const fs = require('fs');
|
|
const path = require('path');
|
|
const axios = require('axios');
|
|
const StreamZip = require('node-stream-zip');
|
|
const crypto = require('crypto');
|
|
const cmd = require('node-cmd');
|
|
const regedit = require('regedit');
|
|
|
|
// 设置外部VBS路径
|
|
const getVbsDirectory = async () => {
|
|
const exePath = await ipcRenderer.invoke('app-get-path', 'exe');
|
|
return path.join(path.dirname(exePath), './resources/vbs');
|
|
};
|
|
|
|
// 异步初始化vbs目录
|
|
getVbsDirectory().then(vbsDirectory => {
|
|
regedit.setExternalVBSLocation(vbsDirectory);
|
|
});
|
|
|
|
// 运行库下载地址
|
|
const VC_REDISTRIB_PACKAGE_X86_URL = 'https://cdn.r0csgo.com/runtime/VC_redist.x86.exe';
|
|
const VC_REDISTRIB_PACKAGE_X64_URL = 'https://cdn.r0csgo.com/runtime/VC_redist.x64.exe';
|
|
const DOTNET_SDK_X64_URL = 'https://cdn.r0csgo.com/runtime/windowsdesktop-runtime-8.0.21-win-x64.exe';
|
|
const DOTNET_SDK_X86_URL = 'https://cdn.r0csgo.com/runtime/windowsdesktop-runtime-8.0.21-win-x86.exe';
|
|
const ASPNET_CORE_X64_URL = 'https://cdn.r0csgo.com/runtime/aspnetcore-runtime-8.0.21-win-x64.exe';
|
|
const ASPNET_CORE_X86_URL = 'https://cdn.r0csgo.com/runtime/aspnetcore-runtime-8.0.21-win-x86.exe';
|
|
|
|
setTimeout(() => {
|
|
//页面停留超过60秒,太慢了
|
|
var slowEl = document.getElementById('slow');
|
|
slowEl.style.opacity = '1';
|
|
slowEl.style.pointerEvents = 'auto';
|
|
}, 60000);
|
|
|
|
function openExternal(url, close = false) {
|
|
require('electron').shell.openExternal(url);
|
|
if (close) {
|
|
setTimeout(() => {
|
|
require('electron').ipcRenderer.invoke('app-quit');
|
|
}, 2000);
|
|
}
|
|
}
|
|
|
|
function sleep(ms) {
|
|
return new Promise(resolve => setTimeout(resolve, ms))
|
|
}
|
|
|
|
// 检测VC运行库是否已安装
|
|
function checkVCRedistributable() {
|
|
return new Promise((resolve) => {
|
|
// VC运行库注册表键值
|
|
const vcKeys = [
|
|
'HKLM\\SOFTWARE\\Microsoft\\VisualStudio\\14.0\\VC\\Runtimes\\x86',
|
|
'HKLM\\SOFTWARE\\Microsoft\\VisualStudio\\15.0\\VC\\Runtimes\\x86',
|
|
'HKLM\\SOFTWARE\\Microsoft\\VisualStudio\\16.0\\VC\\Runtimes\\x86',
|
|
'HKLM\\SOFTWARE\\Microsoft\\VisualStudio\\17.0\\VC\\Runtimes\\x86',
|
|
'HKLM\\SOFTWARE\\Microsoft\\VisualStudio\\14.0\\VC\\Runtimes\\x64',
|
|
'HKLM\\SOFTWARE\\Microsoft\\VisualStudio\\15.0\\VC\\Runtimes\\x64',
|
|
'HKLM\\SOFTWARE\\Microsoft\\VisualStudio\\16.0\\VC\\Runtimes\\x64',
|
|
'HKLM\\SOFTWARE\\Microsoft\\VisualStudio\\17.0\\VC\\Runtimes\\x64'
|
|
];
|
|
|
|
regedit.list(vcKeys, (err, result) => {
|
|
if (err) {
|
|
console.log('检测VC运行库时出错:', err);
|
|
resolve(false);
|
|
return;
|
|
}
|
|
|
|
// 检查是否有任何一个键存在且已安装
|
|
for (const key of vcKeys) {
|
|
if (result[key] && result[key].exists && result[key].values && result[key].values.Installed) {
|
|
const installed = result[key].values.Installed.value;
|
|
if (installed === 1) {
|
|
console.log('找到已安装的VC运行库:', key);
|
|
resolve(true);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
console.log('未找到已安装的VC运行库');
|
|
resolve(false);
|
|
});
|
|
});
|
|
}
|
|
|
|
// 检测.NET运行库是否已安装
|
|
function checkDotNetRuntime() {
|
|
return new Promise((resolve) => {
|
|
// 使用node-cmd执行dotnet --list-runtimes命令
|
|
cmd.run('dotnet --list-runtimes', (err, data, stderr) => {
|
|
if (err || stderr) {
|
|
console.log('检测.NET运行库时出错:', err || stderr);
|
|
resolve(false);
|
|
return;
|
|
}
|
|
|
|
// 检查输出中是否包含8.0.21版本的Microsoft.WindowsDesktop.App
|
|
const lines = data.split('\n');
|
|
for (const line of lines) {
|
|
if (line.includes('Microsoft.WindowsDesktop.App') && line.includes('8.0.21')) {
|
|
console.log('找到已安装的.NET运行库:', line.trim());
|
|
resolve(true);
|
|
return;
|
|
}
|
|
}
|
|
|
|
console.log('未找到.NET WindowsDesktop 8.0.21运行库');
|
|
resolve(false);
|
|
});
|
|
});
|
|
}
|
|
|
|
// 检测ASP.NET Core运行库是否已安装
|
|
function checkAspNetCoreRuntime() {
|
|
return new Promise((resolve) => {
|
|
// 使用node-cmd执行dotnet --list-runtimes命令
|
|
cmd.run('dotnet --list-runtimes', (err, data, stderr) => {
|
|
if (err || stderr) {
|
|
console.log('检测ASP.NET Core运行库时出错:', err || stderr);
|
|
resolve(false);
|
|
return;
|
|
}
|
|
|
|
// 检查输出中是否包含8.0.21版本的Microsoft.AspNetCore.App
|
|
const lines = data.split('\n');
|
|
for (const line of lines) {
|
|
if (line.includes('Microsoft.AspNetCore.App') && line.includes('8.0.21')) {
|
|
console.log('找到已安装的ASP.NET Core运行库:', line.trim());
|
|
resolve(true);
|
|
return;
|
|
}
|
|
}
|
|
|
|
console.log('未找到ASP.NET Core 8.0.21运行库');
|
|
resolve(false);
|
|
});
|
|
});
|
|
}
|
|
|
|
// 获取系统架构
|
|
function getSystemArchitecture() {
|
|
return process.arch === 'x64' ? 'x64' : 'x86';
|
|
}
|
|
|
|
// 下载运行库
|
|
async function downloadRuntime(url, fileName) {
|
|
return new Promise((resolve, reject) => {
|
|
const request = require('request');
|
|
const progress = require('request-progress');
|
|
|
|
progress(request(url), {})
|
|
.on('progress', function(state) {
|
|
const percentCompleted = Math.floor(state.percent * 100);
|
|
updateProgress(percentCompleted, `正在下载运行库 ${percentCompleted}%`);
|
|
})
|
|
.on('error', function(err) {
|
|
reject(err);
|
|
})
|
|
.on('end', function() {
|
|
setTimeout(()=>{
|
|
resolve();//防止EBUSY,下载完等一会再安装
|
|
}, 500);
|
|
})
|
|
.pipe(fs.createWriteStream(fileName));
|
|
});
|
|
}
|
|
|
|
// 安装运行库
|
|
async function installRuntime(fileName, runtimeName) {
|
|
return new Promise((resolve, reject) => {
|
|
document.getElementById("text-tip").innerHTML = `正在安装${runtimeName}...`;
|
|
|
|
const { spawn } = require('child_process');
|
|
|
|
// 使用静默安装参数
|
|
const installer = spawn(fileName, ['/install', '/passive', '/norestart'], {
|
|
stdio: ['ignore', 'pipe', 'pipe']
|
|
});
|
|
|
|
let output = '';
|
|
let errorOutput = '';
|
|
|
|
installer.stdout.on('data', (data) => {
|
|
output += data.toString();
|
|
});
|
|
|
|
installer.stderr.on('data', (data) => {
|
|
errorOutput += data.toString();
|
|
});
|
|
|
|
installer.on('close', (code) => {
|
|
console.log(`${runtimeName}安装完成,退出代码: ${code}`);
|
|
console.log('安装输出:', output);
|
|
if (errorOutput) {
|
|
console.log('安装错误输出:', errorOutput);
|
|
}
|
|
|
|
// 删除安装包文件
|
|
try {
|
|
if (fs.existsSync(fileName)) {
|
|
fs.unlinkSync(fileName);
|
|
}
|
|
} catch (deleteError) {
|
|
console.log('删除安装包失败:', deleteError);
|
|
}
|
|
|
|
if (code === 0 || code === 3010) { // 0表示成功,3010表示需要重启但安装成功
|
|
resolve(true);
|
|
} else {
|
|
reject(new Error(`${runtimeName}安装失败,退出代码: ${code}`));
|
|
}
|
|
});
|
|
|
|
installer.on('error', (error) => {
|
|
console.error(`${runtimeName}安装进程启动失败:`, error);
|
|
reject(error);
|
|
});
|
|
});
|
|
}
|
|
|
|
// 安装VC运行库
|
|
async function installVCRedistributable() {
|
|
const arch = getSystemArchitecture();
|
|
const url = arch === 'x64' ? VC_REDISTRIB_PACKAGE_X64_URL : VC_REDISTRIB_PACKAGE_X86_URL;
|
|
const fileName = `VC_redist.${arch}.exe`;
|
|
|
|
try {
|
|
await downloadRuntime(url, fileName);
|
|
await installRuntime(fileName, 'VC++运行库');
|
|
return true;
|
|
} catch (error) {
|
|
console.error('VC运行库安装失败:', error);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// 安装.NET运行库
|
|
async function installDotNetRuntime() {
|
|
const arch = getSystemArchitecture();
|
|
const url = arch === 'x64' ? DOTNET_SDK_X64_URL : DOTNET_SDK_X86_URL;
|
|
const fileName = `windowsdesktop-runtime-8.0.21-win-${arch}.exe`;
|
|
|
|
try {
|
|
await downloadRuntime(url, fileName);
|
|
await installRuntime(fileName, '.NET运行库');
|
|
return true;
|
|
} catch (error) {
|
|
console.error('.NET运行库安装失败:', error);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// 安装ASP.NET Core运行库
|
|
async function installAspNetCoreRuntime() {
|
|
const arch = getSystemArchitecture();
|
|
const url = arch === 'x64' ? ASPNET_CORE_X64_URL : ASPNET_CORE_X86_URL;
|
|
const fileName = `aspnetcore-runtime-8.0.21-win-${arch}.exe`;
|
|
|
|
try {
|
|
await downloadRuntime(url, fileName);
|
|
await installRuntime(fileName, 'ASP.NET Core运行库');
|
|
return true;
|
|
} catch (error) {
|
|
console.error('ASP.NET Core运行库安装失败:', error);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// 检查并安装必要的运行环境
|
|
async function checkAndInstallRuntimeEnvironments() {
|
|
try {
|
|
document.getElementById("text-tip").innerHTML = "正在检测运行环境...";
|
|
document.getElementById("progress").style.width = "5%";
|
|
|
|
// 检测VC运行库
|
|
const vcInstalled = await checkVCRedistributable();
|
|
document.getElementById("progress").style.width = "20%";
|
|
|
|
// 检测.NET运行库
|
|
const dotnetInstalled = await checkDotNetRuntime();
|
|
document.getElementById("progress").style.width = "35%";
|
|
|
|
// 检测ASP.NET Core运行库
|
|
const aspnetInstalled = await checkAspNetCoreRuntime();
|
|
document.getElementById("progress").style.width = "50%";
|
|
|
|
let installationNeeded = false;
|
|
|
|
// 如果VC运行库未安装,则安装
|
|
if (!vcInstalled) {
|
|
console.log('VC运行库未安装,开始安装...');
|
|
document.getElementById("text-tip").innerHTML = "正在安装VC++运行库...";
|
|
const vcInstallSuccess = await installVCRedistributable();
|
|
if (!vcInstallSuccess) {
|
|
console.error('VC运行库安装失败');
|
|
document.getElementById("text-tip").innerHTML = "VC++运行库安装失败,但程序将继续运行...";
|
|
await sleep(2000);
|
|
}
|
|
installationNeeded = true;
|
|
document.getElementById("progress").style.width = "65%";
|
|
} else {
|
|
console.log('VC运行库已安装');
|
|
document.getElementById("progress").style.width = "65%";
|
|
}
|
|
|
|
// 如果.NET运行库未安装,则安装
|
|
if (!dotnetInstalled) {
|
|
console.log('.NET运行库未安装,开始安装...');
|
|
document.getElementById("text-tip").innerHTML = "正在安装.NET运行库...";
|
|
const dotnetInstallSuccess = await installDotNetRuntime();
|
|
if (!dotnetInstallSuccess) {
|
|
console.error('.NET运行库安装失败');
|
|
document.getElementById("text-tip").innerHTML = ".NET运行库安装失败,但程序将继续运行...";
|
|
await sleep(2000);
|
|
}
|
|
installationNeeded = true;
|
|
document.getElementById("progress").style.width = "80%";
|
|
} else {
|
|
console.log('.NET运行库已安装');
|
|
document.getElementById("progress").style.width = "80%";
|
|
}
|
|
|
|
// 如果ASP.NET Core运行库未安装,则安装
|
|
if (!aspnetInstalled) {
|
|
console.log('ASP.NET Core运行库未安装,开始安装...');
|
|
document.getElementById("text-tip").innerHTML = "正在安装ASP.NET Core运行库...";
|
|
const aspnetInstallSuccess = await installAspNetCoreRuntime();
|
|
if (!aspnetInstallSuccess) {
|
|
console.error('ASP.NET Core运行库安装失败');
|
|
document.getElementById("text-tip").innerHTML = "ASP.NET Core运行库安装失败,但程序将继续运行...";
|
|
await sleep(2000);
|
|
}
|
|
installationNeeded = true;
|
|
} else {
|
|
console.log('ASP.NET Core运行库已安装');
|
|
}
|
|
|
|
document.getElementById("progress").style.width = "100%";
|
|
|
|
if (installationNeeded) {
|
|
document.getElementById("text-tip").innerHTML = "运行环境安装完成";
|
|
await sleep(1000);
|
|
} else {
|
|
document.getElementById("text-tip").innerHTML = "运行环境检测完成";
|
|
await sleep(500);
|
|
}
|
|
|
|
return true;
|
|
|
|
} catch (error) {
|
|
console.error('运行环境检测失败:', error);
|
|
document.getElementById("text-tip").innerHTML = "运行环境检测失败,程序将继续运行...";
|
|
await sleep(2000);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// 计算文件hash
|
|
function calculateFileHash(filePath) {
|
|
return new Promise((resolve, reject) => {
|
|
const hash = crypto.createHash('md5');
|
|
const stream = fs.createReadStream(filePath);
|
|
stream.on('error', reject);
|
|
stream.on('data', chunk => hash.update(chunk));
|
|
stream.on('end', () => resolve(hash.digest('hex')));
|
|
});
|
|
}
|
|
|
|
// 解压business.asar
|
|
async function extractBusinessAsar() {
|
|
const asarPath = './resources/business.asar';
|
|
const extractPath = './business_temp';
|
|
|
|
if (fs.existsSync(extractPath)) {
|
|
fs.rmSync(extractPath, { recursive: true, force: true });
|
|
}
|
|
fs.mkdirSync(extractPath, { recursive: true });
|
|
|
|
const asar = require('asar');
|
|
await asar.extractAll(asarPath, extractPath);
|
|
return extractPath;
|
|
}
|
|
|
|
// 重新打包business.asar
|
|
async function packBusinessAsar(sourcePath) {
|
|
const asarPath = './resources/business.asar';
|
|
const asar = require('asar');
|
|
await asar.createPackage(sourcePath, asarPath);
|
|
}
|
|
|
|
// 应用增量更新
|
|
async function applyIncrementalUpdate(updatePath, manifest) {
|
|
const businessPath = await extractBusinessAsar();
|
|
|
|
for (const file of manifest.files) {
|
|
const targetPath = path.join(businessPath, file.path);
|
|
const sourcePath = path.join(updatePath, file.path);
|
|
|
|
switch (file.action) {
|
|
case 'add':
|
|
case 'modify':
|
|
// 确保目标目录存在
|
|
fs.mkdirSync(path.dirname(targetPath), { recursive: true });
|
|
fs.copyFileSync(sourcePath, targetPath);
|
|
break;
|
|
case 'delete':
|
|
if (fs.existsSync(targetPath)) {
|
|
fs.unlinkSync(targetPath);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
await packBusinessAsar(businessPath);
|
|
|
|
// 清理临时文件
|
|
fs.rmSync(businessPath, { recursive: true, force: true });
|
|
fs.rmSync(updatePath, { recursive: true, force: true });
|
|
}
|
|
|
|
// 应用多个增量更新(优化版本:只解压和打包business.asar一次)
|
|
async function applyMultipleIncrementalUpdates(extractedUpdates) {
|
|
// 只解压business.asar一次
|
|
const businessPath = await extractBusinessAsar();
|
|
|
|
// 按版本顺序依次应用所有更新
|
|
for (let i = 0; i < extractedUpdates.length; i++) {
|
|
const { update, extractPath, manifest } = extractedUpdates[i];
|
|
|
|
console.log(`应用更新: ${update.from_version} -> ${update.to_version}`);
|
|
|
|
// 应用当前更新包的所有文件变更
|
|
for (const file of manifest.files) {
|
|
const targetPath = path.join(businessPath, file.path);
|
|
const sourcePath = path.join(extractPath, file.path);
|
|
|
|
switch (file.action) {
|
|
case 'add':
|
|
case 'modify':
|
|
// 确保目标目录存在
|
|
fs.mkdirSync(path.dirname(targetPath), { recursive: true });
|
|
if (fs.existsSync(sourcePath)) {
|
|
fs.copyFileSync(sourcePath, targetPath);
|
|
} else {
|
|
console.warn(`源文件不存在: ${sourcePath}`);
|
|
}
|
|
break;
|
|
case 'delete':
|
|
if (fs.existsSync(targetPath)) {
|
|
fs.unlinkSync(targetPath);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// 只重新打包business.asar一次
|
|
await packBusinessAsar(businessPath);
|
|
|
|
// 清理business临时目录
|
|
fs.rmSync(businessPath, { recursive: true, force: true });
|
|
}
|
|
|
|
// 下载增量更新包
|
|
async function downloadIncrementalUpdate(downloadUrl, fileName) {
|
|
return new Promise((resolve, reject) => {
|
|
const request = require('request');
|
|
const progress = require('request-progress');
|
|
|
|
progress(request(downloadUrl), {})
|
|
.on('progress', function(state) {
|
|
const percentCompleted = Math.floor(state.percent * 100);
|
|
updateProgress(percentCompleted, `正在下载更新包 ${percentCompleted}%`);
|
|
})
|
|
.on('error', function(err) {
|
|
reject(err);
|
|
})
|
|
.on('end', function() {
|
|
resolve();
|
|
})
|
|
.pipe(fs.createWriteStream(fileName));
|
|
});
|
|
}
|
|
|
|
// 下载全量更新包(exe安装包)
|
|
async function downloadFullUpdate(downloadUrl, fileName) {
|
|
return new Promise((resolve, reject) => {
|
|
const request = require('request');
|
|
const progress = require('request-progress');
|
|
|
|
progress(request(downloadUrl), {})
|
|
.on('progress', function(state) {
|
|
const percentCompleted = Math.floor(state.percent * 100);
|
|
const speedMBps = Math.floor(state.speed / 1024 / 1024);
|
|
updateProgress(percentCompleted, `正在下载完整安装包 ${percentCompleted}% - ${speedMBps}MB/s`);
|
|
})
|
|
.on('error', function(err) {
|
|
reject(err);
|
|
})
|
|
.on('end', function() {
|
|
resolve();
|
|
})
|
|
.pipe(fs.createWriteStream(fileName));
|
|
});
|
|
}
|
|
|
|
// 安装全量更新
|
|
async function installFullUpdate(fileName) {
|
|
try {
|
|
document.getElementById("text-tip").innerHTML = "正在启动安装程序...";
|
|
|
|
const { spawn } = require('child_process');
|
|
|
|
// 启动安装程序,使用静默安装参数
|
|
const installer = spawn(fileName, ['/SILENT'], {
|
|
detached: true,
|
|
stdio: 'ignore'
|
|
});
|
|
|
|
installer.unref();
|
|
|
|
document.getElementById("text-tip").innerHTML = "安装程序已启动,即将退出当前版本...";
|
|
await sleep(2000);
|
|
|
|
// 监控安装程序是否启动
|
|
const cmd = require("node-cmd");
|
|
let installationStarted = false;
|
|
let checkCount = 0;
|
|
const maxChecks = 10;
|
|
|
|
const checkInstaller = () => {
|
|
return new Promise((resolve) => {
|
|
// 检查安装程序进程是否存在
|
|
const execs = require('child_process').exec;
|
|
execs(`tasklist /FI "IMAGENAME eq ${fileName}" /FO CSV /NH`, (err, stdout, stderr) => {
|
|
if (!err && stdout.includes(fileName)) {
|
|
resolve(true);
|
|
} else {
|
|
resolve(false);
|
|
}
|
|
});
|
|
});
|
|
};
|
|
|
|
// 循环检查安装程序是否启动
|
|
while (checkCount < maxChecks && !installationStarted) {
|
|
installationStarted = await checkInstaller();
|
|
if (!installationStarted) {
|
|
await sleep(1000);
|
|
checkCount++;
|
|
}
|
|
}
|
|
|
|
if (installationStarted) {
|
|
document.getElementById("text-tip").innerHTML = "安装程序运行中,正在退出应用...";
|
|
await sleep(1000);
|
|
|
|
// 退出当前应用
|
|
ipcRenderer.send('quit-app');
|
|
} else {
|
|
document.getElementById("text-tip").innerHTML = "安装程序启动失败,请手动运行安装包";
|
|
await sleep(3000);
|
|
|
|
// 显示慢速下载提示
|
|
slow();
|
|
}
|
|
|
|
} catch (error) {
|
|
console.error("全量更新安装失败:", error);
|
|
document.getElementById("text-tip").innerHTML = "安装失败,请手动运行安装包";
|
|
await sleep(3000);
|
|
slow();
|
|
}
|
|
}
|
|
|
|
// 解压增量更新包
|
|
async function extractIncrementalUpdate(zipPath) {
|
|
const extractPath = './update_temp';
|
|
if (fs.existsSync(extractPath)) {
|
|
fs.rmSync(extractPath, { recursive: true, force: true });
|
|
}
|
|
fs.mkdirSync(extractPath, { recursive: true });
|
|
|
|
const zip = new StreamZip.async({ file: zipPath });
|
|
await zip.extract(null, extractPath);
|
|
await zip.close();
|
|
|
|
// 读取manifest.json
|
|
const manifestPath = path.join(extractPath, 'manifest.json');
|
|
const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
|
|
|
|
return { extractPath, manifest };
|
|
}
|
|
|
|
// 处理多个增量更新(优化版本:批量下载,统一处理)
|
|
async function processMultipleUpdates(updates) {
|
|
const downloadedUpdates = [];
|
|
|
|
// 第一阶段:批量下载所有更新包
|
|
document.getElementById("text-tip").innerHTML = `正在下载 ${updates.length} 个更新包...`;
|
|
for (let i = 0; i < updates.length; i++) {
|
|
const update = updates[i];
|
|
const fileName = `update_${update.to_version}.zip`;
|
|
|
|
updateProgress(Math.floor((i + 1) / updates.length * 50), `正在下载更新包 ${i + 1}/${updates.length} (${update.to_version})`);
|
|
await downloadIncrementalUpdate(update.download_url, fileName);
|
|
|
|
downloadedUpdates.push({
|
|
update,
|
|
fileName,
|
|
zipPath: `./${fileName}`
|
|
});
|
|
}
|
|
|
|
// 第二阶段:解压所有更新包
|
|
updateProgress(60, "正在解压更新包...");
|
|
const extractedUpdates = [];
|
|
for (let i = 0; i < downloadedUpdates.length; i++) {
|
|
const downloadedUpdate = downloadedUpdates[i];
|
|
const { extractPath, manifest } = await extractIncrementalUpdate(downloadedUpdate.zipPath);
|
|
|
|
extractedUpdates.push({
|
|
update: downloadedUpdate.update,
|
|
extractPath,
|
|
manifest,
|
|
zipPath: downloadedUpdate.zipPath
|
|
});
|
|
}
|
|
|
|
// 第三阶段:统一应用所有更新(只解压和打包business.asar一次)
|
|
updateProgress(80, "正在应用所有更新...");
|
|
await applyMultipleIncrementalUpdates(extractedUpdates);
|
|
|
|
// 第四阶段:清理所有临时文件
|
|
updateProgress(95, "正在清理临时文件...");
|
|
for (const extractedUpdate of extractedUpdates) {
|
|
// 清理解压目录
|
|
if (fs.existsSync(extractedUpdate.extractPath)) {
|
|
fs.rmSync(extractedUpdate.extractPath, { recursive: true, force: true });
|
|
}
|
|
// 清理下载的zip文件
|
|
if (fs.existsSync(extractedUpdate.zipPath)) {
|
|
fs.unlinkSync(extractedUpdate.zipPath);
|
|
}
|
|
}
|
|
}
|
|
|
|
// 获取business.asar内的版本号
|
|
async function getBusinessVersion() {
|
|
try {
|
|
const businessAsarPath = './resources/business.asar';
|
|
|
|
// 检查business.asar是否存在
|
|
if (!fs.existsSync(businessAsarPath)) {
|
|
console.log('business.asar不存在,使用主应用版本');
|
|
return await ipcRenderer.invoke('get-app-version');
|
|
}
|
|
|
|
const asar = require('asar');
|
|
|
|
// 从business.asar中读取package.json
|
|
try {
|
|
const packageJsonContent = asar.extractFile(businessAsarPath, 'package.json');
|
|
const businessPackageJson = JSON.parse(packageJsonContent.toString());
|
|
return businessPackageJson.version;
|
|
} catch (error) {
|
|
console.log('无法从business.asar读取版本,使用主应用版本:', error);
|
|
return await ipcRenderer.invoke('get-app-version');
|
|
}
|
|
} catch (error) {
|
|
console.log('获取business版本失败,使用主应用版本:', error);
|
|
return await ipcRenderer.invoke('get-app-version');
|
|
}
|
|
}
|
|
|
|
// 增量更新检测和处理
|
|
async function checkIncrementalUpdate() {
|
|
try {
|
|
const version = await getBusinessVersion();
|
|
|
|
// 使用request替代axios进行API调用
|
|
const request = require('request');
|
|
const response = await new Promise((resolve, reject) => {
|
|
request(`https://a-p-i.r0csgo.com/v2/update/check?current_version=${version}`, (error, response, body) => {
|
|
if (error) {
|
|
reject(error);
|
|
} else {
|
|
try {
|
|
resolve(JSON.parse(body));
|
|
} catch (e) {
|
|
reject(e);
|
|
}
|
|
}
|
|
});
|
|
});
|
|
|
|
const data = response;
|
|
|
|
console.log("更新检测结果:", data);
|
|
|
|
if (!data.has_update) {
|
|
// 无更新,检测运行环境后启动应用
|
|
document.getElementById("text-tip").innerHTML = "当前版本最新,正在检测运行环境...";
|
|
await checkAndInstallRuntimeEnvironments();
|
|
document.getElementById("text-tip").innerHTML = "正在启动应用...";
|
|
await sleep(1000);
|
|
await startApplication();
|
|
return;
|
|
}
|
|
|
|
if (data.update_type === 'incremental') {
|
|
// 单个增量更新
|
|
document.getElementById("text-tip").innerHTML = `检测到新版本 ${data.latest_version},正在下载增量更新...`;
|
|
|
|
const fileName = `update_${data.latest_version}.zip`;
|
|
await downloadIncrementalUpdate(data.download_url, fileName);
|
|
|
|
const { extractPath, manifest } = await extractIncrementalUpdate(fileName);
|
|
await applyIncrementalUpdate(extractPath, manifest);
|
|
|
|
// 清理下载的zip文件
|
|
fs.unlinkSync(fileName);
|
|
|
|
document.getElementById("text-tip").innerHTML = "增量更新完成,正在检测运行环境...";
|
|
await checkAndInstallRuntimeEnvironments();
|
|
document.getElementById("text-tip").innerHTML = "正在加载应用...";
|
|
await sleep(1000);
|
|
|
|
// 直接启动应用,无需重启
|
|
await startApplication();
|
|
|
|
} else if (data.update_type === 'multi_incremental') {
|
|
// 多个增量更新
|
|
document.getElementById("text-tip").innerHTML = `检测到新版本 ${data.latest_version},需要应用 ${data.incremental_updates.length} 个增量更新...`;
|
|
|
|
await processMultipleUpdates(data.incremental_updates);
|
|
|
|
document.getElementById("text-tip").innerHTML = "所有增量更新完成,正在检测运行环境...";
|
|
await checkAndInstallRuntimeEnvironments();
|
|
document.getElementById("text-tip").innerHTML = "正在加载应用...";
|
|
await sleep(1000);
|
|
|
|
// 直接启动应用,无需重启
|
|
await startApplication();
|
|
|
|
} else if (data.update_type === 'full') {
|
|
// 全量更新 - 下载完整安装包
|
|
document.getElementById("text-tip").innerHTML = `检测到新版本 ${data.latest_version},正在下载完整安装包...`;
|
|
|
|
const fileName = `r0_arena_${data.latest_version}.exe`;
|
|
await downloadFullUpdate(data.download_url, fileName);
|
|
|
|
document.getElementById("text-tip").innerHTML = "下载完成,正在安装新版本...";
|
|
await sleep(1000);
|
|
|
|
// 运行全量更新安装
|
|
await installFullUpdate(fileName);
|
|
}
|
|
|
|
} catch (error) {
|
|
console.error("增量更新检测失败:", error);
|
|
document.getElementById("text-tip").innerHTML = "更新检测失败,正在检测运行环境...";
|
|
await checkAndInstallRuntimeEnvironments();
|
|
document.getElementById("text-tip").innerHTML = "使用当前版本启动...";
|
|
await sleep(1000);
|
|
await startApplication();
|
|
}
|
|
}
|
|
|
|
// 启动应用
|
|
async function startApplication() {
|
|
updateProgress(100, "加载完成,正在启动...");
|
|
|
|
// 添加完成效果
|
|
setTimeout(() => {
|
|
const progress = document.getElementById("progress");
|
|
if (progress) {
|
|
progress.classList.add('complete');
|
|
}
|
|
}, 100);
|
|
|
|
// 淡出动画后跳转
|
|
setTimeout(() => {
|
|
fadeOutAndNavigate(async () => {
|
|
// 检测是否为开发环境
|
|
const isDev = await ipcRenderer.invoke('is-dev-environment');
|
|
|
|
// 根据环境设置不同的窗口大小
|
|
if (isDev) {
|
|
await ipcRenderer.invoke('window-set-size', 1451, 750);
|
|
} else {
|
|
await ipcRenderer.invoke('window-set-size', 1200, 750);
|
|
}
|
|
|
|
await ipcRenderer.invoke('window-center');
|
|
|
|
// 获取端口信息
|
|
const ports = await ipcRenderer.invoke('get-ports');
|
|
window.location.href = `http://127.0.0.1:${ports.pagePort}/signin`;
|
|
});
|
|
}, 500);
|
|
}
|
|
|
|
// 启动检测
|
|
console.log("开始增量更新检测");
|
|
|
|
// 初始化界面
|
|
async function initUI() {
|
|
// 获取并显示版本号
|
|
try {
|
|
const version = await getBusinessVersion();
|
|
const versionElement = document.querySelector('.version-info');
|
|
if (versionElement) {
|
|
versionElement.textContent = `v${version}`;
|
|
}
|
|
} catch (error) {
|
|
console.error('获取版本号失败:', error);
|
|
}
|
|
|
|
// 添加初始动画效果
|
|
setTimeout(() => {
|
|
const progressBar = document.getElementById("progress");
|
|
if (progressBar) {
|
|
progressBar.style.width = "5%";
|
|
}
|
|
}, 500);
|
|
}
|
|
|
|
// 页面加载完成后初始化
|
|
window.addEventListener('DOMContentLoaded', () => {
|
|
initUI();
|
|
setTimeout(() => {
|
|
checkIncrementalUpdate();
|
|
}, 100);
|
|
});
|
|
|
|
function slow() {
|
|
var slowEl = document.getElementById("slow");
|
|
slowEl.style.opacity = '1';
|
|
slowEl.style.pointerEvents = 'auto';
|
|
}
|
|
|
|
// 优化进度条更新函数
|
|
function updateProgress(percent, message) {
|
|
const progress = document.getElementById("progress");
|
|
const textTip = document.getElementById("text-tip");
|
|
const textProgress = document.getElementById("text-progress");
|
|
|
|
if (progress) {
|
|
progress.style.width = percent + "%";
|
|
// 当进度达到100%时添加完成效果
|
|
if (percent >= 100) {
|
|
progress.classList.add('complete');
|
|
}
|
|
}
|
|
if (textTip && message) {
|
|
textTip.innerHTML = message;
|
|
textTip.classList.add('loading');
|
|
}
|
|
if (textProgress && percent > 0) {
|
|
textProgress.innerHTML = ` ${percent}%`;
|
|
}
|
|
}
|
|
|
|
// 添加页面淡出效果
|
|
function fadeOutAndNavigate(callback) {
|
|
const box = document.querySelector('.box');
|
|
if (box) {
|
|
box.classList.add('fade-out');
|
|
setTimeout(callback, 500);
|
|
} else {
|
|
callback();
|
|
}
|
|
}
|
|
</script>
|
|
|
|
</html>
|
|
|