mirror of
https://github.com/jxxghp/MoviePilot-Frontend.git
synced 2026-06-07 16:49:52 +08:00
统一顶栏及侧边栏各项ui风格
This commit is contained in:
@@ -15,6 +15,9 @@ const display = useDisplay()
|
||||
// App捷径
|
||||
const appsMenu = ref(false)
|
||||
|
||||
// 菜单最大宽度
|
||||
const menuMaxWidth = ref(480)
|
||||
|
||||
// 名称测试弹窗
|
||||
const nameTestDialog = ref(false)
|
||||
|
||||
@@ -40,14 +43,13 @@ const user_message = ref('')
|
||||
const sendButtonDisabled = ref(false)
|
||||
|
||||
// 聊天容器
|
||||
const chatContainer = ref<HTMLDivElement>()
|
||||
const chatContainer = ref<HTMLElement>()
|
||||
|
||||
// 滚动到底部
|
||||
function scrollMessageToEnd() {
|
||||
nextTick(() => {
|
||||
if (chatContainer.value) {
|
||||
const scrollDiv = chatContainer.value.$el
|
||||
scrollDiv.scrollTop = scrollDiv.scrollHeight
|
||||
chatContainer.value.scrollTop = chatContainer.value.scrollHeight
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -103,91 +105,99 @@ onMounted(() => {
|
||||
<template>
|
||||
<VMenu
|
||||
v-model="appsMenu"
|
||||
max-width="600"
|
||||
width="340"
|
||||
:max-width="menuMaxWidth"
|
||||
width="100%"
|
||||
max-height="560"
|
||||
location="top end"
|
||||
origin="top end"
|
||||
transition="scale-transition"
|
||||
close-on-content-click
|
||||
:close-on-content-click="false"
|
||||
:close-on-back="true"
|
||||
>
|
||||
<!-- Menu Activator -->
|
||||
<template #activator="{ props }">
|
||||
<IconBtn class="ms-2" v-bind="props">
|
||||
<VIcon icon="mdi-checkbox-multiple-blank-outline" />
|
||||
<VIcon icon="mdi-apps" />
|
||||
</IconBtn>
|
||||
</template>
|
||||
<!-- Menu Content -->
|
||||
<VCard elevation="1">
|
||||
<VCardItem class="border-b">
|
||||
<VCardTitle>捷径</VCardTitle>
|
||||
<VCard elevation="1" class="shortcut-menu-card">
|
||||
<VCardItem class="shortcut-header border-b">
|
||||
<VCardTitle class="font-weight-medium text-primary">捷径</VCardTitle>
|
||||
<template #append>
|
||||
<IconBtn @click="() => {}">
|
||||
<VIcon icon="mdi-checkbox-multiple-blank-outline" />
|
||||
<IconBtn @click="appsMenu = false" class="shortcut-close-btn">
|
||||
<VIcon icon="mdi-close" />
|
||||
</IconBtn>
|
||||
</template>
|
||||
</VCardItem>
|
||||
<div class="ps ps--active-y">
|
||||
<VRow class="ma-0 mt-n1">
|
||||
<VCol cols="6" class="text-center cursor-pointer pa-0 shortcut-icon border-e">
|
||||
<VListItem class="pa-4" @click="nameTestDialog = true">
|
||||
<VAvatar size="48" variant="tonal">
|
||||
<VIcon icon="mdi-text-recognition" />
|
||||
</VAvatar>
|
||||
<h6 class="text-base font-weight-medium mt-2 mb-0">识别</h6>
|
||||
<span class="text-sm">名称识别测试</span>
|
||||
</VListItem>
|
||||
</VCol>
|
||||
<VCol cols="6" class="text-center cursor-pointer pa-0 shortcut-icon" @click="() => {}">
|
||||
<VListItem class="pa-4" @click="ruleTestDialog = true">
|
||||
<VAvatar size="48" variant="tonal">
|
||||
<VIcon icon="mdi-filter-cog-outline" />
|
||||
</VAvatar>
|
||||
<h6 class="text-base font-weight-medium mt-2 mb-0">规则</h6>
|
||||
<span class="text-sm">规则测试</span>
|
||||
</VListItem>
|
||||
</VCol>
|
||||
</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>
|
||||
<VCol cols="6" class="text-center cursor-pointer pa-0 shortcut-icon" @click="() => {}">
|
||||
<VListItem class="pa-4" @click="netTestDialog = true">
|
||||
<VAvatar size="48" variant="tonal">
|
||||
<VIcon icon="mdi-network-outline" />
|
||||
</VAvatar>
|
||||
<h6 class="text-base font-weight-medium mt-2 mb-0">网络</h6>
|
||||
<span class="text-sm">网速连通性测试</span>
|
||||
</VListItem>
|
||||
</VCol>
|
||||
</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="systemTestDialog = true">
|
||||
<VAvatar size="48" variant="tonal">
|
||||
<VIcon icon="mdi-cog-outline" />
|
||||
</VAvatar>
|
||||
<h6 class="text-base font-weight-medium mt-2 mb-0">系统</h6>
|
||||
<span class="text-sm">健康检查</span>
|
||||
</VListItem>
|
||||
</VCol>
|
||||
<VCol cols="6" class="text-center cursor-pointer pa-0 shortcut-icon" @click="() => {}">
|
||||
<VListItem class="pa-4" @click="messageDialog = true">
|
||||
<VAvatar size="48" variant="tonal">
|
||||
<VIcon icon="mdi-message-outline" />
|
||||
</VAvatar>
|
||||
<h6 class="text-base font-weight-medium mt-2 mb-0">消息</h6>
|
||||
<span class="text-sm">消息中心</span>
|
||||
</VListItem>
|
||||
</VCol>
|
||||
</VRow>
|
||||
<div class="ps ps--active-y shortcut-menu-container">
|
||||
<div class="shortcut-grid">
|
||||
<!-- 识别 -->
|
||||
<div class="shortcut-item" @click="nameTestDialog = true">
|
||||
<div class="shortcut-icon-wrapper">
|
||||
<VIcon icon="mdi-text-recognition" size="24" />
|
||||
</div>
|
||||
<div class="shortcut-text">
|
||||
<div class="shortcut-title">识别</div>
|
||||
<div class="shortcut-subtitle">名称识别测试</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 规则 -->
|
||||
<div class="shortcut-item" @click="ruleTestDialog = true">
|
||||
<div class="shortcut-icon-wrapper">
|
||||
<VIcon icon="mdi-filter-cog" size="24" />
|
||||
</div>
|
||||
<div class="shortcut-text">
|
||||
<div class="shortcut-title">规则</div>
|
||||
<div class="shortcut-subtitle">规则测试</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 日志 -->
|
||||
<div class="shortcut-item" @click="loggingDialog = true">
|
||||
<div class="shortcut-icon-wrapper">
|
||||
<VIcon icon="mdi-file-document" size="24" />
|
||||
</div>
|
||||
<div class="shortcut-text">
|
||||
<div class="shortcut-title">日志</div>
|
||||
<div class="shortcut-subtitle">实时日志</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 网络 -->
|
||||
<div class="shortcut-item" @click="netTestDialog = true">
|
||||
<div class="shortcut-icon-wrapper">
|
||||
<VIcon icon="mdi-network" size="24" />
|
||||
</div>
|
||||
<div class="shortcut-text">
|
||||
<div class="shortcut-title">网络</div>
|
||||
<div class="shortcut-subtitle">网速连通性测试</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 系统 -->
|
||||
<div class="shortcut-item" @click="systemTestDialog = true">
|
||||
<div class="shortcut-icon-wrapper">
|
||||
<VIcon icon="mdi-cog" size="24" />
|
||||
</div>
|
||||
<div class="shortcut-text">
|
||||
<div class="shortcut-title">系统</div>
|
||||
<div class="shortcut-subtitle">健康检查</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 消息 -->
|
||||
<div class="shortcut-item" @click="messageDialog = true">
|
||||
<div class="shortcut-icon-wrapper">
|
||||
<VIcon icon="mdi-message" size="24" />
|
||||
</div>
|
||||
<div class="shortcut-text">
|
||||
<div class="shortcut-title">消息</div>
|
||||
<div class="shortcut-subtitle">消息中心</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</VCard>
|
||||
</VMenu>
|
||||
@@ -289,3 +299,119 @@ onMounted(() => {
|
||||
</VCard>
|
||||
</VDialog>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.shortcut-menu-card {
|
||||
border-radius: 16px;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 8px 30px rgba(var(--v-theme-on-surface), 0.12), 0 4px 12px rgba(var(--v-theme-on-surface), 0.08) !important;
|
||||
border: 1px solid rgba(var(--v-theme-on-surface), 0.05);
|
||||
}
|
||||
|
||||
.shortcut-header {
|
||||
background: linear-gradient(to right, rgba(var(--v-theme-primary), 0.04), rgba(var(--v-theme-primary), 0.01));
|
||||
border-bottom: 1px solid rgba(var(--v-theme-on-surface), 0.08);
|
||||
padding: 12px 16px;
|
||||
}
|
||||
|
||||
.shortcut-close-btn {
|
||||
transition: transform 0.3s ease;
|
||||
&:hover {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
}
|
||||
|
||||
.shortcut-menu-container {
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.shortcut-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.shortcut-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 16px;
|
||||
border-radius: 12px;
|
||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
cursor: pointer;
|
||||
background-color: rgba(var(--v-theme-surface));
|
||||
border: 1px solid rgba(var(--v-theme-on-surface), 0.05);
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
overflow: hidden;
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
z-index: -1;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: linear-gradient(135deg, rgba(var(--v-theme-primary), 0.08) 0%, rgba(var(--v-theme-primary), 0) 60%);
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s ease;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
transform: translateY(-4px);
|
||||
box-shadow: 0 6px 20px rgba(var(--v-theme-on-surface), 0.12);
|
||||
border-color: rgba(var(--v-theme-primary), 0.15);
|
||||
|
||||
&::before {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.shortcut-icon-wrapper {
|
||||
transform: scale(1.1);
|
||||
background-color: rgba(var(--v-theme-primary), 0.12);
|
||||
|
||||
.v-icon {
|
||||
transform: scale(1.2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&:active {
|
||||
transform: translateY(0);
|
||||
box-shadow: 0 3px 10px rgba(var(--v-theme-on-surface), 0.08);
|
||||
}
|
||||
}
|
||||
|
||||
.shortcut-icon-wrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
border-radius: 12px;
|
||||
background-color: rgba(var(--v-theme-primary), 0.08);
|
||||
margin-right: 16px;
|
||||
transition: all 0.3s ease;
|
||||
|
||||
.v-icon {
|
||||
transition: transform 0.3s ease;
|
||||
color: rgba(var(--v-theme-primary), 1);
|
||||
}
|
||||
}
|
||||
|
||||
.shortcut-text {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.shortcut-title {
|
||||
font-weight: 600;
|
||||
font-size: 1rem;
|
||||
margin-bottom: 4px;
|
||||
color: rgba(var(--v-theme-on-surface), 0.95);
|
||||
}
|
||||
|
||||
.shortcut-subtitle {
|
||||
font-size: 0.8rem;
|
||||
color: rgba(var(--v-theme-on-surface), 0.7);
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -39,8 +39,79 @@ onBeforeUnmount(() => {
|
||||
if (eventSource) eventSource.close()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.notification-header {
|
||||
border-bottom: 1px solid rgba(var(--v-theme-on-surface), 0.08);
|
||||
padding: 16px;
|
||||
|
||||
.v-card-title {
|
||||
font-size: 1.1rem;
|
||||
font-weight: 600;
|
||||
color: rgba(var(--v-theme-primary), 0.9);
|
||||
}
|
||||
}
|
||||
|
||||
.notification-list {
|
||||
max-height: 500px;
|
||||
overflow-y: auto;
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
.notification-item {
|
||||
border-radius: 12px;
|
||||
margin-bottom: 8px;
|
||||
transition: all 0.2s ease;
|
||||
border: 1px solid rgba(var(--v-theme-on-surface), 0.05);
|
||||
|
||||
&:hover {
|
||||
background-color: rgba(var(--v-theme-primary), 0.03);
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 8px rgba(var(--v-theme-on-surface), 0.06);
|
||||
}
|
||||
|
||||
.notification-avatar {
|
||||
background-color: rgba(var(--v-theme-primary), 0.1);
|
||||
box-shadow: 0 4px 8px rgba(var(--v-theme-primary), 0.15);
|
||||
}
|
||||
|
||||
.notification-title {
|
||||
font-weight: 600;
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
|
||||
.notification-text {
|
||||
font-size: 0.85rem;
|
||||
color: rgba(var(--v-theme-on-surface), 0.75);
|
||||
margin-top: 6px;
|
||||
}
|
||||
|
||||
.notification-time {
|
||||
font-size: 0.8rem;
|
||||
color: rgba(var(--v-theme-primary), 0.8);
|
||||
margin-top: 6px;
|
||||
}
|
||||
}
|
||||
|
||||
.no-notification {
|
||||
padding: 30px 0;
|
||||
color: rgba(var(--v-theme-on-surface), 0.6);
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
|
||||
.mark-read-btn {
|
||||
transition: all 0.2s ease;
|
||||
border-radius: 8px;
|
||||
|
||||
&:hover {
|
||||
background-color: rgba(var(--v-theme-primary), 0.1);
|
||||
transform: scale(1.05);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<template>
|
||||
<VMenu v-model="appsMenu" width="400" transition="scale-transition" close-on-content-click>
|
||||
<VMenu v-model="appsMenu" width="400" transition="scale-transition" close-on-content-click class="notification-menu">
|
||||
<!-- Menu Activator -->
|
||||
<template #activator="{ props }">
|
||||
<VBadge v-if="hasNewMessage" dot color="error" :offset-x="5" :offset-y="5" v-bind="props">
|
||||
@@ -53,13 +124,14 @@ onBeforeUnmount(() => {
|
||||
</IconBtn>
|
||||
</template>
|
||||
<!-- Menu Content -->
|
||||
<VCard elevation="1">
|
||||
<VCardItem class="border-b">
|
||||
<VCardTitle>通知</VCardTitle>
|
||||
<VCard elevation="0">
|
||||
<VCardItem class="notification-header">
|
||||
<VCardTitle>通知中心</VCardTitle>
|
||||
<template #append>
|
||||
<VTooltip text="设为已读">
|
||||
<template #activator="{ props }">
|
||||
<IconBtn
|
||||
class="mark-read-btn"
|
||||
v-bind="props"
|
||||
@click="
|
||||
() => {
|
||||
@@ -68,33 +140,36 @@ onBeforeUnmount(() => {
|
||||
}
|
||||
"
|
||||
>
|
||||
<VIcon icon="mdi-email-mark-as-unread" />
|
||||
<VIcon icon="mdi-email-check-outline" size="20" />
|
||||
</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">
|
||||
<div v-if="notificationList.length > 0" class="notification-list">
|
||||
<VListItem v-for="(item, i) in notificationList" :key="i" lines="two" class="notification-item">
|
||||
<template #prepend>
|
||||
<VAvatar rounded>
|
||||
<VAvatar rounded class="notification-avatar">
|
||||
<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-if="item.type === 'plugin'" icon="mdi-robot" 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>
|
||||
<div class="notification-content">
|
||||
<div class="notification-title overflow-visiable break-words whitespace-break-spaces">
|
||||
{{ item.title }}
|
||||
</div>
|
||||
<div class="notification-text">{{ item.text }}</div>
|
||||
<div class="notification-time">{{ formatDateDifference(item.date) }}</div>
|
||||
</div>
|
||||
</VListItem>
|
||||
</VList>
|
||||
<VList v-else>
|
||||
<VListItem>
|
||||
<VListItemTitle class="text-center">暂无通知</VListItemTitle>
|
||||
</VListItem>
|
||||
</VList>
|
||||
</div>
|
||||
<div v-else class="no-notification">
|
||||
<div class="text-center">
|
||||
<VIcon icon="mdi-bell-sleep-outline" size="40" class="mb-3 text-primary" />
|
||||
<div>暂无通知</div>
|
||||
</div>
|
||||
</div>
|
||||
</VCard>
|
||||
</VMenu>
|
||||
</template>
|
||||
|
||||
@@ -85,75 +85,84 @@ const userLevel = computed(() => userStore.level)
|
||||
<VAvatar class="cursor-pointer ms-3" color="primary" variant="tonal">
|
||||
<VImg :src="avatar" />
|
||||
|
||||
<VMenu activator="parent" width="230" location="bottom end" offset="14px">
|
||||
<VList elevation="1">
|
||||
<VMenu activator="parent" width="230" location="bottom end" offset="14px" class="user-menu">
|
||||
<VList elevation="0" class="user-profile-list px-2">
|
||||
<!-- 👉 User Avatar & Name -->
|
||||
<VListItem>
|
||||
<template #prepend>
|
||||
<VListItemAction start>
|
||||
<VAvatar color="primary" variant="tonal">
|
||||
<VImg :src="avatar" />
|
||||
</VAvatar>
|
||||
</VListItemAction>
|
||||
</template>
|
||||
|
||||
<VListItemTitle class="font-weight-semibold">
|
||||
{{ superUser ? '管理员' : '普通用户' }}
|
||||
</VListItemTitle>
|
||||
<VListItemSubtitle>{{ userName }}</VListItemSubtitle>
|
||||
</VListItem>
|
||||
|
||||
<VDivider class="my-2" />
|
||||
<div class="user-profile-header px-2 py-4 mb-2">
|
||||
<div class="d-flex align-center">
|
||||
<VAvatar size="60" class="user-avatar" color="primary" rounded="sm">
|
||||
<VImg :src="avatar" />
|
||||
</VAvatar>
|
||||
<div class="ms-4">
|
||||
<div class="user-role">
|
||||
{{ superUser ? '管理员' : '普通用户' }}
|
||||
</div>
|
||||
<div class="user-name">
|
||||
{{ userName }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 👉 Profile -->
|
||||
<VListItem link @click="router.push('/profile')">
|
||||
<VListItem link @click="router.push('/profile')" class="user-menu-item mb-1">
|
||||
<template #prepend>
|
||||
<VIcon class="me-2" icon="mdi-account-outline" size="22" />
|
||||
<div class="user-menu-icon">
|
||||
<VIcon icon="mdi-account-outline" />
|
||||
</div>
|
||||
</template>
|
||||
<VListItemTitle>个人信息</VListItemTitle>
|
||||
</VListItem>
|
||||
|
||||
<VListItem link @click="router.push('/apps')">
|
||||
<VListItem link @click="router.push('/apps')" class="user-menu-item mb-1">
|
||||
<template #prepend>
|
||||
<VIcon class="me-2" icon="mdi-view-grid-outline" size="22" />
|
||||
<div class="user-menu-icon">
|
||||
<VIcon icon="mdi-view-grid-outline" />
|
||||
</div>
|
||||
</template>
|
||||
<VListItemTitle>功能视图</VListItemTitle>
|
||||
</VListItem>
|
||||
|
||||
<!-- 👉 Site Auth -->
|
||||
<VListItem v-if="userLevel < 2 && superUser" link @click="showSiteAuthDialog">
|
||||
<VListItem v-if="userLevel < 2 && superUser" link @click="showSiteAuthDialog" class="user-menu-item mb-1">
|
||||
<template #prepend>
|
||||
<VIcon class="me-2" icon="mdi-lock-check-outline" size="22" />
|
||||
<div class="user-menu-icon">
|
||||
<VIcon icon="mdi-lock-check-outline" />
|
||||
</div>
|
||||
</template>
|
||||
<VListItemTitle>用户认证</VListItemTitle>
|
||||
</VListItem>
|
||||
|
||||
<!-- 👉 FAQ -->
|
||||
<VListItem href="https://wiki.movie-pilot.org" target="_blank">
|
||||
<VListItem href="https://wiki.movie-pilot.org" target="_blank" class="user-menu-item mb-1">
|
||||
<template #prepend>
|
||||
<VIcon class="me-2" icon="mdi-help-circle-outline" size="22" />
|
||||
<div class="user-menu-icon">
|
||||
<VIcon icon="mdi-help-circle-outline" />
|
||||
</div>
|
||||
</template>
|
||||
<VListItemTitle>帮助文档</VListItemTitle>
|
||||
</VListItem>
|
||||
|
||||
<!-- Divider -->
|
||||
<VDivider v-if="superUser" class="my-2" />
|
||||
<VDivider v-if="superUser" class="my-3" />
|
||||
|
||||
<!-- 👉 restart -->
|
||||
<VListItem v-if="superUser" @click="showRestartDialog">
|
||||
<VListItem v-if="superUser" @click="showRestartDialog" class="user-menu-item mb-1">
|
||||
<template #prepend>
|
||||
<VIcon class="me-2" icon="mdi-restart" size="22" />
|
||||
<div class="user-menu-icon restart-icon">
|
||||
<VIcon icon="mdi-restart" />
|
||||
</div>
|
||||
</template>
|
||||
<VListItemTitle>重启</VListItemTitle>
|
||||
</VListItem>
|
||||
|
||||
<!-- 👉 Logout -->
|
||||
<VListItem @click="logout">
|
||||
<VBtn color="error" block>
|
||||
<template #append> <VIcon size="small" icon="mdi-logout" /> </template>
|
||||
<div class="px-2 mt-3 mb-2">
|
||||
<VBtn color="error" block class="logout-btn" @click="logout">
|
||||
<template #prepend> <VIcon icon="mdi-logout" /> </template>
|
||||
退出登录
|
||||
</VBtn>
|
||||
</VListItem>
|
||||
</div>
|
||||
</VList>
|
||||
</VMenu>
|
||||
<!-- !SECTION -->
|
||||
@@ -184,3 +193,71 @@ const userLevel = computed(() => userStore.level)
|
||||
</VCard>
|
||||
</VDialog>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.user-profile-header {
|
||||
background: linear-gradient(135deg, rgba(var(--v-theme-primary), 0.05), rgba(var(--v-theme-primary), 0.02));
|
||||
border-radius: 12px;
|
||||
}
|
||||
|
||||
.user-role {
|
||||
font-size: 0.875rem;
|
||||
color: rgba(var(--v-theme-primary), 0.9);
|
||||
font-weight: 500;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.user-name {
|
||||
font-size: 1.125rem;
|
||||
font-weight: 600;
|
||||
color: rgba(var(--v-theme-on-surface), 0.9);
|
||||
}
|
||||
|
||||
.user-avatar {
|
||||
box-shadow: 0 4px 12px rgba(var(--v-theme-primary), 0.2);
|
||||
border: 2px solid rgba(var(--v-theme-on-surface), 0.1);
|
||||
}
|
||||
|
||||
.user-menu-item {
|
||||
border-radius: 8px;
|
||||
margin: 4px 0;
|
||||
transition: all 0.2s ease;
|
||||
|
||||
&:hover {
|
||||
background-color: rgba(var(--v-theme-primary), 0.06);
|
||||
transform: translateX(4px);
|
||||
}
|
||||
}
|
||||
|
||||
.user-menu-icon {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 8px;
|
||||
background-color: rgba(var(--v-theme-primary), 0.08);
|
||||
margin-right: 12px;
|
||||
transition: all 0.2s ease;
|
||||
|
||||
.v-icon {
|
||||
color: rgba(var(--v-theme-primary), 0.9);
|
||||
}
|
||||
}
|
||||
|
||||
.restart-icon {
|
||||
background-color: rgba(var(--v-theme-error), 0.1);
|
||||
|
||||
.v-icon {
|
||||
color: rgba(var(--v-theme-error), 0.9);
|
||||
}
|
||||
}
|
||||
|
||||
.logout-btn {
|
||||
border-radius: 10px;
|
||||
padding: 12px;
|
||||
font-weight: 500;
|
||||
letter-spacing: 0.5px;
|
||||
box-shadow: 0 4px 12px rgba(var(--v-theme-error), 0.2);
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user