mirror of
https://github.com/jxxghp/MoviePilot-Frontend.git
synced 2026-06-08 09:09:57 +08:00
fix layout ui
This commit is contained in:
@@ -6,6 +6,7 @@ import VerticalNavLink from '@layouts/components/VerticalNavLink.vue'
|
||||
// Components
|
||||
import Footer from '@/layouts/components/Footer.vue'
|
||||
import NavbarThemeSwitcher from '@/layouts/components/NavbarThemeSwitcher.vue'
|
||||
import UserNofification from '@/layouts/components/UserNotification.vue'
|
||||
import SearchBar from '@/layouts/components/SearchBar.vue'
|
||||
import ShortcutBar from '@/layouts/components/ShortcutBar.vue'
|
||||
import UserProfile from '@/layouts/components/UserProfile.vue'
|
||||
@@ -34,7 +35,10 @@ const superUser = store.state.auth.superUser
|
||||
<ShortcutBar v-if="superUser" />
|
||||
|
||||
<!-- 👉 Theme -->
|
||||
<NavbarThemeSwitcher class="me-2" />
|
||||
<NavbarThemeSwitcher />
|
||||
|
||||
<!-- 👉 Notification -->
|
||||
<UserNofification />
|
||||
|
||||
<!-- 👉 UserProfile -->
|
||||
<UserProfile />
|
||||
|
||||
@@ -2,25 +2,29 @@
|
||||
import type { ThemeSwitcherTheme } from '@layouts/types'
|
||||
|
||||
const themes: ThemeSwitcherTheme[] = [
|
||||
{
|
||||
name: 'auto',
|
||||
title: '跟随系统',
|
||||
icon: 'mdi-laptop',
|
||||
},
|
||||
{
|
||||
name: 'light',
|
||||
title: '明亮',
|
||||
icon: 'mdi-weather-sunny',
|
||||
},
|
||||
{
|
||||
name: 'dark',
|
||||
title: '暗黑',
|
||||
icon: 'mdi-weather-night',
|
||||
},
|
||||
{
|
||||
name: 'purple',
|
||||
title: '紫韵幽兰',
|
||||
icon: 'mdi-brightness-4',
|
||||
},
|
||||
{
|
||||
name: 'auto',
|
||||
icon: 'mdi-brightness-auto',
|
||||
},
|
||||
]
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ThemeSwitcher :themes="themes" />
|
||||
<ThemeSwitcher class="ms-2" :themes="themes" />
|
||||
</template>
|
||||
|
||||
@@ -91,7 +91,7 @@ onMounted(() => {
|
||||
>
|
||||
<!-- Menu Activator -->
|
||||
<template #activator="{ props }">
|
||||
<IconBtn class="me-2" v-bind="props">
|
||||
<IconBtn class="ms-2" v-bind="props">
|
||||
<VIcon icon="mdi-checkbox-multiple-blank-outline" />
|
||||
</IconBtn>
|
||||
</template>
|
||||
|
||||
102
src/layouts/components/UserNotification.vue
Normal file
102
src/layouts/components/UserNotification.vue
Normal file
@@ -0,0 +1,102 @@
|
||||
<script setup lang="ts">
|
||||
import store from '@/store'
|
||||
import { formatDateDifference } from '@core/utils/formatters'
|
||||
import { SystemNotification } from '@/api/types'
|
||||
|
||||
// 是否有新消息
|
||||
const hasNewMessage = ref(false)
|
||||
|
||||
// 通知列表
|
||||
const notificationList = ref<SystemNotification[]>([])
|
||||
|
||||
// 事件源
|
||||
let eventSource: EventSource | null = null
|
||||
|
||||
// 弹窗
|
||||
const appsMenu = ref(false)
|
||||
|
||||
// SSE持续接收消息
|
||||
function startSSEMessager() {
|
||||
const token = store.state.auth.token
|
||||
if (token) {
|
||||
eventSource = new EventSource(`${import.meta.env.VITE_API_BASE_URL}system/message?token=${token}`)
|
||||
eventSource.addEventListener('message', event => {
|
||||
if (event.data) {
|
||||
const noti: SystemNotification = JSON.parse(event.data)
|
||||
notificationList.value.unshift(noti)
|
||||
hasNewMessage.value = true
|
||||
// TODO 在顶部显示消息汽泡
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 页面加载时,加载当前用户数据
|
||||
onBeforeMount(async () => {
|
||||
startSSEMessager()
|
||||
})
|
||||
|
||||
// 页面卸载时,关闭事件源
|
||||
onBeforeUnmount(() => {
|
||||
if (eventSource) eventSource.close()
|
||||
})
|
||||
</script>
|
||||
<template>
|
||||
<VMenu v-model="appsMenu" width="400" transition="scale-transition" close-on-content-click>
|
||||
<!-- Menu Activator -->
|
||||
<template #activator="{ props }">
|
||||
<VBadge v-if="hasNewMessage" dot color="error" :offset-x="5" :offset-y="5" v-bind="props">
|
||||
<IconBtn>
|
||||
<VIcon icon="mdi-bell-outline" />
|
||||
</IconBtn>
|
||||
</VBadge>
|
||||
<IconBtn v-else v-bind="props">
|
||||
<VIcon icon="mdi-bell-outline" />
|
||||
</IconBtn>
|
||||
</template>
|
||||
<!-- Menu Content -->
|
||||
<VCard>
|
||||
<VCardItem class="border-b">
|
||||
<VCardTitle>通知</VCardTitle>
|
||||
<template #append>
|
||||
<VTooltip text="设为已读">
|
||||
<template #activator="{ props }">
|
||||
<IconBtn
|
||||
v-bind="props"
|
||||
@click="
|
||||
() => {
|
||||
hasNewMessage = false
|
||||
appsMenu = false
|
||||
}
|
||||
"
|
||||
>
|
||||
<VIcon icon="mdi-email-mark-as-unread" />
|
||||
</IconBtn>
|
||||
</template>
|
||||
</VTooltip>
|
||||
</template>
|
||||
</VCardItem>
|
||||
<VList lines="two" v-if="notificationList.length > 0" max-height="600">
|
||||
<VListItem v-for="(item, i) in notificationList" :key="i">
|
||||
<template #prepend>
|
||||
<VAvatar rounded>
|
||||
<VIcon v-if="item.type === 'user'" icon="mdi-account-alert" size="large"></VIcon>
|
||||
<VIcon v-else-if="item.type === 'plugin'" icon="mdi-robot-happy" size="large"></VIcon>
|
||||
<VIcon v-else icon="mdi-laptop" size="large"></VIcon>
|
||||
</VAvatar>
|
||||
</template>
|
||||
<VListItemTitle class="overflow-visiable break-words whitespace-break-spaces">
|
||||
{{ item.title }}
|
||||
</VListItemTitle>
|
||||
<VListItemSubtitle class="mt-2">{{ item.text }}</VListItemSubtitle>
|
||||
<VListItemSubtitle class="mt-2">{{ formatDateDifference(item.date) }}</VListItemSubtitle>
|
||||
</VListItem>
|
||||
</VList>
|
||||
<VList v-else>
|
||||
<VListItem>
|
||||
<VListItemTitle class="text-center">暂无通知</VListItemTitle>
|
||||
</VListItem>
|
||||
</VList>
|
||||
</VCard>
|
||||
</VMenu>
|
||||
</template>
|
||||
@@ -64,7 +64,7 @@ const avatar = store.state.auth.avatar
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<VAvatar class="cursor-pointer" color="primary" variant="tonal">
|
||||
<VAvatar class="cursor-pointer ms-3" color="primary" variant="tonal">
|
||||
<VImg :src="avatar ?? avatar1" />
|
||||
|
||||
<!-- SECTION Menu -->
|
||||
@@ -92,10 +92,17 @@ const avatar = store.state.auth.avatar
|
||||
<template #prepend>
|
||||
<VIcon class="me-2" icon="mdi-account-outline" size="22" />
|
||||
</template>
|
||||
|
||||
<VListItemTitle>设定</VListItemTitle>
|
||||
</VListItem>
|
||||
|
||||
<!-- 👉 FAQ -->
|
||||
<VListItem href="https://github.com/jxxghp/MoviePilot/blob/main/README.md" target="_blank">
|
||||
<template #prepend>
|
||||
<VIcon class="me-2" icon="mdi-help-circle-outline" size="22" />
|
||||
</template>
|
||||
<VListItemTitle>帮助</VListItemTitle>
|
||||
</VListItem>
|
||||
|
||||
<!-- Divider -->
|
||||
<VDivider class="my-2" />
|
||||
|
||||
@@ -104,26 +111,18 @@ const avatar = store.state.auth.avatar
|
||||
<template #prepend>
|
||||
<VIcon class="me-2" icon="mdi-restart" size="22" />
|
||||
</template>
|
||||
|
||||
<VListItemTitle>重启</VListItemTitle>
|
||||
</VListItem>
|
||||
|
||||
<!-- 👉 FAQ -->
|
||||
<VListItem href="https://github.com/jxxghp/MoviePilot/blob/main/README.md" target="_blank">
|
||||
<template #prepend>
|
||||
<VIcon class="me-2" icon="mdi-help-circle-outline" size="22" />
|
||||
</template>
|
||||
|
||||
<VListItemTitle>帮助</VListItemTitle>
|
||||
</VListItem>
|
||||
<!-- Divider -->
|
||||
<VDivider class="my-2" />
|
||||
|
||||
<!-- 👉 Logout -->
|
||||
<VListItem @click="logout">
|
||||
<template #prepend>
|
||||
<VIcon class="me-2" icon="mdi-logout" size="22" />
|
||||
</template>
|
||||
|
||||
<VListItemTitle>注销</VListItemTitle>
|
||||
<VBtn color="error" block>
|
||||
<template #append> <VIcon size="small" icon="mdi-logout" /> </template>
|
||||
退出登录
|
||||
</VBtn>
|
||||
</VListItem>
|
||||
</VList>
|
||||
</VMenu>
|
||||
|
||||
Reference in New Issue
Block a user