style:格式化代码
This commit is contained in:
@@ -4,7 +4,13 @@ import * as fs from 'fs'
|
||||
import { spawn } from 'child_process'
|
||||
import { getAppRoot, checkEnvironment } from './services/environmentService'
|
||||
import { setMainWindow as setDownloadMainWindow } from './services/downloadService'
|
||||
import { setMainWindow as setPythonMainWindow, downloadPython, installPipPackage, installDependencies, startBackend } from './services/pythonService'
|
||||
import {
|
||||
setMainWindow as setPythonMainWindow,
|
||||
downloadPython,
|
||||
installPipPackage,
|
||||
installDependencies,
|
||||
startBackend,
|
||||
} from './services/pythonService'
|
||||
import { setMainWindow as setGitMainWindow, downloadGit, cloneBackend } from './services/gitService'
|
||||
|
||||
// 检查是否以管理员权限运行
|
||||
@@ -34,13 +40,17 @@ function restartAsAdmin(): void {
|
||||
const args = process.argv.slice(1)
|
||||
|
||||
// 使用PowerShell以管理员权限启动
|
||||
spawn('powershell', [
|
||||
spawn(
|
||||
'powershell',
|
||||
[
|
||||
'-Command',
|
||||
`Start-Process -FilePath "${exePath}" -ArgumentList "${args.join(' ')}" -Verb RunAs`
|
||||
], {
|
||||
`Start-Process -FilePath "${exePath}" -ArgumentList "${args.join(' ')}" -Verb RunAs`,
|
||||
],
|
||||
{
|
||||
detached: true,
|
||||
stdio: 'ignore'
|
||||
})
|
||||
stdio: 'ignore',
|
||||
}
|
||||
)
|
||||
|
||||
app.quit()
|
||||
}
|
||||
@@ -144,15 +154,21 @@ ipcMain.handle('download-git', async () => {
|
||||
return downloadGit(appRoot)
|
||||
})
|
||||
|
||||
ipcMain.handle('clone-backend', async (event, repoUrl = 'https://github.com/DLmaster361/AUTO_MAA.git') => {
|
||||
ipcMain.handle(
|
||||
'clone-backend',
|
||||
async (event, repoUrl = 'https://github.com/DLmaster361/AUTO_MAA.git') => {
|
||||
const appRoot = getAppRoot()
|
||||
return cloneBackend(appRoot, repoUrl)
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
ipcMain.handle('update-backend', async (event, repoUrl = 'https://github.com/DLmaster361/AUTO_MAA.git') => {
|
||||
ipcMain.handle(
|
||||
'update-backend',
|
||||
async (event, repoUrl = 'https://github.com/DLmaster361/AUTO_MAA.git') => {
|
||||
const appRoot = getAppRoot()
|
||||
return cloneBackend(appRoot, repoUrl) // 使用相同的逻辑,会自动判断是pull还是clone
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
// 配置文件操作
|
||||
ipcMain.handle('save-config', async (event, config) => {
|
||||
|
||||
@@ -39,5 +39,5 @@ contextBridge.exposeInMainWorld('electronAPI', {
|
||||
},
|
||||
removeDownloadProgressListener: () => {
|
||||
ipcRenderer.removeAllListeners('download-progress')
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
@@ -18,7 +18,6 @@ export function downloadFile(url: string, outputPath: string): Promise<void> {
|
||||
// 创建HTTP客户端,兼容https和http
|
||||
const client = url.startsWith('https') ? https : http
|
||||
|
||||
|
||||
client
|
||||
.get(url, response => {
|
||||
const totalSize = parseInt(response.headers['content-length'] || '0', 10)
|
||||
|
||||
@@ -4,9 +4,7 @@ import { app } from 'electron'
|
||||
|
||||
// 获取应用根目录
|
||||
export function getAppRoot(): string {
|
||||
return process.env.NODE_ENV === 'development'
|
||||
? process.cwd()
|
||||
: path.dirname(app.getPath('exe'))
|
||||
return process.env.NODE_ENV === 'development' ? process.cwd() : path.dirname(app.getPath('exe'))
|
||||
}
|
||||
|
||||
// 检查环境
|
||||
@@ -23,13 +21,14 @@ export function checkEnvironment(appRoot: string) {
|
||||
|
||||
// 检查依赖是否已安装(简单检查是否存在site-packages目录)
|
||||
const sitePackagesPath = path.join(pythonPath, 'Lib', 'site-packages')
|
||||
const dependenciesInstalled = fs.existsSync(sitePackagesPath) && fs.readdirSync(sitePackagesPath).length > 10
|
||||
const dependenciesInstalled =
|
||||
fs.existsSync(sitePackagesPath) && fs.readdirSync(sitePackagesPath).length > 10
|
||||
|
||||
return {
|
||||
pythonExists,
|
||||
gitExists,
|
||||
backendExists,
|
||||
dependenciesInstalled,
|
||||
isInitialized: pythonExists && gitExists && backendExists && dependenciesInstalled
|
||||
isInitialized: pythonExists && gitExists && backendExists && dependenciesInstalled,
|
||||
}
|
||||
}
|
||||
@@ -31,7 +31,6 @@ function copyDirSync(src: string, dest: string) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 获取Git环境变量配置
|
||||
function getGitEnvironment(appRoot: string) {
|
||||
const gitDir = path.join(appRoot, 'environment', 'git')
|
||||
@@ -249,11 +248,26 @@ export async function cloneBackend(
|
||||
})
|
||||
}
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
const proc = spawn(gitPath, ['clone', '--progress', '--verbose','--single-branch','--depth','1','--branch', 'feature/refactor-backend', repoUrl, tmpDir], {
|
||||
const proc = spawn(
|
||||
gitPath,
|
||||
[
|
||||
'clone',
|
||||
'--progress',
|
||||
'--verbose',
|
||||
'--single-branch',
|
||||
'--depth',
|
||||
'1',
|
||||
'--branch',
|
||||
'feature/refactor-backend',
|
||||
repoUrl,
|
||||
tmpDir,
|
||||
],
|
||||
{
|
||||
stdio: 'pipe',
|
||||
env: gitEnv,
|
||||
cwd: appRoot,
|
||||
})
|
||||
}
|
||||
)
|
||||
proc.stdout?.on('data', d => console.log('git clone:', d.toString()))
|
||||
proc.stderr?.on('data', d => console.log('git clone err:', d.toString()))
|
||||
proc.on('close', code =>
|
||||
|
||||
@@ -16,8 +16,9 @@ const pythonMirrorUrls = {
|
||||
official: 'https://www.python.org/ftp/python/3.12.0/python-3.12.0-embed-amd64.zip',
|
||||
tsinghua: 'https://mirrors.tuna.tsinghua.edu.cn/python/3.12.0/python-3.12.0-embed-amd64.zip',
|
||||
ustc: 'https://mirrors.ustc.edu.cn/python/3.12.0/python-3.12.0-embed-amd64.zip',
|
||||
huawei: 'https://mirrors.huaweicloud.com/repository/toolkit/python/3.12.0/python-3.12.0-embed-amd64.zip',
|
||||
aliyun: 'https://mirrors.aliyun.com/python-release/windows/python-3.12.0-embed-amd64.zip'
|
||||
huawei:
|
||||
'https://mirrors.huaweicloud.com/repository/toolkit/python/3.12.0/python-3.12.0-embed-amd64.zip',
|
||||
aliyun: 'https://mirrors.aliyun.com/python-release/windows/python-3.12.0-embed-amd64.zip',
|
||||
}
|
||||
|
||||
// 检查pip是否已安装
|
||||
@@ -80,7 +81,8 @@ async function installPip(pythonPath: string, appRoot: string): Promise<void> {
|
||||
const stats = fs.statSync(getPipPath)
|
||||
console.log(`get-pip.py文件大小: ${stats.size} bytes`)
|
||||
|
||||
if (stats.size < 10000) { // 如果文件小于10KB,可能是无效文件
|
||||
if (stats.size < 10000) {
|
||||
// 如果文件小于10KB,可能是无效文件
|
||||
throw new Error(`get-pip.py文件大小异常: ${stats.size} bytes,可能下载失败`)
|
||||
}
|
||||
} catch (error) {
|
||||
@@ -94,20 +96,20 @@ async function installPip(pythonPath: string, appRoot: string): Promise<void> {
|
||||
|
||||
const process = spawn(pythonExe, [getPipPath], {
|
||||
cwd: pythonPath,
|
||||
stdio: 'pipe'
|
||||
stdio: 'pipe',
|
||||
})
|
||||
|
||||
process.stdout?.on('data', (data) => {
|
||||
process.stdout?.on('data', data => {
|
||||
const output = data.toString()
|
||||
console.log('pip安装输出:', output)
|
||||
})
|
||||
|
||||
process.stderr?.on('data', (data) => {
|
||||
process.stderr?.on('data', data => {
|
||||
const errorOutput = data.toString()
|
||||
console.log('pip安装错误输出:', errorOutput)
|
||||
})
|
||||
|
||||
process.on('close', (code) => {
|
||||
process.on('close', code => {
|
||||
console.log(`pip安装完成,退出码: ${code}`)
|
||||
if (code === 0) {
|
||||
console.log('pip安装成功')
|
||||
@@ -117,7 +119,7 @@ async function installPip(pythonPath: string, appRoot: string): Promise<void> {
|
||||
}
|
||||
})
|
||||
|
||||
process.on('error', (error) => {
|
||||
process.on('error', error => {
|
||||
console.error('pip安装进程错误:', error)
|
||||
reject(error)
|
||||
})
|
||||
@@ -128,20 +130,20 @@ async function installPip(pythonPath: string, appRoot: string): Promise<void> {
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
const verifyProcess = spawn(pythonExe, ['-m', 'pip', '--version'], {
|
||||
cwd: pythonPath,
|
||||
stdio: 'pipe'
|
||||
stdio: 'pipe',
|
||||
})
|
||||
|
||||
verifyProcess.stdout?.on('data', (data) => {
|
||||
verifyProcess.stdout?.on('data', data => {
|
||||
const output = data.toString()
|
||||
console.log('pip版本信息:', output)
|
||||
})
|
||||
|
||||
verifyProcess.stderr?.on('data', (data) => {
|
||||
verifyProcess.stderr?.on('data', data => {
|
||||
const errorOutput = data.toString()
|
||||
console.log('pip版本检查错误:', errorOutput)
|
||||
})
|
||||
|
||||
verifyProcess.on('close', (code) => {
|
||||
verifyProcess.on('close', code => {
|
||||
if (code === 0) {
|
||||
console.log('pip验证成功')
|
||||
resolve()
|
||||
@@ -150,7 +152,7 @@ async function installPip(pythonPath: string, appRoot: string): Promise<void> {
|
||||
}
|
||||
})
|
||||
|
||||
verifyProcess.on('error', (error) => {
|
||||
verifyProcess.on('error', error => {
|
||||
console.error('pip验证进程错误:', error)
|
||||
reject(error)
|
||||
})
|
||||
@@ -171,7 +173,10 @@ async function installPip(pythonPath: string, appRoot: string): Promise<void> {
|
||||
}
|
||||
|
||||
// 下载Python
|
||||
export async function downloadPython(appRoot: string, mirror = 'ustc'): Promise<{ success: boolean; error?: string }> {
|
||||
export async function downloadPython(
|
||||
appRoot: string,
|
||||
mirror = 'ustc'
|
||||
): Promise<{ success: boolean; error?: string }> {
|
||||
try {
|
||||
const environmentPath = path.join(appRoot, 'environment')
|
||||
const pythonPath = path.join(environmentPath, 'python')
|
||||
@@ -186,24 +191,30 @@ export async function downloadPython(appRoot: string, mirror = 'ustc'): Promise<
|
||||
type: 'python',
|
||||
progress: 0,
|
||||
status: 'downloading',
|
||||
message: '开始下载Python...'
|
||||
message: '开始下载Python...',
|
||||
})
|
||||
}
|
||||
|
||||
// 根据选择的镜像源获取下载链接
|
||||
const pythonUrl = pythonMirrorUrls[mirror as keyof typeof pythonMirrorUrls] || pythonMirrorUrls.ustc
|
||||
const pythonUrl =
|
||||
pythonMirrorUrls[mirror as keyof typeof pythonMirrorUrls] || pythonMirrorUrls.ustc
|
||||
const zipPath = path.join(environmentPath, 'python.zip')
|
||||
|
||||
await downloadFile(pythonUrl, zipPath)
|
||||
|
||||
// 检查下载的Python文件大小
|
||||
const stats = fs.statSync(zipPath)
|
||||
console.log(`Python压缩包大小: ${stats.size} bytes (${(stats.size / 1024 / 1024).toFixed(2)} MB)`)
|
||||
console.log(
|
||||
`Python压缩包大小: ${stats.size} bytes (${(stats.size / 1024 / 1024).toFixed(2)} MB)`
|
||||
)
|
||||
|
||||
// Python 3.12.0嵌入式版本应该大约30MB,如果小于5MB可能是无效文件
|
||||
if (stats.size < 5 * 1024 * 1024) { // 5MB
|
||||
if (stats.size < 5 * 1024 * 1024) {
|
||||
// 5MB
|
||||
fs.unlinkSync(zipPath) // 删除无效文件
|
||||
throw new Error(`Python下载文件大小异常: ${stats.size} bytes (${(stats.size / 1024).toFixed(2)} KB),可能是镜像站返回的错误页面或无效文件。请选择一个其他可用镜像源进行下载!`)
|
||||
throw new Error(
|
||||
`Python下载文件大小异常: ${stats.size} bytes (${(stats.size / 1024).toFixed(2)} KB),可能是镜像站返回的错误页面或无效文件。请选择一个其他可用镜像源进行下载!`
|
||||
)
|
||||
}
|
||||
|
||||
if (mainWindow) {
|
||||
@@ -211,7 +222,7 @@ export async function downloadPython(appRoot: string, mirror = 'ustc'): Promise<
|
||||
type: 'python',
|
||||
progress: 100,
|
||||
status: 'extracting',
|
||||
message: '正在解压Python...'
|
||||
message: '正在解压Python...',
|
||||
})
|
||||
}
|
||||
|
||||
@@ -241,14 +252,13 @@ export async function downloadPython(appRoot: string, mirror = 'ustc'): Promise<
|
||||
console.log('已启用 site-packages 支持')
|
||||
}
|
||||
|
||||
|
||||
// 安装pip
|
||||
if (mainWindow) {
|
||||
mainWindow.webContents.send('download-progress', {
|
||||
type: 'python',
|
||||
progress: 80,
|
||||
status: 'installing',
|
||||
message: '正在安装pip...'
|
||||
message: '正在安装pip...',
|
||||
})
|
||||
}
|
||||
|
||||
@@ -259,7 +269,7 @@ export async function downloadPython(appRoot: string, mirror = 'ustc'): Promise<
|
||||
type: 'python',
|
||||
progress: 100,
|
||||
status: 'completed',
|
||||
message: 'Python和pip安装完成'
|
||||
message: 'Python和pip安装完成',
|
||||
})
|
||||
}
|
||||
|
||||
@@ -271,7 +281,7 @@ export async function downloadPython(appRoot: string, mirror = 'ustc'): Promise<
|
||||
type: 'python',
|
||||
progress: 0,
|
||||
status: 'error',
|
||||
message: `Python下载失败: ${errorMessage}`
|
||||
message: `Python下载失败: ${errorMessage}`,
|
||||
})
|
||||
}
|
||||
return { success: false, error: errorMessage }
|
||||
@@ -284,11 +294,17 @@ const pipMirrorUrls = {
|
||||
tsinghua: 'https://pypi.tuna.tsinghua.edu.cn/simple/',
|
||||
ustc: 'https://pypi.mirrors.ustc.edu.cn/simple/',
|
||||
aliyun: 'https://mirrors.aliyun.com/pypi/simple/',
|
||||
douban: 'https://pypi.douban.com/simple/'
|
||||
douban: 'https://pypi.douban.com/simple/',
|
||||
}
|
||||
|
||||
// 安装Python依赖
|
||||
export async function installDependencies(appRoot: string, mirror = 'tsinghua'): Promise<{ success: boolean; error?: string }> {
|
||||
export async function installDependencies(
|
||||
appRoot: string,
|
||||
mirror = 'tsinghua'
|
||||
): Promise<{
|
||||
success: boolean
|
||||
error?: string
|
||||
}> {
|
||||
try {
|
||||
const pythonPath = path.join(appRoot, 'environment', 'python', 'python.exe')
|
||||
const backendPath = path.join(appRoot)
|
||||
@@ -307,12 +323,13 @@ export async function installDependencies(appRoot: string, mirror = 'tsinghua'):
|
||||
type: 'dependencies',
|
||||
progress: 0,
|
||||
status: 'downloading',
|
||||
message: '正在安装Python依赖包...'
|
||||
message: '正在安装Python依赖包...',
|
||||
})
|
||||
}
|
||||
|
||||
// 获取pip镜像源URL
|
||||
const pipMirrorUrl = pipMirrorUrls[mirror as keyof typeof pipMirrorUrls] || pipMirrorUrls.tsinghua
|
||||
const pipMirrorUrl =
|
||||
pipMirrorUrls[mirror as keyof typeof pipMirrorUrls] || pipMirrorUrls.tsinghua
|
||||
|
||||
// 使用Scripts文件夹中的pip.exe
|
||||
const pythonDir = path.join(appRoot, 'environment', 'python')
|
||||
@@ -331,17 +348,24 @@ export async function installDependencies(appRoot: string, mirror = 'tsinghua'):
|
||||
|
||||
// 安装依赖 - 直接使用pip.exe而不是python -m pip
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
const process = spawn(pipExePath, [
|
||||
const process = spawn(
|
||||
pipExePath,
|
||||
[
|
||||
'install',
|
||||
'-r', requirementsPath,
|
||||
'-i', pipMirrorUrl,
|
||||
'--trusted-host', new URL(pipMirrorUrl).hostname
|
||||
], {
|
||||
'-r',
|
||||
requirementsPath,
|
||||
'-i',
|
||||
pipMirrorUrl,
|
||||
'--trusted-host',
|
||||
new URL(pipMirrorUrl).hostname,
|
||||
],
|
||||
{
|
||||
cwd: backendPath,
|
||||
stdio: 'pipe'
|
||||
})
|
||||
stdio: 'pipe',
|
||||
}
|
||||
)
|
||||
|
||||
process.stdout?.on('data', (data) => {
|
||||
process.stdout?.on('data', data => {
|
||||
const output = data.toString()
|
||||
console.log('Pip output:', output)
|
||||
|
||||
@@ -350,17 +374,17 @@ export async function installDependencies(appRoot: string, mirror = 'tsinghua'):
|
||||
type: 'dependencies',
|
||||
progress: 50,
|
||||
status: 'downloading',
|
||||
message: '正在安装依赖包...'
|
||||
message: '正在安装依赖包...',
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
process.stderr?.on('data', (data) => {
|
||||
process.stderr?.on('data', data => {
|
||||
const errorOutput = data.toString()
|
||||
console.error('Pip error:', errorOutput)
|
||||
})
|
||||
|
||||
process.on('close', (code) => {
|
||||
process.on('close', code => {
|
||||
console.log(`pip安装完成,退出码: ${code}`)
|
||||
if (code === 0) {
|
||||
resolve()
|
||||
@@ -369,7 +393,7 @@ export async function installDependencies(appRoot: string, mirror = 'tsinghua'):
|
||||
}
|
||||
})
|
||||
|
||||
process.on('error', (error) => {
|
||||
process.on('error', error => {
|
||||
console.error('pip进程错误:', error)
|
||||
reject(error)
|
||||
})
|
||||
@@ -380,7 +404,7 @@ export async function installDependencies(appRoot: string, mirror = 'tsinghua'):
|
||||
type: 'dependencies',
|
||||
progress: 100,
|
||||
status: 'completed',
|
||||
message: 'Python依赖安装完成'
|
||||
message: 'Python依赖安装完成',
|
||||
})
|
||||
}
|
||||
|
||||
@@ -392,7 +416,7 @@ export async function installDependencies(appRoot: string, mirror = 'tsinghua'):
|
||||
type: 'dependencies',
|
||||
progress: 0,
|
||||
status: 'error',
|
||||
message: `依赖安装失败: ${errorMessage}`
|
||||
message: `依赖安装失败: ${errorMessage}`,
|
||||
})
|
||||
}
|
||||
return { success: false, error: errorMessage }
|
||||
@@ -400,7 +424,9 @@ export async function installDependencies(appRoot: string, mirror = 'tsinghua'):
|
||||
}
|
||||
|
||||
// 导出pip安装函数
|
||||
export async function installPipPackage(appRoot: string): Promise<{ success: boolean; error?: string }> {
|
||||
export async function installPipPackage(
|
||||
appRoot: string
|
||||
): Promise<{ success: boolean; error?: string }> {
|
||||
try {
|
||||
const pythonPath = path.join(appRoot, 'environment', 'python')
|
||||
|
||||
@@ -413,7 +439,7 @@ export async function installPipPackage(appRoot: string): Promise<{ success: boo
|
||||
type: 'pip',
|
||||
progress: 0,
|
||||
status: 'installing',
|
||||
message: '正在安装pip...'
|
||||
message: '正在安装pip...',
|
||||
})
|
||||
}
|
||||
|
||||
@@ -424,7 +450,7 @@ export async function installPipPackage(appRoot: string): Promise<{ success: boo
|
||||
type: 'pip',
|
||||
progress: 100,
|
||||
status: 'completed',
|
||||
message: 'pip安装完成'
|
||||
message: 'pip安装完成',
|
||||
})
|
||||
}
|
||||
|
||||
@@ -436,7 +462,7 @@ export async function installPipPackage(appRoot: string): Promise<{ success: boo
|
||||
type: 'pip',
|
||||
progress: 0,
|
||||
status: 'error',
|
||||
message: `pip安装失败: ${errorMessage}`
|
||||
message: `pip安装失败: ${errorMessage}`,
|
||||
})
|
||||
}
|
||||
return { success: false, error: errorMessage }
|
||||
@@ -466,19 +492,17 @@ export async function startBackend(appRoot: string): Promise<{ success: boolean;
|
||||
stdio: 'pipe',
|
||||
env: {
|
||||
...process.env,
|
||||
PYTHONIOENCODING: 'utf-8' // 设置Python输出编码为UTF-8
|
||||
}
|
||||
PYTHONIOENCODING: 'utf-8', // 设置Python输出编码为UTF-8
|
||||
},
|
||||
})
|
||||
|
||||
|
||||
|
||||
// 等待后端启动
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
const timeout = setTimeout(() => {
|
||||
reject(new Error('后端启动超时'))
|
||||
}, 30000) // 30秒超时
|
||||
|
||||
backendProcess.stdout?.on('data', (data) => {
|
||||
backendProcess.stdout?.on('data', data => {
|
||||
const output = data.toString()
|
||||
console.log('Backend output:', output)
|
||||
|
||||
@@ -490,7 +514,7 @@ export async function startBackend(appRoot: string): Promise<{ success: boolean;
|
||||
})
|
||||
|
||||
// ✅ 重要:也要监听 stderr
|
||||
backendProcess.stderr?.on('data', (data) => {
|
||||
backendProcess.stderr?.on('data', data => {
|
||||
const output = data.toString()
|
||||
console.error('Backend error:', output) // 保留原有日志
|
||||
|
||||
@@ -501,11 +525,11 @@ export async function startBackend(appRoot: string): Promise<{ success: boolean;
|
||||
}
|
||||
})
|
||||
|
||||
backendProcess.stderr?.on('data', (data) => {
|
||||
backendProcess.stderr?.on('data', data => {
|
||||
console.error('Backend error:', data.toString())
|
||||
})
|
||||
|
||||
backendProcess.on('error', (error) => {
|
||||
backendProcess.on('error', error => {
|
||||
clearTimeout(timeout)
|
||||
reject(error)
|
||||
})
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -174,12 +181,15 @@ const toggleCollapse = () => {
|
||||
scrollbar-width: thin;
|
||||
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);
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -2,11 +2,7 @@
|
||||
<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>
|
||||
@@ -44,16 +40,10 @@
|
||||
</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 ref="logContainer" class="log-container" @scroll="handleScroll">
|
||||
<div
|
||||
v-for="(log, index) in filteredLogs"
|
||||
:key="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>
|
||||
@@ -84,7 +72,7 @@ import { ref, computed, nextTick, onMounted, onUnmounted } from 'vue'
|
||||
import {
|
||||
DeleteOutlined,
|
||||
DownloadOutlined,
|
||||
VerticalAlignBottomOutlined
|
||||
VerticalAlignBottomOutlined,
|
||||
} from '@ant-design/icons-vue'
|
||||
import { logger, type LogEntry, type LogLevel } from '@/utils/logger'
|
||||
|
||||
@@ -108,7 +96,8 @@ const filteredLogs = computed(() => {
|
||||
// 按搜索文本过滤
|
||||
if (searchText.value) {
|
||||
const search = searchText.value.toLowerCase()
|
||||
filtered = filtered.filter(log =>
|
||||
filtered = filtered.filter(
|
||||
log =>
|
||||
log.message.toLowerCase().includes(search) ||
|
||||
log.component?.toLowerCase().includes(search) ||
|
||||
(log.data && JSON.stringify(log.data).toLowerCase().includes(search))
|
||||
@@ -178,7 +167,8 @@ onMounted(() => {
|
||||
})
|
||||
|
||||
// 监听日志变化
|
||||
unwatchLogs = logs.value && typeof logs.value === 'object' && 'length' in logs.value
|
||||
unwatchLogs =
|
||||
logs.value && typeof logs.value === 'object' && 'length' in logs.value
|
||||
? () => {} // 如果logs是响应式的,Vue会自动处理
|
||||
: null
|
||||
})
|
||||
|
||||
@@ -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="编辑用户配置">
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -54,7 +54,6 @@ import { GIT_MIRRORS } from '@/config/mirrors'
|
||||
|
||||
const gitMirrors = ref<Mirror[]>(GIT_MIRRORS)
|
||||
|
||||
|
||||
const selectedGitMirror = ref('github')
|
||||
const testingGitSpeed = ref(false)
|
||||
|
||||
@@ -89,7 +88,7 @@ async function testMirrorWithTimeout(url: string, timeout = 3000): Promise<numbe
|
||||
await fetch(url.replace('.git', ''), {
|
||||
method: 'HEAD',
|
||||
mode: 'no-cors',
|
||||
signal: controller.signal
|
||||
signal: controller.signal,
|
||||
})
|
||||
|
||||
clearTimeout(timeoutId)
|
||||
@@ -102,7 +101,7 @@ 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
|
||||
})
|
||||
@@ -131,7 +130,7 @@ function getSpeedClass(speed: number | null) {
|
||||
defineExpose({
|
||||
selectedGitMirror,
|
||||
testGitMirrorSpeed,
|
||||
gitMirrors
|
||||
gitMirrors,
|
||||
})
|
||||
|
||||
// 组件挂载时加载配置并自动开始测速
|
||||
|
||||
@@ -84,7 +84,7 @@ async function testMirrorWithTimeout(url: string, timeout = 3000): Promise<numbe
|
||||
await fetch(url, {
|
||||
method: 'HEAD',
|
||||
mode: 'no-cors',
|
||||
signal: controller.signal
|
||||
signal: controller.signal,
|
||||
})
|
||||
|
||||
clearTimeout(timeoutId)
|
||||
@@ -97,7 +97,7 @@ 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
|
||||
})
|
||||
@@ -125,7 +125,7 @@ function getSpeedClass(speed: number | null) {
|
||||
|
||||
defineExpose({
|
||||
selectedPipMirror,
|
||||
testPipMirrorSpeed
|
||||
testPipMirrorSpeed,
|
||||
})
|
||||
|
||||
// 组件挂载时加载配置并自动开始测速
|
||||
|
||||
@@ -62,7 +62,7 @@ async function handleForceReinstall() {
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
handleForceReinstall
|
||||
handleForceReinstall,
|
||||
})
|
||||
</script>
|
||||
|
||||
|
||||
@@ -8,17 +8,18 @@
|
||||
<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 包管理器" />
|
||||
@@ -43,7 +44,11 @@
|
||||
<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,7 +76,6 @@
|
||||
上一步
|
||||
</a-button>
|
||||
|
||||
|
||||
<a-button
|
||||
v-if="currentStep < 6"
|
||||
size="large"
|
||||
@@ -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 '下一步'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -63,7 +63,7 @@ async function handleForceReinstall() {
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
handleForceReinstall
|
||||
handleForceReinstall,
|
||||
})
|
||||
</script>
|
||||
|
||||
|
||||
@@ -5,8 +5,13 @@
|
||||
<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
|
||||
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)">
|
||||
@@ -93,7 +98,7 @@ async function testMirrorWithTimeout(url: string, timeout = 3000): Promise<numbe
|
||||
await fetch(url, {
|
||||
method: 'HEAD',
|
||||
mode: 'no-cors',
|
||||
signal: controller.signal
|
||||
signal: controller.signal,
|
||||
})
|
||||
|
||||
clearTimeout(timeoutId)
|
||||
@@ -106,7 +111,7 @@ async function testMirrorWithTimeout(url: string, timeout = 3000): Promise<numbe
|
||||
async function testPythonMirrorSpeed() {
|
||||
testingSpeed.value = true
|
||||
try {
|
||||
const promises = pythonMirrors.value.map(async (mirror) => {
|
||||
const promises = pythonMirrors.value.map(async mirror => {
|
||||
mirror.speed = await testMirrorWithTimeout(mirror.url)
|
||||
return mirror
|
||||
})
|
||||
@@ -164,7 +169,7 @@ async function handleForceReinstall() {
|
||||
defineExpose({
|
||||
selectedPythonMirror,
|
||||
testPythonMirrorSpeed,
|
||||
handleForceReinstall
|
||||
handleForceReinstall,
|
||||
})
|
||||
|
||||
// 组件挂载时加载配置并自动开始测速
|
||||
|
||||
@@ -24,7 +24,7 @@ defineExpose({
|
||||
startingService,
|
||||
showServiceProgress,
|
||||
serviceProgress,
|
||||
serviceStatus
|
||||
serviceStatus,
|
||||
})
|
||||
</script>
|
||||
|
||||
|
||||
@@ -53,7 +53,7 @@ async function saveSettings() {
|
||||
await saveThemeConfig(selectedThemeMode.value, selectedThemeColor.value)
|
||||
console.log('主题设置已保存:', {
|
||||
themeMode: selectedThemeMode.value,
|
||||
themeColor: selectedThemeColor.value
|
||||
themeColor: selectedThemeColor.value,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -66,7 +66,7 @@ async function loadSettings() {
|
||||
setThemeColor(selectedThemeColor.value)
|
||||
console.log('主题设置已加载:', {
|
||||
themeMode: selectedThemeMode.value,
|
||||
themeColor: selectedThemeColor.value
|
||||
themeColor: selectedThemeColor.value,
|
||||
})
|
||||
} catch (error) {
|
||||
console.warn('Failed to load theme settings:', error)
|
||||
@@ -78,7 +78,7 @@ defineExpose({
|
||||
loadSettings,
|
||||
saveSettings,
|
||||
selectedThemeMode,
|
||||
selectedThemeColor
|
||||
selectedThemeColor,
|
||||
})
|
||||
|
||||
// 组件挂载时加载设置
|
||||
|
||||
@@ -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) => {
|
||||
watch(
|
||||
() => props.queueItems,
|
||||
newQueueItems => {
|
||||
queueItems.value = newQueueItems
|
||||
}, { deep: true })
|
||||
|
||||
},
|
||||
{ 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) {
|
||||
|
||||
@@ -96,6 +96,6 @@ export function usePlanApi() {
|
||||
createPlan,
|
||||
updatePlan,
|
||||
deletePlan,
|
||||
reorderPlans
|
||||
reorderPlans,
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,7 @@ export function useScriptApi() {
|
||||
|
||||
try {
|
||||
const requestData: ScriptCreateIn = {
|
||||
type: type === 'MAA' ? ScriptCreateIn.type.MAA : ScriptCreateIn.type.GENERAL
|
||||
type: type === 'MAA' ? ScriptCreateIn.type.MAA : ScriptCreateIn.type.GENERAL,
|
||||
}
|
||||
|
||||
const response = await Service.addScriptApiScriptsAddPost(requestData)
|
||||
@@ -28,7 +28,7 @@ export function useScriptApi() {
|
||||
return {
|
||||
scriptId: response.scriptId,
|
||||
message: response.message || '脚本添加成功',
|
||||
data: response.data
|
||||
data: response.data,
|
||||
}
|
||||
} catch (err) {
|
||||
const errorMsg = err instanceof Error ? err.message : '添加脚本失败'
|
||||
@@ -105,7 +105,7 @@ export function useScriptApi() {
|
||||
type: scriptType,
|
||||
name: config?.Info?.Name || `${item.type}脚本`,
|
||||
config,
|
||||
createTime: new Date().toLocaleString()
|
||||
createTime: new Date().toLocaleString(),
|
||||
}
|
||||
} catch (err) {
|
||||
const errorMsg = err instanceof Error ? err.message : '获取脚本详情失败'
|
||||
@@ -157,7 +157,7 @@ export function useScriptApi() {
|
||||
|
||||
const response = await Service.updateScriptApiScriptsUpdatePost({
|
||||
scriptId,
|
||||
data: dataToSend
|
||||
data: dataToSend,
|
||||
})
|
||||
|
||||
if (response.code !== 200) {
|
||||
|
||||
@@ -42,7 +42,7 @@ export function useSettingsApi() {
|
||||
|
||||
try {
|
||||
const response = await Service.updateScriptApiSettingUpdatePost({
|
||||
data: settings
|
||||
data: settings,
|
||||
})
|
||||
|
||||
// 根据code判断是否成功(非200就是不成功)
|
||||
|
||||
@@ -109,15 +109,17 @@ const updateCSSVariables = () => {
|
||||
// 颜色工具函数
|
||||
const hexToRgb = (hex: string) => {
|
||||
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex)
|
||||
return result ? {
|
||||
return result
|
||||
? {
|
||||
r: parseInt(result[1], 16),
|
||||
g: parseInt(result[2], 16),
|
||||
b: parseInt(result[3], 16)
|
||||
} : null
|
||||
b: parseInt(result[3], 16),
|
||||
}
|
||||
: null
|
||||
}
|
||||
|
||||
const rgbToHex = (r: number, g: number, b: number) => {
|
||||
return "#" + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1)
|
||||
return '#' + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1)
|
||||
}
|
||||
|
||||
const lightenColor = (hex: string, percent: number) => {
|
||||
@@ -127,11 +129,7 @@ const lightenColor = (hex: string, percent: number) => {
|
||||
const { r, g, b } = rgb
|
||||
const amount = Math.round(2.55 * percent)
|
||||
|
||||
return rgbToHex(
|
||||
Math.min(255, r + amount),
|
||||
Math.min(255, g + amount),
|
||||
Math.min(255, b + amount)
|
||||
)
|
||||
return rgbToHex(Math.min(255, r + amount), Math.min(255, g + amount), Math.min(255, b + amount))
|
||||
}
|
||||
|
||||
const darkenColor = (hex: string, percent: number) => {
|
||||
@@ -141,11 +139,7 @@ const darkenColor = (hex: string, percent: number) => {
|
||||
const { r, g, b } = rgb
|
||||
const amount = Math.round(2.55 * percent)
|
||||
|
||||
return rgbToHex(
|
||||
Math.max(0, r - amount),
|
||||
Math.max(0, g - amount),
|
||||
Math.max(0, b - amount)
|
||||
)
|
||||
return rgbToHex(Math.max(0, r - amount), Math.max(0, g - amount), Math.max(0, b - amount))
|
||||
}
|
||||
|
||||
// 监听系统主题变化
|
||||
|
||||
@@ -14,7 +14,7 @@ export function useUserApi() {
|
||||
|
||||
try {
|
||||
const requestData: UserInBase = {
|
||||
scriptId
|
||||
scriptId,
|
||||
}
|
||||
|
||||
const response = await Service.addUserApiScriptsUserAddPost(requestData)
|
||||
@@ -39,7 +39,11 @@ export function useUserApi() {
|
||||
}
|
||||
|
||||
// 更新用户
|
||||
const updateUser = async (scriptId: string, userId: string, userData: Record<string, Record<string, any>>): Promise<boolean> => {
|
||||
const updateUser = async (
|
||||
scriptId: string,
|
||||
userId: string,
|
||||
userData: Record<string, Record<string, any>>
|
||||
): Promise<boolean> => {
|
||||
loading.value = true
|
||||
error.value = null
|
||||
|
||||
@@ -47,7 +51,7 @@ export function useUserApi() {
|
||||
const requestData: UserUpdateIn = {
|
||||
scriptId,
|
||||
userId,
|
||||
data: userData
|
||||
data: userData,
|
||||
}
|
||||
|
||||
const response = await Service.updateUserApiScriptsUserUpdatePost(requestData)
|
||||
@@ -80,7 +84,7 @@ export function useUserApi() {
|
||||
try {
|
||||
const requestData: UserDeleteIn = {
|
||||
scriptId,
|
||||
userId
|
||||
userId,
|
||||
}
|
||||
|
||||
const response = await Service.deleteUserApiScriptsUserDeletePost(requestData)
|
||||
|
||||
@@ -22,38 +22,38 @@ export const GIT_MIRRORS: MirrorConfig[] = [
|
||||
key: 'github',
|
||||
name: 'GitHub 官方',
|
||||
url: 'https://github.com/DLmaster361/AUTO_MAA.git',
|
||||
speed: null
|
||||
speed: null,
|
||||
},
|
||||
{
|
||||
key: 'ghfast',
|
||||
name: 'ghfast 镜像',
|
||||
url: 'https://ghfast.top/https://github.com/DLmaster361/AUTO_MAA.git',
|
||||
speed: null
|
||||
speed: null,
|
||||
},
|
||||
{
|
||||
key: 'ghproxy_cloudflare',
|
||||
name: 'gh-proxy (Cloudflare加速)',
|
||||
url: 'https://gh-proxy.com/https://github.com/DLmaster361/AUTO_MAA.git',
|
||||
speed: null
|
||||
speed: null,
|
||||
},
|
||||
{
|
||||
key: 'ghproxy_hongkong',
|
||||
name: 'gh-proxy (香港节点加速)',
|
||||
url: 'https://hk.gh-proxy.com/https://github.com/DLmaster361/AUTO_MAA.git',
|
||||
speed: null
|
||||
speed: null,
|
||||
},
|
||||
{
|
||||
key: 'ghproxy_fastly',
|
||||
name: 'gh-proxy (Fastly CDN加速)',
|
||||
url: 'https://cdn.gh-proxy.com/https://github.com/DLmaster361/AUTO_MAA.git',
|
||||
speed: null
|
||||
speed: null,
|
||||
},
|
||||
{
|
||||
key: 'ghproxy_edgeone',
|
||||
name: 'gh-proxy (EdgeOne加速)',
|
||||
url: 'https://edgeone.gh-proxy.com/https://github.com/DLmaster361/AUTO_MAA.git',
|
||||
speed: null
|
||||
}
|
||||
speed: null,
|
||||
},
|
||||
]
|
||||
|
||||
/**
|
||||
@@ -64,32 +64,32 @@ export const PYTHON_MIRRORS: MirrorConfig[] = [
|
||||
key: 'official',
|
||||
name: 'Python 官方',
|
||||
url: 'https://www.python.org/ftp/python/3.12.0/python-3.12.0-embed-amd64.zip',
|
||||
speed: null
|
||||
speed: null,
|
||||
},
|
||||
{
|
||||
key: 'tsinghua',
|
||||
name: '清华 TUNA 镜像',
|
||||
url: 'https://mirrors.tuna.tsinghua.edu.cn/python/3.12.0/python-3.12.0-embed-amd64.zip',
|
||||
speed: null
|
||||
speed: null,
|
||||
},
|
||||
{
|
||||
key: 'ustc',
|
||||
name: '中科大镜像',
|
||||
url: 'https://mirrors.ustc.edu.cn/python/3.12.0/python-3.12.0-embed-amd64.zip',
|
||||
speed: null
|
||||
speed: null,
|
||||
},
|
||||
{
|
||||
key: 'huawei',
|
||||
name: '华为云镜像',
|
||||
url: 'https://mirrors.huaweicloud.com/repository/toolkit/python/3.12.0/python-3.12.0-embed-amd64.zip',
|
||||
speed: null
|
||||
speed: null,
|
||||
},
|
||||
{
|
||||
key: 'aliyun',
|
||||
name: '阿里云镜像',
|
||||
url: 'https://mirrors.aliyun.com/python-release/windows/python-3.12.0-embed-amd64.zip',
|
||||
speed: null
|
||||
}
|
||||
speed: null,
|
||||
},
|
||||
]
|
||||
|
||||
/**
|
||||
@@ -100,32 +100,32 @@ export const PIP_MIRRORS: MirrorConfig[] = [
|
||||
key: 'official',
|
||||
name: 'PyPI 官方',
|
||||
url: 'https://pypi.org/simple/',
|
||||
speed: null
|
||||
speed: null,
|
||||
},
|
||||
{
|
||||
key: 'tsinghua',
|
||||
name: '清华大学',
|
||||
url: 'https://pypi.tuna.tsinghua.edu.cn/simple/',
|
||||
speed: null
|
||||
speed: null,
|
||||
},
|
||||
{
|
||||
key: 'ustc',
|
||||
name: '中科大',
|
||||
url: 'https://pypi.mirrors.ustc.edu.cn/simple/',
|
||||
speed: null
|
||||
speed: null,
|
||||
},
|
||||
{
|
||||
key: 'aliyun',
|
||||
name: '阿里云',
|
||||
url: 'https://mirrors.aliyun.com/pypi/simple/',
|
||||
speed: null
|
||||
speed: null,
|
||||
},
|
||||
{
|
||||
key: 'douban',
|
||||
name: '豆瓣',
|
||||
url: 'https://pypi.douban.com/simple/',
|
||||
speed: null
|
||||
}
|
||||
speed: null,
|
||||
},
|
||||
]
|
||||
|
||||
/**
|
||||
@@ -137,7 +137,7 @@ export const API_ENDPOINTS = {
|
||||
// WebSocket连接基础URL
|
||||
websocket: 'ws://localhost:8000',
|
||||
// 代理服务器示例
|
||||
proxy: 'http://127.0.0.1:7890'
|
||||
proxy: 'http://127.0.0.1:7890',
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -148,7 +148,7 @@ export const DOWNLOAD_LINKS = {
|
||||
getPip: 'http://221.236.27.82:10197/d/AUTO_MAA/get-pip.py',
|
||||
|
||||
// Git 客户端下载链接
|
||||
git: 'http://221.236.27.82:10197/d/AUTO_MAA/git.zip'
|
||||
git: 'http://221.236.27.82:10197/d/AUTO_MAA/git.zip',
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -157,7 +157,7 @@ export const DOWNLOAD_LINKS = {
|
||||
export const ALL_MIRRORS: MirrorCategory = {
|
||||
git: GIT_MIRRORS,
|
||||
python: PYTHON_MIRRORS,
|
||||
pip: PIP_MIRRORS
|
||||
pip: PIP_MIRRORS,
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -3,7 +3,6 @@ import type { RouteRecordRaw } from 'vue-router'
|
||||
|
||||
import { isAppInitialized } from '@/utils/config'
|
||||
|
||||
|
||||
const routes: RouteRecordRaw[] = [
|
||||
{
|
||||
path: '/',
|
||||
@@ -90,7 +89,6 @@ const router = createRouter({
|
||||
routes,
|
||||
})
|
||||
|
||||
|
||||
// 添加路由守卫,确保在生产环境中也能正确进入初始化页面
|
||||
router.beforeEach(async (to, from, next) => {
|
||||
console.log('路由守卫:', { to: to.path, from: from.path })
|
||||
@@ -118,5 +116,4 @@ router.beforeEach(async (to, from, next) => {
|
||||
next()
|
||||
})
|
||||
|
||||
|
||||
export default router
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
// 设置相关类型定义
|
||||
export interface SettingsData {
|
||||
Function: {
|
||||
|
||||
@@ -38,7 +38,7 @@ const DEFAULT_CONFIG: FrontendConfig = {
|
||||
gitInstalled: false,
|
||||
backendExists: false,
|
||||
dependenciesInstalled: false,
|
||||
pipInstalled: false
|
||||
pipInstalled: false,
|
||||
}
|
||||
|
||||
// 读取配置(内部使用,不触发保存)
|
||||
@@ -83,7 +83,8 @@ export async function getConfig(): Promise<FrontendConfig> {
|
||||
const config = await getConfigInternal()
|
||||
|
||||
// 如果是从localStorage迁移的配置,保存到文件并清理localStorage
|
||||
const hasLocalStorage = localStorage.getItem('app-config') || localStorage.getItem('theme-settings')
|
||||
const hasLocalStorage =
|
||||
localStorage.getItem('app-config') || localStorage.getItem('theme-settings')
|
||||
if (hasLocalStorage) {
|
||||
try {
|
||||
await window.electronAPI.saveConfig(config)
|
||||
@@ -155,7 +156,11 @@ export async function saveThemeConfig(themeMode: ThemeMode, themeColor: ThemeCol
|
||||
}
|
||||
|
||||
// 保存镜像源设置
|
||||
export async function saveMirrorConfig(gitMirror: string, pythonMirror?: string, pipMirror?: string): Promise<void> {
|
||||
export async function saveMirrorConfig(
|
||||
gitMirror: string,
|
||||
pythonMirror?: string,
|
||||
pipMirror?: string
|
||||
): Promise<void> {
|
||||
const config: Partial<FrontendConfig> = { selectedGitMirror: gitMirror }
|
||||
if (pythonMirror) config.selectedPythonMirror = pythonMirror
|
||||
if (pipMirror) config.selectedPipMirror = pipMirror
|
||||
|
||||
@@ -34,7 +34,7 @@ class Logger {
|
||||
level,
|
||||
message,
|
||||
data,
|
||||
component
|
||||
component,
|
||||
}
|
||||
|
||||
// 添加到内存日志
|
||||
@@ -188,5 +188,5 @@ export default {
|
||||
install(app: any) {
|
||||
app.config.globalProperties.$logger = logger
|
||||
app.provide('logger', logger)
|
||||
}
|
||||
},
|
||||
}
|
||||
@@ -12,7 +12,7 @@ import {
|
||||
getMirrorUrl,
|
||||
updateMirrorSpeed,
|
||||
sortMirrorsBySpeed,
|
||||
getFastestMirror
|
||||
getFastestMirror,
|
||||
} from '@/config/mirrors'
|
||||
|
||||
/**
|
||||
@@ -83,7 +83,7 @@ export class MirrorManager {
|
||||
const response = await fetch(url, {
|
||||
method: 'HEAD',
|
||||
signal: controller.signal,
|
||||
cache: 'no-cache'
|
||||
cache: 'no-cache',
|
||||
})
|
||||
|
||||
clearTimeout(timeoutId)
|
||||
@@ -103,7 +103,7 @@ export class MirrorManager {
|
||||
*/
|
||||
async testAllMirrorSpeeds(type: keyof MirrorCategory): Promise<MirrorConfig[]> {
|
||||
const mirrors = this.getMirrors(type)
|
||||
const promises = mirrors.map(async (mirror) => {
|
||||
const promises = mirrors.map(async mirror => {
|
||||
const speed = await this.testMirrorSpeed(mirror.url)
|
||||
this.updateMirrorSpeed(type, mirror.key, speed)
|
||||
return { ...mirror, speed }
|
||||
@@ -172,7 +172,7 @@ export class MirrorManager {
|
||||
return {
|
||||
mirrors: this.mirrorConfigs,
|
||||
apiEndpoints: this.apiEndpoints,
|
||||
downloadLinks: this.downloadLinks
|
||||
downloadLinks: this.downloadLinks,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -182,6 +182,9 @@ export const mirrorManager = MirrorManager.getInstance()
|
||||
|
||||
// 导出便捷函数
|
||||
export const getMirrors = (type: keyof MirrorCategory) => mirrorManager.getMirrors(type)
|
||||
export const getMirrorUrlByManager = (type: keyof MirrorCategory, key: string) => mirrorManager.getMirrorUrl(type, key)
|
||||
export const testMirrorSpeed = (url: string, timeout?: number) => mirrorManager.testMirrorSpeed(url, timeout)
|
||||
export const testAllMirrorSpeeds = (type: keyof MirrorCategory) => mirrorManager.testAllMirrorSpeeds(type)
|
||||
export const getMirrorUrlByManager = (type: keyof MirrorCategory, key: string) =>
|
||||
mirrorManager.getMirrorUrl(type, key)
|
||||
export const testMirrorSpeed = (url: string, timeout?: number) =>
|
||||
mirrorManager.testMirrorSpeed(url, timeout)
|
||||
export const testAllMirrorSpeeds = (type: keyof MirrorCategory) =>
|
||||
mirrorManager.testAllMirrorSpeeds(type)
|
||||
|
||||
@@ -38,7 +38,7 @@ import ManualMode from '@/components/initialization/ManualMode.vue'
|
||||
import type { DownloadProgress } from '@/types/initialization'
|
||||
|
||||
const router = useRouter()
|
||||
const logger = createComponentLogger('InitializationNew')
|
||||
const logger = createComponentLogger('Initialization')
|
||||
|
||||
// 基础状态
|
||||
const isAdmin = ref(true)
|
||||
@@ -91,7 +91,7 @@ async function checkCriticalFiles() {
|
||||
pythonExists: config.pythonInstalled || false,
|
||||
pipExists: config.pipInstalled || false,
|
||||
gitExists: config.gitInstalled || false,
|
||||
mainPyExists: config.backendExists || false
|
||||
mainPyExists: config.backendExists || false,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -109,7 +109,7 @@ async function checkCriticalFiles() {
|
||||
pythonExists: criticalFiles.pythonExists,
|
||||
pipExists: criticalFiles.pipExists,
|
||||
gitExists: criticalFiles.gitExists,
|
||||
mainPyExists: criticalFiles.mainPyExists
|
||||
mainPyExists: criticalFiles.mainPyExists,
|
||||
}
|
||||
|
||||
console.log('🔍 最终返回结果:', result)
|
||||
@@ -125,13 +125,13 @@ async function checkCriticalFiles() {
|
||||
pythonInstalled: config.pythonInstalled,
|
||||
pipInstalled: config.pipInstalled,
|
||||
gitInstalled: config.gitInstalled,
|
||||
backendExists: config.backendExists
|
||||
backendExists: config.backendExists,
|
||||
})
|
||||
return {
|
||||
pythonExists: config.pythonInstalled || false,
|
||||
pipExists: config.pipInstalled || false,
|
||||
gitExists: config.gitInstalled || false,
|
||||
mainPyExists: config.backendExists || false
|
||||
mainPyExists: config.backendExists || false,
|
||||
}
|
||||
} catch (configError) {
|
||||
console.error('❌ 读取配置文件也失败了:', configError)
|
||||
@@ -139,7 +139,7 @@ async function checkCriticalFiles() {
|
||||
pythonExists: false,
|
||||
pipExists: false,
|
||||
gitExists: false,
|
||||
mainPyExists: false
|
||||
mainPyExists: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -177,7 +177,8 @@ async function checkEnvironment() {
|
||||
console.log('是否第一次启动:', isFirst)
|
||||
|
||||
// 检查所有关键exe文件是否都存在
|
||||
const allExeFilesExist = criticalFiles.pythonExists &&
|
||||
const allExeFilesExist =
|
||||
criticalFiles.pythonExists &&
|
||||
criticalFiles.pipExists &&
|
||||
criticalFiles.gitExists &&
|
||||
criticalFiles.mainPyExists
|
||||
@@ -203,7 +204,14 @@ async function checkEnvironment() {
|
||||
} else {
|
||||
logger.info('需要进入手动模式进行配置')
|
||||
console.log('进入手动模式')
|
||||
console.log('原因: isFirst =', isFirst, ', config.init =', config.init, ', allExeFilesExist =', allExeFilesExist)
|
||||
console.log(
|
||||
'原因: isFirst =',
|
||||
isFirst,
|
||||
', config.init =',
|
||||
config.init,
|
||||
', allExeFilesExist =',
|
||||
allExeFilesExist
|
||||
)
|
||||
|
||||
// 如果关键文件缺失,重置初始化状态
|
||||
if (!allExeFilesExist && config.init) {
|
||||
|
||||
@@ -27,7 +27,11 @@
|
||||
|
||||
<!-- 空状态 -->
|
||||
<div v-if="!queueList.length || !currentQueueData" class="empty-state">
|
||||
<div class="empty-content empty-content-fancy" @click="handleAddQueue" style="cursor: pointer">
|
||||
<div
|
||||
class="empty-content empty-content-fancy"
|
||||
@click="handleAddQueue"
|
||||
style="cursor: pointer"
|
||||
>
|
||||
<div class="empty-icon">
|
||||
<PlusOutlined />
|
||||
</div>
|
||||
@@ -157,13 +161,13 @@ const fetchQueues = async () => {
|
||||
console.log('Queue ID:', queueId, 'Name:', queueName, 'Type:', typeof queueId) // 调试日志
|
||||
return {
|
||||
id: queueId,
|
||||
name: queueName
|
||||
name: queueName,
|
||||
}
|
||||
} catch (itemError) {
|
||||
console.warn('解析队列项失败:', itemError, item)
|
||||
return {
|
||||
id: `queue_${index}`,
|
||||
name: `队列 ${index + 1}`
|
||||
name: `队列 ${index + 1}`,
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -290,7 +294,7 @@ const refreshTimeSets = async () => {
|
||||
id: timeSetId,
|
||||
time: timeString,
|
||||
enabled: Boolean(timeSetData.Info.Enabled),
|
||||
description: timeSetData.Info.Description || ''
|
||||
description: timeSetData.Info.Description || '',
|
||||
})
|
||||
}
|
||||
} catch (itemError) {
|
||||
@@ -353,7 +357,7 @@ const refreshQueueItems = async () => {
|
||||
if (queueItemData?.Info) {
|
||||
queueItems.push({
|
||||
id: queueItemId,
|
||||
script: queueItemData.Info.ScriptId || ''
|
||||
script: queueItemData.Info.ScriptId || '',
|
||||
})
|
||||
}
|
||||
} catch (itemError) {
|
||||
@@ -382,7 +386,8 @@ const onQueueNameBlur = () => {
|
||||
if (activeQueueId.value) {
|
||||
const currentQueue = queueList.value.find(queue => queue.id === activeQueueId.value)
|
||||
if (currentQueue) {
|
||||
currentQueue.name = currentQueueName.value || `队列 ${queueList.value.indexOf(currentQueue) + 1}`
|
||||
currentQueue.name =
|
||||
currentQueueName.value || `队列 ${queueList.value.indexOf(currentQueue) + 1}`
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -505,7 +510,7 @@ const saveQueueData = async () => {
|
||||
|
||||
const response = await Service.updateQueueApiQueueUpdatePost({
|
||||
queueId: activeQueueId.value,
|
||||
data: queueData
|
||||
data: queueData,
|
||||
})
|
||||
|
||||
if (response.code !== 200) {
|
||||
@@ -701,7 +706,9 @@ onMounted(async () => {
|
||||
}
|
||||
|
||||
.empty-content-fancy {
|
||||
transition: box-shadow 0.3s, transform 0.2s;
|
||||
transition:
|
||||
box-shadow 0.3s,
|
||||
transform 0.2s;
|
||||
border: none;
|
||||
border-radius: 24px;
|
||||
}
|
||||
|
||||
@@ -154,7 +154,6 @@
|
||||
<!-- 实时日志 (60%) -->
|
||||
<a-col :span="14">
|
||||
<a-card size="small" style="height: 100%" title="实时日志">
|
||||
|
||||
<div class="realtime-logs-panel">
|
||||
<!-- <a-row justify="space-between" align="middle" style="margin-bottom: 8px">-->
|
||||
<!-- <!– 左侧标题 –>-->
|
||||
@@ -624,17 +623,17 @@ const handleWebSocketMessage = (task: RunningTask, data: any) => {
|
||||
|
||||
case 'Info':
|
||||
// 通知信息
|
||||
let level = 'info';
|
||||
let content = '未知通知';
|
||||
let level = 'info'
|
||||
let content = '未知通知'
|
||||
|
||||
// 检查数据中是否有 Error 字段
|
||||
if (data.data?.Error) {
|
||||
// 如果是错误信息,设置为 error 级别
|
||||
level = 'error';
|
||||
content = data.data.Error; // 错误信息内容
|
||||
level = 'error'
|
||||
content = data.data.Error // 错误信息内容
|
||||
} else {
|
||||
// 如果没有 Error 字段,继续处理 val 或 message 字段
|
||||
content = data.data?.val || data.data?.message || '未知通知';
|
||||
content = data.data?.val || data.data?.message || '未知通知'
|
||||
}
|
||||
|
||||
addTaskLog(task, content, level as any)
|
||||
|
||||
@@ -204,21 +204,31 @@ onMounted(() => {
|
||||
<div class="setting-item">
|
||||
<h4>主题模式</h4>
|
||||
<p class="setting-description">选择应用程序的外观主题</p>
|
||||
<Radio.Group :value="themeMode" @change="handleThemeModeChange" :options="themeModeOptions" />
|
||||
<Radio.Group
|
||||
:value="themeMode"
|
||||
@change="handleThemeModeChange"
|
||||
:options="themeModeOptions"
|
||||
/>
|
||||
</div>
|
||||
<Divider />
|
||||
<div class="setting-item">
|
||||
<h4>主题色</h4>
|
||||
<p class="setting-description">选择应用程序的主色调</p>
|
||||
<Select :value="themeColor" @change="handleThemeColorChange" style="width: 200px">
|
||||
<Select.Option v-for="option in themeColorOptions" :key="option.value" :value="option.value">
|
||||
<Select.Option
|
||||
v-for="option in themeColorOptions"
|
||||
:key="option.value"
|
||||
:value="option.value"
|
||||
>
|
||||
<div style="display: flex; align-items: center; gap: 8px">
|
||||
<div :style="{
|
||||
<div
|
||||
:style="{
|
||||
width: '16px',
|
||||
height: '16px',
|
||||
borderRadius: '50%',
|
||||
backgroundColor: option.color,
|
||||
}" />
|
||||
}"
|
||||
/>
|
||||
{{ option.label }}
|
||||
</div>
|
||||
</Select.Option>
|
||||
@@ -235,9 +245,12 @@ onMounted(() => {
|
||||
<div class="setting-item">
|
||||
<h4>Boss键</h4>
|
||||
<p class="setting-description">设置快速隐藏窗口的快捷键</p>
|
||||
<Input v-model:value="settings.Function.BossKey"
|
||||
@blur="handleSettingChange('Function', 'BossKey', settings.Function.BossKey)" placeholder="例如: Ctrl+H"
|
||||
style="width: 300px" />
|
||||
<Input
|
||||
v-model:value="settings.Function.BossKey"
|
||||
@blur="handleSettingChange('Function', 'BossKey', settings.Function.BossKey)"
|
||||
placeholder="例如: Ctrl+H"
|
||||
style="width: 300px"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Divider />
|
||||
@@ -245,9 +258,12 @@ onMounted(() => {
|
||||
<div class="setting-item">
|
||||
<h4>历史记录保留时间</h4>
|
||||
<p class="setting-description">设置历史记录的保留时间</p>
|
||||
<Select v-model:value="settings.Function.HistoryRetentionTime"
|
||||
@change="(value) => handleSettingChange('Function', 'HistoryRetentionTime', value)"
|
||||
:options="historyRetentionOptions" style="width: 200px" />
|
||||
<Select
|
||||
v-model:value="settings.Function.HistoryRetentionTime"
|
||||
@change="value => handleSettingChange('Function', 'HistoryRetentionTime', value)"
|
||||
:options="historyRetentionOptions"
|
||||
style="width: 200px"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Divider />
|
||||
@@ -255,9 +271,12 @@ onMounted(() => {
|
||||
<div class="setting-item">
|
||||
<h4>主页图像模式</h4>
|
||||
<p class="setting-description">选择主页显示的图像模式</p>
|
||||
<Select v-model:value="settings.Function.HomeImageMode"
|
||||
@change="(value) => handleSettingChange('Function', 'HomeImageMode', value)"
|
||||
:options="homeImageModeOptions" style="width: 200px" />
|
||||
<Select
|
||||
v-model:value="settings.Function.HomeImageMode"
|
||||
@change="value => handleSettingChange('Function', 'HomeImageMode', value)"
|
||||
:options="homeImageModeOptions"
|
||||
style="width: 200px"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Divider />
|
||||
@@ -266,28 +285,40 @@ onMounted(() => {
|
||||
<h4>功能开关</h4>
|
||||
<Space direction="vertical" size="middle">
|
||||
<div class="switch-item">
|
||||
<Switch v-model:checked="settings.Function.IfAllowSleep"
|
||||
@change="(checked) => handleSettingChange('Function', 'IfAllowSleep', checked)" />
|
||||
<Switch
|
||||
v-model:checked="settings.Function.IfAllowSleep"
|
||||
@change="checked => handleSettingChange('Function', 'IfAllowSleep', checked)"
|
||||
/>
|
||||
<span class="switch-label">启动时阻止系统休眠</span>
|
||||
</div>
|
||||
<div class="switch-item">
|
||||
<Switch v-model:checked="settings.Function.IfSilence"
|
||||
@change="(checked) => handleSettingChange('Function', 'IfSilence', checked)" />
|
||||
<Switch
|
||||
v-model:checked="settings.Function.IfSilence"
|
||||
@change="checked => handleSettingChange('Function', 'IfSilence', checked)"
|
||||
/>
|
||||
<span class="switch-label">静默模式</span>
|
||||
</div>
|
||||
<div class="switch-item">
|
||||
<Switch v-model:checked="settings.Function.UnattendedMode"
|
||||
@change="(checked) => handleSettingChange('Function', 'UnattendedMode', checked)" />
|
||||
<Switch
|
||||
v-model:checked="settings.Function.UnattendedMode"
|
||||
@change="checked => handleSettingChange('Function', 'UnattendedMode', checked)"
|
||||
/>
|
||||
<span class="switch-label">无人值守模式</span>
|
||||
</div>
|
||||
<div class="switch-item">
|
||||
<Switch v-model:checked="settings.Function.IfAgreeBilibili"
|
||||
@change="(checked) => handleSettingChange('Function', 'IfAgreeBilibili', checked)" />
|
||||
<Switch
|
||||
v-model:checked="settings.Function.IfAgreeBilibili"
|
||||
@change="checked => handleSettingChange('Function', 'IfAgreeBilibili', checked)"
|
||||
/>
|
||||
<span class="switch-label">托管Bilibili游戏隐私政策</span>
|
||||
</div>
|
||||
<div class="switch-item">
|
||||
<Switch v-model:checked="settings.Function.IfSkipMumuSplashAds"
|
||||
@change="(checked) => handleSettingChange('Function', 'IfSkipMumuSplashAds', checked)" />
|
||||
<Switch
|
||||
v-model:checked="settings.Function.IfSkipMumuSplashAds"
|
||||
@change="
|
||||
checked => handleSettingChange('Function', 'IfSkipMumuSplashAds', checked)
|
||||
"
|
||||
/>
|
||||
<span class="switch-label">跳过MuMu模拟器启动广告</span>
|
||||
</div>
|
||||
</Space>
|
||||
@@ -303,9 +334,12 @@ onMounted(() => {
|
||||
<div class="setting-item">
|
||||
<h4>任务结果推送时间</h4>
|
||||
<p class="setting-description">设置何时推送任务执行结果</p>
|
||||
<Select v-model:value="settings.Notify.SendTaskResultTime"
|
||||
@change="(value) => handleSettingChange('Notify', 'SendTaskResultTime', value)"
|
||||
:options="sendTaskResultTimeOptions" style="width: 200px" />
|
||||
<Select
|
||||
v-model:value="settings.Notify.SendTaskResultTime"
|
||||
@change="value => handleSettingChange('Notify', 'SendTaskResultTime', value)"
|
||||
:options="sendTaskResultTimeOptions"
|
||||
style="width: 200px"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Divider />
|
||||
@@ -314,18 +348,24 @@ onMounted(() => {
|
||||
<h4>通知开关</h4>
|
||||
<Space direction="vertical" size="middle">
|
||||
<div class="switch-item">
|
||||
<Switch v-model:checked="settings.Notify.IfSendStatistic"
|
||||
@change="(checked) => handleSettingChange('Notify', 'IfSendStatistic', checked)" />
|
||||
<Switch
|
||||
v-model:checked="settings.Notify.IfSendStatistic"
|
||||
@change="checked => handleSettingChange('Notify', 'IfSendStatistic', checked)"
|
||||
/>
|
||||
<span class="switch-label">发送统计信息</span>
|
||||
</div>
|
||||
<div class="switch-item">
|
||||
<Switch v-model:checked="settings.Notify.IfSendSixStar"
|
||||
@change="(checked) => handleSettingChange('Notify', 'IfSendSixStar', checked)" />
|
||||
<Switch
|
||||
v-model:checked="settings.Notify.IfSendSixStar"
|
||||
@change="checked => handleSettingChange('Notify', 'IfSendSixStar', checked)"
|
||||
/>
|
||||
<span class="switch-label">发送六星通知</span>
|
||||
</div>
|
||||
<div class="switch-item">
|
||||
<Switch v-model:checked="settings.Notify.IfPushPlyer"
|
||||
@change="(checked) => handleSettingChange('Notify', 'IfPushPlyer', checked)" />
|
||||
<Switch
|
||||
v-model:checked="settings.Notify.IfPushPlyer"
|
||||
@change="checked => handleSettingChange('Notify', 'IfPushPlyer', checked)"
|
||||
/>
|
||||
<span class="switch-label">启用PushPlus推送</span>
|
||||
</div>
|
||||
</Space>
|
||||
@@ -337,33 +377,61 @@ onMounted(() => {
|
||||
<h4>邮件通知</h4>
|
||||
<Space direction="vertical" size="middle" style="width: 100%">
|
||||
<div class="switch-item">
|
||||
<Switch v-model:checked="settings.Notify.IfSendMail"
|
||||
@change="(checked) => handleSettingChange('Notify', 'IfSendMail', checked)" />
|
||||
<Switch
|
||||
v-model:checked="settings.Notify.IfSendMail"
|
||||
@change="checked => handleSettingChange('Notify', 'IfSendMail', checked)"
|
||||
/>
|
||||
<span class="switch-label">启用邮件通知</span>
|
||||
</div>
|
||||
<div class="input-group">
|
||||
<label>SMTP服务器地址</label>
|
||||
<Input v-model:value="settings.Notify.SMTPServerAddress"
|
||||
@blur="handleSettingChange('Notify', 'SMTPServerAddress', settings.Notify.SMTPServerAddress)"
|
||||
placeholder="例如: smtp.gmail.com" style="width: 300px" />
|
||||
<Input
|
||||
v-model:value="settings.Notify.SMTPServerAddress"
|
||||
@blur="
|
||||
handleSettingChange(
|
||||
'Notify',
|
||||
'SMTPServerAddress',
|
||||
settings.Notify.SMTPServerAddress
|
||||
)
|
||||
"
|
||||
placeholder="例如: smtp.gmail.com"
|
||||
style="width: 300px"
|
||||
/>
|
||||
</div>
|
||||
<div class="input-group">
|
||||
<label>授权码</label>
|
||||
<Input.Password v-model:value="settings.Notify.AuthorizationCode"
|
||||
@blur="handleSettingChange('Notify', 'AuthorizationCode', settings.Notify.AuthorizationCode)"
|
||||
placeholder="邮箱授权码" style="width: 300px" />
|
||||
<Input.Password
|
||||
v-model:value="settings.Notify.AuthorizationCode"
|
||||
@blur="
|
||||
handleSettingChange(
|
||||
'Notify',
|
||||
'AuthorizationCode',
|
||||
settings.Notify.AuthorizationCode
|
||||
)
|
||||
"
|
||||
placeholder="邮箱授权码"
|
||||
style="width: 300px"
|
||||
/>
|
||||
</div>
|
||||
<div class="input-group">
|
||||
<label>发件人地址</label>
|
||||
<Input v-model:value="settings.Notify.FromAddress"
|
||||
@blur="handleSettingChange('Notify', 'FromAddress', settings.Notify.FromAddress)"
|
||||
placeholder="发件人邮箱地址" style="width: 300px" />
|
||||
<Input
|
||||
v-model:value="settings.Notify.FromAddress"
|
||||
@blur="
|
||||
handleSettingChange('Notify', 'FromAddress', settings.Notify.FromAddress)
|
||||
"
|
||||
placeholder="发件人邮箱地址"
|
||||
style="width: 300px"
|
||||
/>
|
||||
</div>
|
||||
<div class="input-group">
|
||||
<label>收件人地址</label>
|
||||
<Input v-model:value="settings.Notify.ToAddress"
|
||||
@blur="handleSettingChange('Notify', 'ToAddress', settings.Notify.ToAddress)" placeholder="收件人邮箱地址"
|
||||
style="width: 300px" />
|
||||
<Input
|
||||
v-model:value="settings.Notify.ToAddress"
|
||||
@blur="handleSettingChange('Notify', 'ToAddress', settings.Notify.ToAddress)"
|
||||
placeholder="收件人邮箱地址"
|
||||
style="width: 300px"
|
||||
/>
|
||||
</div>
|
||||
</Space>
|
||||
</div>
|
||||
@@ -374,15 +442,22 @@ onMounted(() => {
|
||||
<h4>Server酱通知</h4>
|
||||
<Space direction="vertical" size="middle" style="width: 100%">
|
||||
<div class="switch-item">
|
||||
<Switch v-model:checked="settings.Notify.IfServerChan"
|
||||
@change="(checked) => handleSettingChange('Notify', 'IfServerChan', checked)" />
|
||||
<Switch
|
||||
v-model:checked="settings.Notify.IfServerChan"
|
||||
@change="checked => handleSettingChange('Notify', 'IfServerChan', checked)"
|
||||
/>
|
||||
<span class="switch-label">启用Server酱通知</span>
|
||||
</div>
|
||||
<div class="input-group">
|
||||
<label>Server酱Key</label>
|
||||
<Input v-model:value="settings.Notify.ServerChanKey"
|
||||
@blur="handleSettingChange('Notify', 'ServerChanKey', settings.Notify.ServerChanKey)"
|
||||
placeholder="Server酱推送Key" style="width: 300px" />
|
||||
<Input
|
||||
v-model:value="settings.Notify.ServerChanKey"
|
||||
@blur="
|
||||
handleSettingChange('Notify', 'ServerChanKey', settings.Notify.ServerChanKey)
|
||||
"
|
||||
placeholder="Server酱推送Key"
|
||||
style="width: 300px"
|
||||
/>
|
||||
</div>
|
||||
</Space>
|
||||
</div>
|
||||
@@ -393,15 +468,28 @@ onMounted(() => {
|
||||
<h4>企业微信机器人</h4>
|
||||
<Space direction="vertical" size="middle" style="width: 100%">
|
||||
<div class="switch-item">
|
||||
<Switch v-model:checked="settings.Notify.IfCompanyWebHookBot"
|
||||
@change="(checked) => handleSettingChange('Notify', 'IfCompanyWebHookBot', checked)" />
|
||||
<Switch
|
||||
v-model:checked="settings.Notify.IfCompanyWebHookBot"
|
||||
@change="
|
||||
checked => handleSettingChange('Notify', 'IfCompanyWebHookBot', checked)
|
||||
"
|
||||
/>
|
||||
<span class="switch-label">启用企业微信机器人</span>
|
||||
</div>
|
||||
<div class="input-group">
|
||||
<label>Webhook URL</label>
|
||||
<Input v-model:value="settings.Notify.CompanyWebHookBotUrl"
|
||||
@blur="handleSettingChange('Notify', 'CompanyWebHookBotUrl', settings.Notify.CompanyWebHookBotUrl)"
|
||||
placeholder="企业微信机器人Webhook地址" style="width: 400px" />
|
||||
<Input
|
||||
v-model:value="settings.Notify.CompanyWebHookBotUrl"
|
||||
@blur="
|
||||
handleSettingChange(
|
||||
'Notify',
|
||||
'CompanyWebHookBotUrl',
|
||||
settings.Notify.CompanyWebHookBotUrl
|
||||
)
|
||||
"
|
||||
placeholder="企业微信机器人Webhook地址"
|
||||
style="width: 400px"
|
||||
/>
|
||||
</div>
|
||||
</Space>
|
||||
</div>
|
||||
@@ -416,8 +504,10 @@ onMounted(() => {
|
||||
<div class="setting-item">
|
||||
<h4>自动更新</h4>
|
||||
<p class="setting-description">是否启用自动更新功能</p>
|
||||
<Switch v-model:checked="settings.Update.IfAutoUpdate"
|
||||
@change="(checked) => handleSettingChange('Update', 'IfAutoUpdate', checked)" />
|
||||
<Switch
|
||||
v-model:checked="settings.Update.IfAutoUpdate"
|
||||
@change="checked => handleSettingChange('Update', 'IfAutoUpdate', checked)"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Divider />
|
||||
@@ -425,9 +515,12 @@ onMounted(() => {
|
||||
<div class="setting-item">
|
||||
<h4>更新类型</h4>
|
||||
<p class="setting-description">选择更新版本类型</p>
|
||||
<Select v-model:value="settings.Update.UpdateType"
|
||||
@change="(value) => handleSettingChange('Update', 'UpdateType', value)" :options="updateTypeOptions"
|
||||
style="width: 200px" />
|
||||
<Select
|
||||
v-model:value="settings.Update.UpdateType"
|
||||
@change="value => handleSettingChange('Update', 'UpdateType', value)"
|
||||
:options="updateTypeOptions"
|
||||
style="width: 200px"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Divider />
|
||||
@@ -435,9 +528,13 @@ onMounted(() => {
|
||||
<div class="setting-item">
|
||||
<h4>下载线程数</h4>
|
||||
<p class="setting-description">设置下载时使用的线程数量 (1-32)</p>
|
||||
<InputNumber v-model:value="settings.Update.ThreadNumb"
|
||||
@change="(value) => handleSettingChange('Update', 'ThreadNumb', value)" :min="1" :max="32"
|
||||
style="width: 120px" />
|
||||
<InputNumber
|
||||
v-model:value="settings.Update.ThreadNumb"
|
||||
@change="value => handleSettingChange('Update', 'ThreadNumb', value)"
|
||||
:min="1"
|
||||
:max="32"
|
||||
style="width: 120px"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Divider />
|
||||
@@ -447,9 +544,14 @@ onMounted(() => {
|
||||
<Space direction="vertical" size="middle" style="width: 100%">
|
||||
<div class="input-group">
|
||||
<label>代理地址</label>
|
||||
<Input v-model:value="settings.Update.ProxyAddress"
|
||||
@blur="handleSettingChange('Update', 'ProxyAddress', settings.Update.ProxyAddress)"
|
||||
placeholder="例如: http://127.0.0.1:7890" style="width: 300px" />
|
||||
<Input
|
||||
v-model:value="settings.Update.ProxyAddress"
|
||||
@blur="
|
||||
handleSettingChange('Update', 'ProxyAddress', settings.Update.ProxyAddress)
|
||||
"
|
||||
placeholder="例如: http://127.0.0.1:7890"
|
||||
style="width: 300px"
|
||||
/>
|
||||
</div>
|
||||
</Space>
|
||||
</div>
|
||||
@@ -459,9 +561,14 @@ onMounted(() => {
|
||||
<div class="setting-item">
|
||||
<h4>Mirror酱 CDK</h4>
|
||||
<p class="setting-description">设置Mirror酱CDK</p>
|
||||
<Input v-model:value="settings.Update.MirrorChyanCDK"
|
||||
@blur="handleSettingChange('Update', 'MirrorChyanCDK', settings.Update.MirrorChyanCDK)"
|
||||
placeholder="镜像CDK" style="width: 300px" />
|
||||
<Input
|
||||
v-model:value="settings.Update.MirrorChyanCDK"
|
||||
@blur="
|
||||
handleSettingChange('Update', 'MirrorChyanCDK', settings.Update.MirrorChyanCDK)
|
||||
"
|
||||
placeholder="镜像CDK"
|
||||
style="width: 300px"
|
||||
/>
|
||||
</div>
|
||||
</Space>
|
||||
</Card>
|
||||
@@ -474,8 +581,10 @@ onMounted(() => {
|
||||
<div class="setting-item">
|
||||
<h4>开机自启</h4>
|
||||
<p class="setting-description">是否在系统启动时自动启动应用</p>
|
||||
<Switch v-model:checked="settings.Start.IfSelfStart"
|
||||
@change="(checked) => handleSettingChange('Start', 'IfSelfStart', checked)" />
|
||||
<Switch
|
||||
v-model:checked="settings.Start.IfSelfStart"
|
||||
@change="checked => handleSettingChange('Start', 'IfSelfStart', checked)"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Divider />
|
||||
@@ -483,8 +592,10 @@ onMounted(() => {
|
||||
<div class="setting-item">
|
||||
<h4>启动后直接最小化</h4>
|
||||
<p class="setting-description">启动后是否直接最小化到系统托盘</p>
|
||||
<Switch v-model:checked="settings.Start.IfMinimizeDirectly"
|
||||
@change="(checked) => handleSettingChange('Start', 'IfMinimizeDirectly', checked)" />
|
||||
<Switch
|
||||
v-model:checked="settings.Start.IfMinimizeDirectly"
|
||||
@change="checked => handleSettingChange('Start', 'IfMinimizeDirectly', checked)"
|
||||
/>
|
||||
</div>
|
||||
</Space>
|
||||
</Card>
|
||||
@@ -498,13 +609,17 @@ onMounted(() => {
|
||||
<h4>系统托盘</h4>
|
||||
<Space direction="vertical" size="middle">
|
||||
<div class="switch-item">
|
||||
<Switch v-model:checked="settings.UI.IfShowTray"
|
||||
@change="(checked) => handleSettingChange('UI', 'IfShowTray', checked)" />
|
||||
<Switch
|
||||
v-model:checked="settings.UI.IfShowTray"
|
||||
@change="checked => handleSettingChange('UI', 'IfShowTray', checked)"
|
||||
/>
|
||||
<span class="switch-label">显示系统托盘图标</span>
|
||||
</div>
|
||||
<div class="switch-item">
|
||||
<Switch v-model:checked="settings.UI.IfToTray"
|
||||
@change="(checked) => handleSettingChange('UI', 'IfToTray', checked)" />
|
||||
<Switch
|
||||
v-model:checked="settings.UI.IfToTray"
|
||||
@change="checked => handleSettingChange('UI', 'IfToTray', checked)"
|
||||
/>
|
||||
<span class="switch-label">关闭时最小化到托盘</span>
|
||||
</div>
|
||||
</Space>
|
||||
@@ -544,8 +659,10 @@ onMounted(() => {
|
||||
<div class="setting-item">
|
||||
<h4>语音提示</h4>
|
||||
<p class="setting-description">是否启用语音提示功能</p>
|
||||
<Switch v-model:checked="settings.Voice.Enabled"
|
||||
@change="(checked) => handleSettingChange('Voice', 'Enabled', checked)" />
|
||||
<Switch
|
||||
v-model:checked="settings.Voice.Enabled"
|
||||
@change="checked => handleSettingChange('Voice', 'Enabled', checked)"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Divider />
|
||||
@@ -553,9 +670,13 @@ onMounted(() => {
|
||||
<div class="setting-item">
|
||||
<h4>语音类型</h4>
|
||||
<p class="setting-description">选择语音提示的详细程度</p>
|
||||
<Select v-model:value="settings.Voice.Type"
|
||||
@change="(value) => handleSettingChange('Voice', 'Type', value)" :options="voiceTypeOptions"
|
||||
style="width: 200px" :disabled="!settings.Voice.Enabled" />
|
||||
<Select
|
||||
v-model:value="settings.Voice.Type"
|
||||
@change="value => handleSettingChange('Voice', 'Type', value)"
|
||||
:options="voiceTypeOptions"
|
||||
style="width: 200px"
|
||||
:disabled="!settings.Voice.Enabled"
|
||||
/>
|
||||
</div>
|
||||
</Space>
|
||||
</Card>
|
||||
@@ -646,5 +767,4 @@ onMounted(() => {
|
||||
:deep(.ant-tabs-tab) {
|
||||
color: v-bind(textSecondaryColor);
|
||||
}
|
||||
|
||||
</style>
|
||||
@@ -9,7 +9,7 @@ export default defineConfig({
|
||||
resolve: {
|
||||
extensions: ['.js', '.ts', '.vue', '.json'],
|
||||
alias: {
|
||||
'@': path.resolve(__dirname, 'src')
|
||||
}
|
||||
}
|
||||
'@': path.resolve(__dirname, 'src'),
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user