Vue+Element Plus后台管理主界面搭建实现

​ 续接Django REST Framework,使用Vite构建Vue3的前端项目

1. 后台管理系统主界面框架搭建

  • 后台系统主界面搭建

新建后台管理文件目录

  • 完成后台整体布局
TypeScript 复制代码
// 1.主界面 index.vue

<script setup lang="ts">

</script>

<template>
    <el-container  class="layout">
        <el-aside width="200px" class="aside">Aside</el-aside>
        <el-container>
            <el-header class="header">Header</el-header>
            <el-main class="main">Main</el-main>
        </el-container>
    </el-container>
  </template>


<style scoped>
.layout {
    height: 100%;
}

.aside {
  background-color: #304156;
}
.header {
  background-color: lightblue;
  height: 50px;
  border-bottom: 1px solid #e5e5e5;
  display: flex;
  align-items: center;
  justify-content: space-between;
}
</style>

// 2.配置路由
import { createRouter, createWebHistory, RouteRecordRaw } from "vue-router";
import Index from '../layout/index.vue'
const routes: Array<RouteRecordRaw>= [
    {
        path: "/",
        // name: 'Home',
        component: Index,    
    },
    
]

// 创建一个 vue-router对象
const router = createRouter({
    history: createWebHistory(),
})

// 暴露接口
export default router

3.app.vue引入路由
<script setup lang="ts">

</script>
<template>
  <router-view></router-view>
</template>

<style scoped>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
}
 
</style>
  • 后台页面满屏显示
TypeScript 复制代码
1.style.css 注销 #app{}
2.index.html添加样式
<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <link rel="icon" type="image/svg+xml" href="/vite.svg" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>后台管理</title>
  </head>
<!--添加样式-->
  <style>
    html, body,#app{
      padding: 0px;
      margin: 0px;
      height: 100%;
      width: 100%;
    }
  </style>
  <body>
    <div id="app"></div>
    <script type="module" src="/src/main.ts"></script>
  </body>
</html>
  • 侧边栏布局实现
TypeScript 复制代码
// Menu.vue

<script setup lang="ts">

</script>

<template>
    <el-menu active-text-color="#ffd04b" background-color="#545c64" class="el-menu-vertical-demo" default-active="2"
        text-color="#fff">
        <el-sub-menu index="1">
            <template #title>
                <el-icon>
                    <location />
                </el-icon>
                <span>基础数据</span>
            </template>
                <el-menu-item index="1-1">院系管理</el-menu-item>
                <el-menu-item index="1-2">专业管理</el-menu-item>
                <el-menu-item index="1-3">学生管理</el-menu-item>
        </el-sub-menu>
        <el-sub-menu index="2">
            <template #title>
                <el-icon>
                    <location />
                </el-icon>
                <span>学生管理</span>
            </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-sub-menu index="3">
            <template #title>
                <el-icon>
                    <location />
                </el-icon>
                <span>用户角色</span>
            </template>
                <el-menu-item index="3-1">登录账号</el-menu-item>
                <el-menu-item index="3-2">用户角色</el-menu-item>
                <el-menu-item index="3-3">菜单管理</el-menu-item>
                <el-menu-item index="3-4">权限信息</el-menu-item>
        </el-sub-menu>
        <!-- <el-menu-item index="2">
            <el-icon><icon-menu /></el-icon>
            <span>Navigator Two</span>
        </el-menu-item>
        <el-menu-item index="3" disabled>
            <el-icon>
                <document />
            </el-icon>
            <span>Navigator Three</span>
        </el-menu-item>
        <el-menu-item index="4">
            <el-icon>
                <setting />
            </el-icon>
            <span>Navigator Four</span>
        </el-menu-item> -->
    </el-menu>
</template>


<style scoped>
.el-menu-item {
    display: block;
    text-align: center;
}
</style>
TypeScript 复制代码
// index.vue 注册
<script setup lang="ts">
// 引入组件
import MenuVue from './menu/Menu.vue';
import HeaderVue from './header/Header.vue';

</script>

<template>
    <el-container  class="layout">
        <el-aside width="200px" class="aside">
            <!-- 调用组件 -->
             <MenuVue></MenuVue>
        </el-aside>
        <el-container>
            <el-header class="header">
                <!-- 调用组件 -->
                <HeaderVue></HeaderVue>
            </el-header>
            <el-main class="main">Main</el-main>
        </el-container>
    </el-container>
  </template>


<style scoped>
.layout {
    height: 100%;
}

.aside {
  background-color: #304156;
}
.header {
  background-color: lightblue;
  height: 50px;
  border-bottom: 1px solid #e5e5e5;
  display: flex;
  align-items: center;
  justify-content: space-between;
}
</style>
  • 添加侧边栏Logo
TypeScript 复制代码
// 新建MenuLogo.vue

<script setup lang="ts">

</script>

<template>
    <div class="logo">
        <!-- 图片 -->
         <img src="../../assets/logo_main.png" alt="Logo">
         <span class="title">信息管理系统</span>
    </div>
</template>


<style scoped>
.logo {
  background-color: #2b2f3a;
  height: 50px;
  border: none;
  line-height: 50px;
  display: flex;
  align-items: center;
  padding-left: 15px;
  color: #fff;
}
.logo img {
  width: 32px;
  height: 32px;
  margin-right: 12px;
}
.logo span {
  font-weight: 600;
  line-height: 50px;
  font-size: 16px;
  font-family: Avenir, Helvetica Neue, Arial, Helvetica, sans-serif;
  vertical-align: middle;
}
</style>
TypeScript 复制代码
// Menu.vue 绑定并注册 MenuLogo
<script setup lang="ts">
import MenuLogo from './MenuLogo.vue';

</script>

<template>
    <!-- 导入Logo组件 -->
    <MenuLogo class="layout-logo"></MenuLogo>
    <el-menu active-text-color="#ffd04b" background-color="#545c64" class="el-menu-vertical-demo" default-active="2"
        text-color="#fff">
        <el-sub-menu index="1">
            <template #title>
                <el-icon>
                    <location />
                </el-icon>
                <span>基础数据</span>
            </template>
                <el-menu-item index="1-1">院系管理</el-menu-item>
                <el-menu-item index="1-2">专业管理</el-menu-item>
                <el-menu-item index="1-3">学生管理</el-menu-item>
        </el-sub-menu>
        <el-sub-menu index="2">
            <template #title>
                <el-icon>
                    <location />
                </el-icon>
                <span>学生管理</span>
            </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-sub-menu index="3">
            <template #title>
                <el-icon>
                    <location />
                </el-icon>
                <span>用户角色</span>
            </template>
                <el-menu-item index="3-1">登录账号</el-menu-item>
                <el-menu-item index="3-2">用户角色</el-menu-item>
                <el-menu-item index="3-3">菜单管理</el-menu-item>
                <el-menu-item index="3-4">权限信息</el-menu-item>
        </el-sub-menu>
        <!-- <el-menu-item index="2">
            <el-icon><icon-menu /></el-icon>
            <span>Navigator Two</span>
        </el-menu-item>
        <el-menu-item index="3" disabled>
            <el-icon>
                <document />
            </el-icon>
            <span>Navigator Three</span>
        </el-menu-item>
        <el-menu-item index="4">
            <el-icon>
                <setting />
            </el-icon>
            <span>Navigator Four</span>
        </el-menu-item> -->
    </el-menu>
</template>


<style scoped>
.el-menu-item {
  display: block;
  text-align: center;
}
.el-menu-vertical-demo:not(.el-menu--collapse) {
  width: 230px;
  min-height: 400px;
}

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

.el-menu-item {
  color: #f4f4f5 !important;
}

:deep(.el-sub-menu .el-sub-menu__title) {
  color: #f4f4f5 !important;
}
/* .el-submenu .is-active .el-submenu__title {
  border-bottom-color: #1890ff;
} */
:deep(.el-menu .el-menu-item) {
  color: #bfcbd9;
}

/* 菜单点中文字的颜色 */
:deep(.el-menu-item.is-active) {
  color: #409eff !important;
}
/* 当前打开菜单的所有子菜单颜色 */
:deep(.is-opened .el-menu-item) {
  background-color: #1f2d3d !important;
}
/* 鼠标移动菜单的颜色 */
:deep(.el-menu-item:hover) {
  background-color: #001528 !important;
}

/* Logo CSS部分的动画 */
@keyframes logoAnimation {
  0% {
    transform: scale(0);
  }
  50% {
    transform: scale(1);
  }
  100% {
    transform: scale(1);
  }
}

.layout-logo {
  animation: logoAnimation 1s ease-out;
}
</style>
  • 动态生成侧边栏菜单
TypeScript 复制代码
// 新建MenuItem.vue
<script setup lang="ts">
// 导入基本模块
import { reactive } from 'vue'; 

// 初始化构造数据
let menuList = reactive([
  {
    path: "/",
    meta: {
      title: "首页",
      icon: "HomeFilled",
    },
  },
  {
    path: "/baisc",
    meta: {
      title: "基础数据",
      icon: "Setting",
    },
    children: [
      {
        path: "/basic/faculty",
        meta: {
          title: "院系信息",
          icon: "Ship",
        },
      },
      {
        path: "/basic/major",
        meta: {
          title: "专业信息",
          icon: "ShoppingBag",
        },
      },
      {
        path: "/basic/teacher",
        meta: {
          title: "教师信息",
          icon: "ShoppingCartFull",
        },
      },
    ],
  },

  {
    path: "/student",
    meta: {
      title: "学生管理",
      icon: "UserFilled",
    },
    children: [
      {
        path: "/student/info",
        meta: {
          title: "学生信息",
          icon: "VideoCameraFilled",
        },
      },
      {
        path: "/student/exam",
        meta: {
          title: "考试信息",
          icon: "OfficeBuilding",
        },
      },
      {
        path: "/student/image",
        meta: {
          title: "学生照片",
          icon: "TakeawayBox",
        },
      },
    ],
  },
  {
    path: "/user",
    meta: {
      title: "用户角色",
      icon: "Ticket",
    },
    children: [
      {
        path: "/user/account",
        meta: {
          title: "登录账号",
          icon: "Coordinate",
        },
      },
      {
        path: "/user/roles",
        meta: {
          title: "角色信息",
          icon: "CreditCard",
        },
      },
      {
        path: "/user/menu",
        meta: {
          title: "菜单管理",
          icon: "DeleteLocation",
        },
      },
      {
        path: "/user/permission",
        meta: {
          title: "权限管理",
          icon: "Goods",
        },
      },
    ],
  },
]);

</script>

<template>
    <template v-for="menu in menuList">
        <!-- 
            1.循环遍历menuList
            2.如果children存在并且不为空及长度大于0,则创建一级菜单,反之则创建多级
            3. 点击菜单,跳转到对应路径
        -->
        <el-sub-menu v-if="menu.children && menu.children.length>0" :index="menu.path">
            <!-- 展示二级菜单第一层 -->
            <template #title>
                <span>{{ menu.meta.title }}</span>
            </template>
            <!-- 展示二级菜单第二层 -->
            <!-- <template v-for="child in menu.children">
                <el-menu-item :index="child.path">
                    <span>{{ child.meta.title }}</span>
                </el-menu-item>
            </template> -->
            <template #default>
                <el-menu-item v-for="child in menu.children" :key="child.path" :index="child.path">
                    <span>{{ child.meta.title }}</span>
                </el-menu-item>
            </template>
        </el-sub-menu>
        <el-menu-item v-else :index="menu.path" style="text-align: left;">
            <span>{{ menu.meta.title }}</span>
        </el-menu-item>
        
    </template>
 
</template>


<style scoped>
.el-menu-item {
  display: block;
  text-align: center;
  padding-right: 80px;
}


</style>
TypeScript 复制代码
// Menu.vue 注册
<script setup lang="ts">
import MenuLogo from './MenuLogo.vue';
import MenuItem from './MenuItem.vue';

</script>

<template>
    <!-- 导入Logo组件 -->
    <MenuLogo class="layout-logo"></MenuLogo>
    <el-menu active-text-color="#ffd04b" background-color="#545c64" class="el-menu-vertical-demo" default-active="2"
        text-color="#fff">
        <MenuItem></MenuItem>
    </el-menu>
</template>


<style scoped>
.el-menu-item {
  display: block;
  text-align: center;
}
.el-menu-vertical-demo:not(.el-menu--collapse) {
  width: 230px;
  min-height: 400px;
}

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

.el-menu-item {
  color: #f4f4f5 !important;
}

:deep(.el-sub-menu .el-sub-menu__title) {
  color: #f4f4f5 !important;
}
/* .el-submenu .is-active .el-submenu__title {
  border-bottom-color: #1890ff;
} */
:deep(.el-menu .el-menu-item) {
  color: #bfcbd9;
}

/* 菜单点中文字的颜色 */
:deep(.el-menu-item.is-active) {
  color: #409eff !important;
}
/* 当前打开菜单的所有子菜单颜色 */
:deep(.is-opened .el-menu-item) {
  background-color: #1f2d3d !important;
}
/* 鼠标移动菜单的颜色 */
:deep(.el-menu-item:hover) {
  background-color: #001528 !important;
}

/* Logo CSS部分的动画 */
@keyframes logoAnimation {
  0% {
    transform: scale(0);
  }
  50% {
    transform: scale(1);
  }
  100% {
    transform: scale(1);
  }
}

.layout-logo {
  animation: logoAnimation 1s ease-out;
}
</style>
  • 侧边栏菜单添加图标

安装

TypeScript 复制代码
npm install @element-plus/icons-vue

全局注册

TypeScript 复制代码
// main.ts

import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
import router from './router' // 导入路由文件
import * as ElementPlusIconsVue from '@element-plus/icons-vue' // 导入所以 icon图标

import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css' 

// 创建app
const app= createApp(App)

app.use(router).use(ElementPlus).mount('#app')

// 遍历所有 icon将每个图标以组件方式加载到app中
Object.keys(ElementPlusIconsVue).forEach((key) => {
    app.component(key, ElementPlusIconsVue[key as keyof typeof ElementPlusIconsVue]); // 注册成全局组件
})

使用

TypeScript 复制代码
// MenuItem.vue
<script setup lang="ts">
// 导入基本模块
import { reactive } from 'vue'; 

// 初始化数据
let menuList = reactive([
  {
    path: "/",
    meta: {
      title: "首页",
      icon: "HomeFilled",
    },
  },
  {
    path: "/baisc",
    meta: {
      title: "基础数据",
      icon: "Setting",
    },
    children: [
      {
        path: "/basic/faculty",
        meta: {
          title: "院系信息",
          icon: "Ship",
        },
      },
      {
        path: "/basic/major",
        meta: {
          title: "专业信息",
          icon: "ShoppingBag",
        },
      },
      {
        path: "/basic/teacher",
        meta: {
          title: "教师信息",
          icon: "ShoppingCartFull",
        },
      },
    ],
  },

  {
    path: "/student",
    meta: {
      title: "学生管理",
      icon: "UserFilled",
    },
    children: [
      {
        path: "/student/info",
        meta: {
          title: "学生信息",
          icon: "VideoCameraFilled",
        },
      },
      {
        path: "/student/exam",
        meta: {
          title: "考试信息",
          icon: "OfficeBuilding",
        },
      },
      {
        path: "/student/image",
        meta: {
          title: "学生照片",
          icon: "TakeawayBox",
        },
      },
    ],
  },
  {
    path: "/user",
    meta: {
      title: "用户角色",
      icon: "Ticket",
    },
    children: [
      {
        path: "/user/account",
        meta: {
          title: "登录账号",
          icon: "Coordinate",
        },
      },
      {
        path: "/user/roles",
        meta: {
          title: "角色信息",
          icon: "CreditCard",
        },
      },
      {
        path: "/user/menu",
        meta: {
          title: "菜单管理",
          icon: "DeleteLocation",
        },
      },
      {
        path: "/user/permission",
        meta: {
          title: "权限管理",
          icon: "Goods",
        },
      },
    ],
  },
]);

</script>

<template>
    <template v-for="menu in menuList">
        <!-- 
            1.循环遍历menuList
            2.如果children存在并且不为空及长度大于0,则创建一级菜单,反之则创建多级
            3. 点击菜单,跳转到对应路径
        -->
        <el-sub-menu v-if="menu.children && menu.children.length>0" :index="menu.path">
            <!-- 展示二级菜单第一层 -->
            <template #title>
                <el-icon>
                    <component class="icons" :is="menu.meta.icon"></component>
                </el-icon>
                <span>{{ menu.meta.title }}</span>
            </template>
            <!-- 展示二级菜单第二层 -->
            <!-- <template v-for="child in menu.children">
                <el-menu-item :index="child.path">
                    <span>{{ child.meta.title }}</span>
                </el-menu-item>
            </template> -->
            <template #default>
                <el-menu-item v-for="child in menu.children" :key="child.path" :index="child.path">
                    <el-icon>
                        <component class="icons" :is="child.meta.icon"></component>
                    </el-icon>
                    <span style="color: aliceblue;">{{ child.meta.title }}</span>
                </el-menu-item>
            </template>
        </el-sub-menu>
        <el-menu-item v-else :index="menu.path" style="text-align: left;">
            <el-icon>
                <component class="icons" :is="menu.meta.icon"></component>
            </el-icon>
            <span>{{ menu.meta.title }}</span>
        </el-menu-item>
        
    </template>
</template>


<style scoped>
.el-menu-item {
  display: block;
  text-align: center;
  padding-right: 80px;
}


</style>
  • Header区域基本布局
TypeScript 复制代码
// 图标组件定义 Collaspe.vue

<script setup lang="ts">

</script>

<template>
   <!-- 使用全局定义的折叠图标 -->
    <el-icon>
        <component class="icons" is="Fold"></component>
    </el-icon>
</template>


<style scoped>
.el-icon{
    font-style: 22px;
    margin-right: 10px;
}
</style>

// 面包屑导航定义 Bredcum.vue
<script setup lang="ts">

</script>

<template>
    <el-breadcrumb separator="/">
    <el-breadcrumb-item>首页</el-breadcrumb-item>
    <el-breadcrumb-item>基础数据</el-breadcrumb-item>
    <el-breadcrumb-item>院系管理</el-breadcrumb-item>
  </el-breadcrumb>
    
</template>


<style scoped>

</style>

// 用户下拉框定义 UserInfo.vue
<script setup lang="ts">

</script>

<template>
    <el-dropdown>
    <span class="el-dropdown-link">
      <el-icon style="margin-right: 5px;"><user-filled /></el-icon>   
      <span>admin</span>
      <el-icon class="el-icon--right" style="margin-left: 5px;">
        <arrow-down />
      </el-icon>
    </span>
    <template #dropdown>
      <el-dropdown-menu>
        <el-dropdown-item>用户信息</el-dropdown-item>
        <el-dropdown-item>修改密码</el-dropdown-item>
        <el-dropdown-item>安全退出</el-dropdown-item>
        <!-- <el-dropdown-item disabled>Action 4</el-dropdown-item>
        <el-dropdown-item divided>Action 5</el-dropdown-item> -->
      </el-dropdown-menu>
    </template>
  </el-dropdown>
</template>


<style scoped>

</style>
TypeScript 复制代码
// 父组件 Header.vue引入
<script setup lang="ts">
// 导入组件
import UserInfo from './UserInfo.vue';
import Collaspe from './Collaspe.vue';
import Bredcum from './Bredcum.vue';
</script>

<template>
    <!-- 靠着横排显示将两组件放在同一个div中 -->
    <div style="display: flex; align-items: center;">
        <Collaspe></Collaspe>
        <Bredcum></Bredcum>
    </div>
    <UserInfo></UserInfo>
</template>


<style scoped>

</style>
  • 侧边栏收缩思路
TypeScript 复制代码
控制菜单栏收缩---->Menu.vue---><el-menu :collapse=true></el-menu>
操作菜单栏收缩---->Collapse.vue
// 点击按钮实现切换(true/false) Collapse.vue
<script setup lang="ts">
// 导入vue
import {ref} from 'vue'
let isCollapsed = ref(false)
const changeCollapse = ()=>{
    isCollapsed.value =!isCollapsed.value 
    console.log(isCollapsed.value )
}

</script>

<template>
   <!-- 使用全局定义的折叠图标 -->
    <el-icon>
        <component class="icons" is="Fold" @click="changeCollapse"></component>
    </el-icon>
</template>
<style scoped>
.el-icon{
    font-style: 22px;
    margin-right: 10px;
}
</style>
TypeScript 复制代码
跨组件传值{
    父子组件---->直接传值{
        // 父组件定义传递的值
        // 导入vue
        import {ref} from 'vue'
        // 定义待传递的值
        let name = ref('admin')
        // 传递给子组件
        <template>
            <UserInfo :username='name'></UserInfo>
        </template>
        // 子组件接收父组件传递过来的值
        defineProps<{username: string}>();
        // 调用值
        {{ username }}
        
}
    兄弟组件---->vuex
}

2. Vuex实现组件间的传值

vuex是基于vue框架的一个状态管理库。可以管理复杂应用的数据状态,比如兄弟组件的通信、多层嵌套的组件的传值等等

store---store是yuex的核心对象,它记录了整个vue应用的数据状态以及操作数据的方式。

state---就是store操作的数据状态对象,

mutation---提供了一种简单易用的同步的方式改变state的状态。

getter---获取state中数据对象

  • vuex安装
TypeScript 复制代码
// 安装最新版 vuex
npm install vuex@next --save

// 新建 store文件夹并在文件夹下新建 index.ts导入vuex模块

import { createStore } from 'vuex'

// 报错:/node_modules/vuex/types/index.d.ts', but this result could not be resolved when respecting package.json "exports". 

// 解决报错 node_modules\vuex\package.json在exports字段中添加"types": "./types/index.d.ts"
  • store对象创建
TypeScript 复制代码
// 导入 vuex 模块
import { createStore } from 'vuex'
// 定义实体(存储结构)
export interface MyStore {
    // 控制侧边栏收缩
    collapse: boolean,
}

// 创建store对象
export const store = createStore<MyStore>({
    // 初始状态
    state: {
        collapse: false, // 侧边栏收缩
    },
    //  mutations 用于更改 Vuex 的 store 中的状态
    mutations: {
        toggleCollapse(state: MyStore) {
            state.collapse =!state.collapse
        },
    },
    // actions 用于提交 mutations,可以包含异步操作
    actions: {
        toggleCollapse({ commit }) {
            commit('toggleCollapse')
        },
    },
    // getters 用于获取状态的某些派生数据
    getters: {
        // 侧边栏是否收缩
        isCollapse(state: MyStore) {
            return state.collapse
        },
    },
})
// 暴露
export default store;
  • store对象注册
TypeScript 复制代码
// 注册到全局变量
import store from './store' // 导入创建的store对象

app.use(store).use(router).use(ElementPlus).mount('#app')
  • 侧边栏收缩功能完成
TypeScript 复制代码
// Menu.vue
// 组件中调用vuex中定义的store
// 导入usestore的方法
import { useStore } from 'vuex'
// 获取当前vuex的store对象
const store = useStore()
// 导入计算
import { computed } from 'vue' 
// 获取store->state中的collapse值
const isCollapse = computed(()=>{
    return store.getters['isCollapse']
})

// 在标签中实现绑定
<el-menu :collapse = isCollapse></el-menu>

// 解决侧边栏收缩 logo显示问题 侧边栏不是收缩状态时展示
<MenuLogo class="layout-logo" v-if="!isCollapse"></MenuLogo>


// Collaspe.vue
// 导入usestore的方法
import { useStore } from 'vuex'
// 获取当前vuex的store对象
const store = useStore();
// 导入计算
import { computed } from 'vue' 
// 获取store->state中的collapse值
const isCollapse = computed(()=>{
    return store.getters['isCollapse']
})

// 点击切换收缩事件
const changeCollapse=()=>{
    // 切换collapse的值
    store.commit('toggleCollapse')
}


// 使用三元表达式判断收缩图标,此时全局定义的icon图标将失效需重新导入
import { Fold,Expand } from '@element-plus/icons-vue';
<component class="icons" :is="isCollapse? Fold: Expand " @click="changeCollapse"></component>

3. Vue-router实现侧边栏导航

  • 实现侧边栏导航
TypeScript 复制代码
// router/index.ts 配置路由

const routes: Array<RouteRecordRaw>= [
    // 首页
    {
        path: "/",
        name: 'Layout',
        component: Layout,
        // 自动跳转
        redirect: '/dashboard',
        children:[
            {
                path: '/dashboard',
                name: 'Dashboard',
                component: () => import('../views/index/Dashboard.vue')
            }
        ]
        
    },
    // 基础数据--->院系信息、专业信息、教师信息
    {
        path: '/basic',
        name: 'Basic',
        component: Layout,
        children: [
            {
                path: '/basic/faculty',
                name: 'Faculty',
                component: () => import('../views/basic/Faculty.vue')
            },
            {
                path: '/basic/major',
                name: 'Major',
                component: () => import('../views/basic/Major.vue')
            },
            {
                path: '/basic/teacher',
                name: 'Teacher',
                component: () => import('../views/basic/Teacher.vue')
            }
        ]
            
    },
    // 学生管理
    {
        path: '/student',
        name:'Student',
        component: Layout,
        children: [
            {
                path: '/student/info',
                name: 'StudentInfo',
                component: () => import('../views/student/Info.vue')
            },
            {
                path: '/student/exam',
                name: 'StudentExam',
                component: () => import('../views/student/Exam.vue')
            },
            {
                path: '/student/image',
                name: 'StudentImage',
                component: () => import('../views/student/Image.vue')
            }
        ]
    },
    // 用户管理
    {
        path: '/user',
        name: 'User',
        component: Layout,
        children: [
            {
                path: '/user/account',
                name: 'UserAccount',
                component: () => import('../views/user/Account.vue')
            },
            {
                path: '/user/roles',
                name: 'UserRoles',
                component: () => import('../views/user/Roles.vue')
            },
            {
                path: '/user/menu',
                name: 'UserMenu',
                component: () => import('../views/user/Menu.vue')
            },
            {
                path: '/user/permission',
                name: 'UserPermission',
                component: () => import('../views/user/Permission.vue')
            }
        ]
    }
    
]
TypeScript 复制代码
// layout/index.vue引入路由在指定区域显示

<el-main class="main"><router-view></router-view></el-main>

// layout/menu/Menu.vue 配置点击侧边栏切换路由
<el-menu active-text-color="#ffd04b" background-color="#304156" class="el-menu-vertical-demo" default-active="2"
        text-color="#fff"
        :collapse = isCollapse
        router
        >

4. 捕获路由信息自动生成面包屑导航

  • 自动生成面包屑导航
TypeScript 复制代码
// 路由添加 meta字段记录标题名

const routes: Array<RouteRecordRaw>= [
    // 首页
    {
        path: "/",
        name: 'Layout',
        component: Layout,
        // 自动跳转
        redirect: '/dashboard',
        children:[
            {
                path: '/dashboard',
                name: 'Dashboard',
                component: () => import('../views/index/Dashboard.vue'),
                meta: { title: '首页' }
            }
        ]
        
    },
    // 基础数据--->院系信息、专业信息、教师信息
    {
        path: '/basic',
        name: 'Basic',
        component: Layout,
        meta: { title: '基础数据' },
        children: [
            {
                path: '/basic/faculty',
                name: 'Faculty',
                component: () => import('../views/basic/Faculty.vue'),
                meta: { title: '院系信息' }
            },
            {
                path: '/basic/major',
                name: 'Major',
                component: () => import('../views/basic/Major.vue'),
                meta: { title: '专业信息' }
            },
            {
                path: '/basic/teacher',
                name: 'Teacher',
                component: () => import('../views/basic/Teacher.vue'),
                meta: { title: '教师信息' }
            }
        ]
            
    },
    // 学生管理
    {
        path: '/student',
        name:'Student',
        component: Layout,
        meta: { title: '学生管理' },
        children: [
            {
                path: '/student/info',
                name: 'StudentInfo',
                component: () => import('../views/student/Info.vue'),
                meta: { title: '学生信息' }
            },
            {
                path: '/student/exam',
                name: 'StudentExam',
                component: () => import('../views/student/Exam.vue'),
                meta: { title: '考试信息' }
            },
            {
                path: '/student/image',
                name: 'StudentImage',
                component: () => import('../views/student/Image.vue'),
                meta: { title: '学生照片' }
            }
        ]
    },
    // 用户管理
    {
        path: '/user',
        name: 'User',
        component: Layout,
        meta: { title: '用户角色' },
        children: [
            {
                path: '/user/account',
                name: 'UserAccount',
                component: () => import('../views/user/Account.vue'),
                meta: { title: '登录账号' }
            },
            {
                path: '/user/roles',
                name: 'UserRoles',
                component: () => import('../views/user/Roles.vue'),
                meta: { title: '角色信息' }
            },
            {
                path: '/user/menu',
                name: 'UserMenu',
                component: () => import('../views/user/Menu.vue'),
                meta: { title: '菜单管理' }
            },
            {
                path: '/user/permission',
                name: 'UserPermission',
                component: () => import('../views/user/Permission.vue'),
                meta: { title: '权限管理' }
            }
        ]
    }
    
]
TypeScript 复制代码
// 动态获取面包屑导航标准流程 /layout/header/Bredcum.vue

<script setup lang="ts">
import { Ref, ref, watch } from 'vue'

// 定义 useRouter模块
import { useRoute,RouteLocationMatched} from "vue-router"
// 定义面包屑集合
const tabs: Ref<RouteLocationMatched[]> = ref([])

// 获取路由信息
const route = useRoute();

// 函数定义
const getBredCum=()=>{
  // 在路由中筛选过滤路由匹配信息 --- route.metched --meta --meta.title
  let matched = route.matched.filter(item=>item.meta && item.meta.title)
  // 获取metched中的第一条 判断是否为首页
  const first = matched[0]
  // 判断是否为首页,为首页时将首页绑定在最前边
  if(first.path !== '/dashboard'){
    matched = [{path: '/dashboard', meta:{'title':'首页'}} as any].concat(matched)
  }
  // 将拼接好的值传递给tabs
  tabs.value = matched

  console.log(matched)
}

getBredCum();

watch(()=>route.path, ()=>getBredCum()) // 路由变化时重新获取面包屑
// watch(()=>route.matched, getBredCum, { immediate: true }) // 路由变化时重新获取面包屑

</script>

<template>
    <el-breadcrumb separator="/">
      <el-breadcrumb-item v-for="item in tabs">{{ item.meta.title }}</el-breadcrumb-item>
    </el-breadcrumb>
    <!-- <el-breadcrumb-item>首页</el-breadcrumb-item>
    <el-breadcrumb-item>基础数据</el-breadcrumb-item>
    <el-breadcrumb-item>院系管理</el-breadcrumb-item> -->
    
</template>


<style scoped>

</style>

5. 学生信息页面布局

  • 顶部搜索框布局实现
TypeScript 复制代码
// views/students/info.vue
<script lang="ts" setup>
import { ref, reactive } from "vue"


// 定义存储集合
var Data = reactive({
    // 定义输入的查询条件
    q_str: ref(""),
    // 存储从后台获取的所有院系信息
    FacultyOptions: reactive([
        {
            value:1,
            label:'计算机学院'
        },
        {
            value:2,
            label:'外语学院'
        },
    ]),
    // 存储选择院系后的值
    FacultySelected: ref(""),

     // 存储从后台获取的所有专业信息
     MajorOptions: reactive([
        {
            value:1,
            label:'计算机专业'
        },
        {
            value:2,
            label:'外语专业'
        },
    ]),
    // 存储选择专业后的值
    MajorSelected: ref("")
})

</script>

<template>
    <!-- 顶部查询区域   styple="display: flax;"横向显示-->
    <el-form :inline="true"  class="demo-form-inline">
    <el-form-item label="查询条件">
      <el-input v-model="Data.q_str" placeholder="请输入查询条件" clearable />
    </el-form-item>
    <!-- 动态获取院系信息 -->
    <el-form-item label="院系">
      <el-select v-model="Data.FacultySelected" placeholder="请选择院系">
        <el-option 
        v-for="item in Data.FacultyOptions"
        :key="item.value"
        :label="item.label"
        :value="item.value" />
      </el-select>
    </el-form-item>
    <!-- 动态获取专业信息 -->
    <el-form-item label="专业">
      <el-select v-model="Data.MajorSelected" placeholder="请选择专业">
        <el-option 
        v-for="item in Data.MajorOptions"
        :key="item.value"
        :label="item.label"
        :value="item.value" />
      </el-select>
    </el-form-item>
    <el-form-item>
      <el-button type="primary">
        <!-- 引入方法1 -->
        <el-icon><component class="icons" is="Search"></component></el-icon>
        <span>查询</span></el-button>
      <el-button type="primary">
        <!-- 引入方法2 -->
        <el-icon><Finished /></el-icon>
        <span>全部</span></el-button>
      <el-button type="primary">
        <el-icon><Pointer /></el-icon>
        <span>添加</span></el-button>
    </el-form-item>
  </el-form>
</template>

<style scoped>
.demo-form-inline .el-input {
  --el-input-width: 220px;
}

.demo-form-inline .el-select {
  --el-select-width: 220px;
}
</style>
  • 学生信息详情布局实现
TypeScript 复制代码
<script lang="ts" setup>
import { ref, reactive } from "vue"
import {More,Edit,Delete} from "@element-plus/icons-vue"


// 定义存储集合
var Data = reactive({
    
    // ===表格区域定义====
    students: reactive([
        {
            sno:'95001',
            name:'张武',
            gender:'男',
            birthday: '2001-10-10',
            faculty: '计算机学院',
            major:'计算机网络',
            mobile: '13514623594',
            email: '123@163.com',
            address: '郑州市金水区'
        },
        {
            sno:'95001',
            name:'张武',
            gender:'男',
            birthday: '2001-10-10',
            faculty: '计算机学院',
            major:'计算机网络',
            mobile: '13514623594',
            email: '123@163.com',
            address: '郑州市金水区'
        }
    ]),
    
});

</script>

<template>
    
    <!-- 2.表格信息部分 -->
    <el-table :data="Data.students" stripe border style="width: 100%" :header-cell-style="{ backgroundColor:'#409EFF',color:'#FFF',FontSize:'14px' }">
        <el-table-column label="序号" type="index" align="center" width="60" />
        <el-table-column prop="sno" label="学号" align="center" width="80" />
        <el-table-column prop="name" label="姓名" align="center" width="80" />
        <el-table-column prop="gender" label="性别" align="center" width="80"  />
        <el-table-column prop="birthday" label="出生日期" align="center" width="180" />
        <el-table-column prop="faculty" label="院系" align="center" width="120" />
        <el-table-column prop="major" label="专业" align="center" width="120" />
        <el-table-column prop="mobile" label="电话" align="center" width="140" />
        <el-table-column prop="email" label="Email" align="center" width="180" />
        <el-table-column prop="address" label="地址" align="center"/>
        <!-- 按钮区域 -->
        <el-table-column label="操作" align="center">
            <el-button type="primary" :icon="More" circle size="small"/>
            <el-button type="warning" :icon="Edit" circle size="small"/>
            <el-button type="danger" :icon="Delete" circle size="small"/>
        </el-table-column>

    </el-table>
    
</template>

<style scoped>
.demo-form-inline .el-input {
  --el-input-width: 220px;
}

.demo-form-inline .el-select {
  --el-select-width: 220px;
}

</style>
  • 底部分页实现
TypeScript 复制代码
<script lang="ts" setup>
import { ref, reactive } from "vue"
import {More,Edit,Delete} from "@element-plus/icons-vue"


// 定义存储集合
var Data = reactive({
    // =====分页====
    // 当前页
    currentsPage: ref(1),
    // 每页显示的数据量
    pageSize: ref(15),
    // 总数据量所有记录条数
    total: ref(0),
});

// 分页中修改每页的pageSize
const handleSizeChange=()=>{}

// 分页中修改每页的currentsPage
const handleCurrentChange=()=>{}
</script>

<template>
    <!-- 3.分页 currentPage4当前页 pageSize4每页大小 total记录条数 handleSizeChange改变每页大小 handleCurrentChange改变当前页  -->
    <el-pagination style="margin-top: 20px;"
    background
      v-model:current-page="Data.currentsPage"
      v-model:page-size="Data.pageSize"
      :page-sizes="[10, 12, 15, 18,20,25,40,50]"
      layout="total, sizes, prev, pager, next, jumper"
      :total="Data.total"
      @size-change="handleSizeChange"
      @current-change="handleCurrentChange"
    />
</template>

<style scoped>
.demo-form-inline .el-input {
  --el-input-width: 220px;
}

.demo-form-inline .el-select {
  --el-select-width: 220px;
}

</style>
  • Element Plus中文化
TypeScript 复制代码
// 全局定义
import zhCn from 'element-plus/es/locale/lang/zh-cn' // 导入中文语言包

// 创建app
const app= createApp(App)

// 添加 locale:zhCn
app.use(store).use(router).use(ElementPlus,{locale:zhCn}).mount('#app')
相关推荐
别拿曾经看以后~15 分钟前
【el-form】记一例好用的el-input输入框回车调接口和el-button按钮防重点击
javascript·vue.js·elementui
我要洋人死18 分钟前
导航栏及下拉菜单的实现
前端·css·css3
龙哥说跨境29 分钟前
如何利用指纹浏览器爬虫绕过Cloudflare的防护?
服务器·网络·python·网络爬虫
科技探秘人29 分钟前
Chrome与火狐哪个浏览器的隐私追踪功能更好
前端·chrome
科技探秘人30 分钟前
Chrome与傲游浏览器性能与功能的深度对比
前端·chrome
JerryXZR35 分钟前
前端开发中ES6的技术细节二
前端·javascript·es6
七星静香37 分钟前
laravel chunkById 分块查询 使用时的问题
java·前端·laravel
q24985969340 分钟前
前端预览word、excel、ppt
前端·word·excel
小白学大数据1 小时前
正则表达式在Kotlin中的应用:提取图片链接
开发语言·python·selenium·正则表达式·kotlin
小华同学ai1 小时前
wflow-web:开源啦 ,高仿钉钉、飞书、企业微信的审批流程设计器,轻松打造属于你的工作流设计器
前端·钉钉·飞书