web-usercenter更新
This commit is contained in:
@@ -3,12 +3,12 @@
|
||||
<div class="nav-container">
|
||||
<!-- Logo区域 -->
|
||||
<div class="nav-logo">
|
||||
<img src="../../assets/imgs/logo-icon.svg" alt="Logo" class="logo-icon" />
|
||||
<img src="@/assets/imgs/logo-icon.svg" alt="Logo" class="logo-icon" />
|
||||
<span class="logo-text">红色思政学习平台</span>
|
||||
</div>
|
||||
|
||||
<!-- 导航菜单 -->
|
||||
<div class="nav-menu">
|
||||
<div class="nav-menu" @wheel="handleWheel">
|
||||
<div
|
||||
v-for="menu in navigationMenus"
|
||||
:key="menu.menuID"
|
||||
@@ -88,9 +88,15 @@ const dropdownPositions = ref<Record<string, { left: number; top: number; width:
|
||||
const allMenus = computed(() => store.getters['auth/menuTree']);
|
||||
const userInfo = computed(() => store.getters['auth/userInfo']);
|
||||
|
||||
// 获取第一层的导航菜单(MenuType.NAVIGATION)
|
||||
// 获取第一层的导航菜单(MenuType.NAVIGATION),过滤掉用户相关菜单
|
||||
const navigationMenus = computed(() => {
|
||||
const menus = allMenus.value.filter((menu: SysMenu) => menu.type === MenuType.NAVIGATION);
|
||||
const menus = allMenus.value.filter((menu: SysMenu) => {
|
||||
// 过滤掉"用户下拉菜单"容器,这些显示在UserDropdown中
|
||||
if (menu.menuID === 'menu_user_dropdown') {
|
||||
return false;
|
||||
}
|
||||
return menu.type === MenuType.NAVIGATION;
|
||||
});
|
||||
console.log('导航菜单数据:', menus);
|
||||
menus.forEach((menu: SysMenu) => {
|
||||
console.log(`菜单 ${menu.name}:`, {
|
||||
@@ -190,6 +196,15 @@ function handleMouseLeave() {
|
||||
activeDropdown.value = null;
|
||||
}
|
||||
|
||||
// 处理鼠标滚轮水平滚动
|
||||
function handleWheel(event: WheelEvent) {
|
||||
const container = event.currentTarget as HTMLElement;
|
||||
if (container) {
|
||||
event.preventDefault();
|
||||
container.scrollLeft += event.deltaY;
|
||||
}
|
||||
}
|
||||
|
||||
// 处理导航点击
|
||||
function handleNavClick(menu: SysMenu) {
|
||||
activeDropdown.value = null;
|
||||
@@ -227,6 +242,7 @@ function handleLogout() {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 1000;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.nav-container {
|
||||
@@ -238,6 +254,7 @@ function handleLogout() {
|
||||
gap: 20px;
|
||||
width: 100%;
|
||||
position: relative;
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
.nav-logo {
|
||||
@@ -268,36 +285,31 @@ function handleLogout() {
|
||||
gap: 0;
|
||||
flex: 1;
|
||||
overflow-x: auto;
|
||||
overflow-y: hidden;
|
||||
overflow-y: visible;
|
||||
scroll-behavior: smooth;
|
||||
padding-bottom: 2px; /* 为滚动条留出空间 */
|
||||
|
||||
/* 自定义滚动条样式 */
|
||||
scrollbar-width: thin;
|
||||
scrollbar-width: auto;
|
||||
scrollbar-color: #C62828 #f5f5f5;
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
height: 6px;
|
||||
height: 8px;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-track {
|
||||
background: #f5f5f5;
|
||||
border-radius: 3px;
|
||||
border-radius: 4px;
|
||||
margin: 0 10px; /* 滚动条内缩 */
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-thumb {
|
||||
background: #C62828;
|
||||
border-radius: 3px;
|
||||
border-radius: 4px;
|
||||
|
||||
&:hover {
|
||||
background: #B71C1C;
|
||||
}
|
||||
}
|
||||
|
||||
/* 当内容可以滚动时显示滚动条 */
|
||||
&:hover::-webkit-scrollbar-thumb {
|
||||
background: #C62828;
|
||||
}
|
||||
}
|
||||
|
||||
.nav-item {
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
<template>
|
||||
<div class="user-dropdown" @click="toggleDropdown" v-click-outside="closeDropdown">
|
||||
<div
|
||||
ref="dropdownRef"
|
||||
class="user-dropdown"
|
||||
@click="toggleDropdown"
|
||||
@mouseenter="openDropdown"
|
||||
@mouseleave="closeDropdown"
|
||||
v-click-outside="forceCloseDropdown"
|
||||
>
|
||||
<!-- 未登录状态 -->
|
||||
<div class="login-info" v-if="!isLoggedIn">
|
||||
<div class="login-text">
|
||||
@@ -23,8 +30,15 @@
|
||||
</div>
|
||||
|
||||
<!-- 下拉菜单 -->
|
||||
<transition name="dropdown">
|
||||
<div class="dropdown-menu" v-if="dropdownVisible">
|
||||
<Teleport to="body">
|
||||
<div
|
||||
class="dropdown-menu"
|
||||
:class="{ 'show': dropdownVisible }"
|
||||
:style="dropdownStyle"
|
||||
v-if="dropdownVisible"
|
||||
@mouseenter="openDropdown"
|
||||
@mouseleave="closeDropdown"
|
||||
>
|
||||
<!-- 未登录时的菜单 -->
|
||||
<template v-if="!isLoggedIn">
|
||||
<div class="dropdown-item" @click="goToLogin">
|
||||
@@ -39,22 +53,24 @@
|
||||
|
||||
<!-- 已登录时的菜单 -->
|
||||
<template v-else>
|
||||
<div class="dropdown-item" @click="goToProfile">
|
||||
<i class="item-icon icon-profile"></i>
|
||||
<span>个人资料</span>
|
||||
<!-- 动态菜单项(个人中心、账号中心) -->
|
||||
<div
|
||||
v-for="menu in userMenus"
|
||||
:key="menu.menuID"
|
||||
class="dropdown-item"
|
||||
@click="goToMenu(menu)"
|
||||
>
|
||||
<i class="item-icon">{{ getMenuIcon(menu) }}</i>
|
||||
<span>{{ menu.name }}</span>
|
||||
</div>
|
||||
<div class="dropdown-item" @click="goToSettings">
|
||||
<i class="item-icon icon-settings"></i>
|
||||
<span>账户设置</span>
|
||||
</div>
|
||||
<div class="dropdown-divider"></div>
|
||||
<div class="dropdown-divider" v-if="userMenus.length > 0"></div>
|
||||
<div class="dropdown-item danger" @click="handleLogout">
|
||||
<i class="item-icon icon-logout"></i>
|
||||
<span>退出登录</span>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</transition>
|
||||
</Teleport>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -62,7 +78,7 @@
|
||||
import { ref, computed } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { useStore } from 'vuex';
|
||||
import type { UserVO } from '@/types';
|
||||
import type { UserVO, SysMenu } from '@/types';
|
||||
|
||||
// Props
|
||||
interface Props {
|
||||
@@ -79,6 +95,9 @@ const emit = defineEmits<{
|
||||
|
||||
// 状态
|
||||
const dropdownVisible = ref(false);
|
||||
const dropdownRef = ref<HTMLElement | null>(null);
|
||||
const dropdownPosition = ref({ top: 0, left: 0 });
|
||||
const closeTimer = ref<number | null>(null);
|
||||
|
||||
// Composition API
|
||||
const router = useRouter();
|
||||
@@ -103,39 +122,106 @@ const primaryRole = computed(() => {
|
||||
return '管理员'; // 暂时硬编码
|
||||
});
|
||||
|
||||
// 获取用户下拉菜单(个人中心和账号中心)
|
||||
const userMenus = computed(() => {
|
||||
const allMenus = store.getters['auth/menuTree'] as SysMenu[];
|
||||
|
||||
// 查找"用户下拉菜单"容器
|
||||
const userDropdownMenu = allMenus.find((menu: SysMenu) => menu.menuID === 'menu_user_dropdown');
|
||||
|
||||
if (!userDropdownMenu || !userDropdownMenu.children) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// 返回用户下拉菜单下的所有NAVIGATION类型子菜单(个人中心、账号中心)
|
||||
return userDropdownMenu.children.filter((menu: SysMenu) => menu.type === 1); // MenuType.NAVIGATION = 1
|
||||
});
|
||||
|
||||
// 下拉菜单样式
|
||||
const dropdownStyle = computed(() => {
|
||||
return {
|
||||
position: 'fixed' as const,
|
||||
top: `${dropdownPosition.value.top}px`,
|
||||
left: `${dropdownPosition.value.left}px`,
|
||||
minWidth: '160px',
|
||||
};
|
||||
});
|
||||
|
||||
// 方法 - 使用 function 声明
|
||||
function openDropdown() {
|
||||
// 清除关闭定时器
|
||||
if (closeTimer.value) {
|
||||
clearTimeout(closeTimer.value);
|
||||
closeTimer.value = null;
|
||||
}
|
||||
|
||||
dropdownVisible.value = true;
|
||||
|
||||
// 计算下拉菜单位置
|
||||
if (dropdownRef.value) {
|
||||
const rect = dropdownRef.value.getBoundingClientRect();
|
||||
dropdownPosition.value = {
|
||||
top: rect.bottom + 4, // 距离触发器底部4px
|
||||
left: rect.right - 160, // 右对齐:触发器右边缘减去下拉菜单宽度
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function toggleDropdown() {
|
||||
dropdownVisible.value = !dropdownVisible.value;
|
||||
if (dropdownVisible.value) {
|
||||
forceCloseDropdown();
|
||||
} else {
|
||||
openDropdown();
|
||||
}
|
||||
}
|
||||
|
||||
function closeDropdown() {
|
||||
// 延迟关闭,给用户时间移动到下拉菜单
|
||||
closeTimer.value = setTimeout(() => {
|
||||
dropdownVisible.value = false;
|
||||
closeTimer.value = null;
|
||||
}, 200);
|
||||
}
|
||||
|
||||
function forceCloseDropdown() {
|
||||
if (closeTimer.value) {
|
||||
clearTimeout(closeTimer.value);
|
||||
closeTimer.value = null;
|
||||
}
|
||||
dropdownVisible.value = false;
|
||||
}
|
||||
|
||||
// 未登录时的操作
|
||||
function goToLogin() {
|
||||
closeDropdown();
|
||||
forceCloseDropdown();
|
||||
router.push('/login');
|
||||
}
|
||||
|
||||
function goToRegister() {
|
||||
closeDropdown();
|
||||
forceCloseDropdown();
|
||||
router.push('/register');
|
||||
}
|
||||
|
||||
// 已登录时的操作
|
||||
function goToProfile() {
|
||||
closeDropdown();
|
||||
router.push('/profile');
|
||||
function goToMenu(menu: SysMenu) {
|
||||
forceCloseDropdown();
|
||||
if (menu.url) {
|
||||
router.push(menu.url);
|
||||
}
|
||||
}
|
||||
|
||||
function goToSettings() {
|
||||
closeDropdown();
|
||||
router.push('/profile/settings');
|
||||
function getMenuIcon(menu: SysMenu) {
|
||||
// 根据菜单ID返回对应图标
|
||||
if (menu.menuID === 'menu_user_center') {
|
||||
return '👤';
|
||||
} else if (menu.menuID === 'menu_profile') {
|
||||
return '⚙️';
|
||||
}
|
||||
return '📋';
|
||||
}
|
||||
|
||||
function handleLogout() {
|
||||
closeDropdown();
|
||||
forceCloseDropdown();
|
||||
emit('logout');
|
||||
}
|
||||
|
||||
@@ -252,16 +338,21 @@ const vClickOutside = {
|
||||
}
|
||||
|
||||
.dropdown-menu {
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
right: 0;
|
||||
min-width: 160px;
|
||||
background: white;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
|
||||
border: 1px solid #e8e8e8;
|
||||
z-index: 1000;
|
||||
z-index: 10000;
|
||||
overflow: hidden;
|
||||
opacity: 0;
|
||||
transform: scaleY(0);
|
||||
transform-origin: top center;
|
||||
transition: opacity 0.2s ease, transform 0.2s ease;
|
||||
|
||||
&.show {
|
||||
opacity: 1;
|
||||
transform: scaleY(1);
|
||||
}
|
||||
}
|
||||
|
||||
.dropdown-item {
|
||||
@@ -299,18 +390,6 @@ const vClickOutside = {
|
||||
margin: 4px 0;
|
||||
}
|
||||
|
||||
/* 动画效果 */
|
||||
.dropdown-enter-active,
|
||||
.dropdown-leave-active {
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.dropdown-enter-from,
|
||||
.dropdown-leave-to {
|
||||
opacity: 0;
|
||||
transform: translateY(-4px);
|
||||
}
|
||||
|
||||
/* 图标字体类 */
|
||||
.icon-login::before { content: "🔑"; }
|
||||
.icon-register::before { content: "📝"; }
|
||||
|
||||
Reference in New Issue
Block a user