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

@@ -4,7 +4,13 @@ import * as fs from 'fs'
import { spawn } from 'child_process' import { spawn } from 'child_process'
import { getAppRoot, checkEnvironment } from './services/environmentService' import { getAppRoot, checkEnvironment } from './services/environmentService'
import { setMainWindow as setDownloadMainWindow } from './services/downloadService' 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' import { setMainWindow as setGitMainWindow, downloadGit, cloneBackend } from './services/gitService'
// 检查是否以管理员权限运行 // 检查是否以管理员权限运行
@@ -34,13 +40,17 @@ function restartAsAdmin(): void {
const args = process.argv.slice(1) const args = process.argv.slice(1)
// 使用PowerShell以管理员权限启动 // 使用PowerShell以管理员权限启动
spawn('powershell', [ spawn(
'powershell',
[
'-Command', '-Command',
`Start-Process -FilePath "${exePath}" -ArgumentList "${args.join(' ')}" -Verb RunAs` `Start-Process -FilePath "${exePath}" -ArgumentList "${args.join(' ')}" -Verb RunAs`,
], { ],
{
detached: true, detached: true,
stdio: 'ignore' stdio: 'ignore',
}) }
)
app.quit() app.quit()
} }
@@ -144,15 +154,21 @@ ipcMain.handle('download-git', async () => {
return downloadGit(appRoot) 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() const appRoot = getAppRoot()
return cloneBackend(appRoot, repoUrl) 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() const appRoot = getAppRoot()
return cloneBackend(appRoot, repoUrl) // 使用相同的逻辑会自动判断是pull还是clone return cloneBackend(appRoot, repoUrl) // 使用相同的逻辑会自动判断是pull还是clone
}) }
)
// 配置文件操作 // 配置文件操作
ipcMain.handle('save-config', async (event, config) => { ipcMain.handle('save-config', async (event, config) => {

View File

@@ -39,5 +39,5 @@ contextBridge.exposeInMainWorld('electronAPI', {
}, },
removeDownloadProgressListener: () => { removeDownloadProgressListener: () => {
ipcRenderer.removeAllListeners('download-progress') ipcRenderer.removeAllListeners('download-progress')
} },
}) })

View File

@@ -18,7 +18,6 @@ export function downloadFile(url: string, outputPath: string): Promise<void> {
// 创建HTTP客户端兼容https和http // 创建HTTP客户端兼容https和http
const client = url.startsWith('https') ? https : http const client = url.startsWith('https') ? https : http
client client
.get(url, response => { .get(url, response => {
const totalSize = parseInt(response.headers['content-length'] || '0', 10) const totalSize = parseInt(response.headers['content-length'] || '0', 10)

View File

@@ -4,9 +4,7 @@ import { app } from 'electron'
// 获取应用根目录 // 获取应用根目录
export function getAppRoot(): string { export function getAppRoot(): string {
return process.env.NODE_ENV === 'development' return process.env.NODE_ENV === 'development' ? process.cwd() : path.dirname(app.getPath('exe'))
? process.cwd()
: path.dirname(app.getPath('exe'))
} }
// 检查环境 // 检查环境
@@ -23,13 +21,14 @@ export function checkEnvironment(appRoot: string) {
// 检查依赖是否已安装简单检查是否存在site-packages目录 // 检查依赖是否已安装简单检查是否存在site-packages目录
const sitePackagesPath = path.join(pythonPath, 'Lib', '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 { return {
pythonExists, pythonExists,
gitExists, gitExists,
backendExists, backendExists,
dependenciesInstalled, dependenciesInstalled,
isInitialized: pythonExists && gitExists && backendExists && dependenciesInstalled isInitialized: pythonExists && gitExists && backendExists && dependenciesInstalled,
} }
} }

View File

@@ -31,7 +31,6 @@ function copyDirSync(src: string, dest: string) {
} }
} }
// 获取Git环境变量配置 // 获取Git环境变量配置
function getGitEnvironment(appRoot: string) { function getGitEnvironment(appRoot: string) {
const gitDir = path.join(appRoot, 'environment', 'git') const gitDir = path.join(appRoot, 'environment', 'git')
@@ -249,11 +248,26 @@ export async function cloneBackend(
}) })
} }
await new Promise<void>((resolve, reject) => { 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', stdio: 'pipe',
env: gitEnv, env: gitEnv,
cwd: appRoot, cwd: appRoot,
}) }
)
proc.stdout?.on('data', d => console.log('git clone:', d.toString())) proc.stdout?.on('data', d => console.log('git clone:', d.toString()))
proc.stderr?.on('data', d => console.log('git clone err:', d.toString())) proc.stderr?.on('data', d => console.log('git clone err:', d.toString()))
proc.on('close', code => proc.on('close', code =>

View File

@@ -16,8 +16,9 @@ const pythonMirrorUrls = {
official: 'https://www.python.org/ftp/python/3.12.0/python-3.12.0-embed-amd64.zip', 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', 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', 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', huawei:
aliyun: 'https://mirrors.aliyun.com/python-release/windows/python-3.12.0-embed-amd64.zip' '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是否已安装 // 检查pip是否已安装
@@ -80,7 +81,8 @@ async function installPip(pythonPath: string, appRoot: string): Promise<void> {
const stats = fs.statSync(getPipPath) const stats = fs.statSync(getPipPath)
console.log(`get-pip.py文件大小: ${stats.size} bytes`) 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可能下载失败`) throw new Error(`get-pip.py文件大小异常: ${stats.size} bytes可能下载失败`)
} }
} catch (error) { } catch (error) {
@@ -94,20 +96,20 @@ async function installPip(pythonPath: string, appRoot: string): Promise<void> {
const process = spawn(pythonExe, [getPipPath], { const process = spawn(pythonExe, [getPipPath], {
cwd: pythonPath, cwd: pythonPath,
stdio: 'pipe' stdio: 'pipe',
}) })
process.stdout?.on('data', (data) => { process.stdout?.on('data', data => {
const output = data.toString() const output = data.toString()
console.log('pip安装输出:', output) console.log('pip安装输出:', output)
}) })
process.stderr?.on('data', (data) => { process.stderr?.on('data', data => {
const errorOutput = data.toString() const errorOutput = data.toString()
console.log('pip安装错误输出:', errorOutput) console.log('pip安装错误输出:', errorOutput)
}) })
process.on('close', (code) => { process.on('close', code => {
console.log(`pip安装完成退出码: ${code}`) console.log(`pip安装完成退出码: ${code}`)
if (code === 0) { if (code === 0) {
console.log('pip安装成功') 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) console.error('pip安装进程错误:', error)
reject(error) reject(error)
}) })
@@ -128,20 +130,20 @@ async function installPip(pythonPath: string, appRoot: string): Promise<void> {
await new Promise<void>((resolve, reject) => { await new Promise<void>((resolve, reject) => {
const verifyProcess = spawn(pythonExe, ['-m', 'pip', '--version'], { const verifyProcess = spawn(pythonExe, ['-m', 'pip', '--version'], {
cwd: pythonPath, cwd: pythonPath,
stdio: 'pipe' stdio: 'pipe',
}) })
verifyProcess.stdout?.on('data', (data) => { verifyProcess.stdout?.on('data', data => {
const output = data.toString() const output = data.toString()
console.log('pip版本信息:', output) console.log('pip版本信息:', output)
}) })
verifyProcess.stderr?.on('data', (data) => { verifyProcess.stderr?.on('data', data => {
const errorOutput = data.toString() const errorOutput = data.toString()
console.log('pip版本检查错误:', errorOutput) console.log('pip版本检查错误:', errorOutput)
}) })
verifyProcess.on('close', (code) => { verifyProcess.on('close', code => {
if (code === 0) { if (code === 0) {
console.log('pip验证成功') console.log('pip验证成功')
resolve() 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) console.error('pip验证进程错误:', error)
reject(error) reject(error)
}) })
@@ -171,7 +173,10 @@ async function installPip(pythonPath: string, appRoot: string): Promise<void> {
} }
// 下载Python // 下载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 { try {
const environmentPath = path.join(appRoot, 'environment') const environmentPath = path.join(appRoot, 'environment')
const pythonPath = path.join(environmentPath, 'python') const pythonPath = path.join(environmentPath, 'python')
@@ -186,24 +191,30 @@ export async function downloadPython(appRoot: string, mirror = 'ustc'): Promise<
type: 'python', type: 'python',
progress: 0, progress: 0,
status: 'downloading', 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') const zipPath = path.join(environmentPath, 'python.zip')
await downloadFile(pythonUrl, zipPath) await downloadFile(pythonUrl, zipPath)
// 检查下载的Python文件大小 // 检查下载的Python文件大小
const stats = fs.statSync(zipPath) 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可能是无效文件 // Python 3.12.0嵌入式版本应该大约30MB如果小于5MB可能是无效文件
if (stats.size < 5 * 1024 * 1024) { // 5MB if (stats.size < 5 * 1024 * 1024) {
// 5MB
fs.unlinkSync(zipPath) // 删除无效文件 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) { if (mainWindow) {
@@ -211,7 +222,7 @@ export async function downloadPython(appRoot: string, mirror = 'ustc'): Promise<
type: 'python', type: 'python',
progress: 100, progress: 100,
status: 'extracting', status: 'extracting',
message: '正在解压Python...' message: '正在解压Python...',
}) })
} }
@@ -241,14 +252,13 @@ export async function downloadPython(appRoot: string, mirror = 'ustc'): Promise<
console.log('已启用 site-packages 支持') console.log('已启用 site-packages 支持')
} }
// 安装pip // 安装pip
if (mainWindow) { if (mainWindow) {
mainWindow.webContents.send('download-progress', { mainWindow.webContents.send('download-progress', {
type: 'python', type: 'python',
progress: 80, progress: 80,
status: 'installing', status: 'installing',
message: '正在安装pip...' message: '正在安装pip...',
}) })
} }
@@ -259,7 +269,7 @@ export async function downloadPython(appRoot: string, mirror = 'ustc'): Promise<
type: 'python', type: 'python',
progress: 100, progress: 100,
status: 'completed', status: 'completed',
message: 'Python和pip安装完成' message: 'Python和pip安装完成',
}) })
} }
@@ -271,7 +281,7 @@ export async function downloadPython(appRoot: string, mirror = 'ustc'): Promise<
type: 'python', type: 'python',
progress: 0, progress: 0,
status: 'error', status: 'error',
message: `Python下载失败: ${errorMessage}` message: `Python下载失败: ${errorMessage}`,
}) })
} }
return { success: false, error: errorMessage } return { success: false, error: errorMessage }
@@ -284,11 +294,17 @@ const pipMirrorUrls = {
tsinghua: 'https://pypi.tuna.tsinghua.edu.cn/simple/', tsinghua: 'https://pypi.tuna.tsinghua.edu.cn/simple/',
ustc: 'https://pypi.mirrors.ustc.edu.cn/simple/', ustc: 'https://pypi.mirrors.ustc.edu.cn/simple/',
aliyun: 'https://mirrors.aliyun.com/pypi/simple/', aliyun: 'https://mirrors.aliyun.com/pypi/simple/',
douban: 'https://pypi.douban.com/simple/' douban: 'https://pypi.douban.com/simple/',
} }
// 安装Python依赖 // 安装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 { try {
const pythonPath = path.join(appRoot, 'environment', 'python', 'python.exe') const pythonPath = path.join(appRoot, 'environment', 'python', 'python.exe')
const backendPath = path.join(appRoot) const backendPath = path.join(appRoot)
@@ -307,12 +323,13 @@ export async function installDependencies(appRoot: string, mirror = 'tsinghua'):
type: 'dependencies', type: 'dependencies',
progress: 0, progress: 0,
status: 'downloading', status: 'downloading',
message: '正在安装Python依赖包...' message: '正在安装Python依赖包...',
}) })
} }
// 获取pip镜像源URL // 获取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 // 使用Scripts文件夹中的pip.exe
const pythonDir = path.join(appRoot, 'environment', 'python') const pythonDir = path.join(appRoot, 'environment', 'python')
@@ -331,17 +348,24 @@ export async function installDependencies(appRoot: string, mirror = 'tsinghua'):
// 安装依赖 - 直接使用pip.exe而不是python -m pip // 安装依赖 - 直接使用pip.exe而不是python -m pip
await new Promise<void>((resolve, reject) => { await new Promise<void>((resolve, reject) => {
const process = spawn(pipExePath, [ const process = spawn(
pipExePath,
[
'install', 'install',
'-r', requirementsPath, '-r',
'-i', pipMirrorUrl, requirementsPath,
'--trusted-host', new URL(pipMirrorUrl).hostname '-i',
], { pipMirrorUrl,
'--trusted-host',
new URL(pipMirrorUrl).hostname,
],
{
cwd: backendPath, cwd: backendPath,
stdio: 'pipe' stdio: 'pipe',
}) }
)
process.stdout?.on('data', (data) => { process.stdout?.on('data', data => {
const output = data.toString() const output = data.toString()
console.log('Pip output:', output) console.log('Pip output:', output)
@@ -350,17 +374,17 @@ export async function installDependencies(appRoot: string, mirror = 'tsinghua'):
type: 'dependencies', type: 'dependencies',
progress: 50, progress: 50,
status: 'downloading', status: 'downloading',
message: '正在安装依赖包...' message: '正在安装依赖包...',
}) })
} }
}) })
process.stderr?.on('data', (data) => { process.stderr?.on('data', data => {
const errorOutput = data.toString() const errorOutput = data.toString()
console.error('Pip error:', errorOutput) console.error('Pip error:', errorOutput)
}) })
process.on('close', (code) => { process.on('close', code => {
console.log(`pip安装完成退出码: ${code}`) console.log(`pip安装完成退出码: ${code}`)
if (code === 0) { if (code === 0) {
resolve() 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) console.error('pip进程错误:', error)
reject(error) reject(error)
}) })
@@ -380,7 +404,7 @@ export async function installDependencies(appRoot: string, mirror = 'tsinghua'):
type: 'dependencies', type: 'dependencies',
progress: 100, progress: 100,
status: 'completed', status: 'completed',
message: 'Python依赖安装完成' message: 'Python依赖安装完成',
}) })
} }
@@ -392,7 +416,7 @@ export async function installDependencies(appRoot: string, mirror = 'tsinghua'):
type: 'dependencies', type: 'dependencies',
progress: 0, progress: 0,
status: 'error', status: 'error',
message: `依赖安装失败: ${errorMessage}` message: `依赖安装失败: ${errorMessage}`,
}) })
} }
return { success: false, error: errorMessage } return { success: false, error: errorMessage }
@@ -400,7 +424,9 @@ export async function installDependencies(appRoot: string, mirror = 'tsinghua'):
} }
// 导出pip安装函数 // 导出pip安装函数
export async function installPipPackage(appRoot: string): Promise<{ success: boolean; error?: string }> { export async function installPipPackage(
appRoot: string
): Promise<{ success: boolean; error?: string }> {
try { try {
const pythonPath = path.join(appRoot, 'environment', 'python') const pythonPath = path.join(appRoot, 'environment', 'python')
@@ -413,7 +439,7 @@ export async function installPipPackage(appRoot: string): Promise<{ success: boo
type: 'pip', type: 'pip',
progress: 0, progress: 0,
status: 'installing', status: 'installing',
message: '正在安装pip...' message: '正在安装pip...',
}) })
} }
@@ -424,7 +450,7 @@ export async function installPipPackage(appRoot: string): Promise<{ success: boo
type: 'pip', type: 'pip',
progress: 100, progress: 100,
status: 'completed', status: 'completed',
message: 'pip安装完成' message: 'pip安装完成',
}) })
} }
@@ -436,7 +462,7 @@ export async function installPipPackage(appRoot: string): Promise<{ success: boo
type: 'pip', type: 'pip',
progress: 0, progress: 0,
status: 'error', status: 'error',
message: `pip安装失败: ${errorMessage}` message: `pip安装失败: ${errorMessage}`,
}) })
} }
return { success: false, error: errorMessage } return { success: false, error: errorMessage }
@@ -466,19 +492,17 @@ export async function startBackend(appRoot: string): Promise<{ success: boolean;
stdio: 'pipe', stdio: 'pipe',
env: { env: {
...process.env, ...process.env,
PYTHONIOENCODING: 'utf-8' // 设置Python输出编码为UTF-8 PYTHONIOENCODING: 'utf-8', // 设置Python输出编码为UTF-8
} },
}) })
// 等待后端启动 // 等待后端启动
await new Promise<void>((resolve, reject) => { await new Promise<void>((resolve, reject) => {
const timeout = setTimeout(() => { const timeout = setTimeout(() => {
reject(new Error('后端启动超时')) reject(new Error('后端启动超时'))
}, 30000) // 30秒超时 }, 30000) // 30秒超时
backendProcess.stdout?.on('data', (data) => { backendProcess.stdout?.on('data', data => {
const output = data.toString() const output = data.toString()
console.log('Backend output:', output) console.log('Backend output:', output)
@@ -490,7 +514,7 @@ export async function startBackend(appRoot: string): Promise<{ success: boolean;
}) })
// ✅ 重要:也要监听 stderr // ✅ 重要:也要监听 stderr
backendProcess.stderr?.on('data', (data) => { backendProcess.stderr?.on('data', data => {
const output = data.toString() const output = data.toString()
console.error('Backend error:', output) // 保留原有日志 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()) console.error('Backend error:', data.toString())
}) })
backendProcess.on('error', (error) => { backendProcess.on('error', error => {
clearTimeout(timeout) clearTimeout(timeout)
reject(error) reject(error)
}) })

View File

@@ -57,7 +57,13 @@
</a-layout-sider> </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 <a-layout-content
class="content-area" class="content-area"
:style="{ :style="{
@@ -103,9 +109,7 @@ const mainMenuItems = [
{ path: '/history', label: '历史记录', icon: HistoryOutlined }, { path: '/history', label: '历史记录', icon: HistoryOutlined },
] ]
const bottomMenuItems = [ const bottomMenuItems = [{ path: '/settings', label: '设置', icon: SettingOutlined }]
{ path: '/settings', label: '设置', icon: SettingOutlined },
]
// 自动同步选中项 // 自动同步选中项
const selectedKeys = computed(() => { const selectedKeys = computed(() => {
@@ -142,9 +146,11 @@ const toggleCollapse = () => {
border-radius: 6px; border-radius: 6px;
cursor: pointer; cursor: pointer;
} }
.logo:hover { .logo:hover {
background-color: rgba(255, 255, 255, 0.5); background-color: rgba(255, 255, 255, 0.5);
} }
:deep(.ant-layout-sider-light) .logo:hover { :deep(.ant-layout-sider-light) .logo:hover {
background-color: rgba(0, 0, 0, 0.04); background-color: rgba(0, 0, 0, 0.04);
} }
@@ -162,6 +168,7 @@ const toggleCollapse = () => {
opacity: 1; opacity: 1;
transition: opacity 0.2s ease; transition: opacity 0.2s ease;
} }
.logo-text.text-hidden { .logo-text.text-hidden {
opacity: 0; opacity: 0;
} }
@@ -174,12 +181,15 @@ const toggleCollapse = () => {
scrollbar-width: thin; 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 { .main-menu-container::-webkit-scrollbar {
width: 6px; width: 6px;
} }
.main-menu-container::-webkit-scrollbar-track { .main-menu-container::-webkit-scrollbar-track {
background: transparent; background: transparent;
} }
.main-menu-container::-webkit-scrollbar-thumb { .main-menu-container::-webkit-scrollbar-thumb {
background: rgba(0, 0, 0, 0.2); background: rgba(0, 0, 0, 0.2);
border-radius: 4px; border-radius: 4px;
@@ -190,6 +200,7 @@ const toggleCollapse = () => {
margin-top: auto; margin-top: auto;
border-top: 1px solid rgba(255, 255, 255, 0.08); border-top: 1px solid rgba(255, 255, 255, 0.08);
} }
:deep(.ant-layout-sider-light .bottom-menu) { :deep(.ant-layout-sider-light .bottom-menu) {
border-top: 1px solid rgba(0, 0, 0, 0.04); border-top: 1px solid rgba(0, 0, 0, 0.04);
} }
@@ -207,6 +218,7 @@ const toggleCollapse = () => {
:deep(.ant-layout-sider-dark) .menu-text { :deep(.ant-layout-sider-dark) .menu-text {
color: #fff; color: #fff;
} }
:deep(.ant-layout-sider-light) .logo-text, :deep(.ant-layout-sider-light) .logo-text,
:deep(.ant-layout-sider-light) .menu-text { :deep(.ant-layout-sider-light) .menu-text {
color: rgba(0, 0, 0, 0.88); color: rgba(0, 0, 0, 0.88);
@@ -244,6 +256,7 @@ const toggleCollapse = () => {
scrollbar-width: none; scrollbar-width: none;
-ms-overflow-style: none; -ms-overflow-style: none;
} }
.content-area::-webkit-scrollbar { .content-area::-webkit-scrollbar {
display: none; display: none;
} }

View File

@@ -2,11 +2,7 @@
<div class="log-viewer"> <div class="log-viewer">
<div class="log-header"> <div class="log-header">
<div class="log-controls"> <div class="log-controls">
<a-select <a-select v-model:value="selectedLevel" style="width: 120px" @change="filterLogs">
v-model:value="selectedLevel"
style="width: 120px"
@change="filterLogs"
>
<a-select-option value="all">所有级别</a-select-option> <a-select-option value="all">所有级别</a-select-option>
<a-select-option value="debug">Debug</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="info">Info</a-select-option>
@@ -44,16 +40,10 @@
</a-button> </a-button>
</div> </div>
<div class="log-stats"> <div class="log-stats">总计: {{ filteredLogs.length }} 条日志</div>
总计: {{ filteredLogs.length }} 条日志
</div>
</div> </div>
<div <div ref="logContainer" class="log-container" @scroll="handleScroll">
ref="logContainer"
class="log-container"
@scroll="handleScroll"
>
<div <div
v-for="(log, index) in filteredLogs" v-for="(log, index) in filteredLogs"
:key="index" :key="index"
@@ -65,14 +55,12 @@
<div v-if="log.component" class="log-component">[{{ log.component }}]</div> <div v-if="log.component" class="log-component">[{{ log.component }}]</div>
<div class="log-message">{{ log.message }}</div> <div class="log-message">{{ log.message }}</div>
<div v-if="log.data" class="log-data"> <div v-if="log.data" class="log-data">
<a-button <a-button size="small" type="link" @click="toggleDataVisibility(index)">
size="small"
type="link"
@click="toggleDataVisibility(index)"
>
{{ expandedData.has(index) ? '隐藏数据' : '显示数据' }} {{ expandedData.has(index) ? '隐藏数据' : '显示数据' }}
</a-button> </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> </div>
</div> </div>
@@ -84,7 +72,7 @@ import { ref, computed, nextTick, onMounted, onUnmounted } from 'vue'
import { import {
DeleteOutlined, DeleteOutlined,
DownloadOutlined, DownloadOutlined,
VerticalAlignBottomOutlined VerticalAlignBottomOutlined,
} from '@ant-design/icons-vue' } from '@ant-design/icons-vue'
import { logger, type LogEntry, type LogLevel } from '@/utils/logger' import { logger, type LogEntry, type LogLevel } from '@/utils/logger'
@@ -108,7 +96,8 @@ const filteredLogs = computed(() => {
// 按搜索文本过滤 // 按搜索文本过滤
if (searchText.value) { if (searchText.value) {
const search = searchText.value.toLowerCase() const search = searchText.value.toLowerCase()
filtered = filtered.filter(log => filtered = filtered.filter(
log =>
log.message.toLowerCase().includes(search) || log.message.toLowerCase().includes(search) ||
log.component?.toLowerCase().includes(search) || log.component?.toLowerCase().includes(search) ||
(log.data && JSON.stringify(log.data).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会自动处理 ? () => {} // 如果logs是响应式的Vue会自动处理
: null : null
}) })

View File

@@ -83,7 +83,6 @@
row-key="id" row-key="id"
class="user-table" class="user-table"
> >
<template #bodyCell="{ column, record: user }"> <template #bodyCell="{ column, record: user }">
<template v-if="column.key === 'server'"> <template v-if="column.key === 'server'">
<div class="server-cell"> <div class="server-cell">
@@ -102,7 +101,10 @@
<template v-if="column.key === 'lastRun'"> <template v-if="column.key === 'lastRun'">
<div class="last-run-cell"> <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> </div>
<template v-else> <template v-else>
@@ -118,7 +120,6 @@
</div> </div>
</template> </template>
<template v-if="column.key === 'userAction'"> <template v-if="column.key === 'userAction'">
<a-space size="small"> <a-space size="small">
<a-tooltip title="编辑用户配置"> <a-tooltip title="编辑用户配置">

View File

@@ -6,9 +6,7 @@
sub-title="为了正常安装和配置环境请以管理员权限运行此应用" sub-title="为了正常安装和配置环境请以管理员权限运行此应用"
> >
<template #extra> <template #extra>
<a-button type="primary" @click="handleRestartAsAdmin"> <a-button type="primary" @click="handleRestartAsAdmin"> 重新以管理员权限启动 </a-button>
重新以管理员权限启动
</a-button>
</template> </template>
</a-result> </a-result>
</div> </div>

View File

@@ -54,7 +54,6 @@ import { GIT_MIRRORS } from '@/config/mirrors'
const gitMirrors = ref<Mirror[]>(GIT_MIRRORS) const gitMirrors = ref<Mirror[]>(GIT_MIRRORS)
const selectedGitMirror = ref('github') const selectedGitMirror = ref('github')
const testingGitSpeed = ref(false) const testingGitSpeed = ref(false)
@@ -89,7 +88,7 @@ async function testMirrorWithTimeout(url: string, timeout = 3000): Promise<numbe
await fetch(url.replace('.git', ''), { await fetch(url.replace('.git', ''), {
method: 'HEAD', method: 'HEAD',
mode: 'no-cors', mode: 'no-cors',
signal: controller.signal signal: controller.signal,
}) })
clearTimeout(timeoutId) clearTimeout(timeoutId)
@@ -102,7 +101,7 @@ async function testMirrorWithTimeout(url: string, timeout = 3000): Promise<numbe
async function testGitMirrorSpeed() { async function testGitMirrorSpeed() {
testingGitSpeed.value = true testingGitSpeed.value = true
try { try {
const promises = gitMirrors.value.map(async (mirror) => { const promises = gitMirrors.value.map(async mirror => {
mirror.speed = await testMirrorWithTimeout(mirror.url) mirror.speed = await testMirrorWithTimeout(mirror.url)
return mirror return mirror
}) })
@@ -131,7 +130,7 @@ function getSpeedClass(speed: number | null) {
defineExpose({ defineExpose({
selectedGitMirror, selectedGitMirror,
testGitMirrorSpeed, testGitMirrorSpeed,
gitMirrors gitMirrors,
}) })
// 组件挂载时加载配置并自动开始测速 // 组件挂载时加载配置并自动开始测速

View File

@@ -84,7 +84,7 @@ async function testMirrorWithTimeout(url: string, timeout = 3000): Promise<numbe
await fetch(url, { await fetch(url, {
method: 'HEAD', method: 'HEAD',
mode: 'no-cors', mode: 'no-cors',
signal: controller.signal signal: controller.signal,
}) })
clearTimeout(timeoutId) clearTimeout(timeoutId)
@@ -97,7 +97,7 @@ async function testMirrorWithTimeout(url: string, timeout = 3000): Promise<numbe
async function testPipMirrorSpeed() { async function testPipMirrorSpeed() {
testingPipSpeed.value = true testingPipSpeed.value = true
try { try {
const promises = pipMirrors.value.map(async (mirror) => { const promises = pipMirrors.value.map(async mirror => {
mirror.speed = await testMirrorWithTimeout(mirror.url) mirror.speed = await testMirrorWithTimeout(mirror.url)
return mirror return mirror
}) })
@@ -125,7 +125,7 @@ function getSpeedClass(speed: number | null) {
defineExpose({ defineExpose({
selectedPipMirror, selectedPipMirror,
testPipMirrorSpeed testPipMirrorSpeed,
}) })
// 组件挂载时加载配置并自动开始测速 // 组件挂载时加载配置并自动开始测速

View File

@@ -62,7 +62,7 @@ async function handleForceReinstall() {
} }
defineExpose({ defineExpose({
handleForceReinstall handleForceReinstall,
}) })
</script> </script>

View File

@@ -8,17 +8,18 @@
<a-button size="large" type="primary" @click="handleSkipToHome"> <a-button size="large" type="primary" @click="handleSkipToHome">
跳转至首页仅开发用 跳转至首页仅开发用
</a-button> </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> </a-button>
</div> </div>
</div> </div>
<a-steps <a-steps :current="currentStep" :status="stepStatus" class="init-steps">
:current="currentStep"
:status="stepStatus"
class="init-steps"
>
<a-step title="主题设置" description="选择您喜欢的主题" /> <a-step title="主题设置" description="选择您喜欢的主题" />
<a-step title="Python 环境" description="安装 Python 运行环境" /> <a-step title="Python 环境" description="安装 Python 运行环境" />
<a-step title="pip 安装" description="安装 Python 包管理器" /> <a-step title="pip 安装" description="安装 Python 包管理器" />
@@ -43,7 +44,11 @@
<ThemeStep v-if="currentStep === 0" ref="themeStepRef" /> <ThemeStep v-if="currentStep === 0" ref="themeStepRef" />
<!-- 步骤 1: Python 环境 --> <!-- 步骤 1: Python 环境 -->
<PythonStep v-if="currentStep === 1" :python-installed="pythonInstalled" ref="pythonStepRef" /> <PythonStep
v-if="currentStep === 1"
:python-installed="pythonInstalled"
ref="pythonStepRef"
/>
<!-- 步骤 2: pip 安装 --> <!-- 步骤 2: pip 安装 -->
<PipStep v-if="currentStep === 2" :pip-installed="pipInstalled" ref="pipStepRef" /> <PipStep v-if="currentStep === 2" :pip-installed="pipInstalled" ref="pipStepRef" />
@@ -71,7 +76,6 @@
上一步 上一步
</a-button> </a-button>
<a-button <a-button
v-if="currentStep < 6" v-if="currentStep < 6"
size="large" size="large"
@@ -244,14 +248,22 @@ async function handleNextStep() {
function getNextButtonText() { function getNextButtonText() {
switch (currentStep.value) { switch (currentStep.value) {
case 0: return '下一步' case 0:
case 1: return props.pythonInstalled ? '下一步' : '安装 Python' return '下一步'
case 2: return props.pipInstalled ? '下一步' : '安装 pip' case 1:
case 3: return props.gitInstalled ? '下一步' : '安装 Git' return props.pythonInstalled ? '下一步' : '安装 Python'
case 4: return props.backendExists ? '更新代码' : '获取代码' case 2:
case 5: return '安装依赖' return props.pipInstalled ? '下一步' : '安装 pip'
case 6: return '启动服务' case 3:
default: return '下一步' 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({ defineExpose({
currentStep, currentStep,
handleDownloadProgress handleDownloadProgress,
}) })
// 监听 errorMessage一旦有内容就弹窗 // 监听 errorMessage一旦有内容就弹窗
watch(errorMessage, (val) => { watch(errorMessage, val => {
if (val) { if (val) {
message.error(val) message.error(val)
// 弹窗后可选:自动清空 errorMessage // 弹窗后可选:自动清空 errorMessage

View File

@@ -63,7 +63,7 @@ async function handleForceReinstall() {
} }
defineExpose({ defineExpose({
handleForceReinstall handleForceReinstall,
}) })
</script> </script>

View File

@@ -5,8 +5,13 @@
<p>需要安装 Python 3.13.0 运行环境64位嵌入式版本</p> <p>需要安装 Python 3.13.0 运行环境64位嵌入式版本</p>
<div class="mirror-grid"> <div class="mirror-grid">
<div v-for="mirror in pythonMirrors" :key="mirror.key" class="mirror-card" <div
:class="{ active: selectedPythonMirror === mirror.key }" @click="selectedPythonMirror = mirror.key"> v-for="mirror in pythonMirrors"
:key="mirror.key"
class="mirror-card"
:class="{ active: selectedPythonMirror === mirror.key }"
@click="selectedPythonMirror = mirror.key"
>
<div class="mirror-header"> <div class="mirror-header">
<h4>{{ mirror.name }}</h4> <h4>{{ mirror.name }}</h4>
<div class="speed-badge" :class="getSpeedClass(mirror.speed)"> <div class="speed-badge" :class="getSpeedClass(mirror.speed)">
@@ -93,7 +98,7 @@ async function testMirrorWithTimeout(url: string, timeout = 3000): Promise<numbe
await fetch(url, { await fetch(url, {
method: 'HEAD', method: 'HEAD',
mode: 'no-cors', mode: 'no-cors',
signal: controller.signal signal: controller.signal,
}) })
clearTimeout(timeoutId) clearTimeout(timeoutId)
@@ -106,7 +111,7 @@ async function testMirrorWithTimeout(url: string, timeout = 3000): Promise<numbe
async function testPythonMirrorSpeed() { async function testPythonMirrorSpeed() {
testingSpeed.value = true testingSpeed.value = true
try { try {
const promises = pythonMirrors.value.map(async (mirror) => { const promises = pythonMirrors.value.map(async mirror => {
mirror.speed = await testMirrorWithTimeout(mirror.url) mirror.speed = await testMirrorWithTimeout(mirror.url)
return mirror return mirror
}) })
@@ -164,7 +169,7 @@ async function handleForceReinstall() {
defineExpose({ defineExpose({
selectedPythonMirror, selectedPythonMirror,
testPythonMirrorSpeed, testPythonMirrorSpeed,
handleForceReinstall handleForceReinstall,
}) })
// 组件挂载时加载配置并自动开始测速 // 组件挂载时加载配置并自动开始测速

View File

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

View File

@@ -53,7 +53,7 @@ async function saveSettings() {
await saveThemeConfig(selectedThemeMode.value, selectedThemeColor.value) await saveThemeConfig(selectedThemeMode.value, selectedThemeColor.value)
console.log('主题设置已保存:', { console.log('主题设置已保存:', {
themeMode: selectedThemeMode.value, themeMode: selectedThemeMode.value,
themeColor: selectedThemeColor.value themeColor: selectedThemeColor.value,
}) })
} }
@@ -66,7 +66,7 @@ async function loadSettings() {
setThemeColor(selectedThemeColor.value) setThemeColor(selectedThemeColor.value)
console.log('主题设置已加载:', { console.log('主题设置已加载:', {
themeMode: selectedThemeMode.value, themeMode: selectedThemeMode.value,
themeColor: selectedThemeColor.value themeColor: selectedThemeColor.value,
}) })
} catch (error) { } catch (error) {
console.warn('Failed to load theme settings:', error) console.warn('Failed to load theme settings:', error)
@@ -78,7 +78,7 @@ defineExpose({
loadSettings, loadSettings,
saveSettings, saveSettings,
selectedThemeMode, selectedThemeMode,
selectedThemeColor selectedThemeColor,
}) })
// 组件挂载时加载设置 // 组件挂载时加载设置

View File

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

View File

@@ -96,6 +96,6 @@ export function usePlanApi() {
createPlan, createPlan,
updatePlan, updatePlan,
deletePlan, deletePlan,
reorderPlans reorderPlans,
} }
} }

View File

@@ -14,7 +14,7 @@ export function useScriptApi() {
try { try {
const requestData: ScriptCreateIn = { 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) const response = await Service.addScriptApiScriptsAddPost(requestData)
@@ -28,7 +28,7 @@ export function useScriptApi() {
return { return {
scriptId: response.scriptId, scriptId: response.scriptId,
message: response.message || '脚本添加成功', message: response.message || '脚本添加成功',
data: response.data data: response.data,
} }
} catch (err) { } catch (err) {
const errorMsg = err instanceof Error ? err.message : '添加脚本失败' const errorMsg = err instanceof Error ? err.message : '添加脚本失败'
@@ -105,7 +105,7 @@ export function useScriptApi() {
type: scriptType, type: scriptType,
name: config?.Info?.Name || `${item.type}脚本`, name: config?.Info?.Name || `${item.type}脚本`,
config, config,
createTime: new Date().toLocaleString() createTime: new Date().toLocaleString(),
} }
} catch (err) { } catch (err) {
const errorMsg = err instanceof Error ? err.message : '获取脚本详情失败' const errorMsg = err instanceof Error ? err.message : '获取脚本详情失败'
@@ -157,7 +157,7 @@ export function useScriptApi() {
const response = await Service.updateScriptApiScriptsUpdatePost({ const response = await Service.updateScriptApiScriptsUpdatePost({
scriptId, scriptId,
data: dataToSend data: dataToSend,
}) })
if (response.code !== 200) { if (response.code !== 200) {

View File

@@ -42,7 +42,7 @@ export function useSettingsApi() {
try { try {
const response = await Service.updateScriptApiSettingUpdatePost({ const response = await Service.updateScriptApiSettingUpdatePost({
data: settings data: settings,
}) })
// 根据code判断是否成功非200就是不成功 // 根据code判断是否成功非200就是不成功

View File

@@ -109,15 +109,17 @@ const updateCSSVariables = () => {
// 颜色工具函数 // 颜色工具函数
const hexToRgb = (hex: string) => { const hexToRgb = (hex: string) => {
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex) 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), r: parseInt(result[1], 16),
g: parseInt(result[2], 16), g: parseInt(result[2], 16),
b: parseInt(result[3], 16) b: parseInt(result[3], 16),
} : null }
: null
} }
const rgbToHex = (r: number, g: number, b: number) => { 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) => { const lightenColor = (hex: string, percent: number) => {
@@ -127,11 +129,7 @@ const lightenColor = (hex: string, percent: number) => {
const { r, g, b } = rgb const { r, g, b } = rgb
const amount = Math.round(2.55 * percent) const amount = Math.round(2.55 * percent)
return rgbToHex( return rgbToHex(Math.min(255, r + amount), Math.min(255, g + amount), Math.min(255, b + amount))
Math.min(255, r + amount),
Math.min(255, g + amount),
Math.min(255, b + amount)
)
} }
const darkenColor = (hex: string, percent: number) => { const darkenColor = (hex: string, percent: number) => {
@@ -141,11 +139,7 @@ const darkenColor = (hex: string, percent: number) => {
const { r, g, b } = rgb const { r, g, b } = rgb
const amount = Math.round(2.55 * percent) const amount = Math.round(2.55 * percent)
return rgbToHex( return rgbToHex(Math.max(0, r - amount), Math.max(0, g - amount), Math.max(0, b - amount))
Math.max(0, r - amount),
Math.max(0, g - amount),
Math.max(0, b - amount)
)
} }
// 监听系统主题变化 // 监听系统主题变化

View File

@@ -14,7 +14,7 @@ export function useUserApi() {
try { try {
const requestData: UserInBase = { const requestData: UserInBase = {
scriptId scriptId,
} }
const response = await Service.addUserApiScriptsUserAddPost(requestData) 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 loading.value = true
error.value = null error.value = null
@@ -47,7 +51,7 @@ export function useUserApi() {
const requestData: UserUpdateIn = { const requestData: UserUpdateIn = {
scriptId, scriptId,
userId, userId,
data: userData data: userData,
} }
const response = await Service.updateUserApiScriptsUserUpdatePost(requestData) const response = await Service.updateUserApiScriptsUserUpdatePost(requestData)
@@ -80,7 +84,7 @@ export function useUserApi() {
try { try {
const requestData: UserDeleteIn = { const requestData: UserDeleteIn = {
scriptId, scriptId,
userId userId,
} }
const response = await Service.deleteUserApiScriptsUserDeletePost(requestData) const response = await Service.deleteUserApiScriptsUserDeletePost(requestData)

View File

@@ -22,38 +22,38 @@ export const GIT_MIRRORS: MirrorConfig[] = [
key: 'github', key: 'github',
name: 'GitHub 官方', name: 'GitHub 官方',
url: 'https://github.com/DLmaster361/AUTO_MAA.git', url: 'https://github.com/DLmaster361/AUTO_MAA.git',
speed: null speed: null,
}, },
{ {
key: 'ghfast', key: 'ghfast',
name: 'ghfast 镜像', name: 'ghfast 镜像',
url: 'https://ghfast.top/https://github.com/DLmaster361/AUTO_MAA.git', url: 'https://ghfast.top/https://github.com/DLmaster361/AUTO_MAA.git',
speed: null speed: null,
}, },
{ {
key: 'ghproxy_cloudflare', key: 'ghproxy_cloudflare',
name: 'gh-proxy (Cloudflare加速)', name: 'gh-proxy (Cloudflare加速)',
url: 'https://gh-proxy.com/https://github.com/DLmaster361/AUTO_MAA.git', url: 'https://gh-proxy.com/https://github.com/DLmaster361/AUTO_MAA.git',
speed: null speed: null,
}, },
{ {
key: 'ghproxy_hongkong', key: 'ghproxy_hongkong',
name: 'gh-proxy (香港节点加速)', name: 'gh-proxy (香港节点加速)',
url: 'https://hk.gh-proxy.com/https://github.com/DLmaster361/AUTO_MAA.git', url: 'https://hk.gh-proxy.com/https://github.com/DLmaster361/AUTO_MAA.git',
speed: null speed: null,
}, },
{ {
key: 'ghproxy_fastly', key: 'ghproxy_fastly',
name: 'gh-proxy (Fastly CDN加速)', name: 'gh-proxy (Fastly CDN加速)',
url: 'https://cdn.gh-proxy.com/https://github.com/DLmaster361/AUTO_MAA.git', url: 'https://cdn.gh-proxy.com/https://github.com/DLmaster361/AUTO_MAA.git',
speed: null speed: null,
}, },
{ {
key: 'ghproxy_edgeone', key: 'ghproxy_edgeone',
name: 'gh-proxy (EdgeOne加速', name: 'gh-proxy (EdgeOne加速',
url: 'https://edgeone.gh-proxy.com/https://github.com/DLmaster361/AUTO_MAA.git', 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', key: 'official',
name: 'Python 官方', name: 'Python 官方',
url: 'https://www.python.org/ftp/python/3.12.0/python-3.12.0-embed-amd64.zip', url: 'https://www.python.org/ftp/python/3.12.0/python-3.12.0-embed-amd64.zip',
speed: null speed: null,
}, },
{ {
key: 'tsinghua', key: 'tsinghua',
name: '清华 TUNA 镜像', name: '清华 TUNA 镜像',
url: 'https://mirrors.tuna.tsinghua.edu.cn/python/3.12.0/python-3.12.0-embed-amd64.zip', url: 'https://mirrors.tuna.tsinghua.edu.cn/python/3.12.0/python-3.12.0-embed-amd64.zip',
speed: null speed: null,
}, },
{ {
key: 'ustc', key: 'ustc',
name: '中科大镜像', name: '中科大镜像',
url: 'https://mirrors.ustc.edu.cn/python/3.12.0/python-3.12.0-embed-amd64.zip', url: 'https://mirrors.ustc.edu.cn/python/3.12.0/python-3.12.0-embed-amd64.zip',
speed: null speed: null,
}, },
{ {
key: 'huawei', key: 'huawei',
name: '华为云镜像', name: '华为云镜像',
url: 'https://mirrors.huaweicloud.com/repository/toolkit/python/3.12.0/python-3.12.0-embed-amd64.zip', url: 'https://mirrors.huaweicloud.com/repository/toolkit/python/3.12.0/python-3.12.0-embed-amd64.zip',
speed: null speed: null,
}, },
{ {
key: 'aliyun', key: 'aliyun',
name: '阿里云镜像', name: '阿里云镜像',
url: 'https://mirrors.aliyun.com/python-release/windows/python-3.12.0-embed-amd64.zip', 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', key: 'official',
name: 'PyPI 官方', name: 'PyPI 官方',
url: 'https://pypi.org/simple/', url: 'https://pypi.org/simple/',
speed: null speed: null,
}, },
{ {
key: 'tsinghua', key: 'tsinghua',
name: '清华大学', name: '清华大学',
url: 'https://pypi.tuna.tsinghua.edu.cn/simple/', url: 'https://pypi.tuna.tsinghua.edu.cn/simple/',
speed: null speed: null,
}, },
{ {
key: 'ustc', key: 'ustc',
name: '中科大', name: '中科大',
url: 'https://pypi.mirrors.ustc.edu.cn/simple/', url: 'https://pypi.mirrors.ustc.edu.cn/simple/',
speed: null speed: null,
}, },
{ {
key: 'aliyun', key: 'aliyun',
name: '阿里云', name: '阿里云',
url: 'https://mirrors.aliyun.com/pypi/simple/', url: 'https://mirrors.aliyun.com/pypi/simple/',
speed: null speed: null,
}, },
{ {
key: 'douban', key: 'douban',
name: '豆瓣', name: '豆瓣',
url: 'https://pypi.douban.com/simple/', url: 'https://pypi.douban.com/simple/',
speed: null speed: null,
} },
] ]
/** /**
@@ -137,7 +137,7 @@ export const API_ENDPOINTS = {
// WebSocket连接基础URL // WebSocket连接基础URL
websocket: 'ws://localhost:8000', 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', getPip: 'http://221.236.27.82:10197/d/AUTO_MAA/get-pip.py',
// Git 客户端下载链接 // 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 = { export const ALL_MIRRORS: MirrorCategory = {
git: GIT_MIRRORS, git: GIT_MIRRORS,
python: PYTHON_MIRRORS, python: PYTHON_MIRRORS,
pip: PIP_MIRRORS pip: PIP_MIRRORS,
} }
/** /**

View File

@@ -3,7 +3,6 @@ import type { RouteRecordRaw } from 'vue-router'
import { isAppInitialized } from '@/utils/config' import { isAppInitialized } from '@/utils/config'
const routes: RouteRecordRaw[] = [ const routes: RouteRecordRaw[] = [
{ {
path: '/', path: '/',
@@ -90,7 +89,6 @@ const router = createRouter({
routes, routes,
}) })
// 添加路由守卫,确保在生产环境中也能正确进入初始化页面 // 添加路由守卫,确保在生产环境中也能正确进入初始化页面
router.beforeEach(async (to, from, next) => { router.beforeEach(async (to, from, next) => {
console.log('路由守卫:', { to: to.path, from: from.path }) console.log('路由守卫:', { to: to.path, from: from.path })
@@ -118,5 +116,4 @@ router.beforeEach(async (to, from, next) => {
next() next()
}) })
export default router export default router

View File

@@ -1,4 +1,3 @@
// 设置相关类型定义 // 设置相关类型定义
export interface SettingsData { export interface SettingsData {
Function: { Function: {

View File

@@ -38,7 +38,7 @@ const DEFAULT_CONFIG: FrontendConfig = {
gitInstalled: false, gitInstalled: false,
backendExists: false, backendExists: false,
dependenciesInstalled: false, dependenciesInstalled: false,
pipInstalled: false pipInstalled: false,
} }
// 读取配置(内部使用,不触发保存) // 读取配置(内部使用,不触发保存)
@@ -83,7 +83,8 @@ export async function getConfig(): Promise<FrontendConfig> {
const config = await getConfigInternal() const config = await getConfigInternal()
// 如果是从localStorage迁移的配置保存到文件并清理localStorage // 如果是从localStorage迁移的配置保存到文件并清理localStorage
const hasLocalStorage = localStorage.getItem('app-config') || localStorage.getItem('theme-settings') const hasLocalStorage =
localStorage.getItem('app-config') || localStorage.getItem('theme-settings')
if (hasLocalStorage) { if (hasLocalStorage) {
try { try {
await window.electronAPI.saveConfig(config) 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 } const config: Partial<FrontendConfig> = { selectedGitMirror: gitMirror }
if (pythonMirror) config.selectedPythonMirror = pythonMirror if (pythonMirror) config.selectedPythonMirror = pythonMirror
if (pipMirror) config.selectedPipMirror = pipMirror if (pipMirror) config.selectedPipMirror = pipMirror

View File

@@ -34,7 +34,7 @@ class Logger {
level, level,
message, message,
data, data,
component component,
} }
// 添加到内存日志 // 添加到内存日志
@@ -188,5 +188,5 @@ export default {
install(app: any) { install(app: any) {
app.config.globalProperties.$logger = logger app.config.globalProperties.$logger = logger
app.provide('logger', logger) app.provide('logger', logger)
} },
} }

View File

@@ -12,7 +12,7 @@ import {
getMirrorUrl, getMirrorUrl,
updateMirrorSpeed, updateMirrorSpeed,
sortMirrorsBySpeed, sortMirrorsBySpeed,
getFastestMirror getFastestMirror,
} from '@/config/mirrors' } from '@/config/mirrors'
/** /**
@@ -83,7 +83,7 @@ export class MirrorManager {
const response = await fetch(url, { const response = await fetch(url, {
method: 'HEAD', method: 'HEAD',
signal: controller.signal, signal: controller.signal,
cache: 'no-cache' cache: 'no-cache',
}) })
clearTimeout(timeoutId) clearTimeout(timeoutId)
@@ -103,7 +103,7 @@ export class MirrorManager {
*/ */
async testAllMirrorSpeeds(type: keyof MirrorCategory): Promise<MirrorConfig[]> { async testAllMirrorSpeeds(type: keyof MirrorCategory): Promise<MirrorConfig[]> {
const mirrors = this.getMirrors(type) const mirrors = this.getMirrors(type)
const promises = mirrors.map(async (mirror) => { const promises = mirrors.map(async mirror => {
const speed = await this.testMirrorSpeed(mirror.url) const speed = await this.testMirrorSpeed(mirror.url)
this.updateMirrorSpeed(type, mirror.key, speed) this.updateMirrorSpeed(type, mirror.key, speed)
return { ...mirror, speed } return { ...mirror, speed }
@@ -172,7 +172,7 @@ export class MirrorManager {
return { return {
mirrors: this.mirrorConfigs, mirrors: this.mirrorConfigs,
apiEndpoints: this.apiEndpoints, 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 getMirrors = (type: keyof MirrorCategory) => mirrorManager.getMirrors(type)
export const getMirrorUrlByManager = (type: keyof MirrorCategory, key: string) => mirrorManager.getMirrorUrl(type, key) export const getMirrorUrlByManager = (type: keyof MirrorCategory, key: string) =>
export const testMirrorSpeed = (url: string, timeout?: number) => mirrorManager.testMirrorSpeed(url, timeout) mirrorManager.getMirrorUrl(type, key)
export const testAllMirrorSpeeds = (type: keyof MirrorCategory) => mirrorManager.testAllMirrorSpeeds(type) export const testMirrorSpeed = (url: string, timeout?: number) =>
mirrorManager.testMirrorSpeed(url, timeout)
export const testAllMirrorSpeeds = (type: keyof MirrorCategory) =>
mirrorManager.testAllMirrorSpeeds(type)

View File

@@ -38,7 +38,7 @@ import ManualMode from '@/components/initialization/ManualMode.vue'
import type { DownloadProgress } from '@/types/initialization' import type { DownloadProgress } from '@/types/initialization'
const router = useRouter() const router = useRouter()
const logger = createComponentLogger('InitializationNew') const logger = createComponentLogger('Initialization')
// 基础状态 // 基础状态
const isAdmin = ref(true) const isAdmin = ref(true)
@@ -91,7 +91,7 @@ async function checkCriticalFiles() {
pythonExists: config.pythonInstalled || false, pythonExists: config.pythonInstalled || false,
pipExists: config.pipInstalled || false, pipExists: config.pipInstalled || false,
gitExists: config.gitInstalled || false, gitExists: config.gitInstalled || false,
mainPyExists: config.backendExists || false mainPyExists: config.backendExists || false,
} }
} }
@@ -109,7 +109,7 @@ async function checkCriticalFiles() {
pythonExists: criticalFiles.pythonExists, pythonExists: criticalFiles.pythonExists,
pipExists: criticalFiles.pipExists, pipExists: criticalFiles.pipExists,
gitExists: criticalFiles.gitExists, gitExists: criticalFiles.gitExists,
mainPyExists: criticalFiles.mainPyExists mainPyExists: criticalFiles.mainPyExists,
} }
console.log('🔍 最终返回结果:', result) console.log('🔍 最终返回结果:', result)
@@ -125,13 +125,13 @@ async function checkCriticalFiles() {
pythonInstalled: config.pythonInstalled, pythonInstalled: config.pythonInstalled,
pipInstalled: config.pipInstalled, pipInstalled: config.pipInstalled,
gitInstalled: config.gitInstalled, gitInstalled: config.gitInstalled,
backendExists: config.backendExists backendExists: config.backendExists,
}) })
return { return {
pythonExists: config.pythonInstalled || false, pythonExists: config.pythonInstalled || false,
pipExists: config.pipInstalled || false, pipExists: config.pipInstalled || false,
gitExists: config.gitInstalled || false, gitExists: config.gitInstalled || false,
mainPyExists: config.backendExists || false mainPyExists: config.backendExists || false,
} }
} catch (configError) { } catch (configError) {
console.error('❌ 读取配置文件也失败了:', configError) console.error('❌ 读取配置文件也失败了:', configError)
@@ -139,7 +139,7 @@ async function checkCriticalFiles() {
pythonExists: false, pythonExists: false,
pipExists: false, pipExists: false,
gitExists: false, gitExists: false,
mainPyExists: false mainPyExists: false,
} }
} }
} }
@@ -177,7 +177,8 @@ async function checkEnvironment() {
console.log('是否第一次启动:', isFirst) console.log('是否第一次启动:', isFirst)
// 检查所有关键exe文件是否都存在 // 检查所有关键exe文件是否都存在
const allExeFilesExist = criticalFiles.pythonExists && const allExeFilesExist =
criticalFiles.pythonExists &&
criticalFiles.pipExists && criticalFiles.pipExists &&
criticalFiles.gitExists && criticalFiles.gitExists &&
criticalFiles.mainPyExists criticalFiles.mainPyExists
@@ -203,7 +204,14 @@ async function checkEnvironment() {
} else { } else {
logger.info('需要进入手动模式进行配置') logger.info('需要进入手动模式进行配置')
console.log('进入手动模式') 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) { if (!allExeFilesExist && config.init) {

View File

@@ -27,7 +27,11 @@
<!-- 空状态 --> <!-- 空状态 -->
<div v-if="!queueList.length || !currentQueueData" class="empty-state"> <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"> <div class="empty-icon">
<PlusOutlined /> <PlusOutlined />
</div> </div>
@@ -157,13 +161,13 @@ const fetchQueues = async () => {
console.log('Queue ID:', queueId, 'Name:', queueName, 'Type:', typeof queueId) // 调试日志 console.log('Queue ID:', queueId, 'Name:', queueName, 'Type:', typeof queueId) // 调试日志
return { return {
id: queueId, id: queueId,
name: queueName name: queueName,
} }
} catch (itemError) { } catch (itemError) {
console.warn('解析队列项失败:', itemError, item) console.warn('解析队列项失败:', itemError, item)
return { return {
id: `queue_${index}`, id: `queue_${index}`,
name: `队列 ${index + 1}` name: `队列 ${index + 1}`,
} }
} }
}) })
@@ -290,7 +294,7 @@ const refreshTimeSets = async () => {
id: timeSetId, id: timeSetId,
time: timeString, time: timeString,
enabled: Boolean(timeSetData.Info.Enabled), enabled: Boolean(timeSetData.Info.Enabled),
description: timeSetData.Info.Description || '' description: timeSetData.Info.Description || '',
}) })
} }
} catch (itemError) { } catch (itemError) {
@@ -353,7 +357,7 @@ const refreshQueueItems = async () => {
if (queueItemData?.Info) { if (queueItemData?.Info) {
queueItems.push({ queueItems.push({
id: queueItemId, id: queueItemId,
script: queueItemData.Info.ScriptId || '' script: queueItemData.Info.ScriptId || '',
}) })
} }
} catch (itemError) { } catch (itemError) {
@@ -382,7 +386,8 @@ const onQueueNameBlur = () => {
if (activeQueueId.value) { if (activeQueueId.value) {
const currentQueue = queueList.value.find(queue => queue.id === activeQueueId.value) const currentQueue = queueList.value.find(queue => queue.id === activeQueueId.value)
if (currentQueue) { 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({ const response = await Service.updateQueueApiQueueUpdatePost({
queueId: activeQueueId.value, queueId: activeQueueId.value,
data: queueData data: queueData,
}) })
if (response.code !== 200) { if (response.code !== 200) {
@@ -701,7 +706,9 @@ onMounted(async () => {
} }
.empty-content-fancy { .empty-content-fancy {
transition: box-shadow 0.3s, transform 0.2s; transition:
box-shadow 0.3s,
transform 0.2s;
border: none; border: none;
border-radius: 24px; border-radius: 24px;
} }

View File

@@ -154,7 +154,6 @@
<!-- 实时日志 (60%) --> <!-- 实时日志 (60%) -->
<a-col :span="14"> <a-col :span="14">
<a-card size="small" style="height: 100%" title="实时日志"> <a-card size="small" style="height: 100%" title="实时日志">
<div class="realtime-logs-panel"> <div class="realtime-logs-panel">
<!-- <a-row justify="space-between" align="middle" style="margin-bottom: 8px">--> <!-- <a-row justify="space-between" align="middle" style="margin-bottom: 8px">-->
<!-- &lt;!&ndash; 左侧标题 &ndash;&gt;--> <!-- &lt;!&ndash; 左侧标题 &ndash;&gt;-->
@@ -624,17 +623,17 @@ const handleWebSocketMessage = (task: RunningTask, data: any) => {
case 'Info': case 'Info':
// 通知信息 // 通知信息
let level = 'info'; let level = 'info'
let content = '未知通知'; let content = '未知通知'
// 检查数据中是否有 Error 字段 // 检查数据中是否有 Error 字段
if (data.data?.Error) { if (data.data?.Error) {
// 如果是错误信息,设置为 error 级别 // 如果是错误信息,设置为 error 级别
level = 'error'; level = 'error'
content = data.data.Error; // 错误信息内容 content = data.data.Error // 错误信息内容
} else { } else {
// 如果没有 Error 字段,继续处理 val 或 message 字段 // 如果没有 Error 字段,继续处理 val 或 message 字段
content = data.data?.val || data.data?.message || '未知通知'; content = data.data?.val || data.data?.message || '未知通知'
} }
addTaskLog(task, content, level as any) addTaskLog(task, content, level as any)

View File

@@ -204,21 +204,31 @@ onMounted(() => {
<div class="setting-item"> <div class="setting-item">
<h4>主题模式</h4> <h4>主题模式</h4>
<p class="setting-description">选择应用程序的外观主题</p> <p class="setting-description">选择应用程序的外观主题</p>
<Radio.Group :value="themeMode" @change="handleThemeModeChange" :options="themeModeOptions" /> <Radio.Group
:value="themeMode"
@change="handleThemeModeChange"
:options="themeModeOptions"
/>
</div> </div>
<Divider /> <Divider />
<div class="setting-item"> <div class="setting-item">
<h4>主题色</h4> <h4>主题色</h4>
<p class="setting-description">选择应用程序的主色调</p> <p class="setting-description">选择应用程序的主色调</p>
<Select :value="themeColor" @change="handleThemeColorChange" style="width: 200px"> <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="display: flex; align-items: center; gap: 8px">
<div :style="{ <div
:style="{
width: '16px', width: '16px',
height: '16px', height: '16px',
borderRadius: '50%', borderRadius: '50%',
backgroundColor: option.color, backgroundColor: option.color,
}" /> }"
/>
{{ option.label }} {{ option.label }}
</div> </div>
</Select.Option> </Select.Option>
@@ -235,9 +245,12 @@ onMounted(() => {
<div class="setting-item"> <div class="setting-item">
<h4>Boss键</h4> <h4>Boss键</h4>
<p class="setting-description">设置快速隐藏窗口的快捷键</p> <p class="setting-description">设置快速隐藏窗口的快捷键</p>
<Input v-model:value="settings.Function.BossKey" <Input
@blur="handleSettingChange('Function', 'BossKey', settings.Function.BossKey)" placeholder="例如: Ctrl+H" v-model:value="settings.Function.BossKey"
style="width: 300px" /> @blur="handleSettingChange('Function', 'BossKey', settings.Function.BossKey)"
placeholder="例如: Ctrl+H"
style="width: 300px"
/>
</div> </div>
<Divider /> <Divider />
@@ -245,9 +258,12 @@ onMounted(() => {
<div class="setting-item"> <div class="setting-item">
<h4>历史记录保留时间</h4> <h4>历史记录保留时间</h4>
<p class="setting-description">设置历史记录的保留时间</p> <p class="setting-description">设置历史记录的保留时间</p>
<Select v-model:value="settings.Function.HistoryRetentionTime" <Select
@change="(value) => handleSettingChange('Function', 'HistoryRetentionTime', value)" v-model:value="settings.Function.HistoryRetentionTime"
:options="historyRetentionOptions" style="width: 200px" /> @change="value => handleSettingChange('Function', 'HistoryRetentionTime', value)"
:options="historyRetentionOptions"
style="width: 200px"
/>
</div> </div>
<Divider /> <Divider />
@@ -255,9 +271,12 @@ onMounted(() => {
<div class="setting-item"> <div class="setting-item">
<h4>主页图像模式</h4> <h4>主页图像模式</h4>
<p class="setting-description">选择主页显示的图像模式</p> <p class="setting-description">选择主页显示的图像模式</p>
<Select v-model:value="settings.Function.HomeImageMode" <Select
@change="(value) => handleSettingChange('Function', 'HomeImageMode', value)" v-model:value="settings.Function.HomeImageMode"
:options="homeImageModeOptions" style="width: 200px" /> @change="value => handleSettingChange('Function', 'HomeImageMode', value)"
:options="homeImageModeOptions"
style="width: 200px"
/>
</div> </div>
<Divider /> <Divider />
@@ -266,28 +285,40 @@ onMounted(() => {
<h4>功能开关</h4> <h4>功能开关</h4>
<Space direction="vertical" size="middle"> <Space direction="vertical" size="middle">
<div class="switch-item"> <div class="switch-item">
<Switch v-model:checked="settings.Function.IfAllowSleep" <Switch
@change="(checked) => handleSettingChange('Function', 'IfAllowSleep', checked)" /> v-model:checked="settings.Function.IfAllowSleep"
@change="checked => handleSettingChange('Function', 'IfAllowSleep', checked)"
/>
<span class="switch-label">启动时阻止系统休眠</span> <span class="switch-label">启动时阻止系统休眠</span>
</div> </div>
<div class="switch-item"> <div class="switch-item">
<Switch v-model:checked="settings.Function.IfSilence" <Switch
@change="(checked) => handleSettingChange('Function', 'IfSilence', checked)" /> v-model:checked="settings.Function.IfSilence"
@change="checked => handleSettingChange('Function', 'IfSilence', checked)"
/>
<span class="switch-label">静默模式</span> <span class="switch-label">静默模式</span>
</div> </div>
<div class="switch-item"> <div class="switch-item">
<Switch v-model:checked="settings.Function.UnattendedMode" <Switch
@change="(checked) => handleSettingChange('Function', 'UnattendedMode', checked)" /> v-model:checked="settings.Function.UnattendedMode"
@change="checked => handleSettingChange('Function', 'UnattendedMode', checked)"
/>
<span class="switch-label">无人值守模式</span> <span class="switch-label">无人值守模式</span>
</div> </div>
<div class="switch-item"> <div class="switch-item">
<Switch v-model:checked="settings.Function.IfAgreeBilibili" <Switch
@change="(checked) => handleSettingChange('Function', 'IfAgreeBilibili', checked)" /> v-model:checked="settings.Function.IfAgreeBilibili"
@change="checked => handleSettingChange('Function', 'IfAgreeBilibili', checked)"
/>
<span class="switch-label">托管Bilibili游戏隐私政策</span> <span class="switch-label">托管Bilibili游戏隐私政策</span>
</div> </div>
<div class="switch-item"> <div class="switch-item">
<Switch v-model:checked="settings.Function.IfSkipMumuSplashAds" <Switch
@change="(checked) => handleSettingChange('Function', 'IfSkipMumuSplashAds', checked)" /> v-model:checked="settings.Function.IfSkipMumuSplashAds"
@change="
checked => handleSettingChange('Function', 'IfSkipMumuSplashAds', checked)
"
/>
<span class="switch-label">跳过MuMu模拟器启动广告</span> <span class="switch-label">跳过MuMu模拟器启动广告</span>
</div> </div>
</Space> </Space>
@@ -303,9 +334,12 @@ onMounted(() => {
<div class="setting-item"> <div class="setting-item">
<h4>任务结果推送时间</h4> <h4>任务结果推送时间</h4>
<p class="setting-description">设置何时推送任务执行结果</p> <p class="setting-description">设置何时推送任务执行结果</p>
<Select v-model:value="settings.Notify.SendTaskResultTime" <Select
@change="(value) => handleSettingChange('Notify', 'SendTaskResultTime', value)" v-model:value="settings.Notify.SendTaskResultTime"
:options="sendTaskResultTimeOptions" style="width: 200px" /> @change="value => handleSettingChange('Notify', 'SendTaskResultTime', value)"
:options="sendTaskResultTimeOptions"
style="width: 200px"
/>
</div> </div>
<Divider /> <Divider />
@@ -314,18 +348,24 @@ onMounted(() => {
<h4>通知开关</h4> <h4>通知开关</h4>
<Space direction="vertical" size="middle"> <Space direction="vertical" size="middle">
<div class="switch-item"> <div class="switch-item">
<Switch v-model:checked="settings.Notify.IfSendStatistic" <Switch
@change="(checked) => handleSettingChange('Notify', 'IfSendStatistic', checked)" /> v-model:checked="settings.Notify.IfSendStatistic"
@change="checked => handleSettingChange('Notify', 'IfSendStatistic', checked)"
/>
<span class="switch-label">发送统计信息</span> <span class="switch-label">发送统计信息</span>
</div> </div>
<div class="switch-item"> <div class="switch-item">
<Switch v-model:checked="settings.Notify.IfSendSixStar" <Switch
@change="(checked) => handleSettingChange('Notify', 'IfSendSixStar', checked)" /> v-model:checked="settings.Notify.IfSendSixStar"
@change="checked => handleSettingChange('Notify', 'IfSendSixStar', checked)"
/>
<span class="switch-label">发送六星通知</span> <span class="switch-label">发送六星通知</span>
</div> </div>
<div class="switch-item"> <div class="switch-item">
<Switch v-model:checked="settings.Notify.IfPushPlyer" <Switch
@change="(checked) => handleSettingChange('Notify', 'IfPushPlyer', checked)" /> v-model:checked="settings.Notify.IfPushPlyer"
@change="checked => handleSettingChange('Notify', 'IfPushPlyer', checked)"
/>
<span class="switch-label">启用PushPlus推送</span> <span class="switch-label">启用PushPlus推送</span>
</div> </div>
</Space> </Space>
@@ -337,33 +377,61 @@ onMounted(() => {
<h4>邮件通知</h4> <h4>邮件通知</h4>
<Space direction="vertical" size="middle" style="width: 100%"> <Space direction="vertical" size="middle" style="width: 100%">
<div class="switch-item"> <div class="switch-item">
<Switch v-model:checked="settings.Notify.IfSendMail" <Switch
@change="(checked) => handleSettingChange('Notify', 'IfSendMail', checked)" /> v-model:checked="settings.Notify.IfSendMail"
@change="checked => handleSettingChange('Notify', 'IfSendMail', checked)"
/>
<span class="switch-label">启用邮件通知</span> <span class="switch-label">启用邮件通知</span>
</div> </div>
<div class="input-group"> <div class="input-group">
<label>SMTP服务器地址</label> <label>SMTP服务器地址</label>
<Input v-model:value="settings.Notify.SMTPServerAddress" <Input
@blur="handleSettingChange('Notify', 'SMTPServerAddress', settings.Notify.SMTPServerAddress)" v-model:value="settings.Notify.SMTPServerAddress"
placeholder="例如: smtp.gmail.com" style="width: 300px" /> @blur="
handleSettingChange(
'Notify',
'SMTPServerAddress',
settings.Notify.SMTPServerAddress
)
"
placeholder="例如: smtp.gmail.com"
style="width: 300px"
/>
</div> </div>
<div class="input-group"> <div class="input-group">
<label>授权码</label> <label>授权码</label>
<Input.Password v-model:value="settings.Notify.AuthorizationCode" <Input.Password
@blur="handleSettingChange('Notify', 'AuthorizationCode', settings.Notify.AuthorizationCode)" v-model:value="settings.Notify.AuthorizationCode"
placeholder="邮箱授权码" style="width: 300px" /> @blur="
handleSettingChange(
'Notify',
'AuthorizationCode',
settings.Notify.AuthorizationCode
)
"
placeholder="邮箱授权码"
style="width: 300px"
/>
</div> </div>
<div class="input-group"> <div class="input-group">
<label>发件人地址</label> <label>发件人地址</label>
<Input v-model:value="settings.Notify.FromAddress" <Input
@blur="handleSettingChange('Notify', 'FromAddress', settings.Notify.FromAddress)" v-model:value="settings.Notify.FromAddress"
placeholder="发件人邮箱地址" style="width: 300px" /> @blur="
handleSettingChange('Notify', 'FromAddress', settings.Notify.FromAddress)
"
placeholder="发件人邮箱地址"
style="width: 300px"
/>
</div> </div>
<div class="input-group"> <div class="input-group">
<label>收件人地址</label> <label>收件人地址</label>
<Input v-model:value="settings.Notify.ToAddress" <Input
@blur="handleSettingChange('Notify', 'ToAddress', settings.Notify.ToAddress)" placeholder="收件人邮箱地址" v-model:value="settings.Notify.ToAddress"
style="width: 300px" /> @blur="handleSettingChange('Notify', 'ToAddress', settings.Notify.ToAddress)"
placeholder="收件人邮箱地址"
style="width: 300px"
/>
</div> </div>
</Space> </Space>
</div> </div>
@@ -374,15 +442,22 @@ onMounted(() => {
<h4>Server酱通知</h4> <h4>Server酱通知</h4>
<Space direction="vertical" size="middle" style="width: 100%"> <Space direction="vertical" size="middle" style="width: 100%">
<div class="switch-item"> <div class="switch-item">
<Switch v-model:checked="settings.Notify.IfServerChan" <Switch
@change="(checked) => handleSettingChange('Notify', 'IfServerChan', checked)" /> v-model:checked="settings.Notify.IfServerChan"
@change="checked => handleSettingChange('Notify', 'IfServerChan', checked)"
/>
<span class="switch-label">启用Server酱通知</span> <span class="switch-label">启用Server酱通知</span>
</div> </div>
<div class="input-group"> <div class="input-group">
<label>Server酱Key</label> <label>Server酱Key</label>
<Input v-model:value="settings.Notify.ServerChanKey" <Input
@blur="handleSettingChange('Notify', 'ServerChanKey', settings.Notify.ServerChanKey)" v-model:value="settings.Notify.ServerChanKey"
placeholder="Server酱推送Key" style="width: 300px" /> @blur="
handleSettingChange('Notify', 'ServerChanKey', settings.Notify.ServerChanKey)
"
placeholder="Server酱推送Key"
style="width: 300px"
/>
</div> </div>
</Space> </Space>
</div> </div>
@@ -393,15 +468,28 @@ onMounted(() => {
<h4>企业微信机器人</h4> <h4>企业微信机器人</h4>
<Space direction="vertical" size="middle" style="width: 100%"> <Space direction="vertical" size="middle" style="width: 100%">
<div class="switch-item"> <div class="switch-item">
<Switch v-model:checked="settings.Notify.IfCompanyWebHookBot" <Switch
@change="(checked) => handleSettingChange('Notify', 'IfCompanyWebHookBot', checked)" /> v-model:checked="settings.Notify.IfCompanyWebHookBot"
@change="
checked => handleSettingChange('Notify', 'IfCompanyWebHookBot', checked)
"
/>
<span class="switch-label">启用企业微信机器人</span> <span class="switch-label">启用企业微信机器人</span>
</div> </div>
<div class="input-group"> <div class="input-group">
<label>Webhook URL</label> <label>Webhook URL</label>
<Input v-model:value="settings.Notify.CompanyWebHookBotUrl" <Input
@blur="handleSettingChange('Notify', 'CompanyWebHookBotUrl', settings.Notify.CompanyWebHookBotUrl)" v-model:value="settings.Notify.CompanyWebHookBotUrl"
placeholder="企业微信机器人Webhook地址" style="width: 400px" /> @blur="
handleSettingChange(
'Notify',
'CompanyWebHookBotUrl',
settings.Notify.CompanyWebHookBotUrl
)
"
placeholder="企业微信机器人Webhook地址"
style="width: 400px"
/>
</div> </div>
</Space> </Space>
</div> </div>
@@ -416,8 +504,10 @@ onMounted(() => {
<div class="setting-item"> <div class="setting-item">
<h4>自动更新</h4> <h4>自动更新</h4>
<p class="setting-description">是否启用自动更新功能</p> <p class="setting-description">是否启用自动更新功能</p>
<Switch v-model:checked="settings.Update.IfAutoUpdate" <Switch
@change="(checked) => handleSettingChange('Update', 'IfAutoUpdate', checked)" /> v-model:checked="settings.Update.IfAutoUpdate"
@change="checked => handleSettingChange('Update', 'IfAutoUpdate', checked)"
/>
</div> </div>
<Divider /> <Divider />
@@ -425,9 +515,12 @@ onMounted(() => {
<div class="setting-item"> <div class="setting-item">
<h4>更新类型</h4> <h4>更新类型</h4>
<p class="setting-description">选择更新版本类型</p> <p class="setting-description">选择更新版本类型</p>
<Select v-model:value="settings.Update.UpdateType" <Select
@change="(value) => handleSettingChange('Update', 'UpdateType', value)" :options="updateTypeOptions" v-model:value="settings.Update.UpdateType"
style="width: 200px" /> @change="value => handleSettingChange('Update', 'UpdateType', value)"
:options="updateTypeOptions"
style="width: 200px"
/>
</div> </div>
<Divider /> <Divider />
@@ -435,9 +528,13 @@ onMounted(() => {
<div class="setting-item"> <div class="setting-item">
<h4>下载线程数</h4> <h4>下载线程数</h4>
<p class="setting-description">设置下载时使用的线程数量 (1-32)</p> <p class="setting-description">设置下载时使用的线程数量 (1-32)</p>
<InputNumber v-model:value="settings.Update.ThreadNumb" <InputNumber
@change="(value) => handleSettingChange('Update', 'ThreadNumb', value)" :min="1" :max="32" v-model:value="settings.Update.ThreadNumb"
style="width: 120px" /> @change="value => handleSettingChange('Update', 'ThreadNumb', value)"
:min="1"
:max="32"
style="width: 120px"
/>
</div> </div>
<Divider /> <Divider />
@@ -447,9 +544,14 @@ onMounted(() => {
<Space direction="vertical" size="middle" style="width: 100%"> <Space direction="vertical" size="middle" style="width: 100%">
<div class="input-group"> <div class="input-group">
<label>代理地址</label> <label>代理地址</label>
<Input v-model:value="settings.Update.ProxyAddress" <Input
@blur="handleSettingChange('Update', 'ProxyAddress', settings.Update.ProxyAddress)" v-model:value="settings.Update.ProxyAddress"
placeholder="例如: http://127.0.0.1:7890" style="width: 300px" /> @blur="
handleSettingChange('Update', 'ProxyAddress', settings.Update.ProxyAddress)
"
placeholder="例如: http://127.0.0.1:7890"
style="width: 300px"
/>
</div> </div>
</Space> </Space>
</div> </div>
@@ -459,9 +561,14 @@ onMounted(() => {
<div class="setting-item"> <div class="setting-item">
<h4>Mirror酱 CDK</h4> <h4>Mirror酱 CDK</h4>
<p class="setting-description">设置Mirror酱CDK</p> <p class="setting-description">设置Mirror酱CDK</p>
<Input v-model:value="settings.Update.MirrorChyanCDK" <Input
@blur="handleSettingChange('Update', 'MirrorChyanCDK', settings.Update.MirrorChyanCDK)" v-model:value="settings.Update.MirrorChyanCDK"
placeholder="镜像CDK" style="width: 300px" /> @blur="
handleSettingChange('Update', 'MirrorChyanCDK', settings.Update.MirrorChyanCDK)
"
placeholder="镜像CDK"
style="width: 300px"
/>
</div> </div>
</Space> </Space>
</Card> </Card>
@@ -474,8 +581,10 @@ onMounted(() => {
<div class="setting-item"> <div class="setting-item">
<h4>开机自启</h4> <h4>开机自启</h4>
<p class="setting-description">是否在系统启动时自动启动应用</p> <p class="setting-description">是否在系统启动时自动启动应用</p>
<Switch v-model:checked="settings.Start.IfSelfStart" <Switch
@change="(checked) => handleSettingChange('Start', 'IfSelfStart', checked)" /> v-model:checked="settings.Start.IfSelfStart"
@change="checked => handleSettingChange('Start', 'IfSelfStart', checked)"
/>
</div> </div>
<Divider /> <Divider />
@@ -483,8 +592,10 @@ onMounted(() => {
<div class="setting-item"> <div class="setting-item">
<h4>启动后直接最小化</h4> <h4>启动后直接最小化</h4>
<p class="setting-description">启动后是否直接最小化到系统托盘</p> <p class="setting-description">启动后是否直接最小化到系统托盘</p>
<Switch v-model:checked="settings.Start.IfMinimizeDirectly" <Switch
@change="(checked) => handleSettingChange('Start', 'IfMinimizeDirectly', checked)" /> v-model:checked="settings.Start.IfMinimizeDirectly"
@change="checked => handleSettingChange('Start', 'IfMinimizeDirectly', checked)"
/>
</div> </div>
</Space> </Space>
</Card> </Card>
@@ -498,13 +609,17 @@ onMounted(() => {
<h4>系统托盘</h4> <h4>系统托盘</h4>
<Space direction="vertical" size="middle"> <Space direction="vertical" size="middle">
<div class="switch-item"> <div class="switch-item">
<Switch v-model:checked="settings.UI.IfShowTray" <Switch
@change="(checked) => handleSettingChange('UI', 'IfShowTray', checked)" /> v-model:checked="settings.UI.IfShowTray"
@change="checked => handleSettingChange('UI', 'IfShowTray', checked)"
/>
<span class="switch-label">显示系统托盘图标</span> <span class="switch-label">显示系统托盘图标</span>
</div> </div>
<div class="switch-item"> <div class="switch-item">
<Switch v-model:checked="settings.UI.IfToTray" <Switch
@change="(checked) => handleSettingChange('UI', 'IfToTray', checked)" /> v-model:checked="settings.UI.IfToTray"
@change="checked => handleSettingChange('UI', 'IfToTray', checked)"
/>
<span class="switch-label">关闭时最小化到托盘</span> <span class="switch-label">关闭时最小化到托盘</span>
</div> </div>
</Space> </Space>
@@ -544,8 +659,10 @@ onMounted(() => {
<div class="setting-item"> <div class="setting-item">
<h4>语音提示</h4> <h4>语音提示</h4>
<p class="setting-description">是否启用语音提示功能</p> <p class="setting-description">是否启用语音提示功能</p>
<Switch v-model:checked="settings.Voice.Enabled" <Switch
@change="(checked) => handleSettingChange('Voice', 'Enabled', checked)" /> v-model:checked="settings.Voice.Enabled"
@change="checked => handleSettingChange('Voice', 'Enabled', checked)"
/>
</div> </div>
<Divider /> <Divider />
@@ -553,9 +670,13 @@ onMounted(() => {
<div class="setting-item"> <div class="setting-item">
<h4>语音类型</h4> <h4>语音类型</h4>
<p class="setting-description">选择语音提示的详细程度</p> <p class="setting-description">选择语音提示的详细程度</p>
<Select v-model:value="settings.Voice.Type" <Select
@change="(value) => handleSettingChange('Voice', 'Type', value)" :options="voiceTypeOptions" v-model:value="settings.Voice.Type"
style="width: 200px" :disabled="!settings.Voice.Enabled" /> @change="value => handleSettingChange('Voice', 'Type', value)"
:options="voiceTypeOptions"
style="width: 200px"
:disabled="!settings.Voice.Enabled"
/>
</div> </div>
</Space> </Space>
</Card> </Card>
@@ -646,5 +767,4 @@ onMounted(() => {
:deep(.ant-tabs-tab) { :deep(.ant-tabs-tab) {
color: v-bind(textSecondaryColor); color: v-bind(textSecondaryColor);
} }
</style> </style>

View File

@@ -9,7 +9,7 @@ export default defineConfig({
resolve: { resolve: {
extensions: ['.js', '.ts', '.vue', '.json'], extensions: ['.js', '.ts', '.vue', '.json'],
alias: { alias: {
'@': path.resolve(__dirname, 'src') '@': path.resolve(__dirname, 'src'),
} },
} },
}) })