父组件
父组件设置相对定位,我的组件设置绝对定位,通过v-bind绑定left偏移量,因为侧边栏会伸缩收回,用watch监听这个变量。
<!--* @Author: zhangy* @Date: 2022-06-22 21:16:54* @LastEditors: zzd993* @LastEditTime: 2022-08-01 17:10:27* @FilePath: \elabnote-front-main\src\layout\index.vue* Copyright (c) 2022 by BMY, All Rights Reserved.--><template><div class="main-layout-root"><a-layout v-if="isLayout" class="layout-main"><a-layout-sider:collapsed-width="50":default-collapsed="collapsed":collapsed="collapsed"collapsibleclass="layout-sider"><Menu :collapsed="collapsed" /><template #trigger><a-space direction="vertical" :size="8"><a-badge @click="handleMessages" :count="userInfo.unreadCount" dot :offset="[2, -2]"><IconNotification :style="{ fontSize: '18px', verticalAlign: '-3px' }" /></a-badge><a-avatar @click="showInfo" :size="28" :style="{ background: randomColor }">{{userInfo.userNameEn}}</a-avatar><div @click="collapsed = !collapsed"><IconCaretRight v-if="collapsed" /><IconCaretLeft v-else /></div></a-space></template></a-layout-sider><a-layout v-if="false"><a-layout><a-layout-content><router-view v-slot="{ Component }"><keep-alive><component :is="Component" /></keep-alive></router-view></a-layout-content></a-layout></a-layout><a-layout class="mian-layout-content" ref="MainLayoutContentRef"><a-layout-sider:collapsible="false":collapsed="isTagViewCollapsed":collapsed-width="260":width="width"style="height: 100%; z-index: 2"@collapse="() => {isTagViewCollapsed = !isTagViewCollapsed}"><router-view v-slot="{ Component }"><keep-alive><component :is="Component" /></keep-alive></router-view></a-layout-sider><a-layout-content class="tag-view-wrapper"><!-- <a-ffix :offset-top="0" style="background-color: #f4f5f7"> --><TagView style="background-color: #f4f5f7" /><!-- </a-ffix> --><div class="tag-view-detial"><TagViewDetial /></div></a-layout-content></a-layout></a-layout><router-view v-else v-slot="{ Component }"><keep-alive><component :is="Component" /></keep-alive></router-view><!-- 我的组件 --><div class="userInfo"><person-menu :userInfo="userInfo" v-show="show" /></div></div></template><script lang="ts" setup>import { IconCaretRight, IconCaretLeft } from '@arco-design/web-vue/es/icon'import { ref, watch } from 'vue'import { getRandomColor } from '@/util'import { useRoute, useRouter } from 'vue-router'// import { localCache } from '@/util'import { useUserStore, useTagViewStore } from '@/store'import { storeToRefs } from 'pinia'import Menu from './menu/index.vue'import TagView from './tag-view/index.vue'import TagViewDetial from '@/views/tag-view-detial/index.vue'import { useElementSize } from '@vueuse/core'import PersonMenu from './person-menu/index.vue'// 控制信息隐藏显示let show = ref(false)const showInfo = () => {show.value = !show.value}const isShowTagView = ref<'none' | 'block'>('none')const MainLayoutContentRef = ref(null)const { width } = useElementSize(MainLayoutContentRef)const { userInfo } = storeToRefs(useUserStore())// console.log(userInfo)const { isCollapsed } = storeToRefs(useTagViewStore())const randomColor = getRandomColor()const collapsed = ref(true)const collapsedWidth = ref<string>('60px')// const popupVisible = ref(false)const route = useRoute()const router = useRouter()const isLayout = ref(true)const isTagView = ref(false)const isTagViewCollapsed = ref(true)// const handleLogout = async () => {// popupVisible.value = false// await router.replace('/login')// localCache.clearCache()// location.reload()// }const handleMessages = async () => {if (route.path === '/main/vue-app') {location.hash = '#/message/messageManagement'} else {await router.replace('/main/vue-app')location.hash = '#/message/messageManagement'}}// const handlePersonInfo = async () => {// if (route.path === '/main/vue-app') {// location.hash = '#/settings/tips/info'// } else {// await router.replace('/main/vue-app')// location.hash = '#/settings/tips/info'// }// popupVisible.value = false// }watch(() => route.path,() => {isLayout.value = route?.meta?.isLayout === false ? false : trueisTagView.value = Boolean(route?.meta?.isTagView)if (!isTagView.value) {isTagViewCollapsed.value = falseisShowTagView.value = 'none'} else {isTagViewCollapsed.value = isCollapsed.valueisShowTagView.value = 'block'}},{immediate: true})watch(isCollapsed,() => {if (!isTagView.value) {isTagViewCollapsed.value = false} else {isTagViewCollapsed.value = isCollapsed.value}},{immediate: true})/*** 监听菜单的展开或合并*/watch(collapsed, (newCollapsed) => {newCollapsed ? (collapsedWidth.value = '60px') : (collapsedWidth.value = '210px')})</script><style scoped lang="scss">.main-layout-root {position: relative;height: 100%;.userInfo {position: absolute;z-index: 100;left: v-bind(collapsedWidth);bottom: 60px;}}.layout-main {height: 100%;background: var(--color-fill-2);.layout-sider {position: relative;background-color: $themeColor;:deep(.arco-layout-sider-children) {height: 95% !important;.arco-menu-inner {overflow: hidden;}}:deep(.arco-layout-sider) {z-index: 1009;display: flex;flex-direction: column;}}}.layout-main :deep(.arco-layout-sider) .logo {height: 32px;margin: 8px 0;background: $themeColor;}.layout-main :deep(.arco-layout-sider-light) .logo {background: $themeColor;}.layout-main :deep(.arco-layout-header) {height: 64px;line-height: 64px;background: var(--color-bg-3);}.layout-main .layout-sider :deep(.arco-layout-footer) {height: 48px;color: var(--color-text-2);font-weight: 400;font-size: 14px;line-height: 48px;}.layout-main :deep(.arco-layout-content) {color: var(--color-text-2);font-weight: 400;font-size: 14px;background: var(--color-bg-3);}.layout-main .layout-sider :deep(.arco-layout-footer),.layout-main .layout-sider :deep(.arco-layout-sider-trigger),.layout-main .layout-sider :deep(.arco-layout-sider-trigger-light) {// height: 15%;flex: 1;background-color: $themeColor;border: none;color: $themeMenuFrontColor;// layout footer 文字居中text-align: center;}.layout-main .layout-sider :deep(.arco-menu-selected):hover {background-color: $themeMenuHoverColor;}.layout-main .layout-sider :deep(.arco-menu-inline-header):hover {background-color: $themeMenuHoverColor !important;}.layout-main .layout-sider :deep(.arco-menu-item):hover {display: flex;background-color: $themeMenuHoverColor !important;}.layout-main .layout-sider :deep(.arco-menu-item) {display: flex;background-color: $themeColor !important;color: $themeMenuFrontColor !important;}.layout-main .layout-sider :deep(.arco-menu-inline-header) {background-color: $themeColor !important;display: flex;height: 40px;}.layout-main .layout-sider :deep(.arco-menu-selected) {background-color: $themeMenuHoverColor !important;color: $themeMenuFrontActiveColor !important;display: flex;height: 40px;}.mian-layout-content {position: relative;:deep(.arco-layout-sider) {position: absolute;}:deep(.arco-layout-content) {// @todomargin-left: 260px;// display: v-bind(isShowTagView) !important;}:deep(.arco-layout-sider-has-trigger) {padding-bottom: 0;}:deep(.tag-view-wrapper) {height: 100%;display: flex;flex-direction: column;overflow-y: scroll;.tag-view-detial {height: calc(100vh - 38px);overflow-y: scroll;flex: 1;display: flex;flex-direction: column;}}}</style>
子组件:
<!--* @Author: zzd993* @Date: 2022-07-29 15:00:35* @LastEditors: zzd993* @LastEditTime: 2022-08-01 17:54:48* @FilePath: \elabnote-front-main\src\layout\person-menu\index.vue* Copyright (c) 2022 by BMY, All Rights Reserved.--><template><div class="person-menu"><div class="person-box"><a-avatar:size="40":style="{ background: randomColor }"style="margin-left: 16px; margin-right: 16px; border: 1px solid #fff">{{ props.userInfo.userNameEn }}</a-avatar><div class="person-info"><span>{{ getGreetings() }} {{ props.userInfo.userNameEn }}</span><span>{{ props.userInfo.roleNames }} | {{ props.userInfo.levelTwoOrganizationName }}</span></div></div><a-menu mode="pop" theme="light" class="arco-menu"><template v-for="menu in menus" :key="menu.id"><a-menu-item v-if="!menu.children" class="arco-menu-item"><SvgIcon :icon-size="24" :icon-name="menu.iconName" style="margin-right: 8px" />{{ menu.content }}</a-menu-item><a-sub-menu v-else class="arco-sub-menu"><template #title><SvgIcon :icon-size="24" :icon-name="menu.iconName" style="margin-right: 8px" />{{ menu.content }}</template><a-menu-item v-for="subMenu in menu.children" :key="subMenu.id" class="arco-menu-item"><SvgIcon :icon-size="24" :icon-name="subMenu.iconName" style="margin-right: 8px" />{{ subMenu.content }}</a-menu-item></a-sub-menu></template></a-menu></div></template><script setup lang="ts">import { getRandomColor } from '@/util'// import { localCache } from '@/util'import SvgIcon from '@/components/SvgIcon/index.vue'// import { useRoute, useRouter } from 'vue-router'// 菜单列表const menus = [{id: '1',iconName: 'modelset',content: '模块设置',children: [{id: '6',iconName: 'kuwei',content: '库位设置'},{id: '7',iconName: 'template',content: '模板设置'},{id: '8',iconName: 'zhuce',content: '注册设置'}]},{id: '2',iconName: 'shenpi',content: '审批设置'},{id: '3',iconName: 'manage',content: '管理后台'},{id: '4',iconName: 'account',content: '账号设置'},{id: '5',iconName: 'sign-out',content: '退出登录'}]const randomColor = getRandomColor()const props = defineProps<{ userInfo: any }>()// console.log(props.userInfo)// const route = useRoute()// const router = useRouter()// const popupVisible = ref(false)// const handlePersonInfo = async () => {// if (route.path === '/main/vue-app') {// location.hash = '#/settings/tips/info'// } else {// await router.replace('/main/vue-app')// location.hash = '#/settings/tips/info'// }// popupVisible.value = false// }// const handleLogout = async () => {// popupVisible.value = false// await router.replace('/login')// localCache.clearCache()// location.reload()// }// 问候语const getGreetings = () => {let greeting = ''let now = new Date(),hour = now.getHours()if (hour < 6) {greeting = '凌晨好'} else if (hour < 9) {greeting = '早上好'} else if (hour < 12) {greeting = '上午好'} else if (hour < 14) {greeting = '中午好'} else if (hour < 17) {greeting = '下午好'} else if (hour < 19) {greeting = '傍晚好'} else if (hour < 22) {greeting = '晚上好'}return greeting}</script><style scoped lang="scss">.person-menu {z-index: 99;border-radius: 6px;/* .arco-menu-item {} */.person-box {width: 260px;height: 76px;display: flex;background-image: url('../../assets/images/userInfo.png');/* justify-content: center; */align-items: center;.person-info {display: flex;flex-direction: column;span:first-child {font-weight: 700;font-size: 14px;line-height: 22px;color: #fff;}span:nth-child(2) {font-size: 12px;line-height: 22px;color: #c2cdff;}}}/* .arco-menu {} */.arco-sub-menu .arco-menu-item {width: 128px;}}</style>
