207 lines
		
	
	
		
			5.9 KiB
		
	
	
	
		
			Vue
		
	
	
			
		
		
	
	
			207 lines
		
	
	
		
			5.9 KiB
		
	
	
	
		
			Vue
		
	
	
| <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>
 |