首页 前端知识 Layout 布局组件快速搭建

Layout 布局组件快速搭建

2024-08-22 23:08:04 前端知识 前端哥 271 930 我要收藏

文章目录

  • 设置主题样式变量
  • 封装公共布局组件
    • 封装 Logo 组件
    • 封装 Menu 菜单组件
    • 封装 Breadcrumb 面包屑组件
    • 封装 TabBar 标签栏组件
    • 封装 Main 内容区组件
    • 封装 Footer 底部组件
    • 封装 Theme 主题组件
  • 经典布局
  • 水平布局
  • 响应式布局
  • 搭建 Layout 布局组件
  • 添加 Layout 路由配置
  • 启动项目

设置主题样式变量

  1. 创建主题变量文件:src/assets/styles/theme.scss

  2. 添加页面布局组件用到的变量:

    :root {
    --os-layout-aside-width: 224px; // 侧边栏宽度
    --os-layout-logo-height: 56px; // 头部高度
    --os-layout-header-height: 56px; // 头部高度
    --os-layout-header-menu-width: 300px; // 头部菜单宽度
    --os-layout-footer-height: 40px; // 底部高度
    --os-layout-tab-height: 40px; // 底部高度
    }
    复制
  3. 设置全局滚动条样式:

    /* scroll bar */
    ::-webkit-scrollbar {
    width: 6px;
    height: 6px;
    }
    ::-webkit-scrollbar-thumb {
    background-color: var(--el-border-color-darker);
    border-radius: 20px;
    }
    复制
  4. 在统一管理和维护项目的样式文件src/assets/styles/index.scss中导入主题样式文件:

    @import "theme.scss";
    复制

注:统一管理和维护项目的样式文件src/assets/styles/index.scss需在mian.ts中导入,注意导入顺序。

封装公共布局组件

封装 Logo 组件

  1. 创建 Logo 组件:src/layouts/components/Logo/index.vue

  2. 通过import.meta.env获取.env文件中定义的环境变量,环境变量使用详情跳转认识和使用 Vite 环境变量配置:

    <script setup lang="ts">
    const app_title = import.meta.env.VITE_APP_TITLE
    </script>
    复制
  3. Logo 组件内容,添加 Logo 图标和项目标题:

    <template>
    <!-- logo 容器 -->
    <div class="logo-container">
    <!-- logo 图标 -->
    <svg-icon width="32px" height="32px" name="logo" />
    <!-- 应用标题,只在中等及以上屏幕尺寸上可见 -->
    <span class="logo-title hidden md:block">{{ app_title }}</span>
    </div>
    </template>
    复制
  4. 添加样式:

    <style scoped lang="postcss">
    .logo-container {
    @apply flex items-center justify-center;
    height: var(--os-layout-logo-height);
    width: var(--os-layout-aside-width);
    .logo-title {
    @apply text-lg font-bold subpixel-antialiased;
    @apply bg-clip-text text-transparent bg-gradient-to-r from-pink-500 to-violet-500;
    }
    }
    </style>
    复制

封装 Menu 菜单组件

  1. 创建 Menu 菜单组件:src/layouts/components/Menu/index.vue

  2. el-menu 组件中使用 props 来控制 mode 属性,可以实现菜单的不同显示模式(如 verticalhorizontal):

    <script setup lang="ts">
    const props = defineProps<{
    mode?: 'vertical' | 'horizontal'
    }>()
    </script>
    复制
  3. 使用 ElementPlus 的 el-menu 菜单组件搭建垂直菜单:

    <template>
    <!-- 菜单容器 -->
    <div class="menu-container">
    <!-- Element UI 菜单组件 -->
    <el-menu
    class="os-menu"
    :mode="props.mode"
    >
    <!-- 一级菜单 -->
    <el-sub-menu index="1">
    <!-- 一级菜单标题 -->
    <template #title>
    <span>Navigator One</span>
    </template>
    <!-- 二级菜单 -->
    <el-menu-item index="1-1">item one</el-menu-item>
    <el-menu-item index="1-2">item two</el-menu-item>
    </el-sub-menu>
    <!-- 一级菜单 -->
    <el-menu-item index="2">
    <span>Navigator Two</span>
    </el-menu-item>
    </el-menu>
    </div>
    </template>
    复制
  4. 添加样式:

    <style scoped lang="postcss">
    .menu-container{
    background-color: var(--el-menu-bg-color);
    .os-menu{
    @apply border-0;
    &.el-menu--horizontal{
    height: calc(var(--os-layout-logo-height) - 1px);
    }
    }
    }
    </style>
    复制

封装 Breadcrumb 面包屑组件

  1. 创建 src/layouts/components/Breadcrumb/index.vue

  2. 使用 ElementPlus el-breadcrumb 面包屑组件:

    <template>
    <el-breadcrumb separator="/" class="breadcrumb-container">
    <el-breadcrumb-item :to="{ path: '/' }">homepage</el-breadcrumb-item>
    <el-breadcrumb-item>management</el-breadcrumb-item>
    <el-breadcrumb-item>list</el-breadcrumb-item>
    <el-breadcrumb-item>detail</el-breadcrumb-item>
    </el-breadcrumb>
    </template>
    复制
  3. 添加样式:

    <style scoped lang="postcss">
    .breadcrumb-container {
    display: flex;
    flex-wrap: nowrap; /* 防止换行 */
    overflow: hidden; /* 横向滚动溢出处理 */
    padding-right: 12px;
    }
    </style>
    复制

封装 TabBar 标签栏组件

  1. 创建 src/layouts/components/TabBar/index.vue

  2. 生成 TabBar 标签数据:

    <script setup lang="ts">
    const tabs = Array(1).fill({
    label: 'About',
    name: 'about',
    icon: 'About',
    path: '/about'
    })
    </script>
    复制
  3. 使用 ElementPlus el-tabs 标签组件,Scrollbar 组件替换浏览器原生滚动条:

    <template>
    <!-- Tab Bar -->
    <div class="tab-bar-container">
    <div class="tab-bar-box">
    <el-scrollbar>
    <el-tabs
    tab-position="top"
    type="card"
    closable
    >
    <el-tab-pane
    v-for="(item, index) in tabs"
    :key="index"
    :name="item.name"
    >
    <template #label>
    <span class="tab-title">
    <svg-icon :name="item.icon" />
    <span>{{ item.label }}</span>
    </span>
    </template>
    </el-tab-pane>
    </el-tabs>
    </el-scrollbar>
    </div>
    <div class="more-button">
    <el-dropdown trigger="click">
    <span>
    <svg-icon name="More" />
    </span>
    <template #dropdown>
    <el-dropdown-menu>
    <el-dropdown-item>关闭</el-dropdown-item>
    </el-dropdown-menu>
    </template>
    </el-dropdown>
    </div>
    </div>
    </template>
    复制
  4. 添加样式:

    <style scoped lang="scss">
    .tab-bar-container {
    display: flex;
    align-items: center;
    gap: 8px;
    border-bottom: 1px solid var(--el-border-color-light);
    .tab-bar-box {
    display: flex;
    flex-grow: 1;
    margin-left: 8px;
    width: calc(100% - 60px);
    .tab-title{
    display: flex;
    align-items: center;
    gap: 8px;
    }
    }
    .more-button {
    display: flex;
    align-items: center;
    justify-content: center;
    width: var(--os-layout-tab-height);
    height: var(--os-layout-tab-height);
    border-left: 1px solid var(--el-border-color-light);
    }
    }
    :deep(.el-tabs) {
    .el-tabs__header {
    height: var(--os-layout-tab-height);
    padding: 0;
    margin: 0;
    border-bottom: none;
    .el-tabs__nav-wrap {
    .el-tabs__nav-scroll {
    .el-tabs__nav {
    border: none;
    .el-tabs__item {
    height: calc(var(--os-layout-tab-height) - 2px);
    line-height: calc(var(--os-layout-tab-height) - 2px);
    border: none;
    position: relative; // 确保父元素是相对定位的
    &::before {
    content: '';
    position: absolute;
    bottom: 0;
    width: 100%;
    height: 2px;
    background-color: transparent;
    }
    &.is-active {
    color: var(--el-color-primary);
    fill: var(--el-color-primary);
    &::before {
    background-color: var(--el-color-primary); // 改变背景色以显示下划线
    }
    }
    }
    }
    }
    }
    }
    }
    </style>
    复制

封装 Main 内容区组件

  1. 创建 src/layouts/components/Main/index.vue

    <script setup lang="ts">
    </script>
    <template>
    <div>Main</div>
    </template>
    <style scoped lang="scss">
    </style>
    复制
  2. 后续补充内容…

封装 Footer 底部组件

  1. 创建 src/layouts/components/Footer/index.vue

  2. 通过import.meta.env获取.env文件中定义的环境变量

    <script setup lang="ts">
    const app_title = import.meta.env.VITE_APP_TITLE
    </script>
    复制
  3. 添加内容:

    <template>
    <div class="footer-container">
    <!-- 左侧:公司名称或标志 -->
    <div class="flex-content">
    <svg-icon name="logo" />
    <span class="logo-title">{{ app_title }}</span>
    </div>
    <!-- 中间:导航链接 -->
    <div class="flex-content">
    <a href="about">关于我们</a>
    <a href="#contact">联系我们</a>
    <a href="#privacy-policy">隐私政策</a>
    <a href="#terms-of-service">服务条款</a>
    </div>
    <!-- 右侧:版权信息 -->
    <div class="text-right">&copy; 2024 Octopus. 保留所有权利.</div>
    </div>
    </template>
    复制
  4. 添加样式:

    <style scoped lang="postcss">
    .footer-container{
    @apply flex items-center w-full;
    @apply select-none text-xs;
    height: var(--os-layout-footer-height);
    @apply justify-center lg:justify-between;
    .flex-content{
    @apply items-center gap-2;
    @apply flex justify-center gap-1 text-gray-400;
    @apply hidden lg:flex;
    .logo-title {
    @apply font-bold subpixel-antialiased text-sm;
    @apply bg-clip-text text-transparent bg-gradient-to-r from-pink-500 to-violet-500;
    }
    }
    }
    </style>
    复制

封装 Theme 主题组件

  1. 创建 src/stores/modules/theme/types.ts 定义主题类型:

    export type LayoutMode = 'classic' | 'horizontal' | 'responsive'
    // 主题配置
    export interface ThemeConfig {
    showThemeConfig: boolean,
    darkThemeEnabled: boolean,
    grayThemeEnabled: boolean,
    layoutMode: LayoutMode,
    themePrimaryColor: string,
    predefineColors: string[]
    }
    复制
  2. 创建 src/stores/modules/theme/index.ts 仓库存储主题状态:

    import { defineStore } from 'pinia'
    import { reactive, watch } from 'vue'
    import { ThemeConfig } from './types'
    import { ElMessage } from 'element-plus'
    import { getDarkColor, getLightColor } from '@/utils/Color'
    // theme store
    export const useThemeStore = defineStore(
    'theme',
    () => {
    const themeConfig = reactive<ThemeConfig>({
    showThemeConfig: false,
    darkThemeEnabled: false,
    grayThemeEnabled: false,
    layoutMode: 'classic',
    themePrimaryColor: '#409eff',
    predefineColors: ['#409eff']
    })
    // 切换显示状态的方法
    const toggleThemeConfig = () => {
    themeConfig.showThemeConfig = !themeConfig.showThemeConfig
    }
    // 黑暗主题切换
    watch(() => themeConfig.darkThemeEnabled, () => {
    const html = document.documentElement as HTMLElement
    if (themeConfig.darkThemeEnabled) {
    html.setAttribute('class', 'dark')
    } else {
    html.removeAttribute('class')
    }
    changeThemeColor(themeConfig.themePrimaryColor)
    })
    watch(() => themeConfig.grayThemeEnabled,() => {
    if (themeConfig.grayThemeEnabled) {
    document.documentElement.dataset.theme = 'gray'
    } else {
    document.documentElement.dataset.theme = ''
    }
    })
    // 添加预定义颜色
    function addPredefineColor(color:string) {
    console.log('color', color)
    const predefineColors = themeConfig.predefineColors
    // 查找元素的索引
    const index = predefineColors.indexOf(color)
    if (index !== -1) {
    // 使用 splice 方法删除该元素
    predefineColors.splice(index, 1)
    }
    if (themeConfig.predefineColors.length === 10) {
    themeConfig.predefineColors.pop() // 删除队列中的第一个元素
    }
    themeConfig.predefineColors.unshift(color)
    }
    // 修改主题颜色
    const changeThemeColor = (color:string ) => {
    if (!color) {
    color = '#409eff'
    ElMessage({ type: 'success', message: '主题颜色已重置' })
    }
    addPredefineColor(color)
    // 计算主题颜色变化
    document.documentElement.style.setProperty('--el-color-primary', color)
    document.documentElement.style.setProperty(
    '--el-color-primary-dark-2',
    themeConfig.darkThemeEnabled ? `${getLightColor(color, 0.2)}` : `${getDarkColor(color, 0.3)}`
    )
    for (let i = 1; i <= 9; i++) {
    const primaryColor = themeConfig.darkThemeEnabled ? `${getDarkColor(color, i / 10)}` : `${getLightColor(color, i / 10)}`
    document.documentElement.style.setProperty(`--el-color-primary-light-${i}`, primaryColor)
    }
    }
    return {
    themeConfig,
    toggleThemeConfig,
    changeThemeColor
    }
    },
    {
    persist: true
    })
    复制
  3. 创建 src/layouts/components/Theme/index.vue 主题组件并引入主题仓库:

    <script setup lang="ts">
    import { useThemeStore } from '@/stores/modules/theme'
    import { onMounted } from 'vue'
    const { themeConfig, changeThemeColor } = useThemeStore()
    onMounted(() => {
    // 在组件挂载到 DOM 后更新主题颜色
    changeThemeColor(themeConfig.themePrimaryColor)
    })
    </script>
    复制
  4. 使用 el-drawer 抽屉组件进行主题布局:

    <template>
    <div>
    <el-drawer v-model="themeConfig.showThemeConfig" size="300">
    <template #header="{ titleId }">
    <span :id="titleId" class="theme-header"> 主题配置 </span>
    </template>
    <el-scrollbar>
    <div class="mr-4">
    <el-divider>布局样式</el-divider>
    <div class="layout-box">
    <el-tooltip effect="dark" content="经典布局" placement="top" :show-after="200">
    <div
    :class="['layout-item layout-classic', { 'is-active': themeConfig.layoutMode == 'classic' }]"
    @click="themeConfig.layoutMode='classic'"
    >
    <div class="layout-dark"></div>
    <div class="layout-container">
    <div class="layout-light"></div>
    <div class="layout-content"></div>
    </div>
    <svg-icon class="select-layout" name="About" v-if="themeConfig.layoutMode == 'classic'" />
    </div>
    </el-tooltip>
    <el-tooltip
    effect="dark"
    content="横向布局"
    placement="top"
    :show-after="200"
    >
    <div
    :class="['layout-item layout-transverse', { 'is-active': themeConfig.layoutMode == 'horizontal' }]"
    @click="themeConfig.layoutMode='horizontal'"
    >
    <div class="layout-dark"></div>
    <div class="layout-content"></div>
    <svg-icon class="select-layout" name="About" v-if=" themeConfig.layoutMode == 'horizontal'" />
    </div>
    </el-tooltip>
    </div>
    <el-divider>主题风格</el-divider>
    <div class="switch-container">
    <div class="switch-box">
    <div class="switch-title">
    <span>主题颜色</span>
    <el-tooltip content="主题颜色" placement="top">
    <svg-icon class="size-4" name="About"></svg-icon>
    </el-tooltip>
    </div>
    <el-color-picker
    color-format="hex"
    v-model="themeConfig.themePrimaryColor"
    :predefine="themeConfig.predefineColors"
    @change="changeThemeColor"
    />
    </div>
    <div class="switch-box">
    <div class="switch-title">
    <span>暗黑主题</span>
    <el-tooltip content="暗黑主题" placement="top">
    <svg-icon class="size-4" name="About"></svg-icon>
    </el-tooltip>
    </div>
    <el-switch v-model="themeConfig.darkThemeEnabled" />
    </div>
    <div class="switch-box">
    <div class="switch-title">
    <span>灰色模式</span>
    <el-tooltip content="灰色模式" placement="top">
    <svg-icon class="size-4" name="About"></svg-icon>
    </el-tooltip>
    </div>
    <el-switch v-model="themeConfig.grayThemeEnabled" />
    </div>
    </div>
    <el-divider>界面设置</el-divider>
    <div class="switch-container">
    <div class="switch-box">
    <div class="switch-title">
    <span>theme</span>
    </div>
    <el-switch inline-prompt />
    </div>
    </div>
    </div>
    </el-scrollbar>
    </el-drawer>
    </div>
    </template>
    复制
  5. 为组件添加样式:

    <style scoped lang="postcss">
    .theme-header {
    @apply text-base font-bold flex items-center;
    }
    .switch-container {
    @apply flex-grow space-y-2;
    }
    .switch-box {
    @apply flex justify-between items-center w-full;
    }
    .switch-title {
    @apply flex gap-2 items-center;
    }
    :deep(.el-drawer__header) {
    @apply px-5 py-0 h-12 border-b-2 border-solid m-0;
    color: var(--el-text-color-regular);
    border-color: var(--el-border-color-light);
    }
    :deep(.el-drawer__title) {
    font-size: 20px;
    }
    :deep(.el-drawer__body) {
    @apply py-0 pr-0;
    }
    .layout-box {
    position: relative;
    display: flex;
    flex-wrap: wrap;
    justify-content: space-between;
    padding: 15px 15px 0;
    .layout-item {
    position: relative;
    box-sizing: border-box;
    width: 100px;
    height: 70px;
    padding: 6px;
    cursor: pointer;
    border-radius: 5px;
    box-shadow: 0 0 5px 1px var(--el-border-color-dark);
    transition: all 0.2s;
    .layout-dark {
    background-color: var(--el-color-primary);
    border-radius: 3px;
    }
    .layout-light {
    background-color: var(--el-color-primary-light-5);
    border-radius: 3px;
    }
    .layout-content {
    background-color: var(--el-color-primary-light-8);
    border: 1px dashed var(--el-color-primary);
    border-radius: 3px;
    }
    .select-layout {
    position: absolute;
    right: 10px;
    bottom: 10px;
    fill: var(--el-color-primary);
    transition: all 0.2s;
    }
    &:hover {
    box-shadow: 0 0 5px 1px var(--el-text-color-secondary);
    }
    }
    .is-active {
    box-shadow: 0 0 0 2px var(--el-color-primary) !important;
    }
    .layout-classic {
    display: flex;
    justify-content: space-between;
    margin-bottom: 20px;
    .layout-dark {
    width: 20%;
    }
    .layout-container {
    display: flex;
    flex-direction: column;
    justify-content: space-between;
    width: 72%;
    .layout-light {
    height: 20%;
    }
    .layout-content {
    height: 67%;
    }
    }
    }
    .layout-transverse {
    display: flex;
    flex-direction: column;
    justify-content: space-between;
    margin-bottom: 15px;
    .layout-dark {
    height: 20%;
    }
    .layout-content {
    height: 67%;
    }
    }
    }
    </style>
    复制

经典布局

  1. 创建经典页面布局组件:src/layouts/LayoutClassic/index.vue

  2. 导入布局组件:

    <script setup lang="ts">
    import Footer from '@/layouts/components/Footer/index.vue'; // 引入页脚组件
    import Menu from '@/layouts/components/Menu/index.vue'; // 引入菜单组件
    import Logo from '@/layouts/components/Logo/index.vue'; // 引入Logo组件
    import Main from '@/layouts/components/Main/index.vue'; // 引入主要内容组件
    import TabBar from '@/layouts/components/TabBar/index.vue'; // 引入标签栏组件
    import Breadcrumb from '@/layouts/components/Breadcrumb/index.vue'; // 引入面包屑导航组件
    import { useThemeStore } from '@/stores/modules/theme' // 引入主题仓库
    const { toggleThemeConfig } = useThemeStore()
    复制
  3. 使用 ElementPlus 提供的 Container 布局容器快速搭建页面的基本结构:

  4. 在组件中使用布局组件划分出页面结构:侧边菜单栏、顶部、Tab 标签栏、主体和底部:

    <el-container>:外层容器。 当子元素中包含 <el-header><el-footer> 时,全部子元素会垂直上下排列, 否则会水平左右排列。

    <el-header>:顶栏容器。

    <el-aside>:侧边栏容器。

    <el-main>:主要区域容器。

    <el-footer>:底栏容器。

    <template>
    <div class="layout-container">
    <el-container>
    <!-- 左侧边栏 -->
    <el-aside>
    <!-- Logo 组件 -->
    <Logo class="os-logo"/>
    <!-- 菜单组件 -->
    <Menu class="menu-classic" />
    </el-aside>
    <el-container>
    <!-- 页面顶部区域 -->
    <el-header>
    <!-- 头部内容区域 -->
    <div class="header-content">
    <!-- 折叠按钮 -->
    <svg-icon class="fold-expand-button" name="Fold" />
    <!-- 面包屑导航 -->
    <Breadcrumb />
    </div>
    <!-- 头部菜单区域 -->
    <div class="header-menu">
    <!-- 语言切换下拉菜单 -->
    <el-dropdown :hide-on-click="false">
    <svg-icon name="Earth" />
    <template #dropdown>
    <el-dropdown-menu>
    <el-dropdown-item>简体中文</el-dropdown-item>
    </el-dropdown-menu>
    </template>
    </el-dropdown>
    <!-- 搜索按钮 -->
    <svg-icon class="cursor-pointer" name="Search" />
    <!-- 主题配置按钮 -->
    <el-tooltip content="主题配置" placement="bottom">
    <svg-icon class="cursor-pointer" name="Theme" @click="toggleThemeConfig" />
    </el-tooltip>
    <!-- 锁屏按钮 -->
    <el-tooltip content="锁屏" placement="bottom">
    <svg-icon class="cursor-pointer" name="Lock" />
    </el-tooltip>
    <!-- 全屏按钮 -->
    <el-tooltip content="全屏" placement="bottom">
    <svg-icon class="cursor-pointer" name="FullScreen" />
    </el-tooltip>
    <!-- 用户信息下拉菜单 -->
    <el-dropdown>
    <div class="flex items-center gap-1">
    <el-tag type="primary">Admin</el-tag>
    <el-avatar src="https://cube.elemecdn.com/0/88/03b0d39583f48206768a7534e55bcpng.png" />
    </div>
    <template #dropdown>
    <el-dropdown-menu>
    <el-dropdown-item command="profile">
    <template #default>
    <div class="flex items-center gap-1">
    <svg-icon name="User" />
    <span>个人中心</span>
    </div>
    </template>
    </el-dropdown-item>
    <el-dropdown-item command="logout">
    <template #default>
    <div class="flex items-center gap-1">
    <svg-icon name="Logout" />
    <span>退出登录</span>
    </div>
    </template>
    </el-dropdown-item>
    </el-dropdown-menu>
    </template>
    </el-dropdown>
    </div>
    </el-header>
    <!-- 标签栏 -->
    <tab-bar class="tab-container" />
    <!-- 主内容区域 -->
    <el-main>
    <Main />
    </el-main>
    <!-- 底部页脚 -->
    <el-footer>
    <Footer />
    </el-footer>
    </el-container>
    </el-container>
    </div>
    </template>
    复制
  5. 为组件添加样式:

    <style scoped lang="postcss">
    .layout-container {
    @apply w-screen h-screen;
    .el-aside {
    @apply h-full;
    @apply border-r border-solid overflow-hidden;
    border-color: var(--el-border-color-light);
    width: var(--os-layout-aside-width);
    .os-logo {
    @apply overflow-hidden pl-2;
    }
    .menu-classic {
    height: calc(100vh - var(--os-layout-logo-height));
    }
    }
    .el-container {
    @apply h-full w-full;
    .tab-container {
    height: var(--os-layout-tab-height);
    }
    .el-header {
    @apply flex items-center justify-between;
    @apply border-b border-solid overflow-hidden;
    border-color: var(--el-border-color-light);
    height: var(--os-layout-header-height);
    .header-content {
    @apply flex items-center gap-5;
    .fold-expand-button {
    @apply cursor-pointer;
    @apply size-6;
    }
    }
    .header-menu {
    @apply flex items-center justify-between gap-3;
    }
    }
    .el-main {
    @apply h-full p-3;
    background-color: var(--el-bg-color-page);
    }
    .el-footer {
    height: var(--os-layout-footer-height);
    }
    }
    }
    </style>
    复制

水平布局

  1. 创建经典页面布局组件:src/layouts/LayoutClassic/index.vue

  2. 导入布局组件:

    <script setup lang="ts">
    import Footer from '@/layouts/components/Footer/index.vue'; // 引入页脚组件
    import Menu from '@/layouts/components/Menu/index.vue'; // 引入菜单组件
    import Logo from '@/layouts/components/Logo/index.vue'; // 引入Logo组件
    import Main from '@/layouts/components/Main/index.vue'; // 引入主要内容组件
    import TabBar from '@/layouts/components/TabBar/index.vue'; // 引入标签栏组件
    import { useThemeStore } from '@/stores/modules/theme' // 引入主题仓库
    const { toggleThemeConfig } = useThemeStore()
    </script>
    复制
  3. 在组件中使用 Element UI 的布局组件划分出页面结构:顶部、Tab 标签栏、主体和底部:

    <template>
    <div class="layout-container">
    <el-container>
    <el-container>
    <el-header>
    <div class="flex">
    <Logo class="os-logo" />
    <Menu mode="horizontal" class="menu-horizontal " />
    </div>
    <div class="header-menu">
    <!-- 语言转换 -->
    <el-dropdown :hide-on-click="false">
    <svg-icon name="Earth" />
    <template #dropdown>
    <el-dropdown-menu>
    <el-dropdown-item>简体中文</el-dropdown-item>
    </el-dropdown-menu>
    </template>
    </el-dropdown>
    <!-- 搜索按钮 -->
    <svg-icon class="cursor-pointer" name="Search" />
    <!-- 皮肤按钮 -->
    <el-tooltip content="主题配置" placement="bottom">
    <svg-icon class="cursor-pointer" name="Theme" @click="toggleThemeConfig"/>
    </el-tooltip>
    <!-- 锁屏按钮 -->
    <el-tooltip content="锁屏" placement="bottom">
    <svg-icon class="cursor-pointer" name="Lock" />
    </el-tooltip>
    <!-- 全屏按钮 -->
    <el-tooltip content="全屏" placement="bottom">
    <svg-icon class="cursor-pointer" name="FullScreen" />
    </el-tooltip>
    <!-- 用户信息 -->
    <el-dropdown>
    <div class="flex items-center gap-1">
    <el-tag type="primary">Admin</el-tag>
    <el-avatar src="https://cube.elemecdn.com/0/88/03b0d39583f48206768a7534e55bcpng.png" />
    </div>
    <template #dropdown>
    <el-dropdown-menu>
    <el-dropdown-item command="profile">
    <template #default>
    <div class="flex items-center gap-1">
    <svg-icon name="User" />
    <span>个人中心</span>
    </div>
    </template>
    </el-dropdown-item>
    <el-dropdown-item command="logout">
    <template #default>
    <div class="flex items-center gap-1">
    <svg-icon name="Logout" />
    <span>退出登录</span>
    </div>
    </template>
    </el-dropdown-item>
    </el-dropdown-menu>
    </template>
    </el-dropdown>
    </div>
    </el-header>
    <tab-bar class="tab-container" />
    <el-main>
    <Main />
    </el-main>
    <el-footer>
    <Footer />
    </el-footer>
    </el-container>
    </el-container>
    </div>
    </template>
    复制
  4. 添加样式:

    <style scoped lang="postcss">
    .layout-container {
    @apply w-screen h-screen;
    .el-aside {
    @apply bg-amber-300 h-full;
    width: var(--os-layout-aside-width);
    }
    .el-container {
    @apply h-full w-full;
    .tab-container {
    height: var(--os-layout-tab-height);
    }
    .el-header {
    @apply w-full pl-2 pr-2 flex justify-between;
    height: var(--os-layout-header-height);
    @apply border-b border-solid overflow-hidden;
    border-color: var(--el-border-color-light);
    .menu-horizontal{
    width: calc(100vw - var(--os-layout-aside-width) - var(--os-layout-header-menu-width));
    }
    .header-menu{
    @apply flex items-center justify-between gap-3;
    }
    }
    .el-main {
    @apply h-full p-3;
    background-color:var(--el-bg-color-page);
    }
    .el-footer {
    height: var(--os-layout-footer-height);
    }
    }
    }
    </style>
    复制

响应式布局

  1. 创建src/layouts/LayoutResponsive/index.vue响应式布局组件

  2. 添加公共组件:

    <script setup lang="ts">
    import Logo from '@/layouts/components/Logo/index.vue'
    import Main from '@/layouts/components/Main/index.vue'
    import Menu from '@/layouts/components/Menu/index.vue'
    import { ref } from 'vue'
    import { useThemeStore } from '@/stores/modules/theme'
    const { themeConfig } = useThemeStore()
    const navDrawer = ref(false)
    const menuDrawer = ref(false)
    const translateShow = ref(false)
    </script>
    复制
  3. 页面布局:

    <template>
    <div class="layout-responsive">
    <el-container>
    <el-header class="flex items-center justify-between">
    <div class="h-full flex items-center gap-2">
    <svg-icon width="30px" height="30px" name="menu" @click="menuDrawer = !menuDrawer" />
    <Logo />
    </div>
    <div class="header-menu">
    <!-- 搜索按钮 -->
    <svg-icon width="28px" height="28px" class="cursor-pointer" name="Search" />
    <!-- 用户信息 -->
    <el-avatar src="https://cube.elemecdn.com/0/88/03b0d39583f48206768a7534e55bcpng.png"
    @click="navDrawer = !navDrawer" />
    <!-- 导航信息 -->
    <el-drawer v-model="navDrawer" size="100%">
    <template #header>
    <Logo />
    </template>
    <div class="nav-container ">
    <a href="#">个人中心</a>
    <el-divider />
    <div>
    <span class="flex items-center justify-between" @click="translateShow = !translateShow">
    翻译
    <svg-icon name="Earth"></svg-icon>
    </span>
    <!-- 语言列表 -->
    <transition name="fade">
    <div v-show="translateShow" id="language-list" class="language-list mt-2">
    <ul>
    <li class="p-2">英语</li>
    <li class="p-2">中文</li>
    <li class="p-2">西班牙语</li>
    </ul>
    </div>
    </transition>
    </div>
    <el-divider />
    <div class="nav-theme-switch">
    <span>暗黑主题</span>
    <el-switch v-model="themeConfig.darkThemeEnabled" size="large" />
    </div>
    <el-divider />
    <span>退出登录</span>
    </div>
    </el-drawer>
    </div>
    </el-header>
    <el-main>
    <el-backtop target=".el-main" />
    <Main />
    </el-main>
    </el-container>
    <!-- 菜单组件 -->
    <el-drawer
    v-model="menuDrawer"
    direction="ltr"
    size="320px"
    >
    <Menu />
    </el-drawer>
    </div>
    </template>
    复制
  4. 添加样式:

    <style scoped lang="postcss">
    .layout-responsive {
    @apply w-screen h-screen;
    .el-container {
    @apply h-full w-full;
    .el-header {
    @apply w-full flex pl-2;
    height: var(--os-layout-header-height);
    @apply border-b border-solid overflow-hidden;
    border-color: var(--el-border-color-light);
    .header-menu {
    @apply flex items-center justify-between gap-3;
    }
    }
    .tab-container {
    background-color: var(--el-bg-color);
    }
    .el-main {
    @apply h-full;
    background-color: var(--el-bg-color-page);
    }
    }
    }
    :deep(.el-drawer__header) {
    @apply px-3 py-0 h-14 border-solid m-2;
    color: var(--el-text-color-regular);
    border-color: var(--el-border-color-light);
    }
    :deep(.el-drawer__title) {
    font-size: 20px;
    }
    :deep(.el-drawer__body) {
    @apply py-0 px-8;
    }
    :deep(.el-drawer__close-btn) {
    @apply text-3xl;
    }
    .nav-container {
    margin: 0 auto;
    padding: 24px 0 96px;
    max-width: 18rem;
    }
    .nav-theme-switch {
    @apply flex items-center justify-between rounded-md;
    }
    /* 添加动画效果 */
    .fade-enter-active {
    transition: opacity 0.5s ease;
    }
    .fade-leave-active {
    transition: opacity 0.2s ease;
    }
    .fade-enter-from, .fade-leave-to {
    opacity: 0;
    }
    .logo-title {
    @apply text-lg font-bold subpixel-antialiased;
    @apply bg-clip-text text-transparent bg-gradient-to-r from-pink-500 to-violet-500;
    }
    </style>
    复制

搭建 Layout 布局组件

  1. 创建 Layout 布局组件 src/layouts/index.vue

  2. 导入经典布局组件 LayoutClassic 和水平布局组件 LayoutHorizontal,创建一个 layoutMode 对象来将布局模式字符串映射到相应的组件,使用 computed 函数根据 mode 动态选择布局模式。

    <script setup lang="ts">
    import { type Component, computed, onMounted, onUnmounted, ref, watch } from 'vue'
    import LayoutClassic from '@/layouts/LayoutClassic/index.vue'
    import LayoutHorizontal from '@/layouts/LayoutHorizontal/index.vue'
    import LayoutResponsive from '@/layouts/LayoutResponsive/index.vue'
    import Theme from '@/layouts/components/Theme/index.vue'
    import { useThemeStore } from '@/stores/modules/theme'
    const { themeConfig } = useThemeStore()
    // 定义布局模式与对应的组件类型
    const layoutMode: Record<string, Component> = {
    classic: LayoutClassic, // 经典布局
    horizontal: LayoutHorizontal, // 横向布局
    responsive: LayoutResponsive // 响应式布局
    }
    // 布局模式
    const layMode = ref(themeConfig.layoutMode)
    // 监听主题配置模式
    watch(() => themeConfig.layoutMode, (newMode) => {
    layMode.value = newMode
    })
    // 计算属性,根据具体逻辑返回所选布局的组件
    const mode = computed(() => layoutMode[layMode.value])
    // 响应式布局,切换布局模式
    const updateMode = () => {
    if (window.innerWidth <= 768) {
    layMode.value = 'responsive'
    } else {
    layMode.value = themeConfig.layoutMode
    }
    }
    onMounted(() => {
    // 初始更新
    updateMode()
    window.addEventListener('resize', updateMode)
    })
    onUnmounted(() => {
    window.removeEventListener('resize', updateMode)
    })
    </script>
    <template>
    <!-- 根据 mode 的值渲染不同的布局样式 -->
    <component :is="mode" />
    <Theme />
    </template>
    <style scoped lang="postcss">
    </style>
    复制

添加 Layout 路由配置

  1. src/App.vue 中添加代码,当访问路由路径时,Vue Router 会根据路由配置将对应的组件渲染到 <router-view> 中,实现单页应用的页面切换效果:

    <template>
    <router-view />
    </template>
    复制
  2. src/router/index.ts 中添加路由:

    const router = createRouter({
    routes: [
    {
    path: '/',
    name: 'layout',
    component: () => import('@/layouts/index.vue')
    }
    ]
    })
    复制

启动项目

  1. 终端执行命令启动项目:

    pnpm run dev
    复制
  2. 浏览器访问:http://localhost:8080/,默认为经典布局样式:

    image-20240805095808149

  3. src/layouts/index.vue 中切换为水平布局样式,查看页面:

    const mode = computed(() => layoutMode['horizontal'])
    复制

    image-20240805095901012

转载请注明出处或者链接地址:https://www.qianduange.cn//article/16592.html
标签
评论
发布的文章

安装Nodejs后,npm无法使用

2024-11-30 11:11:38

大家推荐的文章
会员中心 联系我 留言建议 回顶部
复制成功!