vue3后台管理系统之layout组件的搭建

news2025/3/1 1:09:02

1.1静态布局

<template>
    <div class="layout_container">
        <!-- 左侧导航 -->
        <div class="layout_slider"></div>
        <!-- 顶部导航 -->
        <div class="layout_tabbar"></div>
        <!-- 内容展示区 -->
        <div class="layout_main"></div>
    </div>
</template>

<!-- <script lang="ts"></script> -->

<style scoped lang="scss">
.layout_container {
    width: 100%;
    height: 100vh;

    .layout_slider {
        color: white;
        width: $base-menu-width;
        height: 100vh;
        background: $base-menu-background;
        transition: all 0.3s;

        .scrollbar {
            width: 100%;
            height: calc(100vh - $base-menu-logo-height);

            .el-menu {
                border-right: none;
            }
        }
    }

    .layout_tabbar {
        position: fixed;
        width: calc(100% - $base-menu-width);
        height: $base-tabbar-height;
        top: 0px;
        left: $base-menu-width;
        transition: all 0.3s;
        background-color: aqua;

        &.fold {
            width: calc(100vw - $base-menu-min-width);
            left: $base-menu-min-width;
        }
    }

    .layout_main {
        position: absolute;
        width: calc(100% - $base-menu-width);
        height: calc(100vh - $base-tabbar-height);
        left: $base-menu-width;
        top: $base-tabbar-height;
        padding: 20px;
        overflow: auto;
        transition: all 0.3s;

        &.fold {
            width: calc(100vw - $base-menu-min-width);
            left: $base-menu-min-width;
        }
    }
}
</style>

//项目提供scss全局变量
// 左侧菜单宽度
//定义项目主题颜色

//左侧的菜单的宽度
$base-menu-width     :260px;
//左侧菜单的背景颜色
$base-menu-background:#001529;
$base-menu-min-width :50px;

// 顶部导航的高度
$base-tabbar-height:50px;

//左侧菜单logo高度设置
$base-menu-logo-height:50px;

//左侧菜单logo右侧文字大小
$base-logo-title-fontSize:20px;

1.2动态logo和标题搭建

src下创建setting.ts

// 项目图标和标题
export default {
    title: 'v3后台管理系统', //项目标题设置
    logo: '../../../../../public/logo.png', //项目配置logo
    logoHidden: true, //logo组件是否隐藏
}

<template>
    <div class="logo" v-if="setting.logoHidden">
        <img :src="setting.logo" alt="" />
        <p>{{ setting.title }}</p>
    </div>
</template>
<script setup lang="ts">
import setting from '@/setting'
</script>
<style scoped lang="scss">
.logo {
    width: 100%;
    height: $base-menu-logo-height;
    color: white;
    display: flex;
    align-items: center;
    padding: 10px;

    img {
        width: 40px;
        height: 40px;
    }

    p {
        font-size: $base-logo-title-fontSize;
        margin-left: 10px;
    }
}
</style>

1.3左侧静态布局

<template>
    <div class="layout_container">
        <!-- 左侧导航 -->
        <div class="layout_slider">
            <Logo />
            <!-- 展示菜单 -->
            <!-- 滚动组件 -->
            <el-scrollbar class="scrollbar">
                <!-- 菜单组件-->
                <el-menu background-color="#001529" text-color="white" active-text-color="yellowgreen">
                    <el-menu-item index="1">首页</el-menu-item>
                    <el-menu-item index="1">数据大屏</el-menu-item>
                    <el-sub-menu index="2">
                        <template #title>权限管理</template>
                        <el-menu-item index="2-1">用户管理</el-menu-item>
                        <el-menu-item index="2-2">角色管理</el-menu-item>
                        <el-menu-item index="2-3">菜单管理</el-menu-item>
                    </el-sub-menu>
                </el-menu>
            </el-scrollbar>
        </div>
        <!-- 顶部导航 -->
        <div class="layout_tabbar"></div>
        <!-- 内容展示区 -->
        <div class="layout_main">
            <h1 style="height: 22222px; background-color: red">我是一个段落</h1>
        </div>
    </div>
</template>
<script setup lang="ts">
import Logo from './components/logo/index.vue'
</script>

<style scoped lang="scss">
.layout_container {
    width: 100%;
    height: 100vh;

    .layout_slider {
        color: white;
        width: $base-menu-width;
        height: 100vh;
        background: $base-menu-background;

        .scrollbar {
            width: 100%;
            height: calc(100vh - $base-menu-logo-height);

            .el-menu {
                border-right: none;
            }
        }
    }

    .layout_tabbar {
        position: fixed;
        width: calc(100% - $base-menu-width);
        height: $base-tabbar-height;
        top: 0px;
        left: $base-menu-width;
        background-color: aqua;
    }

    .layout_main {
        position: absolute;
        width: calc(100% - $base-menu-width);
        height: calc(100vh - $base-tabbar-height);
        left: $base-menu-width;
        top: $base-tabbar-height;
        padding: 20px;
        overflow: auto;
        background-color: yellowgreen;
    }
}
</style>

1.4配置左侧动态路由

store管理路由数组

//引入路由(常量路由)
import { constantRoute } from '@/router/routes'
const useUserStore = defineStore('User', {
    // 小仓库存储数据
    state: (): UserState => {
        return {
            token: GET_TOKEN(),
            menuRoutes: constantRoute, //仓库存储生成菜单需要数组(路由)
        }
    },

import type { RouteRecordRaw } from 'vue-router'
// 定义小仓库state数据类型
export interface UserState {
    token: string | null
    menuRoutes: RouteRecordRaw[]
}

传递路由数组到Menu组件

接收menuList

1.5递归组件生成菜单

修改router

添加meta

meta: {

title: ' 登录 ',

hidden: false //代表路由标题在菜单中是否隐藏

}

hidden代表是否隐藏

// 对外配置路由
import Login from '@/views/login/index.vue'
import Home from '@/views/home/index.vue'
import Error from '@/views/404/index.vue'
import Test from '@/views/test/index.vue'
import Layout from '@/layout/index.vue'
export const constantRoute = [
  {
    path: '/login',
    component: Login,
    name: 'login',
    meta: {
      title: ' 登录 ',
      hidden: false //代表路由标题在菜单中是否隐藏
    }
  },
  {
    path: '/',
    component: Layout,
    name: 'layout',
    meta: {
      title: 'layout ',
      hidden: false //代表路由标题在菜单中是否隐藏  true代表隐藏
    },
    children: [
      {
        path: '/home',
        component: Home,
        meta: {
          title: ' 首页 ',
          hidden: false //代表路由标题在菜单中是否隐藏
        }
      },
      {
        path: '/test',
        component: Test,
        meta: {
          title: ' 测试 ',
          hidden: false //代表路由标题在菜单中是否隐藏
        }
      }
    ]
  },

  {
    path: '/404',
    component: Error,
    name: '404',
    meta: {
      title: ' 404 ',
      hidden: false //代表路由标题在菜单中是否隐藏
    }
  },
  {
    path: '/:pathMatch',
    redirect: '/404',
    name: 'Any',
    meta: {
      title: ' 任意路由 ',
      hidden: true //代表路由标题在菜单中是否隐藏
    }
  }
]

根据判断条件展示路由

<!-- eslint-disable vue/no-reserved-component-names -->
<template>
    <template v-for="item in menuList" :key="item.path">
        <!-- 没有子路由 -->
        <template v-if="!item.children">
            <el-menu-item :index="item.path" v-if="!item.meta.hidden">
                <template #title>
                    <!-- 图标 -->
                    <span>图标</span>
                    <span>{{ item.meta.title }}</span>
                </template>
            </el-menu-item>
        </template>
        <!-- 有子路由但是只有一个子路由 -->
        <template v-if="item.children && item.children.length == 1">
            <el-menu-item
                v-if="!item.children[0].meta.hidden"
                :index="item.children[0].path"
            >
                <template #title>
                    <!-- 图标 -->
                    <span>图标</span>
                    <span>{{ item.children[0].meta.title }}</span>
                </template>
            </el-menu-item>
        </template>

        <!-- 有子路由且个数大于一个 -->
        <el-sub-menu
            :index="item.path"
            v-if="item.children && item.children.length > 1"
        >
            <template #title>
                <span>{{ item.meta.title }}</span>
            </template>
            <Menu :menuList="item.children"></Menu>
        </el-sub-menu>
    </template>
</template>

<script setup lang="ts">
defineProps(['menuList'])
</script>
<script lang="ts">
export default {
    // eslint-disable-next-line vue/no-reserved-component-names
    name: 'Menu',
}
</script>

<style lang="scss" scoped></style>

递归组件实现

1.6配置菜单图标

注册所有图标#

您需要从 @element-plus/icons-vue 中导入所有图标并进行全局注册。

// main.ts

    // 如果您正在使用CDN引入,请删除下面一行。
    import * as ElementPlusIconsVue from '@element-plus/icons-vue'

    const app = createApp(App)
    for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
    app.component(key, component)
    }

import SvgIcon from './SvgIcon/index.vue'
import type { App, Component } from 'vue'
import * as ElementPlusIconsVue from '@element-plus/icons-vue'
const components: { [name: string]: Component } = { SvgIcon }
export default {
    install(app: App) {
        // 注册项目全部的全局组件
        Object.keys(components).forEach((key: string) => {
            app.component(key, components[key])
        })
        // 将element-lpus提供的图标注册为全局组件
        for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
            app.component(key, component)
        }
    },
}

在meta添加属性icon

动态组件component

<!-- 图标 -->
          <el-icon>
            <component :is="item.meta.icon" />
          </el-icon>

<!-- eslint-disable vue/no-reserved-component-names -->
<template>
    <template v-for="item in menuList" :key="item.path">
        <!-- 没有子路由 -->
        <template v-if="!item.children">
            <el-menu-item
                :index="item.path"
                v-if="!item.meta.hidden"
                @click="goRoute"
            >
                <template #title>
                    <!-- 图标 -->
                    <el-icon>
                        <component :is="item.meta.icon"></component>
                    </el-icon>
                    <span>{{ item.meta.title }}</span>
                </template>
            </el-menu-item>
        </template>
        <!-- 有子路由但是只有一个子路由 -->
        <template v-if="item.children && item.children.length == 1">
            <el-menu-item
                v-if="!item.children[0].meta.hidden"
                :index="item.children[0].path"
            >
                <template #title>
                    <!-- 图标 -->
                    <el-icon>
                        <component :is="item.children[0].meta.icon"></component>
                    </el-icon>
                    <span>{{ item.children[0].meta.title }}</span>
                </template>
            </el-menu-item>
        </template>

        <!-- 有子路由且个数大于一个 -->
        <el-sub-menu
            :index="item.path"
            v-if="item.children && item.children.length > 1"
        >
            <template #title>
                <el-icon>
                    <component :is="item.meta.icon"></component>
                </el-icon>
                <span>{{ item.meta.title }}</span>
            </template>
            <Menu :menuList="item.children"></Menu>
        </el-sub-menu>
    </template>
</template>

<script setup lang="ts">
defineProps(['menuList'])
// 点击菜单的回调
const goRoute = (vc: any) => {
    console.log(vc.index)
}
</script>
<script lang="ts">
export default {
    // eslint-disable-next-line vue/no-reserved-component-names
    name: 'Menu',
}
</script>

<style lang="scss" scoped></style>

1.7配置全部路由

一般后台管理系统都有登录、首页、数据大屏、权限管理这几个功能,所以接下来配置这些路由。

// 对外配置路由
import Login from '@/views/login/index.vue'
import Home from '@/views/home/index.vue'
import Error from '@/views/404/index.vue'
import Role from '@/views/acl/role/index.vue'
import User from '@/views/acl/user/index.vue'
import Scree from '@/views/scree/index.vue'
import Layout from '@/layout/index.vue'
export const constantRoute = [
  {
    path: '/login',
    component: Login,
    name: 'login',
    meta: {
      icon: 'Edit',
      title: ' 登录 ',
      hidden: true //代表路由标题在菜单中是否隐藏
    }
  },
  {
    path: '/',
    component: Layout,
    name: 'layout',
    meta: {
      title: 'layout ',
      hidden: false, //代表路由标题在菜单中是否隐藏  true代表隐藏
      icon: 'Promotion'
    },
    redirect: '/home',
    children: [
      {
        path: '/home',
        component: Home,
        meta: {
          title: ' 首页 ',
          hidden: false, //代表路由标题在菜单中是否隐藏
          icon: 'HomeFilled'
        }
      }
    ]
  },
  {
    path: '/scree',
    component: Scree,
    name: 'Screen',
    meta: {
      title: '数据大屏',
      hidden: false,
      icon: 'Histogram'
    }
  },
  {
    path: '/acl',
    component: Layout,
    name: 'Acl',
    meta: {
      title: '权限管理 ',
      icon: 'Lock'
    },
    children: [
      {
        path: '/acl/user',
        component: User,
        meta: {
          title: '用户管理',
          hidden: false, //代表路由标题在菜单中是否隐藏
          icon: 'User'
        }
      },
      {
        path: '/acl/role',
        component: Role,
        meta: {
          title: '角色管理',
          hidden: false, //代表路由标题在菜单中是否隐藏
          icon: 'UserFilled'
        }
      }
    ]
  },

  {
    path: '/404',
    component: Error,
    name: '404',
    meta: {
      icon: 'Edit',
      title: ' 404 ',
      hidden: true //代表路由标题在菜单中是否隐藏
    }
  },
  {
    path: '/:pathMatch',
    redirect: '/404',
    name: 'Any',
    meta: {
      title: ' 任意路由 ',
      hidden: true //代表路由标题在菜单中是否隐藏
    }
  }
]

渲染layout一级路由的子路由

1.8页面加载时默认激活菜单的 index

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1107476.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Linux】Ubuntu存储分析

文章目录 前言1 如何对系统进行存储分析2 如果出现存储空间不足的警告应该怎么办&#xff1f;3 存储空间太小导致不能开机怎么办&#xff1f;4 如何对系统进行扩容 前言 因为要编译一个ARM架构的Linux SDK&#xff0c;结果没想到这个SDK解压编译完大小远超我想象&#xff0c;直…

【算法|前缀和系列No.5】leetcode1314. 矩阵区域和

个人主页&#xff1a;兜里有颗棉花糖 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 兜里有颗棉花糖 原创 收录于专栏【手撕算法系列专栏】【Leetcode】 &#x1f354;本专栏旨在提高自己算法能力的同时&#xff0c;记录一下自己的学习过程&#xff0c;希望…

【茫茫架构路】1. Class File字节码文件解析

本文所有内容的JDK版本为 OpenJDK 11 JDK11 Class File官方说明。 Java解析字节码文件源码参考&#xff0c;以下为部分字节码解析源码展示。 public ClassFile(DataInputStream in) {try {//magic: 0xCAFEBABEthis.magic ClassReader.readInt(in);System.out.println("m…

Vue 异步更新 -- $nextTick

Vue 异步更新 – $nextTick **创建 工程&#xff1a; H:\java_work\java_springboot\vue_study ctrl按住不放 右键 悬着 powershell H:\java_work\java_springboot\js_study\Vue2_3入门到实战-配套资料\01-随堂代码素材\day04\准备代码\16-$nextTick vue --version vue crea…

Spring(四)

1、Spring6整合JUnit 1、JUnit4 User类: package com.songzhishu.spring.bean;import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component;/*** BelongsProject: Spring6* BelongsPackage: com.songzhishu.spring.bean*…

UI自动化测试的痛点

当我们找工作的时候查看招聘信息发现都需要有自动化测试经验&#xff0c;由此看来测试人员不会一点自动化测试技术都不好意思说自己是做软件测试的。大部分测试人员也都是从使用自动化测试工具、录制回放、测试脚本、开发小工具入门自动化测试的&#xff0c;然后在慢慢的接触 U…

微信隐秘功能:如何巧妙隐藏好友和消息的方法教程

在日常使用微信的过程中&#xff0c;难免会有些私密内容或特定的联系人和群聊希望能够暂时隐藏起来&#xff0c;以保护个人隐私。幸运的是&#xff0c;微信提供了一些内置功能以及额外的工具&#xff0c;能够帮助我们实现这一目的。下面就来详细介绍一种简单易行的方法&#xf…

Mac硬盘检测工具

Mac硬盘检测软件是一款用于检测和诊断Mac硬盘健康状态的工具&#xff0c;帮助用户及时发现潜在的硬盘问题&#xff0c;避免数据丢失和系统故障。通过全面的检测和报告功能&#xff0c;用户可以更好地了解自己的硬盘状况&#xff0c;确保数据的安全和可靠。给大家介绍几款好用的…

【算法导论】摊还分析

目录 一、摊还分析简介二、分析的两个问题1.栈操作2. 二进制计数器递增 三、分析方法1. 聚合分析1.1 栈操作1.2 二进制计数递增 2.核算法2.1 栈操作2.2 二进制计数器递增 3. 势能法3.1 栈操作3.2 二进制计数器递增 一、摊还分析简介 在摊还分析中&#xff0c;我们求数据结构的一…

爬取某音乐榜单歌曲

一、打开网页https://music.163.com/&#xff0c;进入榜单&#xff08;热歌榜&#xff09; 二、右键检查、刷新网页&#xff0c;选择元素&#xff08;点击歌曲名&#xff09; 三、相关代码 import requests #正则表达式模块内置模块 import re import osfilename music\\ if …

手写redux的connect方法, 使用了subscribe获取最新数据

一. 公共方法文件 1. connect文件 import React, { useState } from "react"; import MyContext from "./MyContext"; import _ from "lodash";// 模拟react-redux的 connect高阶函数 const connect (mapStateToProps, mapDispatchToProps) &…

都2023了!别再问我,UI自动化测试怎么做了……

本文关键词&#xff1a;移动端UI自动化思路 大家好&#xff0c;我是老司机。之前测试交流群里有同学问“有没有自动化测试在工作中的案例可以分享“&#xff0c;有是有的。 今天我会详细的描述一个【UI自动化实战在实际工作中的应用】&#xff0c;这是之前贝壳找房我们团队做…

vue单向绑定和双向绑定

一、单向绑定就是&#xff1a;修改视图&#xff0c;数据不变&#xff1b;修改数据&#xff08;app.name"1234"&#xff09;&#xff0c;视图会变 二、双向绑定&#xff1a;修改视图&#xff0c;数据会变&#xff1b;修改数据&#xff0c;视图会变 demo&#xff1a; …

屋顶太阳能光伏系统的性能分析指标研究

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

基于晶体结构优化的BP神经网络(分类应用) - 附代码

基于晶体结构优化的BP神经网络&#xff08;分类应用&#xff09; - 附代码 文章目录 基于晶体结构优化的BP神经网络&#xff08;分类应用&#xff09; - 附代码1.鸢尾花iris数据介绍2.数据集整理3.晶体结构优化BP神经网络3.1 BP神经网络参数设置3.2 晶体结构算法应用 4.测试结果…

实现vue导出excel和echart图形分别在不同工作表

背景 实现一键导出excel 并且区分图表和表格为不同的sheet工作表 最终效果为 代码实现 功能实现 <script lang"ts">import * as echarts from echarts;import ExcelJS from exceljs;import { saveAs } from file-saver; import {getAsyncTempCurrentData} …

基于社交网络优化的BP神经网络(分类应用) - 附代码

基于社交网络优化的BP神经网络&#xff08;分类应用&#xff09; - 附代码 文章目录 基于社交网络优化的BP神经网络&#xff08;分类应用&#xff09; - 附代码1.鸢尾花iris数据介绍2.数据集整理3.社交网络优化BP神经网络3.1 BP神经网络参数设置3.2 社交网络算法应用 4.测试结果…

linux进阶(脚本编程/软件安装/进程进阶/系统相关)

一般市第二种,以bash进程执行 shelle脚本编程 env环境变量 set查看所有变量 read设置变量值 echo用于控制台输出 类似java中的sout declear/typeset声明类型 范例 test用于测试表达式 if/else case while for 函数 脚本示例 软件安装及进阶 fork函数(复制一个进程(开启一个进…

TS 泛型你还不会?来!我教你

前言&#xff1a;最近遇到了一些写作上的烦恼&#xff0c;自己好像陷入了程序员的通病想法&#xff0c;“这个知识点这么简单&#xff0c;大家应该都会吧&#xff0c;我说出来是不是显得我很笨。” 思考了近一个月&#xff0c;又翻了翻自己最开始写作的文章&#xff0c;文笔虽…

python二次开发Solidworks:方程式驱动曲线

如果按照维度去划分&#xff0c;SOLIDWORKS中曲线可以划分为平面曲线和空间曲线&#xff0c;并且在二维草图还是3D草图中都提供了“方程式驱动曲线”。但是从使用方法来讲&#xff0c;方程式驱动的曲线分为两种定义方式:“显性”和“参数式”。“显式方程”在定义了起点和终点处…