refactor: 添加回log功能~这次好用了~

This commit is contained in:
2025-09-02 18:20:20 +08:00
parent f4fe4ec019
commit 6b00c8a6be
11 changed files with 582 additions and 37 deletions

View File

@@ -5,6 +5,7 @@ import { ConfigProvider } from 'ant-design-vue'
import { useTheme } from './composables/useTheme.ts'
import AppLayout from './components/AppLayout.vue'
import zhCN from 'ant-design-vue/es/locale/zh_CN'
import { logger } from '@/utils/logger'
const route = useRoute()
const { antdTheme, initTheme } = useTheme()
@@ -13,7 +14,9 @@ const { antdTheme, initTheme } = useTheme()
const isInitializationPage = computed(() => route.name === 'Initialization')
onMounted(() => {
logger.info('App组件已挂载')
initTheme()
logger.info('主题初始化完成')
})
</script>

View File

@@ -0,0 +1,189 @@
<template>
<div class="log-viewer">
<div class="log-controls">
<a-space wrap>
<a-button @click="refreshLogs" :loading="loading">
<template #icon>
<ReloadOutlined />
</template>
刷新日志
</a-button>
<a-select v-model:value="logLines" @change="refreshLogs" style="width: 120px">
<a-select-option :value="100">最近100行</a-select-option>
<a-select-option :value="500">最近500行</a-select-option>
<a-select-option :value="1000">最近1000行</a-select-option>
<a-select-option :value="0">全部日志</a-select-option>
</a-select>
<a-button @click="clearLogs" :loading="clearing" type="primary" danger>
<template #icon>
<DeleteOutlined />
</template>
清空日志
</a-button>
<a-button @click="cleanOldLogs" :loading="cleaning">
<template #icon>
<ClearOutlined />
</template>
清理旧日志
</a-button>
<a-button @click="openLogDirectory">
<template #icon>
<FolderOpenOutlined />
</template>
打开日志目录
</a-button>
</a-space>
</div>
<div class="log-info">
<a-space>
<span>日志文件: {{ logPath }}</span>
<span>总行数: {{ totalLines }}</span>
</a-space>
</div>
<div class="log-content">
<a-textarea v-model:value="logs" :rows="25" readonly class="log-textarea" placeholder="暂无日志内容" />
</div>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted } from 'vue'
import { message } from 'ant-design-vue'
import {
ReloadOutlined,
DeleteOutlined,
ClearOutlined,
FolderOpenOutlined
} from '@ant-design/icons-vue'
import { logger } from '@/utils/logger'
const logs = ref('')
const logPath = ref('')
const logLines = ref(500)
const totalLines = ref(0)
const loading = ref(false)
const clearing = ref(false)
const cleaning = ref(false)
// 刷新日志
const refreshLogs = async () => {
loading.value = true
try {
const logContent = await logger.getLogs(logLines.value || undefined)
logs.value = logContent
totalLines.value = logContent.split('\n').filter(line => line.trim()).length
// 自动滚动到底部
setTimeout(() => {
const textarea = document.querySelector('.log-textarea textarea') as HTMLTextAreaElement
if (textarea) {
textarea.scrollTop = textarea.scrollHeight
}
}, 100)
} catch (error) {
message.error('获取日志失败: ' + error)
logger.error('获取日志失败:', error)
} finally {
loading.value = false
}
}
// 清空日志
const clearLogs = async () => {
clearing.value = true
try {
await logger.clearLogs()
logs.value = ''
totalLines.value = 0
message.success('日志已清空')
} catch (error) {
message.error('清空日志失败: ' + error)
logger.error('清空日志失败:', error)
} finally {
clearing.value = false
}
}
// 清理旧日志
const cleanOldLogs = async () => {
cleaning.value = true
try {
await logger.cleanOldLogs(7)
message.success('已清理7天前的旧日志文件')
} catch (error) {
message.error('清理旧日志失败: ' + error)
logger.error('清理旧日志失败:', error)
} finally {
cleaning.value = false
}
}
// 打开日志目录
const openLogDirectory = async () => {
try {
const path = await logger.getLogPath()
// 获取日志目录路径
const logDir = path.substring(0, path.lastIndexOf('\\') || path.lastIndexOf('/'))
if (window.electronAPI?.openUrl) {
await window.electronAPI.openUrl(`file://${logDir}`)
}
} catch (error) {
message.error('打开日志目录失败: ' + error)
logger.error('打开日志目录失败:', error)
}
}
// 获取日志文件路径
const getLogPath = async () => {
try {
logPath.value = await logger.getLogPath()
} catch (error) {
logger.error('获取日志路径失败:', error)
}
}
onMounted(() => {
getLogPath()
refreshLogs()
})
</script>
<style scoped>
.log-viewer {
padding: 16px;
}
.log-controls {
margin-bottom: 16px;
}
.log-info {
margin-bottom: 12px;
font-size: 12px;
color: #666;
}
.log-content {
border: 1px solid #d9d9d9;
border-radius: 6px;
}
.log-textarea :deep(.ant-input) {
font-family: 'Consolas', 'Monaco', 'Courier New', monospace;
font-size: 12px;
line-height: 1.4;
border: none;
resize: none;
}
.log-textarea :deep(.ant-input:focus) {
box-shadow: none;
}
</style>

View File

@@ -9,6 +9,9 @@ import zhCN from 'ant-design-vue/es/locale/zh_CN'
import dayjs from 'dayjs'
import 'dayjs/locale/zh-cn'
// 导入日志系统
import { logger } from '@/utils/logger'
// 配置dayjs中文本地化
dayjs.locale('zh-cn')
@@ -17,6 +20,10 @@ import { API_ENDPOINTS } from '@/config/mirrors'
// 配置API基础URL
OpenAPI.BASE = API_ENDPOINTS.local
// 记录应用启动
logger.info('前端应用开始初始化')
logger.info(`API基础URL: ${OpenAPI.BASE}`)
// 创建应用实例
const app = createApp(App)
@@ -24,5 +31,12 @@ const app = createApp(App)
app.use(Antd)
app.use(router)
// 全局错误处理
app.config.errorHandler = (err, instance, info) => {
logger.error('Vue应用错误:', err, '组件信息:', info)
}
// 挂载应用
app.mount('#app')
logger.info('前端应用初始化完成')

View File

@@ -1,28 +1,38 @@
export interface ElectronAPI {
openDevTools: () => Promise<void>
selectFolder: () => Promise<string | null>
selectFile: (filters?: any[]) => Promise<string | null>
selectFile: (filters?: any[]) => Promise<string[]>
openUrl: (url: string) => Promise<{ success: boolean; error?: string }>
// 初始化相关API
checkEnvironment: () => Promise<{
pythonExists: boolean
gitExists: boolean
backendExists: boolean
dependenciesInstalled: boolean
isInitialized: boolean
}>
downloadPython: (mirror?: string) => Promise<{ success: boolean; error?: string }>
downloadGit: () => Promise<{ success: boolean; error?: string }>
installDependencies: (mirror?: string) => Promise<{ success: boolean; error?: string }>
cloneBackend: (repoUrl?: string) => Promise<{ success: boolean; error?: string }>
updateBackend: (repoUrl?: string) => Promise<{ success: boolean; error?: string }>
startBackend: () => Promise<{ success: boolean; error?: string }>
checkEnvironment: () => Promise<any>
downloadPython: (mirror?: string) => Promise<any>
installPip: () => Promise<any>
downloadGit: () => Promise<any>
installDependencies: (mirror?: string) => Promise<any>
cloneBackend: (repoUrl?: string) => Promise<any>
updateBackend: (repoUrl?: string) => Promise<any>
startBackend: () => Promise<any>
// 管理员权限相关
checkAdmin: () => Promise<boolean>
restartAsAdmin: () => Promise<void>
// 配置文件操作
saveConfig: (config: any) => Promise<void>
loadConfig: () => Promise<any>
resetConfig: () => Promise<void>
// 日志文件操作
getLogPath: () => Promise<string>
getLogs: (lines?: number) => Promise<string>
clearLogs: () => Promise<void>
cleanOldLogs: (daysToKeep?: number) => Promise<void>
// 保留原有方法以兼容现有代码
saveLogsToFile: (logs: string) => Promise<void>
loadLogsFromFile: () => Promise<string | null>
// 监听下载进度
onDownloadProgress: (callback: (progress: any) => void) => void
removeDownloadProgressListener: () => void

View File

@@ -0,0 +1,89 @@
// 渲染进程日志工具
interface ElectronAPI {
getLogPath: () => Promise<string>
getLogs: (lines?: number) => Promise<string>
clearLogs: () => Promise<void>
cleanOldLogs: (daysToKeep?: number) => Promise<void>
}
declare global {
interface Window {
electronAPI: ElectronAPI
}
}
export enum LogLevel {
DEBUG = 'DEBUG',
INFO = 'INFO',
WARN = 'WARN',
ERROR = 'ERROR'
}
class Logger {
// 直接使用原生console主进程会自动处理日志记录
debug(message: string, ...args: any[]) {
console.debug(message, ...args)
}
info(message: string, ...args: any[]) {
console.info(message, ...args)
}
warn(message: string, ...args: any[]) {
console.warn(message, ...args)
}
error(message: string, ...args: any[]) {
console.error(message, ...args)
}
// 获取日志文件路径
async getLogPath(): Promise<string> {
if (window.electronAPI) {
return await window.electronAPI.getLogPath()
}
throw new Error('Electron API not available')
}
// 获取日志内容
async getLogs(lines?: number): Promise<string> {
if (window.electronAPI) {
return await window.electronAPI.getLogs(lines)
}
throw new Error('Electron API not available')
}
// 清空日志
async clearLogs(): Promise<void> {
if (window.electronAPI) {
await window.electronAPI.clearLogs()
console.info('日志已清空')
} else {
throw new Error('Electron API not available')
}
}
// 清理旧日志
async cleanOldLogs(daysToKeep: number = 7): Promise<void> {
if (window.electronAPI) {
await window.electronAPI.cleanOldLogs(daysToKeep)
console.info(`已清理${daysToKeep}天前的旧日志`)
} else {
throw new Error('Electron API not available')
}
}
}
// 创建全局日志实例
export const logger = new Logger()
// 捕获未处理的错误直接使用console主进程会处理日志记录
window.addEventListener('error', (event) => {
console.error('未处理的错误:', event.error?.message || event.message, event.error?.stack)
})
window.addEventListener('unhandledrejection', (event) => {
console.error('未处理的Promise拒绝:', event.reason)
})
export default logger