feat: 允许http下载&添加了多个镜像源

- 在 AutoMode 组件中添加了多个 gh-proxy 镜像选项
- 在 BackendStep 组件中增加了新的镜像选择项
- 修改了 downloadService 中的下载逻辑,支持 http 和 https
- 更新了 gitService 和 pythonService 中的下载 URL
- 在 Home 组件中添加了动态问候语
This commit is contained in:
2025-08-13 20:00:28 +08:00
parent 9aeda23ade
commit 95126a85d8
7 changed files with 182 additions and 155 deletions

View File

@@ -1,6 +1,7 @@
import * as https from 'https' import * as https from 'https'
import * as fs from 'fs' import * as fs from 'fs'
import { BrowserWindow } from 'electron' import { BrowserWindow } from 'electron'
import * as http from 'http'
let mainWindow: BrowserWindow | null = null let mainWindow: BrowserWindow | null = null
@@ -8,51 +9,55 @@ export function setMainWindow(window: BrowserWindow) {
mainWindow = window mainWindow = window
} }
// 下载文件的通用函数
export function downloadFile(url: string, outputPath: string): Promise<void> { export function downloadFile(url: string, outputPath: string): Promise<void> {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
console.log(`开始下载文件: ${url}`) console.log(`开始下载文件: ${url}`)
console.log(`保存路径: ${outputPath}`) console.log(`保存路径: ${outputPath}`)
const file = fs.createWriteStream(outputPath) const file = fs.createWriteStream(outputPath)
// 创建HTTP客户端兼容https和http
https.get(url, (response) => { const client = url.startsWith('https') ? https : http
const totalSize = parseInt(response.headers['content-length'] || '0', 10)
let downloadedSize = 0
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) => { console.log(`文件大小: ${totalSize} bytes`)
downloadedSize += chunk.length
const progress = Math.round((downloadedSize / totalSize) * 100) response.pipe(file)
console.log(`下载进度: ${progress}% (${downloadedSize}/${totalSize})`) response.on('data', chunk => {
downloadedSize += chunk.length
if (mainWindow) { const progress = totalSize ? Math.round((downloadedSize / totalSize) * 100) : 0
mainWindow.webContents.send('download-progress', {
progress, console.log(`下载进度: ${progress}% (${downloadedSize}/${totalSize})`)
status: 'downloading',
message: `下载中... ${progress}%` 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)
})
}) })
.on('error', err => {
file.on('finish', () => { console.error(`下载错误: ${err.message}`)
file.close()
console.log(`文件下载完成: ${outputPath}`)
resolve()
})
file.on('error', (err) => {
console.error(`文件写入错误: ${err.message}`)
fs.unlink(outputPath, () => {}) // 删除不完整的文件
reject(err) reject(err)
}) })
}).on('error', (err) => {
console.error(`下载错误: ${err.message}`)
reject(err)
})
}) })
} }

View File

@@ -11,7 +11,7 @@ export function setMainWindow(window: BrowserWindow) {
mainWindow = window 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) { function copyDirSync(src: string, dest: string) {

View File

@@ -64,7 +64,7 @@ async function installPip(pythonPath: string, appRoot: string): Promise<void> {
console.log('pip未安装开始安装...') console.log('pip未安装开始安装...')
const getPipPath = path.join(pythonPath, 'get-pip.py') 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(`Python可执行文件路径: ${pythonExe}`)
console.log(`get-pip.py下载URL: ${getPipUrl}`) console.log(`get-pip.py下载URL: ${getPipUrl}`)

View File

@@ -170,7 +170,12 @@ function getGitMirrorUrl(mirrorKey: string): string {
const mirrors = { const mirrors = {
github: 'https://github.com/DLmaster361/AUTO_MAA.git', github: 'https://github.com/DLmaster361/AUTO_MAA.git',
ghfast: 'https://ghfast.top/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 return mirrors[mirrorKey as keyof typeof mirrors] || mirrors.github
} }

View File

@@ -52,9 +52,14 @@ defineProps<{
const gitMirrors = ref<Mirror[]>([ const gitMirrors = ref<Mirror[]>([
{ key: 'github', name: 'GitHub 官方', url: 'https://github.com/DLmaster361/AUTO_MAA.git', speed: null }, { 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 selectedGitMirror = ref('github')
const testingGitSpeed = ref(false) const testingGitSpeed = ref(false)

View File

@@ -90,23 +90,23 @@ const router = createRouter({
}) })
// // 添加路由守卫,确保在生产环境中也能正确进入初始化页面 // 添加路由守卫,确保在生产环境中也能正确进入初始化页面
// router.beforeEach(async (to, from, next) => { router.beforeEach(async (to, from, next) => {
// console.log('路由守卫:', { to: to.path, from: from.path }) console.log('路由守卫:', { to: to.path, from: from.path })
//
// // 如果访问的不是初始化页面,且没有初始化标记,则重定向到初始化页面 // 如果访问的不是初始化页面,且没有初始化标记,则重定向到初始化页面
// if (to.path !== '/initialization') { if (to.path !== '/initialization') {
// const initialized = await isAppInitialized() const initialized = await isAppInitialized()
// console.log('检查初始化状态:', initialized) console.log('检查初始化状态:', initialized)
//
// if (!initialized) { if (!initialized) {
// console.log('应用未初始化,重定向到初始化页面') console.log('应用未初始化,重定向到初始化页面')
// next('/initialization') next('/initialization')
// return return
// } }
// } }
//
// next() next()
// }) })
export default router export default router

View File

@@ -1,101 +1,101 @@
<template> <template>
<div class="header">
<a-typography-title>{{ greeting }}</a-typography-title>
</div>
<div class="header"> <div class="content">
<a-typography-title>主人早上好喵~</a-typography-title> <!-- 当期活动关卡 -->
</div> <a-card title="当期活动关卡" class="activity-card" :loading="loading">
<template #extra>
<div class="content"> <a-button type="text" @click="refreshActivity" :loading="loading">
<!-- 当期活动关卡 --> <template #icon>
<a-card title="当期活动关卡" class="activity-card" :loading="loading"> <ReloadOutlined />
<template #extra> </template>
<a-button type="text" @click="refreshActivity" :loading="loading"> 刷新
<template #icon> </a-button>
<ReloadOutlined /> </template>
</template>
刷新 <div v-if="error" class="error-message">
</a-button> <a-alert :message="error" type="error" show-icon closable @close="error = ''" />
</template> </div>
<div v-if="error" class="error-message"> <!-- 活动信息展示 -->
<a-alert <div v-if="currentActivity && !loading" class="activity-info">
:message="error" <div class="activity-header">
type="error" <div class="activity-left">
show-icon <div class="activity-name">
closable <span class="activity-title">{{ currentActivity.StageName }}</span>
@close="error = ''" <a-tag color="blue" class="activity-tip">{{ currentActivity.Tip }}</a-tag>
/>
</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> </div>
<div class="activity-end-time">
<div class="activity-right"> <ClockCircleOutlined class="time-icon" />
<a-statistic-countdown <span class="time-label">结束时间</span>
title="当期活动剩余时间" <span class="time-value">{{
:value="getCountdownValue(currentActivity.UtcExpireTime)" formatTime(currentActivity.UtcExpireTime, currentActivity.TimeZone)
format="D 天 H 时 m 分" }}</span>
:value-style="getCountdownStyle(currentActivity.UtcExpireTime)" </div>
@finish="onCountdownFinish" </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> <div class="drop-details">
<div class="drop-name">
<div v-if="activityData?.length" class="activity-list"> {{ item.DropName.startsWith('DESC:') ? item.DropName.substring(5) : item.DropName }}
<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> </div>
<!-- <div v-if="item.Drop && !item.DropName.startsWith('DESC:')" class="drop-id">-->
<!-- ID: {{ item.Drop }}-->
<!-- </div>-->
</div> </div>
</div> </div>
</div> </div>
</div>
<div v-else-if="!loading" class="empty-state">
<a-empty description="暂无活动关卡数据" /> <div v-else-if="!loading" class="empty-state">
</div> <a-empty description="暂无活动关卡数据" />
</a-card> </div>
</div> </a-card>
</div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, onMounted, computed } from 'vue' import { ref, onMounted, computed } from 'vue'
import { message } from 'ant-design-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' import { Service } from '@/api'
interface ActivityInfo { interface ActivityInfo {
@@ -134,7 +134,7 @@ const formatTime = (timeString: string, timeZone: number) => {
month: '2-digit', month: '2-digit',
day: '2-digit', day: '2-digit',
hour: '2-digit', hour: '2-digit',
minute: '2-digit' minute: '2-digit',
}) })
} catch { } catch {
return timeString return timeString
@@ -157,25 +157,25 @@ const getCountdownStyle = (expireTime: string) => {
const now = new Date() const now = new Date()
const remaining = expire.getTime() - now.getTime() const remaining = expire.getTime() - now.getTime()
const twoDaysInMs = 2 * 24 * 60 * 60 * 1000 const twoDaysInMs = 2 * 24 * 60 * 60 * 1000
if (remaining <= twoDaysInMs) { if (remaining <= twoDaysInMs) {
return { return {
color: '#ff4d4f', color: '#ff4d4f',
fontWeight: 'bold', fontWeight: 'bold',
fontSize: '18px' fontSize: '18px',
} }
} }
return { return {
color: 'var(--ant-color-text)', color: 'var(--ant-color-text)',
fontWeight: '600', fontWeight: '600',
fontSize: '20px' fontSize: '20px',
} }
} catch { } catch {
return { return {
color: 'var(--ant-color-text)', color: 'var(--ant-color-text)',
fontWeight: '600', fontWeight: '600',
fontSize: '20px' fontSize: '20px',
} }
} }
} }
@@ -203,10 +203,10 @@ const handleImageError = (event: Event) => {
const fetchActivityData = async () => { const fetchActivityData = async () => {
loading.value = true loading.value = true
error.value = '' error.value = ''
try { try {
const response = await Service.addOverviewApiInfoGetOverviewPost() const response = await Service.addOverviewApiInfoGetOverviewPost()
if (response.code === 200 && response.data?.ALL) { if (response.code === 200 && response.data?.ALL) {
activityData.value = response.data.ALL activityData.value = response.data.ALL
} else { } 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(() => { onMounted(() => {
fetchActivityData() fetchActivityData()
}) })
</script> </script>
<style scoped> <style scoped>
.header { .header {
margin-bottom: 24px; margin-bottom: 24px;
} }
@@ -245,8 +259,6 @@ onMounted(() => {
font-weight: 600; font-weight: 600;
} }
.activity-card { .activity-card {
margin-bottom: 24px; margin-bottom: 24px;
} }
@@ -458,15 +470,15 @@ onMounted(() => {
.page-container { .page-container {
padding: 16px; padding: 16px;
} }
.activity-list { .activity-list {
grid-template-columns: 1fr; grid-template-columns: 1fr;
} }
.activity-item { .activity-item {
padding: 12px; padding: 12px;
} }
.drop-image { .drop-image {
width: 40px; width: 40px;
height: 40px; height: 40px;