feat: 允许http下载&添加了多个镜像源
- 在 AutoMode 组件中添加了多个 gh-proxy 镜像选项 - 在 BackendStep 组件中增加了新的镜像选择项 - 修改了 downloadService 中的下载逻辑,支持 http 和 https - 更新了 gitService 和 pythonService 中的下载 URL - 在 Home 组件中添加了动态问候语
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import * as https from 'https'
|
||||
import * as fs from 'fs'
|
||||
import { BrowserWindow } from 'electron'
|
||||
import * as http from 'http'
|
||||
|
||||
let mainWindow: BrowserWindow | null = null
|
||||
|
||||
@@ -8,51 +9,55 @@ export function setMainWindow(window: BrowserWindow) {
|
||||
mainWindow = window
|
||||
}
|
||||
|
||||
// 下载文件的通用函数
|
||||
export function downloadFile(url: string, outputPath: string): Promise<void> {
|
||||
return new Promise((resolve, reject) => {
|
||||
console.log(`开始下载文件: ${url}`)
|
||||
console.log(`保存路径: ${outputPath}`)
|
||||
|
||||
|
||||
const file = fs.createWriteStream(outputPath)
|
||||
|
||||
https.get(url, (response) => {
|
||||
const totalSize = parseInt(response.headers['content-length'] || '0', 10)
|
||||
let downloadedSize = 0
|
||||
// 创建HTTP客户端,兼容https和http
|
||||
const client = url.startsWith('https') ? https : http
|
||||
|
||||
console.log(`文件大小: ${totalSize} bytes`)
|
||||
|
||||
response.pipe(file)
|
||||
client
|
||||
.get(url, response => {
|
||||
const totalSize = parseInt(response.headers['content-length'] || '0', 10)
|
||||
let downloadedSize = 0
|
||||
|
||||
response.on('data', (chunk) => {
|
||||
downloadedSize += chunk.length
|
||||
const progress = Math.round((downloadedSize / totalSize) * 100)
|
||||
|
||||
console.log(`下载进度: ${progress}% (${downloadedSize}/${totalSize})`)
|
||||
|
||||
if (mainWindow) {
|
||||
mainWindow.webContents.send('download-progress', {
|
||||
progress,
|
||||
status: 'downloading',
|
||||
message: `下载中... ${progress}%`
|
||||
})
|
||||
}
|
||||
console.log(`文件大小: ${totalSize} bytes`)
|
||||
|
||||
response.pipe(file)
|
||||
|
||||
response.on('data', chunk => {
|
||||
downloadedSize += chunk.length
|
||||
const progress = totalSize ? Math.round((downloadedSize / totalSize) * 100) : 0
|
||||
|
||||
console.log(`下载进度: ${progress}% (${downloadedSize}/${totalSize})`)
|
||||
|
||||
if (mainWindow) {
|
||||
mainWindow.webContents.send('download-progress', {
|
||||
progress,
|
||||
status: 'downloading',
|
||||
message: `下载中... ${progress}%`,
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
file.on('finish', () => {
|
||||
file.close()
|
||||
console.log(`文件下载完成: ${outputPath}`)
|
||||
resolve()
|
||||
})
|
||||
|
||||
file.on('error', err => {
|
||||
console.error(`文件写入错误: ${err.message}`)
|
||||
fs.unlink(outputPath, () => {}) // 删除不完整的文件
|
||||
reject(err)
|
||||
})
|
||||
})
|
||||
|
||||
file.on('finish', () => {
|
||||
file.close()
|
||||
console.log(`文件下载完成: ${outputPath}`)
|
||||
resolve()
|
||||
})
|
||||
|
||||
file.on('error', (err) => {
|
||||
console.error(`文件写入错误: ${err.message}`)
|
||||
fs.unlink(outputPath, () => {}) // 删除不完整的文件
|
||||
.on('error', err => {
|
||||
console.error(`下载错误: ${err.message}`)
|
||||
reject(err)
|
||||
})
|
||||
}).on('error', (err) => {
|
||||
console.error(`下载错误: ${err.message}`)
|
||||
reject(err)
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ export function setMainWindow(window: BrowserWindow) {
|
||||
mainWindow = window
|
||||
}
|
||||
|
||||
const gitDownloadUrl = 'https://alist-automaa.fearr.xyz/d/AUTO_MAA/git.zip'
|
||||
const gitDownloadUrl = 'http://221.236.27.82:10197/d/AUTO_MAA/git.zip'
|
||||
|
||||
// 递归复制目录,包括文件和隐藏文件
|
||||
function copyDirSync(src: string, dest: string) {
|
||||
|
||||
@@ -64,7 +64,7 @@ async function installPip(pythonPath: string, appRoot: string): Promise<void> {
|
||||
console.log('pip未安装,开始安装...')
|
||||
|
||||
const getPipPath = path.join(pythonPath, 'get-pip.py')
|
||||
const getPipUrl = 'https://alist-automaa.fearr.xyz/d/AUTO_MAA/get-pip.py'
|
||||
const getPipUrl = 'http://221.236.27.82:10197/d/AUTO_MAA/get-pip.py'
|
||||
|
||||
console.log(`Python可执行文件路径: ${pythonExe}`)
|
||||
console.log(`get-pip.py下载URL: ${getPipUrl}`)
|
||||
|
||||
@@ -170,7 +170,12 @@ function getGitMirrorUrl(mirrorKey: string): string {
|
||||
const mirrors = {
|
||||
github: 'https://github.com/DLmaster361/AUTO_MAA.git',
|
||||
ghfast: 'https://ghfast.top/https://github.com/DLmaster361/AUTO_MAA.git',
|
||||
ghproxy_cloudflare: 'https://gh-proxy.com/https://github.com/DLmaster361/AUTO_MAA.git.git',
|
||||
ghproxy_hongkong: 'https://hk.gh-proxy.com/https://github.com/DLmaster361/AUTO_MAA.git.git',
|
||||
ghproxy_fastly: 'https://cdn.gh-proxy.com/https://github.com/DLmaster361/AUTO_MAA.git.git',
|
||||
ghproxy_edgeone: 'https://edgeone.gh-proxy.com/https://github.com/DLmaster361/AUTO_MAA.git.git'
|
||||
}
|
||||
|
||||
return mirrors[mirrorKey as keyof typeof mirrors] || mirrors.github
|
||||
}
|
||||
|
||||
|
||||
@@ -52,9 +52,14 @@ defineProps<{
|
||||
|
||||
const gitMirrors = ref<Mirror[]>([
|
||||
{ key: 'github', name: 'GitHub 官方', url: 'https://github.com/DLmaster361/AUTO_MAA.git', speed: null },
|
||||
{ key: 'ghfast', name: 'ghfast 镜像', url: 'https://ghfast.top/https://github.com/DLmaster361/AUTO_MAA.git', speed: null }
|
||||
{ key: 'ghfast', name: 'ghfast 镜像', url: 'https://ghfast.top/https://github.com/DLmaster361/AUTO_MAA.git', speed: null },
|
||||
{ key: 'ghproxy_cloudflare', name: 'gh-proxy Cloudflare 全球加速', url: 'https://gh-proxy.com/https://github.com/DLmaster361/AUTO_MAA.git.git', speed: null },
|
||||
{ key: 'ghproxy_hongkong', name: 'gh-proxy 香港节点(大陆优化)', url: 'https://hk.gh-proxy.com/https://github.com/DLmaster361/AUTO_MAA.git.git', speed: null },
|
||||
{ key: 'ghproxy_fastly', name: 'gh-proxy Fastly CDN', url: 'https://cdn.gh-proxy.com/https://github.com/DLmaster361/AUTO_MAA.git.git', speed: null },
|
||||
{ key: 'ghproxy_edgeone', name: 'gh-proxy EdgeOne 全球加速', url: 'https://edgeone.gh-proxy.com/https://github.com/DLmaster361/AUTO_MAA.git.git', speed: null }
|
||||
])
|
||||
|
||||
|
||||
const selectedGitMirror = ref('github')
|
||||
const testingGitSpeed = ref(false)
|
||||
|
||||
|
||||
@@ -90,23 +90,23 @@ const router = createRouter({
|
||||
})
|
||||
|
||||
|
||||
// // 添加路由守卫,确保在生产环境中也能正确进入初始化页面
|
||||
// router.beforeEach(async (to, from, next) => {
|
||||
// console.log('路由守卫:', { to: to.path, from: from.path })
|
||||
//
|
||||
// // 如果访问的不是初始化页面,且没有初始化标记,则重定向到初始化页面
|
||||
// if (to.path !== '/initialization') {
|
||||
// const initialized = await isAppInitialized()
|
||||
// console.log('检查初始化状态:', initialized)
|
||||
//
|
||||
// if (!initialized) {
|
||||
// console.log('应用未初始化,重定向到初始化页面')
|
||||
// next('/initialization')
|
||||
// return
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// next()
|
||||
// })
|
||||
// 添加路由守卫,确保在生产环境中也能正确进入初始化页面
|
||||
router.beforeEach(async (to, from, next) => {
|
||||
console.log('路由守卫:', { to: to.path, from: from.path })
|
||||
|
||||
// 如果访问的不是初始化页面,且没有初始化标记,则重定向到初始化页面
|
||||
if (to.path !== '/initialization') {
|
||||
const initialized = await isAppInitialized()
|
||||
console.log('检查初始化状态:', initialized)
|
||||
|
||||
if (!initialized) {
|
||||
console.log('应用未初始化,重定向到初始化页面')
|
||||
next('/initialization')
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
next()
|
||||
})
|
||||
|
||||
export default router
|
||||
|
||||
@@ -1,101 +1,101 @@
|
||||
<template>
|
||||
<div class="header">
|
||||
<a-typography-title>{{ greeting }}</a-typography-title>
|
||||
</div>
|
||||
|
||||
<div class="header">
|
||||
<a-typography-title>主人早上好喵~</a-typography-title>
|
||||
</div>
|
||||
|
||||
<div class="content">
|
||||
<!-- 当期活动关卡 -->
|
||||
<a-card title="当期活动关卡" class="activity-card" :loading="loading">
|
||||
<template #extra>
|
||||
<a-button type="text" @click="refreshActivity" :loading="loading">
|
||||
<template #icon>
|
||||
<ReloadOutlined />
|
||||
</template>
|
||||
刷新
|
||||
</a-button>
|
||||
</template>
|
||||
|
||||
<div v-if="error" class="error-message">
|
||||
<a-alert
|
||||
:message="error"
|
||||
type="error"
|
||||
show-icon
|
||||
closable
|
||||
@close="error = ''"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- 活动信息展示 -->
|
||||
<div v-if="currentActivity && !loading" class="activity-info">
|
||||
<div class="activity-header">
|
||||
<div class="activity-left">
|
||||
<div class="activity-name">
|
||||
<span class="activity-title">{{ currentActivity.StageName }}</span>
|
||||
<a-tag color="blue" class="activity-tip">{{ currentActivity.Tip }}</a-tag>
|
||||
</div>
|
||||
<div class="activity-end-time">
|
||||
<ClockCircleOutlined class="time-icon" />
|
||||
<span class="time-label">结束时间:</span>
|
||||
<span class="time-value">{{ formatTime(currentActivity.UtcExpireTime, currentActivity.TimeZone) }}</span>
|
||||
</div>
|
||||
<div class="content">
|
||||
<!-- 当期活动关卡 -->
|
||||
<a-card title="当期活动关卡" class="activity-card" :loading="loading">
|
||||
<template #extra>
|
||||
<a-button type="text" @click="refreshActivity" :loading="loading">
|
||||
<template #icon>
|
||||
<ReloadOutlined />
|
||||
</template>
|
||||
刷新
|
||||
</a-button>
|
||||
</template>
|
||||
|
||||
<div v-if="error" class="error-message">
|
||||
<a-alert :message="error" type="error" show-icon closable @close="error = ''" />
|
||||
</div>
|
||||
|
||||
<!-- 活动信息展示 -->
|
||||
<div v-if="currentActivity && !loading" class="activity-info">
|
||||
<div class="activity-header">
|
||||
<div class="activity-left">
|
||||
<div class="activity-name">
|
||||
<span class="activity-title">{{ currentActivity.StageName }}</span>
|
||||
<a-tag color="blue" class="activity-tip">{{ currentActivity.Tip }}</a-tag>
|
||||
</div>
|
||||
|
||||
<div class="activity-right">
|
||||
<a-statistic-countdown
|
||||
title="当期活动剩余时间"
|
||||
:value="getCountdownValue(currentActivity.UtcExpireTime)"
|
||||
format="D 天 H 时 m 分"
|
||||
:value-style="getCountdownStyle(currentActivity.UtcExpireTime)"
|
||||
@finish="onCountdownFinish"
|
||||
<div class="activity-end-time">
|
||||
<ClockCircleOutlined class="time-icon" />
|
||||
<span class="time-label">结束时间:</span>
|
||||
<span class="time-value">{{
|
||||
formatTime(currentActivity.UtcExpireTime, currentActivity.TimeZone)
|
||||
}}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="activity-right">
|
||||
<a-statistic-countdown
|
||||
title="当期活动剩余时间"
|
||||
:value="getCountdownValue(currentActivity.UtcExpireTime)"
|
||||
format="D 天 H 时 m 分"
|
||||
:value-style="getCountdownStyle(currentActivity.UtcExpireTime)"
|
||||
@finish="onCountdownFinish"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="activityData?.length" class="activity-list">
|
||||
<div v-for="item in activityData" :key="item.Value" class="activity-item">
|
||||
<div class="stage-info">
|
||||
<div class="stage-name">{{ item.Display }}</div>
|
||||
<!-- <div class="stage-value">{{ item.Value }}</div>-->
|
||||
</div>
|
||||
|
||||
<div class="drop-info">
|
||||
<div class="drop-image">
|
||||
<img
|
||||
:src="
|
||||
item.DropName.startsWith('DESC:')
|
||||
? getMaterialImage('固源岩')
|
||||
: getMaterialImage(item.DropName)
|
||||
"
|
||||
:alt="item.DropName.startsWith('DESC:') ? '固源岩' : item.DropName"
|
||||
@error="handleImageError"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="activityData?.length" class="activity-list">
|
||||
<div
|
||||
v-for="item in activityData"
|
||||
:key="item.Value"
|
||||
class="activity-item"
|
||||
>
|
||||
<div class="stage-info">
|
||||
<div class="stage-name">{{ item.Display }}</div>
|
||||
<!-- <div class="stage-value">{{ item.Value }}</div>-->
|
||||
</div>
|
||||
|
||||
<div class="drop-info">
|
||||
<div class="drop-image">
|
||||
<img
|
||||
:src="item.DropName.startsWith('DESC:') ? getMaterialImage('固源岩') : getMaterialImage(item.DropName)"
|
||||
:alt="item.DropName.startsWith('DESC:') ? '固源岩' : item.DropName"
|
||||
@error="handleImageError"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="drop-details">
|
||||
<div class="drop-name">
|
||||
{{ item.DropName.startsWith('DESC:') ? item.DropName.substring(5) : item.DropName }}
|
||||
</div>
|
||||
<!-- <div v-if="item.Drop && !item.DropName.startsWith('DESC:')" class="drop-id">-->
|
||||
<!-- ID: {{ item.Drop }}-->
|
||||
<!-- </div>-->
|
||||
|
||||
<div class="drop-details">
|
||||
<div class="drop-name">
|
||||
{{ item.DropName.startsWith('DESC:') ? item.DropName.substring(5) : item.DropName }}
|
||||
</div>
|
||||
<!-- <div v-if="item.Drop && !item.DropName.startsWith('DESC:')" class="drop-id">-->
|
||||
<!-- ID: {{ item.Drop }}-->
|
||||
<!-- </div>-->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-else-if="!loading" class="empty-state">
|
||||
<a-empty description="暂无活动关卡数据" />
|
||||
</div>
|
||||
</a-card>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-else-if="!loading" class="empty-state">
|
||||
<a-empty description="暂无活动关卡数据" />
|
||||
</div>
|
||||
</a-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, computed } from 'vue'
|
||||
import { message } from 'ant-design-vue'
|
||||
import { ReloadOutlined, InfoCircleOutlined, CalendarOutlined, ClockCircleOutlined } from '@ant-design/icons-vue'
|
||||
import {
|
||||
ReloadOutlined,
|
||||
InfoCircleOutlined,
|
||||
CalendarOutlined,
|
||||
ClockCircleOutlined,
|
||||
} from '@ant-design/icons-vue'
|
||||
import { Service } from '@/api'
|
||||
|
||||
interface ActivityInfo {
|
||||
@@ -134,7 +134,7 @@ const formatTime = (timeString: string, timeZone: number) => {
|
||||
month: '2-digit',
|
||||
day: '2-digit',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit'
|
||||
minute: '2-digit',
|
||||
})
|
||||
} catch {
|
||||
return timeString
|
||||
@@ -157,25 +157,25 @@ const getCountdownStyle = (expireTime: string) => {
|
||||
const now = new Date()
|
||||
const remaining = expire.getTime() - now.getTime()
|
||||
const twoDaysInMs = 2 * 24 * 60 * 60 * 1000
|
||||
|
||||
|
||||
if (remaining <= twoDaysInMs) {
|
||||
return {
|
||||
color: '#ff4d4f',
|
||||
fontWeight: 'bold',
|
||||
fontSize: '18px'
|
||||
fontSize: '18px',
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return {
|
||||
color: 'var(--ant-color-text)',
|
||||
fontWeight: '600',
|
||||
fontSize: '20px'
|
||||
fontSize: '20px',
|
||||
}
|
||||
} catch {
|
||||
return {
|
||||
color: 'var(--ant-color-text)',
|
||||
fontWeight: '600',
|
||||
fontSize: '20px'
|
||||
fontSize: '20px',
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -203,10 +203,10 @@ const handleImageError = (event: Event) => {
|
||||
const fetchActivityData = async () => {
|
||||
loading.value = true
|
||||
error.value = ''
|
||||
|
||||
|
||||
try {
|
||||
const response = await Service.addOverviewApiInfoGetOverviewPost()
|
||||
|
||||
|
||||
if (response.code === 200 && response.data?.ALL) {
|
||||
activityData.value = response.data.ALL
|
||||
} else {
|
||||
@@ -227,13 +227,27 @@ const refreshActivity = async () => {
|
||||
}
|
||||
}
|
||||
|
||||
const greeting = computed(() => {
|
||||
const hour = new Date().getHours()
|
||||
if (hour >= 5 && hour < 11) {
|
||||
return '主人早上好喵~'
|
||||
} else if (hour >= 11 && hour < 14) {
|
||||
return '主人中午好喵~'
|
||||
} else if (hour >= 14 && hour < 18) {
|
||||
return '主人下午好喵~'
|
||||
} else if (hour >= 18 && hour < 23) {
|
||||
return '主人晚上好喵~'
|
||||
} else {
|
||||
return '主人夜深了喵~早点休息喵~'
|
||||
}
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
fetchActivityData()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
.header {
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
@@ -245,8 +259,6 @@ onMounted(() => {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
|
||||
|
||||
.activity-card {
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
@@ -458,15 +470,15 @@ onMounted(() => {
|
||||
.page-container {
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
|
||||
.activity-list {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
|
||||
.activity-item {
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
|
||||
.drop-image {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
|
||||
Reference in New Issue
Block a user