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> |