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

news2024/11/25 7:13:39

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

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

  • 后台系统主界面搭建

新建后台管理文件目录

  •  完成后台整体布局
// 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>
  • 后台页面满屏显示
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>

  • 侧边栏布局实现 
// 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>
// 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
// 新建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>
// 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>
  • 动态生成侧边栏菜单
// 新建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>
// 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>

  • 侧边栏菜单添加图标

安装

npm install @element-plus/icons-vue

全局注册

// 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]); // 注册成全局组件
})

使用

// 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区域基本布局
// 图标组件定义 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>
// 父组件 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>

  • 侧边栏收缩思路
控制菜单栏收缩---->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>
跨组件传值{
    父子组件---->直接传值{
        // 父组件定义传递的值
        // 导入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安装
// 安装最新版 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对象创建
// 导入 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对象注册
// 注册到全局变量
import store from './store' // 导入创建的store对象

app.use(store).use(router).use(ElementPlus).mount('#app')
  • 侧边栏收缩功能完成
// 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实现侧边栏导航

  • 实现侧边栏导航
// 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')
            }
        ]
    }
    
]
// 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. 捕获路由信息自动生成面包屑导航

  • 自动生成面包屑导航
// 路由添加 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: '权限管理' }
            }
        ]
    }
    
]
// 动态获取面包屑导航标准流程 /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. 学生信息页面布局

  • 顶部搜索框布局实现
// 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>

  • 学生信息详情布局实现 
<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>
  • 底部分页实现
<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中文化
// 全局定义
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')

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

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

相关文章

应用层HTTP协议

文章目录 应用层HTTP协议1、HTTP协议概念2、URL&#xff08;统一资源定位符&#xff09;2.1、URL的encode&#xff08;编码&#xff09;和decode&#xff08;解码&#xff09; 3、HTTP请求和响应报头格式3.1、请求报头3.2、响应报头 4、HTTP的方法4.1、GET方法4.2、POST方法4.3…

生成模型VAE

VAE likelihood-basedELBOVAESGVB估计器和AEVB算法重参数化 likelihood-based likelihood-based generative models是生成模型的一类范式&#xff0c;通过最大化所有观测数据的似然函数来学习模型参数。 该怎么去理解likelihood-based&#xff0c;基于似然的生成模型&#xf…

互联网重构“规则制定权”,周期性谋咒开始轮转!

周期“魔咒”又开始轮转了。 产业趋势叠加资本周期&#xff0c;使得任何产业都有其周期性规律&#xff0c;传统资源产业是如此&#xff0c;科技产业亦非例外。 刚刚迎来30周年庆的中国互联网赛道就正处于新一轮小周期的节点。随着移动用户量逐渐被开发利用至阶段性顶峰&#…

学习c语言第24天(练习)

编程题 第一题 最大公约数最小公倍数求和 //求最大公约数和最小公倍数之和 //暴力求解 //int main() //{ // int n 0; // int m 0; // while (scanf("%d %d", &n, &m)2) // { // int min n < m ? n : m; // int max n > m ? n : m; //…

原神4.8版本角色数据

<!DOCTYPE html> <html lang"zh-CN"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>原神4.8版本角色数据</title><style>…

【浏览器插件】Chrome扩展V3版本

前言&#xff1a;Chrome从2022年6月开始&#xff0c;新发布插件只接受V3版。2024年V2版已从应用商店下架。 浏览器扩展插件开发API文档 chrome官网&#xff08;要翻墙&#xff09;&#xff1a; https://developer.chrome.com/docs/extensions/mv3 MDN中文&#xff1a;https:/…

gitee拉取项目,提交代码

1&#xff0c;安装git 2&#xff0c;gitee配置成员仓库权限 3&#xff0c;克隆项目 git clone gitgitee.com:sky474775788/Python_API_AUTO.git 4&#xff0c;配置用户信息 git config --global user.name 林俊xxx git config --global user.email ohnxxxsee1xxx.com 5&…

机器学习·L2W4-决策树

决策树 从根节点的所有示例开始计算所有可能特征的分割信息增益&#xff0c;并选择信息增益最高的特征根据所选特征分割数据集&#xff0c;并创建树的左分支和右分支不断重复分割过程&#xff0c;直到满足停止条件 信息增益 也可以理解为信息熵的减少 p p p是结果为positive…

住宅代理和数据中心代理:指纹浏览器用哪个更安全?

在当今的数字时代&#xff0c;确保您的在线安全至关重要。这就是住宅和数据中心代理发挥作用的地方&#xff0c;它们可以保护您的身份和个人信息。指纹浏览器解决了账号所在环境指纹参数隔离的安全问题&#xff0c;而IP解决环境的定位与隔离问题&#xff0c;就像Maskfog中提供的…

洛伦兹微分方程与混沌理论

前言 这一段时间在看书中关于深度学习与神经网络的内容&#xff0c;其中有一节介绍神经网络用于预测洛伦兹微分方程的数值解&#xff0c;还提到了“吸引子”这一概念&#xff0c;当时也没太理解是什么&#xff0c;下午搜集了一本书上关于混沌理论的介绍——《混沌的本质》。 这…

【线性回归】——解决运筹优化类问题

目录 文章目录 前言 一、模型原理 1.线性规划模型的三要素 &#x1f60f;&#x1f60f;&#x1f60f; 2.模型特点 3.线性规划的表现形式 二、模型建立步骤 1.找决策变量 2.确定目标函数 3.找到约束条件 4.运用Matlab中的Linprog函数 总结 前言 在实际生活应用中&#xff0c;我…

Unity入门5——材质

创建材质 点击Assets → Create → Material&#xff0c;得到一个默认材质球的副本。 使用材质 直接把材质球拖拽到物体上&#xff0c;或设置mesh renderer组件下的Materials 数组中第一个元素

etcd高可用集群部署

文章目录 一、环境准备二、安装部署2.1 下载安装包2.2 将etcd和etcdctl复制到/usr/local/bin中2.3 创建目录并赋予权限2.4 修改节点配置2.4.1 配置etcd.conf文件2.4.2 配置/etc/systemd/system/etcd.service文件 2.5 启动ectd服务2.6 查看集群成员信息2.7 查看集群状态 在生产环…

【PyQt5】PyQt5 信号和槽

基于GUI的应用程序是事件驱动的。函数或方法按照用户的操作&#xff08;例如点击按钮、从集合中选择项目或点击鼠标等&#xff09;来执行&#xff0c;这些操作被称为 事件 。用于构建GUI界面的小部件充当这些事件的来源。每个PyQt小部件都是从QObject类派生而来&#xff0c;设计…

《Redis设计与实现》读书笔记-复制

目录 1.概述 2.复制命令 3.部分重同步过程 4.部分重同步实现 4.1复制偏移量 4.2复制积压缓冲区 4.3服务器运行ID 5.总结 1.概述 在redis 通过向从服务器发送命令&#xff1a;SLAVE OF&#xff0c;让从服务器复制主服务器&#xff0c;成为复制。 复制的目的 让从服务器…

等保测评 linux设置三权分立

1、首先浅谈一下目录结构 drwxr-xr-x意思如下&#xff1a;第一位表示文件类型。 d是目录文件&#xff0c;l是链接文件&#xff0c;-是普通文件&#xff0c;p是管道。后面的分三个三个来看&#xff0c;即 rwx 、r-x 、r-x。 第一个&#xff1a; root &#xff1a;r 是可读&#…

在Linux中认识pthread库

int *pnullptr; pnullptr; *pnullptr; 指针变量做右值也是变量拥有空间。去承装数据。 *p代表指针所指向的空间&#xff0c;及0号地址&#xff0c;及往虚拟地址的0号地址处写8个字节的数据&#xff0c;全部写为0. &#xff08;此操作不允许&#xff09; 进程和线程的关系如…

Python PDF文本处理技巧 - 查找和高亮文字

目录 使用工具 Python在PDF中查找和高亮文字并统计出现次数和页码 Python在PDF的特定页面区域中查找和高亮文字 Python使用正则表达式在PDF中查找和高亮文字 Python在PDF中查找文字并获取它的坐标位置 其他查找条件设置 在日常工作和学习中&#xff0c;我们常常需要处理各…

命令-响应框架在 ESP RainMaker 中的应用

【如果您之前有关注乐鑫的博客和新闻&#xff0c;那么应该对 ESP RainMaker 及其各项功能有所了解。如果不曾关注&#xff0c;建议先查看相关信息&#xff0c;知晓本文背景。】 在 ESP RainMaker 中&#xff0c;管理员用户可以查看一些基本的节点数据&#xff0c;包括类型、型…

【Liunx】线程与进程的经典面试题总结

在这个浮躁的时代 只有自律的人才能脱颖而出 -- 《觉醒年代》 线程与进程的面试题总结 1 简述什么是LWP2 简述LWP与pthread_create创建的线程之间的关系3 简述轻量级进程ID与进程ID之间的区别4 请简述什么是线程互斥&#xff0c;为什么需要互斥5 简述你了解的进程间通信方式…