父组件
    父组件设置相对定位,我的组件设置绝对定位,通过v-bind绑定left偏移量,因为侧边栏会伸缩收回,用watch监听这个变量。

    1. <!--
    2. * @Author: zhangy
    3. * @Date: 2022-06-22 21:16:54
    4. * @LastEditors: zzd993
    5. * @LastEditTime: 2022-08-01 17:10:27
    6. * @FilePath: \elabnote-front-main\src\layout\index.vue
    7. * Copyright (c) 2022 by BMY, All Rights Reserved.
    8. -->
    9. <template>
    10. <div class="main-layout-root">
    11. <a-layout v-if="isLayout" class="layout-main">
    12. <a-layout-sider
    13. :collapsed-width="50"
    14. :default-collapsed="collapsed"
    15. :collapsed="collapsed"
    16. collapsible
    17. class="layout-sider"
    18. >
    19. <Menu :collapsed="collapsed" />
    20. <template #trigger>
    21. <a-space direction="vertical" :size="8">
    22. <a-badge @click="handleMessages" :count="userInfo.unreadCount" dot :offset="[2, -2]">
    23. <IconNotification :style="{ fontSize: '18px', verticalAlign: '-3px' }" />
    24. </a-badge>
    25. <a-avatar @click="showInfo" :size="28" :style="{ background: randomColor }">{{
    26. userInfo.userNameEn
    27. }}</a-avatar>
    28. <div @click="collapsed = !collapsed">
    29. <IconCaretRight v-if="collapsed" />
    30. <IconCaretLeft v-else />
    31. </div>
    32. </a-space>
    33. </template>
    34. </a-layout-sider>
    35. <a-layout v-if="false">
    36. <a-layout>
    37. <a-layout-content>
    38. <router-view v-slot="{ Component }">
    39. <keep-alive>
    40. <component :is="Component" />
    41. </keep-alive>
    42. </router-view>
    43. </a-layout-content>
    44. </a-layout>
    45. </a-layout>
    46. <a-layout class="mian-layout-content" ref="MainLayoutContentRef">
    47. <a-layout-sider
    48. :collapsible="false"
    49. :collapsed="isTagViewCollapsed"
    50. :collapsed-width="260"
    51. :width="width"
    52. style="height: 100%; z-index: 2"
    53. @collapse="
    54. () => {
    55. isTagViewCollapsed = !isTagViewCollapsed
    56. }
    57. "
    58. >
    59. <router-view v-slot="{ Component }">
    60. <keep-alive>
    61. <component :is="Component" />
    62. </keep-alive>
    63. </router-view>
    64. </a-layout-sider>
    65. <a-layout-content class="tag-view-wrapper">
    66. <!-- <a-ffix :offset-top="0" style="background-color: #f4f5f7"> -->
    67. <TagView style="background-color: #f4f5f7" />
    68. <!-- </a-ffix> -->
    69. <div class="tag-view-detial">
    70. <TagViewDetial />
    71. </div>
    72. </a-layout-content>
    73. </a-layout>
    74. </a-layout>
    75. <router-view v-else v-slot="{ Component }">
    76. <keep-alive>
    77. <component :is="Component" />
    78. </keep-alive>
    79. </router-view>
    80. <!-- 我的组件 -->
    81. <div class="userInfo">
    82. <person-menu :userInfo="userInfo" v-show="show" />
    83. </div>
    84. </div>
    85. </template>
    86. <script lang="ts" setup>
    87. import { IconCaretRight, IconCaretLeft } from '@arco-design/web-vue/es/icon'
    88. import { ref, watch } from 'vue'
    89. import { getRandomColor } from '@/util'
    90. import { useRoute, useRouter } from 'vue-router'
    91. // import { localCache } from '@/util'
    92. import { useUserStore, useTagViewStore } from '@/store'
    93. import { storeToRefs } from 'pinia'
    94. import Menu from './menu/index.vue'
    95. import TagView from './tag-view/index.vue'
    96. import TagViewDetial from '@/views/tag-view-detial/index.vue'
    97. import { useElementSize } from '@vueuse/core'
    98. import PersonMenu from './person-menu/index.vue'
    99. // 控制信息隐藏显示
    100. let show = ref(false)
    101. const showInfo = () => {
    102. show.value = !show.value
    103. }
    104. const isShowTagView = ref<'none' | 'block'>('none')
    105. const MainLayoutContentRef = ref(null)
    106. const { width } = useElementSize(MainLayoutContentRef)
    107. const { userInfo } = storeToRefs(useUserStore())
    108. // console.log(userInfo)
    109. const { isCollapsed } = storeToRefs(useTagViewStore())
    110. const randomColor = getRandomColor()
    111. const collapsed = ref(true)
    112. const collapsedWidth = ref<string>('60px')
    113. // const popupVisible = ref(false)
    114. const route = useRoute()
    115. const router = useRouter()
    116. const isLayout = ref(true)
    117. const isTagView = ref(false)
    118. const isTagViewCollapsed = ref(true)
    119. // const handleLogout = async () => {
    120. // popupVisible.value = false
    121. // await router.replace('/login')
    122. // localCache.clearCache()
    123. // location.reload()
    124. // }
    125. const handleMessages = async () => {
    126. if (route.path === '/main/vue-app') {
    127. location.hash = '#/message/messageManagement'
    128. } else {
    129. await router.replace('/main/vue-app')
    130. location.hash = '#/message/messageManagement'
    131. }
    132. }
    133. // const handlePersonInfo = async () => {
    134. // if (route.path === '/main/vue-app') {
    135. // location.hash = '#/settings/tips/info'
    136. // } else {
    137. // await router.replace('/main/vue-app')
    138. // location.hash = '#/settings/tips/info'
    139. // }
    140. // popupVisible.value = false
    141. // }
    142. watch(
    143. () => route.path,
    144. () => {
    145. isLayout.value = route?.meta?.isLayout === false ? false : true
    146. isTagView.value = Boolean(route?.meta?.isTagView)
    147. if (!isTagView.value) {
    148. isTagViewCollapsed.value = false
    149. isShowTagView.value = 'none'
    150. } else {
    151. isTagViewCollapsed.value = isCollapsed.value
    152. isShowTagView.value = 'block'
    153. }
    154. },
    155. {
    156. immediate: true
    157. }
    158. )
    159. watch(
    160. isCollapsed,
    161. () => {
    162. if (!isTagView.value) {
    163. isTagViewCollapsed.value = false
    164. } else {
    165. isTagViewCollapsed.value = isCollapsed.value
    166. }
    167. },
    168. {
    169. immediate: true
    170. }
    171. )
    172. /**
    173. * 监听菜单的展开或合并
    174. */
    175. watch(collapsed, (newCollapsed) => {
    176. newCollapsed ? (collapsedWidth.value = '60px') : (collapsedWidth.value = '210px')
    177. })
    178. </script>
    179. <style scoped lang="scss">
    180. .main-layout-root {
    181. position: relative;
    182. height: 100%;
    183. .userInfo {
    184. position: absolute;
    185. z-index: 100;
    186. left: v-bind(collapsedWidth);
    187. bottom: 60px;
    188. }
    189. }
    190. .layout-main {
    191. height: 100%;
    192. background: var(--color-fill-2);
    193. .layout-sider {
    194. position: relative;
    195. background-color: $themeColor;
    196. :deep(.arco-layout-sider-children) {
    197. height: 95% !important;
    198. .arco-menu-inner {
    199. overflow: hidden;
    200. }
    201. }
    202. :deep(.arco-layout-sider) {
    203. z-index: 1009;
    204. display: flex;
    205. flex-direction: column;
    206. }
    207. }
    208. }
    209. .layout-main :deep(.arco-layout-sider) .logo {
    210. height: 32px;
    211. margin: 8px 0;
    212. background: $themeColor;
    213. }
    214. .layout-main :deep(.arco-layout-sider-light) .logo {
    215. background: $themeColor;
    216. }
    217. .layout-main :deep(.arco-layout-header) {
    218. height: 64px;
    219. line-height: 64px;
    220. background: var(--color-bg-3);
    221. }
    222. .layout-main .layout-sider :deep(.arco-layout-footer) {
    223. height: 48px;
    224. color: var(--color-text-2);
    225. font-weight: 400;
    226. font-size: 14px;
    227. line-height: 48px;
    228. }
    229. .layout-main :deep(.arco-layout-content) {
    230. color: var(--color-text-2);
    231. font-weight: 400;
    232. font-size: 14px;
    233. background: var(--color-bg-3);
    234. }
    235. .layout-main .layout-sider :deep(.arco-layout-footer),
    236. .layout-main .layout-sider :deep(.arco-layout-sider-trigger),
    237. .layout-main .layout-sider :deep(.arco-layout-sider-trigger-light) {
    238. // height: 15%;
    239. flex: 1;
    240. background-color: $themeColor;
    241. border: none;
    242. color: $themeMenuFrontColor;
    243. // layout footer 文字居中
    244. text-align: center;
    245. }
    246. .layout-main .layout-sider :deep(.arco-menu-selected):hover {
    247. background-color: $themeMenuHoverColor;
    248. }
    249. .layout-main .layout-sider :deep(.arco-menu-inline-header):hover {
    250. background-color: $themeMenuHoverColor !important;
    251. }
    252. .layout-main .layout-sider :deep(.arco-menu-item):hover {
    253. display: flex;
    254. background-color: $themeMenuHoverColor !important;
    255. }
    256. .layout-main .layout-sider :deep(.arco-menu-item) {
    257. display: flex;
    258. background-color: $themeColor !important;
    259. color: $themeMenuFrontColor !important;
    260. }
    261. .layout-main .layout-sider :deep(.arco-menu-inline-header) {
    262. background-color: $themeColor !important;
    263. display: flex;
    264. height: 40px;
    265. }
    266. .layout-main .layout-sider :deep(.arco-menu-selected) {
    267. background-color: $themeMenuHoverColor !important;
    268. color: $themeMenuFrontActiveColor !important;
    269. display: flex;
    270. height: 40px;
    271. }
    272. .mian-layout-content {
    273. position: relative;
    274. :deep(.arco-layout-sider) {
    275. position: absolute;
    276. }
    277. :deep(.arco-layout-content) {
    278. // @todo
    279. margin-left: 260px;
    280. // display: v-bind(isShowTagView) !important;
    281. }
    282. :deep(.arco-layout-sider-has-trigger) {
    283. padding-bottom: 0;
    284. }
    285. :deep(.tag-view-wrapper) {
    286. height: 100%;
    287. display: flex;
    288. flex-direction: column;
    289. overflow-y: scroll;
    290. .tag-view-detial {
    291. height: calc(100vh - 38px);
    292. overflow-y: scroll;
    293. flex: 1;
    294. display: flex;
    295. flex-direction: column;
    296. }
    297. }
    298. }
    299. </style>

    子组件:

    1. <!--
    2. * @Author: zzd993
    3. * @Date: 2022-07-29 15:00:35
    4. * @LastEditors: zzd993
    5. * @LastEditTime: 2022-08-01 17:54:48
    6. * @FilePath: \elabnote-front-main\src\layout\person-menu\index.vue
    7. * Copyright (c) 2022 by BMY, All Rights Reserved.
    8. -->
    9. <template>
    10. <div class="person-menu">
    11. <div class="person-box">
    12. <a-avatar
    13. :size="40"
    14. :style="{ background: randomColor }"
    15. style="margin-left: 16px; margin-right: 16px; border: 1px solid #fff"
    16. >
    17. {{ props.userInfo.userNameEn }}</a-avatar
    18. >
    19. <div class="person-info">
    20. <span>{{ getGreetings() }} {{ props.userInfo.userNameEn }}</span>
    21. <span>{{ props.userInfo.roleNames }} | {{ props.userInfo.levelTwoOrganizationName }}</span>
    22. </div>
    23. </div>
    24. <a-menu mode="pop" theme="light" class="arco-menu">
    25. <template v-for="menu in menus" :key="menu.id">
    26. <a-menu-item v-if="!menu.children" class="arco-menu-item">
    27. <SvgIcon :icon-size="24" :icon-name="menu.iconName" style="margin-right: 8px" />{{ menu.content }}
    28. </a-menu-item>
    29. <a-sub-menu v-else class="arco-sub-menu">
    30. <template #title>
    31. <SvgIcon :icon-size="24" :icon-name="menu.iconName" style="margin-right: 8px" />{{ menu.content }}
    32. </template>
    33. <a-menu-item v-for="subMenu in menu.children" :key="subMenu.id" class="arco-menu-item">
    34. <SvgIcon :icon-size="24" :icon-name="subMenu.iconName" style="margin-right: 8px" />{{ subMenu.content }}
    35. </a-menu-item>
    36. </a-sub-menu>
    37. </template>
    38. </a-menu>
    39. </div>
    40. </template>
    41. <script setup lang="ts">
    42. import { getRandomColor } from '@/util'
    43. // import { localCache } from '@/util'
    44. import SvgIcon from '@/components/SvgIcon/index.vue'
    45. // import { useRoute, useRouter } from 'vue-router'
    46. // 菜单列表
    47. const menus = [
    48. {
    49. id: '1',
    50. iconName: 'modelset',
    51. content: '模块设置',
    52. children: [
    53. {
    54. id: '6',
    55. iconName: 'kuwei',
    56. content: '库位设置'
    57. },
    58. {
    59. id: '7',
    60. iconName: 'template',
    61. content: '模板设置'
    62. },
    63. {
    64. id: '8',
    65. iconName: 'zhuce',
    66. content: '注册设置'
    67. }
    68. ]
    69. },
    70. {
    71. id: '2',
    72. iconName: 'shenpi',
    73. content: '审批设置'
    74. },
    75. {
    76. id: '3',
    77. iconName: 'manage',
    78. content: '管理后台'
    79. },
    80. {
    81. id: '4',
    82. iconName: 'account',
    83. content: '账号设置'
    84. },
    85. {
    86. id: '5',
    87. iconName: 'sign-out',
    88. content: '退出登录'
    89. }
    90. ]
    91. const randomColor = getRandomColor()
    92. const props = defineProps<{ userInfo: any }>()
    93. // console.log(props.userInfo)
    94. // const route = useRoute()
    95. // const router = useRouter()
    96. // const popupVisible = ref(false)
    97. // const handlePersonInfo = async () => {
    98. // if (route.path === '/main/vue-app') {
    99. // location.hash = '#/settings/tips/info'
    100. // } else {
    101. // await router.replace('/main/vue-app')
    102. // location.hash = '#/settings/tips/info'
    103. // }
    104. // popupVisible.value = false
    105. // }
    106. // const handleLogout = async () => {
    107. // popupVisible.value = false
    108. // await router.replace('/login')
    109. // localCache.clearCache()
    110. // location.reload()
    111. // }
    112. // 问候语
    113. const getGreetings = () => {
    114. let greeting = ''
    115. let now = new Date(),
    116. hour = now.getHours()
    117. if (hour < 6) {
    118. greeting = '凌晨好'
    119. } else if (hour < 9) {
    120. greeting = '早上好'
    121. } else if (hour < 12) {
    122. greeting = '上午好'
    123. } else if (hour < 14) {
    124. greeting = '中午好'
    125. } else if (hour < 17) {
    126. greeting = '下午好'
    127. } else if (hour < 19) {
    128. greeting = '傍晚好'
    129. } else if (hour < 22) {
    130. greeting = '晚上好'
    131. }
    132. return greeting
    133. }
    134. </script>
    135. <style scoped lang="scss">
    136. .person-menu {
    137. z-index: 99;
    138. border-radius: 6px;
    139. /* .arco-menu-item {
    140. } */
    141. .person-box {
    142. width: 260px;
    143. height: 76px;
    144. display: flex;
    145. background-image: url('../../assets/images/userInfo.png');
    146. /* justify-content: center; */
    147. align-items: center;
    148. .person-info {
    149. display: flex;
    150. flex-direction: column;
    151. span:first-child {
    152. font-weight: 700;
    153. font-size: 14px;
    154. line-height: 22px;
    155. color: #fff;
    156. }
    157. span:nth-child(2) {
    158. font-size: 12px;
    159. line-height: 22px;
    160. color: #c2cdff;
    161. }
    162. }
    163. }
    164. /* .arco-menu {
    165. } */
    166. .arco-sub-menu .arco-menu-item {
    167. width: 128px;
    168. }
    169. }
    170. </style>