style:格式化代码

This commit is contained in:
2025-08-15 14:21:16 +08:00
parent 9f849608db
commit a738f102a6
40 changed files with 1144 additions and 922 deletions

View File

@@ -57,7 +57,13 @@
</a-layout-sider>
<!-- 主内容区 -->
<a-layout :style="{ marginLeft: collapsed ? '60px' : '180px', transition: 'margin-left 0.2s', height: '100vh' }">
<a-layout
:style="{
marginLeft: collapsed ? '60px' : '180px',
transition: 'margin-left 0.2s',
height: '100vh',
}"
>
<a-layout-content
class="content-area"
:style="{
@@ -103,9 +109,7 @@ const mainMenuItems = [
{ path: '/history', label: '历史记录', icon: HistoryOutlined },
]
const bottomMenuItems = [
{ path: '/settings', label: '设置', icon: SettingOutlined },
]
const bottomMenuItems = [{ path: '/settings', label: '设置', icon: SettingOutlined }]
// 自动同步选中项
const selectedKeys = computed(() => {
@@ -142,9 +146,11 @@ const toggleCollapse = () => {
border-radius: 6px;
cursor: pointer;
}
.logo:hover {
background-color: rgba(255, 255, 255, 0.5);
}
:deep(.ant-layout-sider-light) .logo:hover {
background-color: rgba(0, 0, 0, 0.04);
}
@@ -162,6 +168,7 @@ const toggleCollapse = () => {
opacity: 1;
transition: opacity 0.2s ease;
}
.logo-text.text-hidden {
opacity: 0;
}
@@ -172,16 +179,19 @@ const toggleCollapse = () => {
overflow: auto;
/* 修复滚动条显示问题 */
scrollbar-width: thin;
scrollbar-color: rgba(0,0,0,0.2) transparent;
scrollbar-color: rgba(0, 0, 0, 0.2) transparent;
}
.main-menu-container::-webkit-scrollbar {
width: 6px;
}
.main-menu-container::-webkit-scrollbar-track {
background: transparent;
}
.main-menu-container::-webkit-scrollbar-thumb {
background: rgba(0,0,0,0.2);
background: rgba(0, 0, 0, 0.2);
border-radius: 4px;
}
@@ -190,6 +200,7 @@ const toggleCollapse = () => {
margin-top: auto;
border-top: 1px solid rgba(255, 255, 255, 0.08);
}
:deep(.ant-layout-sider-light .bottom-menu) {
border-top: 1px solid rgba(0, 0, 0, 0.04);
}
@@ -207,6 +218,7 @@ const toggleCollapse = () => {
:deep(.ant-layout-sider-dark) .menu-text {
color: #fff;
}
:deep(.ant-layout-sider-light) .logo-text,
:deep(.ant-layout-sider-light) .menu-text {
color: rgba(0, 0, 0, 0.88);
@@ -244,6 +256,7 @@ const toggleCollapse = () => {
scrollbar-width: none;
-ms-overflow-style: none;
}
.content-area::-webkit-scrollbar {
display: none;
}
@@ -279,4 +292,4 @@ const toggleCollapse = () => {
.app-layout-collapsed .ant-menu-inline-collapsed .bottom-menu {
padding-bottom: 6px;
}
</style>
</style>

View File

@@ -2,18 +2,14 @@
<div class="log-viewer">
<div class="log-header">
<div class="log-controls">
<a-select
v-model:value="selectedLevel"
style="width: 120px"
@change="filterLogs"
>
<a-select v-model:value="selectedLevel" style="width: 120px" @change="filterLogs">
<a-select-option value="all">所有级别</a-select-option>
<a-select-option value="debug">Debug</a-select-option>
<a-select-option value="info">Info</a-select-option>
<a-select-option value="warn">Warn</a-select-option>
<a-select-option value="error">Error</a-select-option>
</a-select>
<a-input-search
v-model:value="searchText"
placeholder="搜索日志..."
@@ -21,21 +17,21 @@
@search="filterLogs"
@change="filterLogs"
/>
<a-button @click="clearLogs" danger>
<template #icon>
<DeleteOutlined />
</template>
清空日志
</a-button>
<a-button @click="downloadLogs" type="primary">
<template #icon>
<DownloadOutlined />
</template>
导出日志
</a-button>
<a-button @click="toggleAutoScroll" :type="autoScroll ? 'primary' : 'default'">
<template #icon>
<VerticalAlignBottomOutlined />
@@ -43,19 +39,13 @@
自动滚动
</a-button>
</div>
<div class="log-stats">
总计: {{ filteredLogs.length }} 条日志
</div>
<div class="log-stats">总计: {{ filteredLogs.length }} 条日志</div>
</div>
<div
ref="logContainer"
class="log-container"
@scroll="handleScroll"
>
<div
v-for="(log, index) in filteredLogs"
<div ref="logContainer" class="log-container" @scroll="handleScroll">
<div
v-for="(log, index) in filteredLogs"
:key="index"
class="log-entry"
:class="[`log-${log.level}`, { 'log-highlight': highlightedIndex === index }]"
@@ -65,14 +55,12 @@
<div v-if="log.component" class="log-component">[{{ log.component }}]</div>
<div class="log-message">{{ log.message }}</div>
<div v-if="log.data" class="log-data">
<a-button
size="small"
type="link"
@click="toggleDataVisibility(index)"
>
<a-button size="small" type="link" @click="toggleDataVisibility(index)">
{{ expandedData.has(index) ? '隐藏数据' : '显示数据' }}
</a-button>
<pre v-if="expandedData.has(index)" class="log-data-content">{{ JSON.stringify(log.data, null, 2) }}</pre>
<pre v-if="expandedData.has(index)" class="log-data-content">{{
JSON.stringify(log.data, null, 2)
}}</pre>
</div>
</div>
</div>
@@ -81,10 +69,10 @@
<script setup lang="ts">
import { ref, computed, nextTick, onMounted, onUnmounted } from 'vue'
import {
DeleteOutlined,
DownloadOutlined,
VerticalAlignBottomOutlined
import {
DeleteOutlined,
DownloadOutlined,
VerticalAlignBottomOutlined,
} from '@ant-design/icons-vue'
import { logger, type LogEntry, type LogLevel } from '@/utils/logger'
@@ -108,10 +96,11 @@ const filteredLogs = computed(() => {
// 按搜索文本过滤
if (searchText.value) {
const search = searchText.value.toLowerCase()
filtered = filtered.filter(log =>
log.message.toLowerCase().includes(search) ||
log.component?.toLowerCase().includes(search) ||
(log.data && JSON.stringify(log.data).toLowerCase().includes(search))
filtered = filtered.filter(
log =>
log.message.toLowerCase().includes(search) ||
log.component?.toLowerCase().includes(search) ||
(log.data && JSON.stringify(log.data).toLowerCase().includes(search))
)
}
@@ -159,10 +148,10 @@ function scrollToBottom() {
function handleScroll() {
if (!logContainer.value) return
const { scrollTop, scrollHeight, clientHeight } = logContainer.value
const isAtBottom = scrollTop + clientHeight >= scrollHeight - 10
if (!isAtBottom) {
autoScroll.value = false
}
@@ -176,11 +165,12 @@ onMounted(() => {
nextTick(() => {
scrollToBottom()
})
// 监听日志变化
unwatchLogs = logs.value && typeof logs.value === 'object' && 'length' in logs.value
? () => {} // 如果logs是响应式的Vue会自动处理
: null
unwatchLogs =
logs.value && typeof logs.value === 'object' && 'length' in logs.value
? () => {} // 如果logs是响应式的Vue会自动处理
: null
})
onUnmounted(() => {
@@ -334,17 +324,17 @@ onUnmounted(() => {
flex-direction: column;
align-items: stretch;
}
.log-controls {
justify-content: center;
}
.log-entry {
flex-direction: column;
align-items: stretch;
gap: 4px;
}
.log-timestamp,
.log-level,
.log-component {

View File

@@ -83,7 +83,6 @@
row-key="id"
class="user-table"
>
<template #bodyCell="{ column, record: user }">
<template v-if="column.key === 'server'">
<div class="server-cell">
@@ -102,7 +101,10 @@
<template v-if="column.key === 'lastRun'">
<div class="last-run-cell">
<div v-if="!user.Data.LastAnnihilationDate && !user.Data.LastProxyDate" class="no-run-text">
<div
v-if="!user.Data.LastAnnihilationDate && !user.Data.LastProxyDate"
class="no-run-text"
>
尚未运行
</div>
<template v-else>
@@ -118,7 +120,6 @@
</div>
</template>
<template v-if="column.key === 'userAction'">
<a-space size="small">
<a-tooltip title="编辑用户配置">

View File

@@ -6,9 +6,7 @@
sub-title="为了正常安装和配置环境请以管理员权限运行此应用"
>
<template #extra>
<a-button type="primary" @click="handleRestartAsAdmin">
重新以管理员权限启动
</a-button>
<a-button type="primary" @click="handleRestartAsAdmin"> 重新以管理员权限启动 </a-button>
</template>
</a-result>
</div>
@@ -35,4 +33,4 @@ async function handleRestartAsAdmin() {
align-items: center;
min-height: 60vh;
}
</style>
</style>

View File

@@ -78,7 +78,7 @@ function handleForceEnterConfirm() {
// 事件处理
function handleSwitchToManual() {
aborted.value = true // 设置中断
aborted.value = true // 设置中断
props.onSwitchToManual()
}
@@ -117,19 +117,19 @@ async function startAutoProcess() {
let pipMirror = config.selectedPipMirror || 'tsinghua'
let pipResult = await window.electronAPI.installDependencies(pipMirror)
if (aborted.value) return
// 如果初始化时的镜像源不通,让用户重新选择
if (!pipResult.success) {
logger.warn(`使用镜像源 ${pipMirror} 安装依赖失败,需要重新选择镜像源`)
// 切换到手动模式让用户重新选择镜像源
progressText.value = '依赖安装失败,需要重新配置镜像源'
progressStatus.value = 'exception'
setTimeout(() => {
progressText.value = '请点击下方按钮重新配置环境'
}, 2000)
return
}

View File

@@ -3,10 +3,10 @@
<h3>获取后端源码</h3>
<div class="install-section">
<p>{{ backendExists ? '更新最新的后端代码' : '获取后端源代码' }}</p>
<div class="mirror-grid">
<div
v-for="mirror in gitMirrors"
<div
v-for="mirror in gitMirrors"
:key="mirror.key"
class="mirror-card"
:class="{ active: selectedGitMirror === mirror.key }"
@@ -24,7 +24,7 @@
<div class="mirror-url">{{ mirror.url }}</div>
</div>
</div>
<div class="test-actions">
<a-button @click="testGitMirrorSpeed" :loading="testingGitSpeed" type="primary">
{{ testingGitSpeed ? '测速中...' : '开始测速' }}
@@ -54,7 +54,6 @@ import { GIT_MIRRORS } from '@/config/mirrors'
const gitMirrors = ref<Mirror[]>(GIT_MIRRORS)
const selectedGitMirror = ref('github')
const testingGitSpeed = ref(false)
@@ -81,17 +80,17 @@ async function saveMirrorConfig() {
async function testMirrorWithTimeout(url: string, timeout = 3000): Promise<number> {
const startTime = Date.now()
try {
const controller = new AbortController()
const timeoutId = setTimeout(() => controller.abort(), timeout)
await fetch(url.replace('.git', ''), {
method: 'HEAD',
await fetch(url.replace('.git', ''), {
method: 'HEAD',
mode: 'no-cors',
signal: controller.signal
signal: controller.signal,
})
clearTimeout(timeoutId)
return Date.now() - startTime
} catch (error) {
@@ -102,14 +101,14 @@ async function testMirrorWithTimeout(url: string, timeout = 3000): Promise<numbe
async function testGitMirrorSpeed() {
testingGitSpeed.value = true
try {
const promises = gitMirrors.value.map(async (mirror) => {
const promises = gitMirrors.value.map(async mirror => {
mirror.speed = await testMirrorWithTimeout(mirror.url)
return mirror
})
await Promise.all(promises)
gitMirrors.value.sort((a, b) => (a.speed || 9999) - (b.speed || 9999))
const fastest = gitMirrors.value.find(m => m.speed !== 9999)
if (fastest) {
selectedGitMirror.value = fastest.key
@@ -131,14 +130,14 @@ function getSpeedClass(speed: number | null) {
defineExpose({
selectedGitMirror,
testGitMirrorSpeed,
gitMirrors
gitMirrors,
})
// 组件挂载时加载配置并自动开始测速
onMounted(async () => {
// 先加载配置
await loadMirrorConfig()
console.log('BackendStep 组件挂载,自动开始测速')
setTimeout(() => {
testGitMirrorSpeed()

View File

@@ -3,10 +3,10 @@
<h3>安装 Python 依赖包</h3>
<div class="install-section">
<p>通过 pip 安装项目所需的 Python 依赖包</p>
<div class="mirror-grid">
<div
v-for="mirror in pipMirrors"
<div
v-for="mirror in pipMirrors"
:key="mirror.key"
class="mirror-card"
:class="{ active: selectedPipMirror === mirror.key }"
@@ -24,7 +24,7 @@
<div class="mirror-url">{{ mirror.url }}</div>
</div>
</div>
<div class="test-actions">
<a-button @click="testPipMirrorSpeed" :loading="testingPipSpeed" type="primary">
{{ testingPipSpeed ? '测速中...' : '重新测速' }}
@@ -76,17 +76,17 @@ async function saveMirrorConfig() {
async function testMirrorWithTimeout(url: string, timeout = 3000): Promise<number> {
const startTime = Date.now()
try {
const controller = new AbortController()
const timeoutId = setTimeout(() => controller.abort(), timeout)
await fetch(url, {
method: 'HEAD',
await fetch(url, {
method: 'HEAD',
mode: 'no-cors',
signal: controller.signal
signal: controller.signal,
})
clearTimeout(timeoutId)
return Date.now() - startTime
} catch (error) {
@@ -97,14 +97,14 @@ async function testMirrorWithTimeout(url: string, timeout = 3000): Promise<numbe
async function testPipMirrorSpeed() {
testingPipSpeed.value = true
try {
const promises = pipMirrors.value.map(async (mirror) => {
const promises = pipMirrors.value.map(async mirror => {
mirror.speed = await testMirrorWithTimeout(mirror.url)
return mirror
})
await Promise.all(promises)
pipMirrors.value.sort((a, b) => (a.speed || 9999) - (b.speed || 9999))
const fastest = pipMirrors.value.find(m => m.speed !== 9999)
if (fastest) {
selectedPipMirror.value = fastest.key
@@ -125,14 +125,14 @@ function getSpeedClass(speed: number | null) {
defineExpose({
selectedPipMirror,
testPipMirrorSpeed
testPipMirrorSpeed,
})
// 组件挂载时加载配置并自动开始测速
onMounted(async () => {
// 先加载配置
await loadMirrorConfig()
console.log('DependenciesStep 组件挂载,自动开始测速')
setTimeout(() => {
testPipMirrorSpeed()

View File

@@ -4,22 +4,22 @@
<div v-if="!gitInstalled" class="install-section">
<p>需要安装 Git 工具来获取源代码</p>
<div class="git-info">
<a-alert
message="Git 工具信息"
<a-alert
message="Git 工具信息"
description="将安装便携版 Git 工具,包含完整的版本控制功能,无需系统安装。"
type="info"
show-icon
type="info"
show-icon
/>
</div>
</div>
<div v-else class="already-installed">
<a-result status="success" title="Git已成功安装无需继续安装" />
<!-- <div class="reinstall-section">-->
<!-- <a-button type="primary" danger @click="handleForceReinstall" :loading="reinstalling">-->
<!-- {{ reinstalling ? '正在重新安装...' : '强制重新安装' }}-->
<!-- </a-button>-->
<!-- <p class="reinstall-note">点击此按钮将删除现有Git环境并重新安装</p>-->
<!-- </div>-->
<!-- <div class="reinstall-section">-->
<!-- <a-button type="primary" danger @click="handleForceReinstall" :loading="reinstalling">-->
<!-- {{ reinstalling ? '正在重新安装...' : '强制重新安装' }}-->
<!-- </a-button>-->
<!-- <p class="reinstall-note">点击此按钮将删除现有Git环境并重新安装</p>-->
<!-- </div>-->
</div>
</div>
</template>
@@ -43,13 +43,13 @@ async function handleForceReinstall() {
if (!deleteResult.success) {
throw new Error(`删除Git失败: ${deleteResult.error}`)
}
// 重新安装Git
const installResult = await window.electronAPI.downloadGit()
if (!installResult.success) {
throw new Error(`重新安装Git失败: ${installResult.error}`)
}
console.log('Git强制重新安装成功')
// 通知父组件更新状态
window.location.reload() // 简单的页面刷新来更新状态
@@ -62,7 +62,7 @@ async function handleForceReinstall() {
}
defineExpose({
handleForceReinstall
handleForceReinstall,
})
</script>

View File

@@ -3,22 +3,23 @@
<div class="header">
<h1>AUTO_MAA 初始化向导</h1>
<p>欢迎使用 AUTO_MAA让我们来配置您的运行环境</p>
<div class="header-actions">
<a-button size="large" type="primary" @click="handleSkipToHome">
跳转至首页仅开发用
</a-button>
<a-button size="large" type="default" @click="handleJumpToStep(6)" style="margin-left: 16px;">
<a-button
size="large"
type="default"
@click="handleJumpToStep(6)"
style="margin-left: 16px"
>
跳到启动服务第七步
</a-button>
</div>
</div>
<a-steps
:current="currentStep"
:status="stepStatus"
class="init-steps"
>
<a-steps :current="currentStep" :status="stepStatus" class="init-steps">
<a-step title="主题设置" description="选择您喜欢的主题" />
<a-step title="Python 环境" description="安装 Python 运行环境" />
<a-step title="pip 安装" description="安装 Python 包管理器" />
@@ -28,22 +29,26 @@
<a-step title="启动服务" description="启动后端服务" />
</a-steps>
<!-- &lt;!&ndash; 全局进度条 &ndash;&gt;-->
<!-- <div v-if="isProcessing" class="global-progress">-->
<!-- <a-progress -->
<!-- :percent="globalProgress" -->
<!-- :status="globalProgressStatus"-->
<!-- :show-info="true"-->
<!-- />-->
<!-- <div class="progress-text">{{ progressText }}</div>-->
<!-- </div>-->
<!-- &lt;!&ndash; 全局进度条 &ndash;&gt;-->
<!-- <div v-if="isProcessing" class="global-progress">-->
<!-- <a-progress -->
<!-- :percent="globalProgress" -->
<!-- :status="globalProgressStatus"-->
<!-- :show-info="true"-->
<!-- />-->
<!-- <div class="progress-text">{{ progressText }}</div>-->
<!-- </div>-->
<div class="step-content">
<!-- 步骤 0: 主题设置 -->
<ThemeStep v-if="currentStep === 0" ref="themeStepRef" />
<!-- 步骤 1: Python 环境 -->
<PythonStep v-if="currentStep === 1" :python-installed="pythonInstalled" ref="pythonStepRef" />
<PythonStep
v-if="currentStep === 1"
:python-installed="pythonInstalled"
ref="pythonStepRef"
/>
<!-- 步骤 2: pip 安装 -->
<PipStep v-if="currentStep === 2" :pip-installed="pipInstalled" ref="pipStepRef" />
@@ -71,20 +76,19 @@
上一步
</a-button>
<a-button
<a-button
v-if="currentStep < 6"
size="large"
type="primary"
type="primary"
@click="handleNextStep"
:loading="isProcessing"
>
{{ getNextButtonText() }}
</a-button>
<!-- 第7步重新启动服务按钮 -->
<a-button
v-if="currentStep === 6"
<a-button
v-if="currentStep === 6"
type="default"
size="large"
@click="handleNextStep"
@@ -94,15 +98,15 @@
</a-button>
</div>
<!-- <div v-if="errorMessage" class="error-message">-->
<!-- <a-alert -->
<!-- :message="errorMessage" -->
<!-- type="error" -->
<!-- show-icon -->
<!-- closable-->
<!-- @close="errorMessage = ''"-->
<!-- />-->
<!-- </div>-->
<!-- <div v-if="errorMessage" class="error-message">-->
<!-- <a-alert -->
<!-- :message="errorMessage" -->
<!-- type="error" -->
<!-- show-icon -->
<!-- closable-->
<!-- @close="errorMessage = ''"-->
<!-- />-->
<!-- </div>-->
</div>
</template>
@@ -130,7 +134,7 @@ interface Props {
backendExists: boolean
dependenciesInstalled: boolean
serviceStarted: boolean
// 事件处理函数
onSkipToHome: () => void
onEnterApp: () => void
@@ -183,7 +187,7 @@ async function handleNextStep() {
console.log('nextStep 被调用,当前步骤:', currentStep.value)
isProcessing.value = true
errorMessage.value = ''
try {
switch (currentStep.value) {
case 0: // 主题设置
@@ -227,7 +231,7 @@ async function handleNextStep() {
await startBackendService()
break
}
if (currentStep.value < 6) {
currentStep.value++
// 进入新步骤时自动开始测速
@@ -244,14 +248,22 @@ async function handleNextStep() {
function getNextButtonText() {
switch (currentStep.value) {
case 0: return '下一步'
case 1: return props.pythonInstalled ? '下一步' : '安装 Python'
case 2: return props.pipInstalled ? '下一步' : '安装 pip'
case 3: return props.gitInstalled ? '下一步' : '安装 Git'
case 4: return props.backendExists ? '更新代码' : '获取代码'
case 5: return '安装依赖'
case 6: return '启动服务'
default: return '下一步'
case 0:
return '下一步'
case 1:
return props.pythonInstalled ? '下一步' : '安装 Python'
case 2:
return props.pipInstalled ? '下一步' : '安装 pip'
case 3:
return props.gitInstalled ? '下一步' : '安装 Git'
case 4:
return props.backendExists ? '更新代码' : '获取代码'
case 5:
return '安装依赖'
case 6:
return '启动服务'
default:
return '下一步'
}
}
@@ -368,13 +380,13 @@ async function autoStartBackendService() {
logger.info('自动启动后端服务')
isProcessing.value = true
errorMessage.value = ''
if (serviceStepRef.value) {
serviceStepRef.value.startingService = true
serviceStepRef.value.showServiceProgress = true
serviceStepRef.value.serviceStatus = '正在自动启动后端服务...'
}
try {
const result = await window.electronAPI.startBackend()
if (result.success) {
@@ -384,7 +396,7 @@ async function autoStartBackendService() {
}
stepStatus.value = 'finish'
logger.info('后端服务自动启动成功延迟1秒后自动进入主页')
// 延迟1秒后自动进入主页
setTimeout(() => {
handleEnterApp()
@@ -413,13 +425,13 @@ async function autoStartBackendService() {
// 手动启动后端服务(用户点击按钮时调用)
async function startBackendService() {
logger.info('手动重新启动后端服务')
if (serviceStepRef.value) {
serviceStepRef.value.startingService = true
serviceStepRef.value.showServiceProgress = true
serviceStepRef.value.serviceStatus = '正在重新启动后端服务...'
}
try {
const result = await window.electronAPI.startBackend()
if (result.success) {
@@ -429,7 +441,7 @@ async function startBackendService() {
}
stepStatus.value = 'finish'
logger.info('后端服务手动启动成功延迟1秒后自动进入主页')
// 延迟1秒后自动进入主页
setTimeout(() => {
handleEnterApp()
@@ -456,7 +468,7 @@ function handleDownloadProgress(progress: any) {
// 更新全局进度条
globalProgress.value = progress.progress
progressText.value = progress.message
if (progress.status === 'error') {
globalProgressStatus.value = 'exception'
} else if (progress.status === 'completed') {
@@ -464,7 +476,7 @@ function handleDownloadProgress(progress: any) {
} else {
globalProgressStatus.value = 'normal'
}
// 通知父组件
props.onProgressUpdate(progress)
}
@@ -472,11 +484,11 @@ function handleDownloadProgress(progress: any) {
// 暴露给父组件的方法
defineExpose({
currentStep,
handleDownloadProgress
handleDownloadProgress,
})
// 监听 errorMessage一旦有内容就弹窗
watch(errorMessage, (val) => {
watch(errorMessage, val => {
if (val) {
message.error(val)
// 弹窗后可选:自动清空 errorMessage
@@ -554,7 +566,7 @@ watch(errorMessage, (val) => {
flex-direction: column;
gap: 8px;
}
.step-actions {
flex-direction: column;
gap: 12px;

View File

@@ -3,24 +3,24 @@
<h3>安装 pip 包管理器</h3>
<div v-if="!pipInstalled" class="install-section">
<p>pip Python 的包管理工具用于安装和管理 Python </p>
<div class="pip-info">
<a-alert
message="pip 安装信息"
<a-alert
message="pip 安装信息"
description="将自动下载并安装 pip 包管理器,这是安装 Python 依赖包的必要工具。"
type="info"
show-icon
type="info"
show-icon
/>
</div>
</div>
<div v-else class="already-installed">
<a-result status="success" title="pip已成功安装无需继续安装" />
<!-- <div class="reinstall-section">-->
<!-- <a-button type="primary" danger @click="handleForceReinstall" :loading="reinstalling">-->
<!-- {{ reinstalling ? '正在重新安装...' : '强制重新安装' }}-->
<!-- </a-button>-->
<!-- <p class="reinstall-note">点击此按钮将删除现有pip环境并重新安装</p>-->
<!-- </div>-->
<!-- <div class="reinstall-section">-->
<!-- <a-button type="primary" danger @click="handleForceReinstall" :loading="reinstalling">-->
<!-- {{ reinstalling ? '正在重新安装...' : '强制重新安装' }}-->
<!-- </a-button>-->
<!-- <p class="reinstall-note">点击此按钮将删除现有pip环境并重新安装</p>-->
<!-- </div>-->
</div>
</div>
</template>
@@ -44,13 +44,13 @@ async function handleForceReinstall() {
if (!deleteResult.success) {
throw new Error(`删除pip失败: ${deleteResult.error}`)
}
// 重新安装pip
const installResult = await window.electronAPI.installPip()
if (!installResult.success) {
throw new Error(`重新安装pip失败: ${installResult.error}`)
}
console.log('pip强制重新安装成功')
// 通知父组件更新状态
window.location.reload() // 简单的页面刷新来更新状态
@@ -63,7 +63,7 @@ async function handleForceReinstall() {
}
defineExpose({
handleForceReinstall
handleForceReinstall,
})
</script>

View File

@@ -1,42 +1,47 @@
<template>
<div class="step-panel">
<h3>Python 运行环境</h3>
<div v-if="!pythonInstalled" class="install-section">
<p>需要安装 Python 3.13.0 运行环境64位嵌入式版本</p>
<div class="step-panel">
<h3>Python 运行环境</h3>
<div v-if="!pythonInstalled" class="install-section">
<p>需要安装 Python 3.13.0 运行环境64位嵌入式版本</p>
<div class="mirror-grid">
<div v-for="mirror in pythonMirrors" :key="mirror.key" class="mirror-card"
:class="{ active: selectedPythonMirror === mirror.key }" @click="selectedPythonMirror = mirror.key">
<div class="mirror-header">
<h4>{{ mirror.name }}</h4>
<div class="speed-badge" :class="getSpeedClass(mirror.speed)">
<span v-if="mirror.speed === null && !testingSpeed">未测试</span>
<span v-else-if="testingSpeed">测试中...</span>
<span v-else-if="mirror.speed === 9999">超时</span>
<span v-else>{{ mirror.speed }}ms</span>
</div>
</div>
<div class="mirror-url">{{ mirror.url }}</div>
</div>
<div class="mirror-grid">
<div
v-for="mirror in pythonMirrors"
:key="mirror.key"
class="mirror-card"
:class="{ active: selectedPythonMirror === mirror.key }"
@click="selectedPythonMirror = mirror.key"
>
<div class="mirror-header">
<h4>{{ mirror.name }}</h4>
<div class="speed-badge" :class="getSpeedClass(mirror.speed)">
<span v-if="mirror.speed === null && !testingSpeed">未测试</span>
<span v-else-if="testingSpeed">测试中...</span>
<span v-else-if="mirror.speed === 9999">超时</span>
<span v-else>{{ mirror.speed }}ms</span>
</div>
</div>
<div class="mirror-url">{{ mirror.url }}</div>
</div>
</div>
<div class="test-actions">
<a-button @click="testPythonMirrorSpeed" :loading="testingSpeed" type="primary">
{{ testingSpeed ? '测速中...' : '重新测速' }}
</a-button>
<span class="test-note">3秒无响应视为超时</span>
</div>
</div>
<div v-else class="already-installed">
<a-result status="success" title="Python已成功安装无需继续安装" />
<!-- <div class="reinstall-section">-->
<!-- <a-button type="primary" danger @click="handleForceReinstall" :loading="reinstalling">-->
<!-- {{ reinstalling ? '正在重新安装...' : '强制重新安装' }}-->
<!-- </a-button>-->
<!-- <p class="reinstall-note">点击此按钮将删除现有Python环境并重新安装</p>-->
<!-- </div>-->
</div>
<div class="test-actions">
<a-button @click="testPythonMirrorSpeed" :loading="testingSpeed" type="primary">
{{ testingSpeed ? '测速中...' : '重新测速' }}
</a-button>
<span class="test-note">3秒无响应视为超时</span>
</div>
</div>
<div v-else class="already-installed">
<a-result status="success" title="Python已成功安装无需继续安装" />
<!-- <div class="reinstall-section">-->
<!-- <a-button type="primary" danger @click="handleForceReinstall" :loading="reinstalling">-->
<!-- {{ reinstalling ? '正在重新安装...' : '强制重新安装' }}-->
<!-- </a-button>-->
<!-- <p class="reinstall-note">点击此按钮将删除现有Python环境并重新安装</p>-->
<!-- </div>-->
</div>
</div>
</template>
<script setup lang="ts">
@@ -44,14 +49,14 @@ import { ref, onMounted } from 'vue'
import { getConfig, saveConfig } from '@/utils/config'
interface Mirror {
key: string
name: string
url: string
speed: number | null
key: string
name: string
url: string
speed: number | null
}
const props = defineProps<{
pythonInstalled: boolean
pythonInstalled: boolean
}>()
import { PYTHON_MIRRORS } from '@/config/mirrors'
@@ -84,239 +89,239 @@ async function saveMirrorConfig() {
}
async function testMirrorWithTimeout(url: string, timeout = 3000): Promise<number> {
const startTime = Date.now()
const startTime = Date.now()
try {
const controller = new AbortController()
const timeoutId = setTimeout(() => controller.abort(), timeout)
try {
const controller = new AbortController()
const timeoutId = setTimeout(() => controller.abort(), timeout)
await fetch(url, {
method: 'HEAD',
mode: 'no-cors',
signal: controller.signal
})
await fetch(url, {
method: 'HEAD',
mode: 'no-cors',
signal: controller.signal,
})
clearTimeout(timeoutId)
return Date.now() - startTime
} catch (error) {
return 9999 // 超时或失败
}
clearTimeout(timeoutId)
return Date.now() - startTime
} catch (error) {
return 9999 // 超时或失败
}
}
async function testPythonMirrorSpeed() {
testingSpeed.value = true
try {
const promises = pythonMirrors.value.map(async (mirror) => {
mirror.speed = await testMirrorWithTimeout(mirror.url)
return mirror
})
testingSpeed.value = true
try {
const promises = pythonMirrors.value.map(async mirror => {
mirror.speed = await testMirrorWithTimeout(mirror.url)
return mirror
})
await Promise.all(promises)
await Promise.all(promises)
pythonMirrors.value.sort((a, b) => (a.speed || 9999) - (b.speed || 9999))
pythonMirrors.value.sort((a, b) => (a.speed || 9999) - (b.speed || 9999))
const fastest = pythonMirrors.value.find(m => m.speed !== 9999)
if (fastest) {
selectedPythonMirror.value = fastest.key
await saveMirrorConfig() // 保存最快的镜像源选择
}
} finally {
testingSpeed.value = false
const fastest = pythonMirrors.value.find(m => m.speed !== 9999)
if (fastest) {
selectedPythonMirror.value = fastest.key
await saveMirrorConfig() // 保存最快的镜像源选择
}
} finally {
testingSpeed.value = false
}
}
function getSpeedClass(speed: number | null) {
if (speed === null) return 'speed-unknown'
if (speed === 9999) return 'speed-timeout'
if (speed < 500) return 'speed-fast'
if (speed < 1500) return 'speed-medium'
return 'speed-slow'
if (speed === null) return 'speed-unknown'
if (speed === 9999) return 'speed-timeout'
if (speed < 500) return 'speed-fast'
if (speed < 1500) return 'speed-medium'
return 'speed-slow'
}
// 强制重新安装Python
async function handleForceReinstall() {
reinstalling.value = true
try {
console.log('开始强制重新安装Python')
// 先删除现有Python目录
const deleteResult = await window.electronAPI.deletePython()
if (!deleteResult.success) {
throw new Error(`删除Python目录失败: ${deleteResult.error}`)
}
// 重新下载安装Python
const installResult = await window.electronAPI.downloadPython(selectedPythonMirror.value)
if (!installResult.success) {
throw new Error(`重新安装Python失败: ${installResult.error}`)
}
console.log('Python强制重新安装成功')
// 通知父组件更新状态
window.location.reload() // 简单的页面刷新来更新状态
} catch (error) {
console.error('Python强制重新安装失败:', error)
// 这里可以添加错误提示
} finally {
reinstalling.value = false
reinstalling.value = true
try {
console.log('开始强制重新安装Python')
// 先删除现有Python目录
const deleteResult = await window.electronAPI.deletePython()
if (!deleteResult.success) {
throw new Error(`删除Python目录失败: ${deleteResult.error}`)
}
// 重新下载安装Python
const installResult = await window.electronAPI.downloadPython(selectedPythonMirror.value)
if (!installResult.success) {
throw new Error(`重新安装Python失败: ${installResult.error}`)
}
console.log('Python强制重新安装成功')
// 通知父组件更新状态
window.location.reload() // 简单的页面刷新来更新状态
} catch (error) {
console.error('Python强制重新安装失败:', error)
// 这里可以添加错误提示
} finally {
reinstalling.value = false
}
}
defineExpose({
selectedPythonMirror,
testPythonMirrorSpeed,
handleForceReinstall
selectedPythonMirror,
testPythonMirrorSpeed,
handleForceReinstall,
})
// 组件挂载时加载配置并自动开始测速
onMounted(async () => {
// 先加载配置
await loadMirrorConfig()
if (!props.pythonInstalled) {
console.log('PythonStep 组件挂载,自动开始测速')
setTimeout(() => {
testPythonMirrorSpeed()
}, 200) // 延迟200ms确保组件完全渲染
}
// 先加载配置
await loadMirrorConfig()
if (!props.pythonInstalled) {
console.log('PythonStep 组件挂载,自动开始测速')
setTimeout(() => {
testPythonMirrorSpeed()
}, 200) // 延迟200ms确保组件完全渲染
}
})
</script>
<style scoped>
.step-panel {
padding: 20px;
background: var(--ant-color-bg-elevated);
border-radius: 8px;
border: 1px solid var(--ant-color-border);
padding: 20px;
background: var(--ant-color-bg-elevated);
border-radius: 8px;
border: 1px solid var(--ant-color-border);
}
.step-panel h3 {
font-size: 20px;
font-weight: 600;
color: var(--ant-color-text);
margin-bottom: 20px;
font-size: 20px;
font-weight: 600;
color: var(--ant-color-text);
margin-bottom: 20px;
}
.install-section {
display: flex;
flex-direction: column;
gap: 20px;
display: flex;
flex-direction: column;
gap: 20px;
}
.install-section p {
color: var(--ant-color-text-secondary);
margin: 0;
color: var(--ant-color-text-secondary);
margin: 0;
}
.mirror-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
gap: 16px;
margin-bottom: 20px;
display: grid;
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
gap: 16px;
margin-bottom: 20px;
}
.mirror-card {
padding: 16px;
border: 2px solid var(--ant-color-border);
border-radius: 8px;
cursor: pointer;
transition: all 0.2s ease;
background: var(--ant-color-bg-container);
padding: 16px;
border: 2px solid var(--ant-color-border);
border-radius: 8px;
cursor: pointer;
transition: all 0.2s ease;
background: var(--ant-color-bg-container);
}
.mirror-card:hover {
border-color: var(--ant-color-primary);
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
border-color: var(--ant-color-primary);
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
.mirror-card.active {
border-color: var(--ant-color-primary);
background: var(--ant-color-primary-bg);
border-color: var(--ant-color-primary);
background: var(--ant-color-primary-bg);
}
.mirror-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 8px;
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 8px;
}
.mirror-header h4 {
margin: 0;
font-size: 16px;
font-weight: 600;
color: var(--ant-color-text);
margin: 0;
font-size: 16px;
font-weight: 600;
color: var(--ant-color-text);
}
.speed-badge {
padding: 4px 8px;
border-radius: 4px;
font-size: 12px;
font-weight: 500;
padding: 4px 8px;
border-radius: 4px;
font-size: 12px;
font-weight: 500;
}
.speed-badge.speed-unknown {
background: var(--ant-color-fill-tertiary);
color: var(--ant-color-text-tertiary);
background: var(--ant-color-fill-tertiary);
color: var(--ant-color-text-tertiary);
}
.speed-badge.speed-fast {
background: var(--ant-color-success-bg);
color: var(--ant-color-success);
background: var(--ant-color-success-bg);
color: var(--ant-color-success);
}
.speed-badge.speed-medium {
background: var(--ant-color-warning-bg);
color: var(--ant-color-warning);
background: var(--ant-color-warning-bg);
color: var(--ant-color-warning);
}
.speed-badge.speed-slow {
background: var(--ant-color-error-bg);
color: var(--ant-color-error);
background: var(--ant-color-error-bg);
color: var(--ant-color-error);
}
.speed-badge.speed-timeout {
background: var(--ant-color-error-bg);
color: var(--ant-color-error);
background: var(--ant-color-error-bg);
color: var(--ant-color-error);
}
.mirror-url {
font-size: 12px;
color: var(--ant-color-text-tertiary);
word-break: break-all;
font-size: 12px;
color: var(--ant-color-text-tertiary);
word-break: break-all;
}
.test-actions {
display: flex;
align-items: center;
gap: 12px;
justify-content: center;
display: flex;
align-items: center;
gap: 12px;
justify-content: center;
}
.test-note {
font-size: 12px;
color: var(--ant-color-text-tertiary);
font-size: 12px;
color: var(--ant-color-text-tertiary);
}
.already-installed {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
min-height: 200px;
gap: 20px;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
min-height: 200px;
gap: 20px;
}
.reinstall-section {
display: flex;
flex-direction: column;
align-items: center;
gap: 12px;
display: flex;
flex-direction: column;
align-items: center;
gap: 12px;
}
.reinstall-note {
font-size: 12px;
color: var(--ant-color-text-tertiary);
text-align: center;
margin: 0;
font-size: 12px;
color: var(--ant-color-text-tertiary);
text-align: center;
margin: 0;
}
</style>

View File

@@ -24,7 +24,7 @@ defineExpose({
startingService,
showServiceProgress,
serviceProgress,
serviceStatus
serviceStatus,
})
</script>

View File

@@ -13,8 +13,8 @@
<div class="setting-group">
<label>主题色彩</label>
<div class="color-picker">
<div
v-for="(color, key) in themeColors"
<div
v-for="(color, key) in themeColors"
:key="key"
class="color-option"
:class="{ active: selectedThemeColor === key }"
@@ -51,9 +51,9 @@ async function onThemeColorChange(color: ThemeColor) {
async function saveSettings() {
await saveThemeConfig(selectedThemeMode.value, selectedThemeColor.value)
console.log('主题设置已保存:', {
themeMode: selectedThemeMode.value,
themeColor: selectedThemeColor.value
console.log('主题设置已保存:', {
themeMode: selectedThemeMode.value,
themeColor: selectedThemeColor.value,
})
}
@@ -64,9 +64,9 @@ async function loadSettings() {
selectedThemeColor.value = config.themeColor
setThemeMode(selectedThemeMode.value)
setThemeColor(selectedThemeColor.value)
console.log('主题设置已加载:', {
themeMode: selectedThemeMode.value,
themeColor: selectedThemeColor.value
console.log('主题设置已加载:', {
themeMode: selectedThemeMode.value,
themeColor: selectedThemeColor.value,
})
} catch (error) {
console.warn('Failed to load theme settings:', error)
@@ -78,7 +78,7 @@ defineExpose({
loadSettings,
saveSettings,
selectedThemeMode,
selectedThemeColor
selectedThemeColor,
})
// 组件挂载时加载设置

View File

@@ -19,9 +19,7 @@
:scroll="{ x: 600 }"
>
<template #bodyCell="{ column, record, index }">
<template v-if="column.key === 'index'">
{{ index + 1 }}个脚本
</template>
<template v-if="column.key === 'index'"> {{ index + 1 }}个脚本 </template>
<template v-else-if="column.key === 'script'">
{{ getScriptName(record.script) }}
</template>
@@ -31,7 +29,12 @@
<EditOutlined />
编辑
</a-button>
<a-popconfirm title="确定要删除这个队列项吗?" @confirm="deleteQueueItem(record.id)" ok-text="确定" cancel-text="取消">
<a-popconfirm
title="确定要删除这个队列项吗?"
@confirm="deleteQueueItem(record.id)"
ok-text="确定"
cancel-text="取消"
>
<a-button size="small" danger>
<DeleteOutlined />
删除
@@ -47,11 +50,22 @@
</div>
<!-- 队列项编辑弹窗 -->
<a-modal v-model:open="modalVisible" :title="editingQueueItem ? '编辑队列项' : '添加队列项'" @ok="saveQueueItem"
@cancel="cancelEdit" :confirm-loading="saving" width="600px">
<a-modal
v-model:open="modalVisible"
:title="editingQueueItem ? '编辑队列项' : '添加队列项'"
@ok="saveQueueItem"
@cancel="cancelEdit"
:confirm-loading="saving"
width="600px"
>
<a-form ref="formRef" :model="form" :rules="rules" layout="vertical">
<a-form-item label="关联脚本" name="script">
<a-select v-model:value="form.script" placeholder="请选择关联脚本" allow-clear :options="scriptOptions" />
<a-select
v-model:value="form.script"
placeholder="请选择关联脚本"
allow-clear
:options="scriptOptions"
/>
</a-form-item>
</a-form>
</a-modal>
@@ -66,7 +80,7 @@ import {
ReloadOutlined,
EditOutlined,
DeleteOutlined,
MoreOutlined
MoreOutlined,
} from '@ant-design/icons-vue'
import { Service } from '@/api'
import type { FormInstance } from 'ant-design-vue'
@@ -103,12 +117,12 @@ const getScriptName = (scriptId: string) => {
// 表单引用和数据
const formRef = ref<FormInstance>()
const form = reactive({
script: ''
script: '',
})
// 表单验证规则
const rules = {
script: [{ required: true, message: '请选择关联脚本', trigger: 'change' }]
script: [{ required: true, message: '请选择关联脚本', trigger: 'change' }],
}
// 表格列配置
@@ -135,10 +149,13 @@ const queueColumns = [
const queueItems = ref(props.queueItems)
// 监听props变化
watch(() => props.queueItems, (newQueueItems) => {
queueItems.value = newQueueItems
}, { deep: true })
watch(
() => props.queueItems,
newQueueItems => {
queueItems.value = newQueueItems
},
{ deep: true }
)
// 加载脚本选项
const loadOptions = async () => {
@@ -161,12 +178,11 @@ const loadOptions = async () => {
}
}
// 添加队列项
const addQueueItem = async () => {
editingQueueItem.value = null
Object.assign(form, {
script: ''
script: '',
})
// 确保在打开弹窗时加载脚本选项
@@ -178,7 +194,7 @@ const addQueueItem = async () => {
const editQueueItem = async (item: any) => {
editingQueueItem.value = item
Object.assign(form, {
script: item.script || ''
script: item.script || '',
})
// 确保在打开弹窗时加载脚本选项
@@ -199,9 +215,9 @@ const saveQueueItem = async () => {
queueItemId: editingQueueItem.value.id,
data: {
Info: {
ScriptId: form.script
}
}
ScriptId: form.script,
},
},
})
if (response.code === 200) {
@@ -214,7 +230,7 @@ const saveQueueItem = async () => {
// 添加队列项 - 先创建,再更新
// 1. 先创建队列项只传queueId
const createResponse = await Service.addItemApiQueueItemAddPost({
queueId: props.queueId
queueId: props.queueId,
})
// 2. 用返回的queueItemId更新队列项数据
@@ -224,9 +240,9 @@ const saveQueueItem = async () => {
queueItemId: createResponse.queueItemId,
data: {
Info: {
ScriptId: form.script
}
}
ScriptId: form.script,
},
},
})
if (updateResponse.code === 200) {
@@ -262,7 +278,7 @@ const deleteQueueItem = async (itemId: string) => {
try {
const response = await Service.deleteItemApiQueueItemDeletePost({
queueId: props.queueId,
queueItemId: itemId
queueItemId: itemId,
})
if (response.code === 200) {