Files
continew-admin-ui/src/layout/LayoutMix.vue
kiki1373639299 e72e2ed1f8 feat(layout): 混合布局和双列布局 补充版权信息组件和弹窗组件
Co-authored-by: kiki1373639299<zkai0106@163.com>



# message auto-generated for no-merge-commit merge:
!15 merge layout-fix into dev

feat(layout): 混合布局和双列布局 补充版权信息组件和弹窗组件

Created-by: kiki1373639299
Commit-by: kiki1373639299
Merged-by: Charles_7c
Description: <!--
  非常感谢您的 PR!在提交之前,请务必确保您 PR 的代码经过了完整测试,并且通过了代码规范检查。
-->

<!-- 在 [] 中输入 x 来勾选) -->

## PR 类型

<!-- 您的 PR 引入了哪种类型的变更? -->
<!-- 只支持选择一种类型,如果有多种类型,可以在更新日志中增加 “类型” 列。 -->

- [X] 新 feature
- [ ] Bug 修复
- [ ] 功能增强
- [ ] 文档变更
- [ ] 代码样式变更
- [ ] 重构
- [ ] 性能改进
- [ ] 单元测试
- [ ] CI/CD
- [ ] 其他

## PR 目的

<!-- 描述一下您的 PR 解决了什么问题。如果可以,请链接到相关 issues。 -->

## 解决方案

<!-- 详细描述您是如何解决的问题 -->

## PR 测试

<!-- 如果可以,请为您的 PR 添加或更新单元测试。 -->
<!-- 请描述一下您是如何测试 PR 的。例如:创建/更新单元测试或添加相关的截图。 -->

## Changelog

| 模块  | Changelog | Related issues |
|-----|-----------| -------------- |
|   src/layout  |      layoutMix 和 layoutColumns 增加 GiFooter 和  NoticePopup     |                |

<!-- 如果有多种类型的变更,可以在变更日志表中增加 “类型” 列,该列的值与上方 “PR 类型” 相同。 -->
<!-- Related issues 格式为 Closes #<issue号>,或者 Fixes #<issue号>,或者 Resolves #<issue号>。 -->

## 其他信息

<!-- 请描述一下还有哪些注意事项。例如:如果引入了一个不向下兼容的变更,请描述其影响。 -->

## 提交前确认

- [X] PR 代码经过了完整测试,并且通过了代码规范检查
- [X] 已经完整填写 Changelog,并链接到了相关 issues
- [X] PR 代码将要提交到 dev 分支

See merge request: continew/continew-admin-ui!15
2025-11-12 15:53:36 +08:00

169 lines
4.2 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!--
@file LayoutMix 组件
@description 混合布局组件支持顶部导航和左侧菜单组合的布局方式
-->
<template>
<div class="layout-mix">
<!-- 左侧菜单区域 -->
<section
v-if="isDesktop" class="layout-mix-left" :class="{ 'app-menu-dark': appStore.menuDark }"
:style="appStore.menuDark ? appStore.themeCSSVar : undefined"
>
<Logo :collapsed="appStore.menuCollapse" />
<Menu :menus="twoLevelMenus" :menu-style="{ width: '200px', flex: 1 }" />
<WwAds class="ads" />
</section>
<!-- 右侧内容区域 -->
<section class="layout-mix-right">
<header class="header">
<MenuFoldBtn />
<a-menu
v-if="isDesktop" mode="horizontal" :selected-keys="activeMenu" :auto-open-selected="false"
:trigger-props="menuTriggerProps" @menu-item-click="handleMenuItemClickByPath"
>
<a-menu-item v-for="item in oneLevelMenus" :key="item.path">
<template #icon>
<GiSvgIcon :name="getMenuIcon(item) || ''" :size="24" />
</template>
<span>{{ item.meta?.title || item.children?.[0]?.meta?.title || '' }}</span>
</a-menu-item>
</a-menu>
<HeaderRightBar />
</header>
<Tabs v-if="appStore.tab" />
<Main />
<GiFooter v-if="appStore.copyrightDisplay" />
</section>
<!-- 公告弹窗 -->
<NoticePopup ref="noticePopupRef" />
</div>
</template>
<script setup lang="ts">
import type { RouteRecordRaw } from 'vue-router'
import HeaderRightBar from './components/HeaderRightBar/index.vue'
import Logo from './components/Logo.vue'
import Main from './components/Main.vue'
import Menu from './components/Menu/index.vue'
import MenuFoldBtn from './components/MenuFoldBtn.vue'
import Tabs from './components/Tabs/index.vue'
import { useAppStore } from '@/stores'
import { useLevelMenu } from '@/layout/hooks/useLevelMenu'
import { useDevice } from '@/hooks'
import { getToken } from '@/utils/auth'
import WwAds from '@/layout/components/WwAds.vue'
import NoticePopup from '@/views/user/message/components/NoticePopup.vue'
/** 组件名称 */
defineOptions({ name: 'LayoutMix' })
const appStore = useAppStore()
const { isDesktop } = useDevice()
/** 菜单配置 */
const menuTriggerProps = {
animationName: 'slide-dynamic-origin',
}
const { oneLevelMenus, twoLevelMenus, oneLevelMenuActiveRoute, getOneLevelMenus, handleMenuItemClickByPath } = useLevelMenu()
getOneLevelMenus()
const activeMenu = computed(() => [oneLevelMenuActiveRoute.value?.path ?? ''])
// 公告弹窗引用
const noticePopupRef = ref<InstanceType<typeof NoticePopup>>()
// 检查并显示未读公告
const checkAndShowNotices = () => {
const token = getToken()
// 如果有token检查未读公告
if (token) {
setTimeout(() => {
console.log(noticePopupRef.value)
noticePopupRef.value?.open()
}, 1000) // 延迟1秒显示让页面先加载完成
}
}
const getMenuIcon = (item: RouteRecordRaw) => {
return item.meta?.icon || item.children?.[0]?.meta?.icon
}
onMounted(() => {
checkAndShowNotices()
})
</script>
<style lang="scss" scoped>
:deep(.arco-menu-pop) {
white-space: nowrap;
}
:deep(.arco-menu.arco-menu-vertical.arco-menu-collapsed) {
// Menu菜单组件修改
.arco-menu-icon {
padding: 10px 0;
margin-right: 0;
}
.arco-menu-has-icon {
justify-content: center;
padding: 0;
}
.arco-menu-title {
display: none;
}
}
:deep(.arco-menu-horizontal) {
flex: 1;
overflow: hidden;
.arco-menu-inner {
padding-left: 0;
.arco-menu-overflow-wrap {
white-space: nowrap;
}
}
}
.layout-mix {
display: flex;
align-items: stretch;
height: 100%;
overflow: hidden;
&-left {
display: flex;
flex-direction: column;
overflow: hidden;
background-color: var(--color-bg-1);
border-right: 1px solid var(--color-border);
}
&-right {
display: flex;
flex: 1;
flex-direction: column;
overflow: hidden;
}
}
.header {
display: flex;
align-items: center;
justify-content: space-between;
height: 56px;
padding: 0 $padding;
overflow: hidden;
color: var(--color-text-1);
background: var(--color-bg-1);
border-bottom: 1px solid var(--color-border);
}
</style>