mirror of
https://github.com/jxxghp/MoviePilot-Frontend.git
synced 2026-09-04 23:18:44 +08:00
style: improve LoggingView table layout and styling, add refreshing indicator using LoadingBanner component
This commit is contained in:
@@ -22,8 +22,7 @@ async function imageLoaded() {
|
|||||||
|
|
||||||
// 链接打开新窗口
|
// 链接打开新窗口
|
||||||
function openLink() {
|
function openLink() {
|
||||||
if (props.message?.link)
|
if (props.message?.link) window.open(props.message.link, '_blank')
|
||||||
window.open(props.message.link, '_blank')
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 将note转换为json
|
// 将note转换为json
|
||||||
@@ -31,8 +30,7 @@ function noteToJson() {
|
|||||||
if (props.message?.note) {
|
if (props.message?.note) {
|
||||||
try {
|
try {
|
||||||
return JSON.parse(props.message.note)
|
return JSON.parse(props.message.note)
|
||||||
}
|
} catch (error) {
|
||||||
catch (error) {
|
|
||||||
console.error(error)
|
console.error(error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -41,23 +39,14 @@ function noteToJson() {
|
|||||||
|
|
||||||
// 将\n转换为html属性的换行符
|
// 将\n转换为html属性的换行符
|
||||||
function replaceNewLine(value: string) {
|
function replaceNewLine(value: string) {
|
||||||
if (!value)
|
if (!value) return ''
|
||||||
return ''
|
|
||||||
return value.replace(/\n/g, '<br/>')
|
return value.replace(/\n/g, '<br/>')
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<VCard
|
<div :width="props.width" :height="props.height" @click="openLink">
|
||||||
:width="props.width"
|
<div v-if="props.message?.image" class="relative text-center card-cover-blurred">
|
||||||
:height="props.height"
|
|
||||||
variant="tonal"
|
|
||||||
@click="openLink"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
v-if="props.message?.image"
|
|
||||||
class="relative text-center card-cover-blurred"
|
|
||||||
>
|
|
||||||
<VImg
|
<VImg
|
||||||
:src="props.message?.image"
|
:src="props.message?.image"
|
||||||
aspect-ratio="4/3"
|
aspect-ratio="4/3"
|
||||||
@@ -70,25 +59,16 @@ function replaceNewLine(value: string) {
|
|||||||
<VCardTitle v-if="props.message?.title" class="whitespace-break-spaces">
|
<VCardTitle v-if="props.message?.title" class="whitespace-break-spaces">
|
||||||
{{ props.message?.title }}
|
{{ props.message?.title }}
|
||||||
</VCardTitle>
|
</VCardTitle>
|
||||||
<VAlert
|
<div
|
||||||
v-if="props.message?.text && props.message?.action === 0"
|
v-if="props.message?.text && props.message?.action === 0"
|
||||||
variant="tonal"
|
class="rounded-md text-body-1 py-2 px-4 elevation-2 bg-primary text-white chat-right mb-1"
|
||||||
type="success"
|
|
||||||
>
|
>
|
||||||
<template #prepend />
|
<p class="mb-0">{{ props.message?.text }}</p>
|
||||||
{{ props.message?.text }}
|
</div>
|
||||||
</VAlert>
|
<VCardText v-if="props.message?.text && props.message?.action === 1" v-html="replaceNewLine(props.message?.text)" />
|
||||||
<VCardText
|
|
||||||
v-if="props.message?.text && props.message?.action === 1"
|
|
||||||
v-html="replaceNewLine(props.message?.text)"
|
|
||||||
/>
|
|
||||||
<VCardText v-if="props.message?.note">
|
<VCardText v-if="props.message?.note">
|
||||||
<VList>
|
<VList>
|
||||||
<VListItem
|
<VListItem v-for="(value, key) in noteToJson()" :key="key" two-line>
|
||||||
v-for="(value, key) in noteToJson()"
|
|
||||||
:key="key"
|
|
||||||
two-line
|
|
||||||
>
|
|
||||||
<VListItemTitle v-if="value.title_year" class="font-bold">
|
<VListItemTitle v-if="value.title_year" class="font-bold">
|
||||||
{{ key + 1 }}. {{ value.title_year }}
|
{{ key + 1 }}. {{ value.title_year }}
|
||||||
</VListItemTitle>
|
</VListItemTitle>
|
||||||
@@ -104,9 +84,11 @@ function replaceNewLine(value: string) {
|
|||||||
</VListItem>
|
</VListItem>
|
||||||
</VList>
|
</VList>
|
||||||
</VCardText>
|
</VCardText>
|
||||||
<div class="text-end">
|
</div>
|
||||||
<span v-if="props.message?.action === 0" class="text-sm italic me-2">{{ props.message?.userid }}</span>
|
<div class="text-end">
|
||||||
<span class="text-sm italic me-2">{{ formatDateDifference(props.message?.reg_time || props.message?.date || '') }}</span>
|
<span v-if="props.message?.action === 0" class="text-sm italic me-2">{{ props.message?.userid }}</span>
|
||||||
</div>
|
<span class="text-sm italic me-2">{{
|
||||||
</VCard>
|
formatDateDifference(props.message?.reg_time || props.message?.date || '')
|
||||||
|
}}</span>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -206,6 +206,7 @@ onMounted(() => {
|
|||||||
<VDialog v-if="netTestDialog" v-model="netTestDialog" max-width="35rem" max-height="85vh" scrollable>
|
<VDialog v-if="netTestDialog" v-model="netTestDialog" max-width="35rem" max-height="85vh" scrollable>
|
||||||
<VCard title="网络测试">
|
<VCard title="网络测试">
|
||||||
<DialogCloseBtn @click="netTestDialog = false" />
|
<DialogCloseBtn @click="netTestDialog = false" />
|
||||||
|
<VDivider />
|
||||||
<VCardText>
|
<VCardText>
|
||||||
<NetTestView />
|
<NetTestView />
|
||||||
</VCardText>
|
</VCardText>
|
||||||
@@ -234,6 +235,7 @@ onMounted(() => {
|
|||||||
</a>
|
</a>
|
||||||
</VCardTitle>
|
</VCardTitle>
|
||||||
</VCardItem>
|
</VCardItem>
|
||||||
|
<VDivider />
|
||||||
<VCardText>
|
<VCardText>
|
||||||
<LoggingView />
|
<LoggingView />
|
||||||
</VCardText>
|
</VCardText>
|
||||||
@@ -252,6 +254,7 @@ onMounted(() => {
|
|||||||
<VDialog v-if="systemTestDialog" v-model="systemTestDialog" max-width="35rem" max-height="85vh" scrollable>
|
<VDialog v-if="systemTestDialog" v-model="systemTestDialog" max-width="35rem" max-height="85vh" scrollable>
|
||||||
<VCard title="系统健康检查">
|
<VCard title="系统健康检查">
|
||||||
<DialogCloseBtn @click="systemTestDialog = false" />
|
<DialogCloseBtn @click="systemTestDialog = false" />
|
||||||
|
<VDivider />
|
||||||
<VCardText>
|
<VCardText>
|
||||||
<ModuleTestView />
|
<ModuleTestView />
|
||||||
</VCardText>
|
</VCardText>
|
||||||
@@ -267,23 +270,20 @@ onMounted(() => {
|
|||||||
>
|
>
|
||||||
<VCard title="消息中心">
|
<VCard title="消息中心">
|
||||||
<DialogCloseBtn @click="messageDialog = false" />
|
<DialogCloseBtn @click="messageDialog = false" />
|
||||||
|
<VDivider />
|
||||||
<VCardText ref="chatContainer">
|
<VCardText ref="chatContainer">
|
||||||
<MessageView @scroll="scrollMessageToEnd" />
|
<MessageView @scroll="scrollMessageToEnd" />
|
||||||
</VCardText>
|
</VCardText>
|
||||||
|
|
||||||
<VCardItem>
|
<VCardItem>
|
||||||
<VTextField
|
<VTextField
|
||||||
v-model="user_message"
|
v-model="user_message"
|
||||||
|
variant="solo"
|
||||||
placeholder="输入消息或命令"
|
placeholder="输入消息或命令"
|
||||||
outlined
|
|
||||||
hide-details
|
|
||||||
single-line
|
|
||||||
clearable
|
clearable
|
||||||
density="compact"
|
|
||||||
:disabled="sendButtonDisabled"
|
:disabled="sendButtonDisabled"
|
||||||
@keydown.enter="sendMessage"
|
@keydown.enter="sendMessage"
|
||||||
>
|
>
|
||||||
<template #append>
|
<template #append-inner>
|
||||||
<VBtn color="primary" :disabled="sendButtonDisabled" @click="sendMessage"> 发送 </VBtn>
|
<VBtn color="primary" :disabled="sendButtonDisabled" @click="sendMessage"> 发送 </VBtn>
|
||||||
</template>
|
</template>
|
||||||
</VTextField>
|
</VTextField>
|
||||||
|
|||||||
@@ -82,7 +82,14 @@ onBeforeUnmount(() => {
|
|||||||
<div v-else>
|
<div v-else>
|
||||||
<VTable class="table-rounded" hide-default-footer disable-sort>
|
<VTable class="table-rounded" hide-default-footer disable-sort>
|
||||||
<tbody>
|
<tbody>
|
||||||
<VDataTableVirtual :headers="headers" :items="extractLogDetails" height="100%" density="compact" hover>
|
<VDataTableVirtual
|
||||||
|
:headers="headers"
|
||||||
|
:items="extractLogDetails"
|
||||||
|
height="100%"
|
||||||
|
density="compact"
|
||||||
|
hover
|
||||||
|
hide-default-header
|
||||||
|
>
|
||||||
<template #item.level="{ item }">
|
<template #item.level="{ item }">
|
||||||
<VChip size="small" :color="getLogColor(item.level)" variant="elevated" v-text="item.level" />
|
<VChip size="small" :color="getLogColor(item.level)" variant="elevated" v-text="item.level" />
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -31,16 +31,13 @@ let eventSource: EventSource | null = null
|
|||||||
function startSSEMessager() {
|
function startSSEMessager() {
|
||||||
const token = store.state.auth.token
|
const token = store.state.auth.token
|
||||||
if (token) {
|
if (token) {
|
||||||
eventSource = new EventSource(
|
eventSource = new EventSource(`${import.meta.env.VITE_API_BASE_URL}system/message?token=${token}&role=user`)
|
||||||
`${import.meta.env.VITE_API_BASE_URL}system/message?token=${token}&role=user`,
|
|
||||||
)
|
|
||||||
|
|
||||||
eventSource.addEventListener('message', (event) => {
|
eventSource.addEventListener('message', event => {
|
||||||
const message = event.data
|
const message = event.data
|
||||||
if (message) {
|
if (message) {
|
||||||
const object = JSON.parse(message)
|
const object = JSON.parse(message)
|
||||||
if (compareTime(object.date, lastTime.value) <= 0)
|
if (compareTime(object.date, lastTime.value) <= 0) return
|
||||||
return
|
|
||||||
messages.value.push(object)
|
messages.value.push(object)
|
||||||
emit('scroll')
|
emit('scroll')
|
||||||
}
|
}
|
||||||
@@ -79,8 +76,7 @@ async function loadMessages({ done }: { done: any }) {
|
|||||||
page.value++
|
page.value++
|
||||||
// 完成
|
// 完成
|
||||||
done('ok')
|
done('ok')
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
// 没有新数据
|
// 没有新数据
|
||||||
done('empty')
|
done('empty')
|
||||||
}
|
}
|
||||||
@@ -88,24 +84,20 @@ async function loadMessages({ done }: { done: any }) {
|
|||||||
loading.value = false
|
loading.value = false
|
||||||
// 监听SSE消息
|
// 监听SSE消息
|
||||||
startSSEMessager()
|
startSSEMessager()
|
||||||
}
|
} catch (error) {
|
||||||
catch (error) {
|
|
||||||
console.error(error)
|
console.error(error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 比较yyyy-MM-dd HH:mm:ss时间大小
|
// 比较yyyy-MM-dd HH:mm:ss时间大小
|
||||||
function compareTime(time1: string, time2: string) {
|
function compareTime(time1: string, time2: string) {
|
||||||
if (!time1)
|
if (!time1) return -1
|
||||||
return -1
|
if (!time2) return 1
|
||||||
if (!time2)
|
|
||||||
return 1
|
|
||||||
return new Date(time1.replaceAll(/-/g, '/')).getTime() - new Date(time2.replaceAll(/-/g, '/')).getTime()
|
return new Date(time1.replaceAll(/-/g, '/')).getTime() - new Date(time2.replaceAll(/-/g, '/')).getTime()
|
||||||
}
|
}
|
||||||
|
|
||||||
onBeforeUnmount(() => {
|
onBeforeUnmount(() => {
|
||||||
if (eventSource)
|
if (eventSource) eventSource.close()
|
||||||
eventSource.close()
|
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -121,35 +113,18 @@ onBeforeUnmount(() => {
|
|||||||
<template #loading>
|
<template #loading>
|
||||||
<LoadingBanner />
|
<LoadingBanner />
|
||||||
</template>
|
</template>
|
||||||
<template #empty>
|
<template #empty> 没有更多数据 </template>
|
||||||
没有更多数据
|
|
||||||
</template>
|
|
||||||
<div>
|
<div>
|
||||||
<VRow
|
<div
|
||||||
v-for="(msg, index) in messages"
|
v-for="(msg, index) in messages"
|
||||||
:key="index"
|
:key="index"
|
||||||
:class="{
|
class="chat-group d-flex mb-8"
|
||||||
'justify-end': msg.action === 0,
|
:class="msg.action == 1 ? 'flex-row align-start' : 'flex-row-reverse align-end'"
|
||||||
'justify-start': msg.action === 1,
|
|
||||||
}"
|
|
||||||
>
|
>
|
||||||
<VCol
|
<div class="d-inline-flex flex-column" :class="msg.action == 1 ? 'align-start' : 'align-end'">
|
||||||
cols="10"
|
<MessageCard :message="msg" />
|
||||||
lg="6"
|
</div>
|
||||||
xl="4"
|
</div>
|
||||||
style="position: relative;"
|
|
||||||
>
|
|
||||||
<MessageCard
|
|
||||||
:message="msg"
|
|
||||||
/>
|
|
||||||
</VCol>
|
|
||||||
</VRow>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
v-if="messages.length === 0 && isLoaded && !loading"
|
|
||||||
class="w-full text-center flex flex-col items-center"
|
|
||||||
>
|
|
||||||
<span class="mb-3">当前没有消息</span>
|
|
||||||
</div>
|
</div>
|
||||||
</VInfiniteScroll>
|
</VInfiniteScroll>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
Reference in New Issue
Block a user