VUE+ElementUI搭建框架-4

提示:前端新人,初来乍到,若文章写的不好大家多包涵。


前言

上一篇(VUE+ElementUI搭建框架-3)文章完善了登录页面功能,主页面搭建基本完成,接下来开始完成左侧菜单、头部菜单和菜单页区域。
注:之后的功能需要用到vuex,想了解的朋友可以先去看一看有个初步的了解(VueX的介绍

一、左侧菜单

左侧菜单在做的时候,我添加了一些模拟数据,通过模拟数据将菜单循环出来,这里我只做了二级菜单的效果。
页面需要注意以下几点(菜单页面东西虽然不多,但是细节很多,非常重要):
1、菜单的状态(展开、折叠),collapse属性控制菜单的收缩功能。我在头部组件里面添加了一个按钮来控制菜单状态,所以使用了vuex进行操作this.$store.state.isCollapse
2、菜单的中的路由,点击菜单选项时要跳转到对应页面,此时就需要配置一下路由来实现这个功能。在这里我是用到了菜单的两个属性:default-active(默认打开的菜单页)、router(点击菜单选项后跳转);
3、菜单选项点击事件,点击后将菜单信息存储到vuex中,需要注意一点就是路由配置中的path、name最好与菜单数据中的保持一致,如果不一致可能跳出会出问题;
4、Logo在展开、折叠时的不同,也是根据菜单状态来判断的;
5、菜单对应的图标,我用的是ElementUI中的 (Icon 图标),如果是引用其他图库的图标那就需要格式调整;
菜单组件的代码如下:

/*左侧菜单页面*/
<template>
    <div class="leftmenu">
        <el-menu class="el-menu-vertical" :collapse="this.$store.state.isCollapse" :default-active="$route.name" unique-opened
            router>
            <div class="logo">
                <span v-if="this.$store.state.isCollapse">Logo</span>
                <span v-else>Logo</span>
            </div>
            <template v-for="item in navlist">
                <template v-if="item.children.length > 0">
                    <el-submenu :index="item.name">
                        <template slot="title">
                            <i class="el-icon-location"></i>
                            <span>{
    
    {
    
    item.lable}}</span>
                        </template>
                        <el-menu-item :index="itemchilder.name" v-for="itemchilder in item.children"
                            :key="itemchilder.menuid" @click=clickMenu(itemchilder)>
                            <i :class="itemchilder.icon"></i>
                            <span slot="title">{
    
    {
    
    itemchilder.lable}}</span>
                        </el-menu-item>
                    </el-submenu>
                </template>
                <template v-else>
                    <el-menu-item :index="item.name" @click=clickMenu(item)>
                        <i :class="item.icon"></i>
                        <span slot="title">{
    
    {
    
    item.lable}}</span>
                    </el-menu-item>
                </template>
            </template>
        </el-menu>
    </div>
</template>

<script>
export default {
    
    
    data() {
    
    
        return {
    
    
            navlist: [
                {
    
    
                    "menuid": 1,
                    "path": "/index",
                    "name": "index",
                    "lable": "首页",
                    "icon": "el-icon-house",
                    "children": []
                },
                {
    
    
                    "menuid": 5,
                    "path": "",
                    "name": "power",
                    "lable": "权限配置",
                    "icon": "el-icon-user-solid",
                    "children": [
                        {
    
    
                            "menuid": 6,
                            "path": "/userinfo",
                            "name": "userinfo",
                            "lable": "用户管理",
                            "icon": "el-icon-user-solid"
                        },
                        {
    
    
                            "menuid": 7,
                            "path": "/roleinfo",
                            "name": "roleinfo",
                            "lable": "角色管理",
                            "icon": "el-icon-s-custom"
                        },
                        {
    
    
                            "menuid": 8,
                            "path": "/setpower",
                            "name": "setpower",
                            "lable": "权限分配",
                            "icon": "el-icon-s-platform"
                        }
                    ]
                },
                {
    
    
                    "menuid": 2,
                    "path": "/poetry",
                    "name": "poetry",
                    "lable": "诗词收藏",
                    "icon": "el-icon-picture",
                    "children": []
                },
                {
    
    
                    "menuid": 3,
                    "path": "/imagefile",
                    "name": "imagefile",
                    "lable": "图片管理",
                    "icon": "el-icon-picture",
                    "children": []
                },
                {
    
    
                    "menuid": 4,
                    "path": "/docfile",
                    "name": "docfile",
                    "lable": "文件管理",
                    "icon": "el-icon-folder-opened",
                    "children": []
                },
                {
    
    
                    "menuid": 5,
                    "path": "/log",
                    "name": "log",
                    "lable": "日志记录",
                    "icon": "el-icon-document",
                    "children": []
                },
            ]
        }
    },
    computed: {
    
    
    },
    methods: {
    
    
        clickMenu(item) {
    
    
            // 将数据存入全局变量中
            this.$store.commit('selectMenu', item);
        }
    }
}
</script>
<style scoped>
.leftmenu {
    
    
    width: auto;
    height: 100%;
    overflow-x: hidden;
}

.leftmenu::-webkit-scrollbar {
    
    
    width: 0;
}

.el-menu-vertical:not(.el-menu--collapse) {
    
    
    width: 200px;
}

.logo {
    
    
    display: flex;
    align-items: center;
    justify-content: center;
    height: 60px;
    width: auto;
    font-size: 24px;
}
</style>

顺便把路由配置和vuex的配置也贴出来,第一个是路由的配置,第二个是vuex中的配置:

/*路由配置*/
import Vue from "vue";
import Router from "vue-router";

Vue.use(Router);

const routes = [
  {
    
    
    path: "/",
    redirect: "/login" /*重定向页面*/
  },
  {
    
    
    path: "/login",
    name: "login",
    component: () => import("@/views/login.vue"),
    meta: {
    
     title: "登录" }
  },
  {
    
    
    path: "/", //跳转后路径显示
    name: "home",
    component: () => import("@/views/home.vue"),
    meta: {
    
     title: "主页" },
    children: [
      {
    
    
        path: "/",
        name: "index",
        component: () => import("@/views/public/index.vue"),
        meta: {
    
     keepAlive: true, title: "首页" }
      },
      {
    
    
        path: "/index",
        name: "index",
        component: () => import("@/views/public/index.vue"),
        meta: {
    
     keepAlive: true, title: "首页" }
      },
      {
    
    
        path: "/userinfo",
        name: "userinfo",
        component: () => import("@/views/power/userinfo.vue"),
        meta: {
    
     keepAlive: true, title: "用户信息" }
      },
      {
    
    
        path: "/roleinfo",
        name: "roleinfo",
        component: () => import("@/views/power/roleinfo.vue"),
        meta: {
    
     keepAlive: true, title: "角色信息" }
      },
      {
    
    
        path: "/setpower",
        name: "setpower",
        component: () => import("@/views/power/setpower.vue"),
        meta: {
    
     keepAlive: true, title: "权限分配" }
      },
      {
    
    
        path: "/poetry",
        name: "poetry",
        component: () => import("@/views/file/poetry.vue"),
        meta: {
    
     keepAlive: true, title: "诗词收藏" }
      },
      {
    
    
        path: "/imagefile",
        name: "imagefile",
        component: () => import("@/views/file/imagefile.vue"),
        meta: {
    
     keepAlive: true, title: "图片管理" }
      },
      {
    
    
        path: "/docfile",
        name: "docfile",
        component: () => import("@/views/file/docfile.vue"),
        meta: {
    
     keepAlive: true, title: "文件管理" }
      },
      {
    
    
        path: "/log",
        name: "log",
        component: () => import("@/views/public/log.vue"),
        meta: {
    
     keepAlive: true, title: "日志记录" }
      },
      {
    
    
        path: "/404",
        name: "404",
        component: () => import("@/views/public/404.vue"),
        meta: {
    
     keepAlive: false, title: "空白页" }
      }
    ]
  }
];

const router = new Router({
    
    
  base: process.env.BASE_URL,
  routes
});

export default router;
/*vuex配置*/
import Vue from "vue";
import Vuex from "vuex";
Vue.use(Vuex);

const store = new Vuex.Store({
    
    
  state: {
    
    
    // 如果在组件中,想要访问store中的数据,只能通过 this.$store.state.*** 来访问
    isCollapse: false, //左侧菜单是否折叠
    taglist: [{
    
     "menuid": 1, "path": "/index","name": "index","lable": "首页","icon": "el-icon-house","children": []}] //标签数组
  },
  mutations: {
    
    
    //如果组件想要调用 mutations中的方法,只能使用this.$store.commit('方法名')
    collapse(state) {
    
    
      state.isCollapse = !state.isCollapse;
    },
    //添加标签到集合中
    selectMenu(state, value) {
    
    
      if (value.name != "index") {
    
    
        const result = state.taglist.findIndex(
          item => item.name === value.name
        );
        if (result === -1) {
    
    
          state.taglist.push(value);
        }
      }
    },
    //删除集合中的标签
    closeTag(state, value) {
    
    
      const result = state.taglist.findIndex(item => item.name === value.name);
      state.taglist.splice(result, 1);
    }
  },
  actions: {
    
    },
  getters: {
    
    
    //这里只负责对外提供数据,不负责修改数据,如果想要修改state中的数据,请去找mutations
  }
});

export default store;

二、右侧头部区域

这部分主要功能有两个,第一个是点击图标后菜单的收缩功能,第二个是用户头像的显示及下拉菜单,包括:个人信息、退出登录(已实现)。

<template>
  <!-- 页面头部部分 -->
  <div class="header">
    <div class="shrink">
      <i v-if="this.$store.state.isCollapse" class="el-icon-s-unfold" @click="isCollapses"></i>
      <i v-else class="el-icon-s-fold" @click="isCollapses"></i>
    </div>
    <div class="header-right">
      <div class="header-user-con">
        <!-- 用户头像 -->
        <div class="user-avator">
          <img src="../assets/Background.jpg" />
        </div>
        <!-- 用户下拉菜单 -->
        <el-dropdown class="user-name" trigger="click" @command="handleCommand">
          <span class="el-dropdown-link"> {
    
    {
    
     username }} <i class="el-icon-caret-bottom"></i></span>
          <el-dropdown-menu slot="dropdown">
            <el-dropdown-item>修改密码</el-dropdown-item>
            <el-dropdown-item command="loginout">退出登录</el-dropdown-item>
          </el-dropdown-menu>
        </el-dropdown>
      </div>
    </div>
  </div>
</template>
 
<script>
export default {
    
    
  data() {
    
    
    return {
    
    
    }
  },
  computed: {
    
    
    username() {
    
    
      return localStorage.getItem('ms_username') || '测试';
    },
  },
  created() {
    
    

  },
  methods: {
    
    
    //菜单是否收缩
    isCollapses() {
    
    
      this.$store.commit('collapse');
    },
    handleCommand(command) {
    
      // 用户下拉菜单选择事件
      if (command == 'loginout') {
    
    
        // localStorage.removeItem('ms_username');
        this.$router.push({
    
     path: "/login" });
      }
    }
  }
}
</script>
 
<style scoped>
.header {
    
    
  display: flex;
  flex-direction: row;
  justify-content: space-between;
  width: 100%;
  height: 60px;
  font-size: 22px;
}
.shrink {
    
    
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 24px;
  cursor: pointer;
}
.shrink i{
    
    
  padding: 0 15px;
}

/* --------------- 用户头像区域的样式 ---------------- */
.header-right {
    
    
  display: flex;
  padding-right: 30px;
}

.header-user-con {
    
    
  display: flex;
  align-items: center;
  justify-content: center;
  height: 60px;
}

.user-avator {
    
    
  margin-left: 20px;
}

.user-avator img {
    
    
  display: block;
  width: 40px;
  height: 40px;
  border-radius: 50%;
}

.user-name {
    
    
  margin-left: 10px;
}

.el-dropdown-link {
    
    
  cursor: pointer;
}

.el-dropdown-menu__item {
    
    
  text-align: center;
}
</style>

三、右侧标签区域

标签区域的功能相对来说也比较重要:
1、左侧菜单选中之后需要通过vuex将数据存储,右侧标签通过vuex获取数据并展示出来;
2、标签的删除功能(vuex中数据也需要删除),点击标签后跳转到对应页面,并且选中对应的左侧菜单;

<template>
    <div class="tags">
        <el-tag v-for="item,index in tagdata" :key="item.name" :closable="item.name!='index'"
            :effect="$route.name === item.name ? 'dark':'plain'" @close="handleClose(item,index)"
            @click="handleClick(item,index)">
            {
    
    {
    
    item.lable}}
        </el-tag>
    </div>
</template>
<script>
export default {
    
    
    data() {
    
    
        return {
    
    
             /*tagdata中的数据格式
            tagdata: [{"menuid": 1,"path": "/index","name": "index","lable": "首页","icon": "el-icon-house","children": [] }]
            */
        };
    },
    computed: {
    
    
        tagdata() {
    
    
            return this.$store.state.taglist;
        }
    },
    methods: {
    
    
        //删除标签
        handleClose(tag, index) {
    
    
            //删除集合中的标签数据
            this.$store.commit('closeTag', tag);
            const length = this.tagdata.length;
            if (tag.name !== this.$route.name) {
    
    
                return;
            }
            if (length === 1) {
    
    
                this.$router.push({
    
     path: "/index" });
            } else if (index === length) {
    
    
                // 如果这两个值相等,就表明当前我们点击的是最后一个tag标签,则事件触发后跳转到左侧的标签
                this.$router.push({
    
     name: this.tagdata[index - 1].name })
            }
            else {
    
    
                // 如果不相等,即只有一种可能性,即当前事件触发的索引不是最后一个,且只会是中间的,那么就跳转到右侧正序的标签
                this.$router.push({
    
     name: this.tagdata[index].name });
            }
        },
        //标签点击
        handleClick(tag, index) {
    
    
            if (index == 0) {
    
     this.$router.push({
    
     path: "/index" }); return; }
            this.$router.push({
    
     name: tag.name });
        }
    }
}
</script>
<style scoped>
.tags {
    
    
    width: 100%;
    height: 30px;
    background-color: #fff;
}

.tags span {
    
    
    height: 30px;
    font-size: 13px;
    line-height: 27px;
    cursor: pointer;
}
</style>

四、效果图

在这里插入图片描述
在这里插入图片描述

五、结束语

做到这里前端的基本框架已经完成了,这是我自己练习的一个程序,主要还是为了熟悉一下vue方便以后开发。项目里面我还添加了一些静态页面,就不细说了,没有什么具体的知识点。最后我把项目源码上传了,大家想看了可以下载下来,下载地址vue项目

猜你喜欢

转载自blog.csdn.net/weixin_43859439/article/details/130129560
今日推荐