zy-vue-library/layout/header/index.vue

207 lines
5.9 KiB
Vue
Raw Permalink Normal View History

2025-10-22 11:19:51 +08:00
<template>
<div class="header">
<div class="logo">
<slot name="logo">{{ logoValueTitle }}</slot>
</div>
<div class="breadcrumb">
<layout-breadcrumb v-if="route.meta.isBreadcrumb !== false" />
</div>
<div class="right">
<slot name="userOtherInfo"></slot>
<div class="menu ml-20 mr-20">
<slot name="NavBar">
<el-dropdown trigger="click" placement="bottom-end" @command="fnMenuDropdownCommand" v-if="useMoreNavBar">
<div class="more">
<div>更多导航栏</div>
<icon-down theme="filled" size="16" fill="#a2c2d3" :stroke-width="3"/>
</div>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item
v-for="(item, index) in menuList"
:key="index"
:command="item.model"
:style="{ color: item.model !== model ? 'var(--el-text-color-regular)' : '#79bbff' }"
>
{{ item.title }}
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
<div v-else class="navs">
<div
:class="['nav', { active: item.model === model }]"
v-for="(item, index) in menuList"
:key="index"
@click="fnMenuDropdownCommand(item.model)"
>
<div>{{ item.title }}</div>
<div class="border" />
</div>
</div>
</slot>
</div>
<div class="user_dropdown">
<el-dropdown trigger="click" placement="bottom-end" @command="fnUserDropdownCommand">
<div class="user_info">
<el-avatar shape="circle" :size="30" fit="fill" :src="txImg" />
<span>{{ userName }}</span>
<icon-down theme="filled" size="16" fill="#a2c2d3" :stroke-width="3"/>
</div>
<template #dropdown>
<el-dropdown-menu>
<slot name="dropdown"></slot>
<el-dropdown-item command="modifyPassword">修改密码</el-dropdown-item>
<el-dropdown-item command="signOut">退出</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</div>
</div>
<change-password :change-password-url="changePasswordUrl" v-model:visible="passwordDialogVisible" @submit="fnSignOut" />
</div>
</template>
<script setup>
import { ref, computed } from "vue";
import { useRoute, useRouter } from "vue-router";
import LayoutBreadcrumb from "../breadcrumb/index.vue";
import ChangePassword from "./components/change_password.vue";
import { postRequest } from "../../axios/index.js";
import { ElDropdown, ElDropdownItem, ElDropdownMenu, ElAvatar } from 'element-plus'
import "element-plus/es/components/dropdown/style/css";
import "element-plus/es/components/dropdown-item/style/css";
import "element-plus/es/components/dropdown-menu/style/css";
import "element-plus/es/components/avatar/style/css";
defineOptions({
name: "LayoutHeader",
});
const props = defineProps({
loginPath: { type: String, required: true },
logoutUrl: { type: String, required: true },
changePasswordUrl: { type: String, required: true },
txImg: { type: String, required: true },
userName: { type: String, required: true },
useMoreNavBar: { type: Boolean, required: true },
menuStore: { type: Object, required: true },
userStore: { type: Object, required: true },
navStore: { type: Object, required: true },
})
const emits = defineEmits(["command"]);
const route = useRoute();
const router = useRouter();
const menuList = computed(() => props.navStore.getNavList);
const model = computed(() => props.menuStore.getModel);
const logoValueTitle = computed(
() => menuList.value.filter((item) => item.model === model.value)?.[0]?.title
);
const passwordDialogVisible = ref(false);
const fnUserDropdownCommand = async (command) => {
if (command === "signOut") await fnSignOut();
if (command === "modifyPassword") passwordDialogVisible.value = true;
emits('command', command)
};
const fnMenuDropdownCommand = (command) => {
props.menuStore.setModel(command);
};
const fnSignOut = async () => {
await postRequest(props.logoutUrl);
props.userStore.$reset();
await router.replace(props.loginPath);
};
</script>
<style lang="scss" scoped>
.header {
display: flex;
align-items: center;
justify-content: space-between;
box-shadow: 0 1px 4px rgb(0 21 41 / 8%);
padding-right: 20px;
background-color: #fff;
.logo {
width: 210px;
height: 50px;
background-color: #2b2f3a;
color: #ffffff;
text-align: center;
line-height: 50px;
margin-right: 20px;
}
.right {
flex: 1;
display: flex;
align-items: center;
justify-content: flex-end;
.menu {
cursor: pointer;
height: var(--el-header-height);
.el-dropdown {
line-height: var(--el-header-height);
}
.more {
display: flex;
align-items: center;
}
.navs {
display: flex;
gap: 20px;
align-items: center;
height: 100%;
line-height: var(--el-header-height);
.nav {
color: #222;
position: relative;
&:hover {
color: #79bbff;
}
&.active {
color: #79bbff;
.border {
width: 100%;
}
}
.border {
position: absolute;
bottom: 0;
left: 50%;
width: 0;
height: 2px;
background-color: #79bbff;
transition: all 0.3s;
transform-origin: center;
transform: translateX(-50%);
}
}
}
}
.user_dropdown {
margin-top: 5px;
cursor: pointer;
.el-avatar {
margin-right: 5px;
}
.user_info {
display: flex;
align-items: center;
}
}
}
}
</style>