mirror of
https://github.com/jxxghp/MoviePilot-Frontend.git
synced 2026-09-05 07:28:37 +08:00
feat 实时日志放快捷栏&&美化日志界面
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import NameTestView from '@/views/system/NameTestView.vue'
|
import NameTestView from '@/views/system/NameTestView.vue'
|
||||||
import NetTestView from '@/views/system/NetTestView.vue'
|
import NetTestView from '@/views/system/NetTestView.vue'
|
||||||
|
import LoggingView from '@/views/system/LoggingView.vue'
|
||||||
|
|
||||||
// App捷径
|
// App捷径
|
||||||
const appsMenu = ref(false)
|
const appsMenu = ref(false)
|
||||||
@@ -10,6 +11,9 @@ const nameTestDialog = ref(false)
|
|||||||
|
|
||||||
// 网络测试弹窗
|
// 网络测试弹窗
|
||||||
const netTestDialog = ref(false)
|
const netTestDialog = ref(false)
|
||||||
|
|
||||||
|
// 实时日志弹窗
|
||||||
|
const loggingDialog = ref(false)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -86,6 +90,29 @@ const netTestDialog = ref(false)
|
|||||||
</VListItem>
|
</VListItem>
|
||||||
</VCol>
|
</VCol>
|
||||||
</VRow>
|
</VRow>
|
||||||
|
<VRow class="ma-0 mt-n1 border-t">
|
||||||
|
<VCol
|
||||||
|
cols="6"
|
||||||
|
class="text-center cursor-pointer pa-0 shortcut-icon border-e"
|
||||||
|
@click="() => {}"
|
||||||
|
>
|
||||||
|
<VListItem
|
||||||
|
class="pa-4"
|
||||||
|
@click="loggingDialog = true"
|
||||||
|
>
|
||||||
|
<VAvatar
|
||||||
|
size="48"
|
||||||
|
variant="tonal"
|
||||||
|
>
|
||||||
|
<VIcon icon="mdi-file-document-outline" />
|
||||||
|
</VAvatar>
|
||||||
|
<h6 class="text-base font-weight-medium mt-2 mb-0">
|
||||||
|
日志
|
||||||
|
</h6>
|
||||||
|
<span class="text-sm">系统实时日志</span>
|
||||||
|
</VListItem>
|
||||||
|
</VCol>
|
||||||
|
</VRow>
|
||||||
</div>
|
</div>
|
||||||
</VCard>
|
</VCard>
|
||||||
</VMenu>
|
</VMenu>
|
||||||
@@ -113,4 +140,17 @@ const netTestDialog = ref(false)
|
|||||||
</VCardItem>
|
</VCardItem>
|
||||||
</VCard>
|
</VCard>
|
||||||
</VDialog>
|
</VDialog>
|
||||||
|
<!-- 实时日志弹窗 -->
|
||||||
|
<VDialog
|
||||||
|
v-model="loggingDialog"
|
||||||
|
max-width="1280"
|
||||||
|
scrollable
|
||||||
|
>
|
||||||
|
<VCard title="实时日志">
|
||||||
|
<DialogCloseBtn @click="loggingDialog = false" />
|
||||||
|
<VCardText>
|
||||||
|
<LoggingView />
|
||||||
|
</VCardText>
|
||||||
|
</VCard>
|
||||||
|
</VDialog>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import AccountSettingNotification from '@/views/setting/AccountSettingNotificati
|
|||||||
import AccountSettingRule from '@/views/setting/AccountSettingRule.vue'
|
import AccountSettingRule from '@/views/setting/AccountSettingRule.vue'
|
||||||
import AccountSettingSite from '@/views/setting/AccountSettingSite.vue'
|
import AccountSettingSite from '@/views/setting/AccountSettingSite.vue'
|
||||||
import AccountSettingWords from '@/views/setting/AccountSettingWords.vue'
|
import AccountSettingWords from '@/views/setting/AccountSettingWords.vue'
|
||||||
import AccountSettingLogging from '@/views/setting/AccountSettingLogging.vue'
|
|
||||||
import AccountSettingAbout from '@/views/setting/AccountSettingAbout.vue'
|
import AccountSettingAbout from '@/views/setting/AccountSettingAbout.vue'
|
||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
@@ -39,11 +38,6 @@ const tabs = [
|
|||||||
icon: 'mdi-file-word-box',
|
icon: 'mdi-file-word-box',
|
||||||
tab: 'words',
|
tab: 'words',
|
||||||
},
|
},
|
||||||
{
|
|
||||||
title: '日志',
|
|
||||||
icon: 'mdi-text-box',
|
|
||||||
tab: 'logging',
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
title: '关于',
|
title: '关于',
|
||||||
icon: 'mdi-information',
|
icon: 'mdi-information',
|
||||||
@@ -96,12 +90,6 @@ const tabs = [
|
|||||||
<AccountSettingWords />
|
<AccountSettingWords />
|
||||||
</transition>
|
</transition>
|
||||||
</VWindowItem>
|
</VWindowItem>
|
||||||
<!-- Logging -->
|
|
||||||
<VWindowItem value="logging">
|
|
||||||
<transition name="fade-slide" appear>
|
|
||||||
<AccountSettingLogging />
|
|
||||||
</transition>
|
|
||||||
</VWindowItem>
|
|
||||||
<!-- About -->
|
<!-- About -->
|
||||||
<VWindowItem value="about">
|
<VWindowItem value="about">
|
||||||
<transition name="fade-slide" appear>
|
<transition name="fade-slide" appear>
|
||||||
|
|||||||
@@ -1,55 +0,0 @@
|
|||||||
<script lang="ts" setup>
|
|
||||||
import store from '@/store'
|
|
||||||
|
|
||||||
// 日志列表
|
|
||||||
const logs = ref<string[]>([])
|
|
||||||
|
|
||||||
// SSE持续获取日志
|
|
||||||
function startSSELogging() {
|
|
||||||
const token = store.state.auth.token
|
|
||||||
if (token) {
|
|
||||||
const eventSource = new EventSource(
|
|
||||||
`${import.meta.env.VITE_API_BASE_URL}system/logging?token=${token}`,
|
|
||||||
)
|
|
||||||
|
|
||||||
eventSource.addEventListener('message', (event) => {
|
|
||||||
const message = event.data
|
|
||||||
if (message)
|
|
||||||
logs.value.push(message)
|
|
||||||
})
|
|
||||||
|
|
||||||
onBeforeUnmount(() => {
|
|
||||||
eventSource.close()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
startSSELogging()
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<VRow>
|
|
||||||
<VCol cols="12">
|
|
||||||
<VCard title="实时日志">
|
|
||||||
<VCardText>
|
|
||||||
<div
|
|
||||||
v-if="logs.length === 0"
|
|
||||||
class="mt-5 w-full text-center text-gray-500 text-sm flex flex-col items-center"
|
|
||||||
>
|
|
||||||
<VProgressCircular
|
|
||||||
size="48"
|
|
||||||
indeterminate
|
|
||||||
color="primary"
|
|
||||||
/>
|
|
||||||
<span class="mt-3">正在刷新 ...</span>
|
|
||||||
</div>
|
|
||||||
<div v-for="(log, i) in logs" :key="i">
|
|
||||||
{{ log }}
|
|
||||||
</div>
|
|
||||||
</VCardText>
|
|
||||||
</VCard>
|
|
||||||
</VCol>
|
|
||||||
</VRow>
|
|
||||||
</template>
|
|
||||||
@@ -0,0 +1,119 @@
|
|||||||
|
<script lang="ts" setup>
|
||||||
|
import store from '@/store'
|
||||||
|
|
||||||
|
// 日志列表
|
||||||
|
const logs = ref<string[]>([])
|
||||||
|
|
||||||
|
// SSE持续获取日志
|
||||||
|
function startSSELogging() {
|
||||||
|
const token = store.state.auth.token
|
||||||
|
if (token) {
|
||||||
|
const eventSource = new EventSource(
|
||||||
|
`${import.meta.env.VITE_API_BASE_URL}system/logging?token=${token}`,
|
||||||
|
)
|
||||||
|
|
||||||
|
eventSource.addEventListener('message', (event) => {
|
||||||
|
const message = event.data
|
||||||
|
if (message)
|
||||||
|
logs.value.push(message)
|
||||||
|
})
|
||||||
|
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
eventSource.close()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 从日志中提取日志详情
|
||||||
|
function extractLogDetailsFromLogs(logs: string[]): { level: string; time: string; program: string; content: string }[] {
|
||||||
|
const logDetails: { level: string; time: string; program: string; content: string }[] = []
|
||||||
|
|
||||||
|
const logPattern = /^【(.*?)】.*\s(.*?)\s-\s(.*?)\s-\s(.*)$/
|
||||||
|
|
||||||
|
for (const log of logs) {
|
||||||
|
const matches = RegExp(logPattern).exec(log)
|
||||||
|
if (matches && matches.length === 5) {
|
||||||
|
const [_, level, time, program, content] = matches
|
||||||
|
logDetails.push({ level, time, program, content })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return logDetails
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算日志颜色
|
||||||
|
function getLogColor(level: string): string {
|
||||||
|
switch (level) {
|
||||||
|
case 'DEBUG':
|
||||||
|
return 'primary'
|
||||||
|
case 'INFO':
|
||||||
|
return 'secondary'
|
||||||
|
case 'WARNING':
|
||||||
|
return 'warning'
|
||||||
|
case 'ERROR':
|
||||||
|
return 'error'
|
||||||
|
default:
|
||||||
|
return 'secondary'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 拆分日志数据计算属性
|
||||||
|
const extractLogDetails = computed(() => {
|
||||||
|
return extractLogDetailsFromLogs(logs.value)
|
||||||
|
})
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
startSSELogging()
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div
|
||||||
|
v-if="logs.length === 0"
|
||||||
|
class="mt-5 w-full text-centerflex flex-col items-center"
|
||||||
|
>
|
||||||
|
<VProgressCircular
|
||||||
|
size="48"
|
||||||
|
indeterminate
|
||||||
|
color="primary"
|
||||||
|
/>
|
||||||
|
<span class="mt-3">正在刷新 ...</span>
|
||||||
|
</div>
|
||||||
|
<VTable
|
||||||
|
class="table-rounded"
|
||||||
|
hide-default-footer
|
||||||
|
disable-sort
|
||||||
|
>
|
||||||
|
<tbody>
|
||||||
|
<tr v-for="(log, i) in extractLogDetails" :key="i" class="text-sm">
|
||||||
|
<td
|
||||||
|
class="text-sm"
|
||||||
|
>
|
||||||
|
<VChip
|
||||||
|
size="small"
|
||||||
|
:color="getLogColor(log.level)"
|
||||||
|
variant="elevated"
|
||||||
|
v-text="log.level"
|
||||||
|
/>
|
||||||
|
</td>
|
||||||
|
<!-- name -->
|
||||||
|
<td
|
||||||
|
class="text-sm"
|
||||||
|
>
|
||||||
|
{{ log.time }}
|
||||||
|
</td>
|
||||||
|
<td
|
||||||
|
class="text-sm"
|
||||||
|
>
|
||||||
|
<h6 class="text-sm font-weight-medium">
|
||||||
|
{{ log.program }}
|
||||||
|
</h6>
|
||||||
|
</td>
|
||||||
|
<td
|
||||||
|
class="text-sm"
|
||||||
|
v-text="log.content"
|
||||||
|
/>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</VTable>
|
||||||
|
</template>
|
||||||
Reference in New Issue
Block a user