style: improve LoggingView table layout and styling, add refreshing indicator using LoadingBanner component

This commit is contained in:
jxxghp
2024-07-14 18:48:36 +08:00
parent 0a34e07cc5
commit 6136095e0f
4 changed files with 48 additions and 84 deletions
+18 -36
View File
@@ -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>
+6 -6
View File
@@ -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>
+8 -1
View File
@@ -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>
+16 -41
View File
@@ -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>