从0到1搭建Element的后台框架

AntoinetteZora 发布于2月前
0 条问题

由于最近公司要开发一个后台管理系统,查阅了很多vue框架,本人觉得element简洁,方便,于是选择它作为我们的首选框架,并分享给大家,如果您觉得有需要改进的地方可以提出来一起探讨, Github地址 。本文篇幅比较长,希望同学们可以耐心的读下去,如有不懂可以下方留言

一、 目录

目录

二、初始化项目

首先全局安装的vue框架,这里是用的npm包管理工具来安装的,如果你的网不是很好的话可以先安装淘宝镜像 npm install -g cnpm -registry=https://registry.npm.taobao.org ,然后通过cnpm来安装

npm install vue or cnpm install vue
复制代码

其次开始安装vue脚手架,当前是第三版本vue-cli 3.x

cnpm install -g @vue/cli
复制代码

安装完成后,你还可以用这个命令来检查其版本是否正确 (3.x):

vue --version
复制代码

安装脚手架后开始创建我们的项目

vue create vue-admin-project
复制代码

随后会出现两个选项

选择第二项并继续,并选择自己需要配置的功能,完成后并继续,然后开始生成项目项目初始化成功接下来按照上面的提示运行 cd app 以及启动本地服务器 npm run serve ,当运行完成之后会提示你打来本地端口 http://localhost:8080

,会出现欢迎页面,此时代表你的vue项目初始化完成。

三、文件目录介绍与整理

整理前的初始目录

|-- vue-admin-project 
  |-- .gitignore            //git项目忽视文件
  |-- babel.config.js       //babel 配置文件
  |-- package-lock.json     //记录安装包的具体版本号
  |-- package.json          //包的类型
  |-- README.md 
  |-- public                //项目打包后的目录
  |   |-- favicon.ico
  |   |-- index.html
  |-- src                   //项目开发目录
      |-- App.vue           //主入口文件
      |-- main.js           //主入口文件
      |-- router.js         //vue-router文件
      |-- store.js          //vuex
      |-- assets //静态文件
         |-- logo.png
      |-- components        //组件存放目录
        |-- HelloWorld.vue
      |-- views             //视图目录
        |-- About.vue
        |-- Home.vue
复制代码

整理后的目录,主要更改 /src 文件夹下的目录

|-- vue-admin-project
  |-- .gitignore
  |-- babel.config.js
  |-- package-lock.json
  |-- package.json
  |-- README.md
  |-- public
     |-- favicon.ico
     |-- index.html
  |-- src
      |-- App.vue
      |-- main.js
      |-- assets
         |-- logo.png
      |-- components
         |-- HelloWorld.vue
      |-- router        //路由配置文件夹
         |-- router.js
      |-- store        //状态管理文件夹 
         |-- store.js
      |-- views
         |-- About.vue
         |-- Home.vue
复制代码

四、开发环境与线上环境配置

vue-cli 3.0x与vue-cli 2.0x最主要的区别是项目结构目录精简化,这也带来了许多问题,很多配置需要自己配置,由于2.0x版本中直接在 cofig/ 文件夹下面配置开发环境与线上环境,3.0x则需要自己配置。

首先配置开发环境,在项目根目录下新建一个文件 .env 文件。

NODE_ENV="development"              //开发环境
   BASE_URL="http://localhost:3000/"   //开发环境接口地址
复制代码

接下来我们配置线上环境,同样在项目根目录新建一个文件 .env.prod 这就表明是生产环境。

NODE_ENV="production"              //生产环境
   BASE_URL="url"   //生产环境的地址
复制代码

现在我们如何在项目中判断当前环境呢?

我们可以根据 process.env.BASE_URL 来获取它是线上环境还是开发环境,后面会有运用

if(process.env.NODE_ENV='development'){
      console.log( process.env.BASE_URL) //http://localhost:3000/
   }else{
        console.log( process.env.BASE_URL) //url
   }
复制代码

至此,我们成功的配置好了开发环境与线上环境。

五、vue.config.js配置

讲到 vue.config.js 项目配置文件,又不得不说下3.x和2.x的区别,2.x里面webpack相关的配置项直接在项目的 build/webpack.base.conf.js 里面配置,而3.x完全在 vue.config.js 中配置,这使得整个项目看起来更加简洁明了,项目运行速度更快。

由于项目初始化的时候没有 vue.config.js 配置文件,因此我们需要在项目根目录下新建一个 vue.config.js 配置项。

在这个配置项里面,本项目主要是配置三个东西,第一个就是目录别名 alias ,另一个是项目启动时自动打开浏览器,最后一个就是处理引入的全局scss文件。当然有 vue.config.js 的配置远远不止这几项,有兴趣的同学可以去看看vue.config.js具体配置,具体代码如下。

let path=require('path');
   function resolve(dir){
       return path.join(__dirname,dir)
   }
   module.exports = {
       chainWebpack: config => {
           //设置别名
           config.resolve.alias
           .set('@',resolve('src'))
       },
       devServer: {
           open:true  //打开浏览器窗口
       },
       //定义scss全局变量
       css: {
           loaderOptions: {
             sass: {
               data: `@import "@/assets/scss/global.scss";`
             }
           }
         }
   }
复制代码

六、ElementUI引入

开始安装ElementUI

vue add element
复制代码

接下来两个选项,第一个是全部引入,第二个是按需引入,我选择第一个 Fully import ,大家可以按照自己的项目而定。接下来会询问是否引入scss,这里选择是,语言选择zh-cn。

接下来会提示安装成功,并在项目首页有一个element样式的按钮。

七、vue-router路由介绍入

路由管理也是本项目核心部分。

1.引入文件

import Vue from 'vue'
    import Router from 'vue-router'
    import store from '../store/store' //引入状态管理
    import NProgress from 'nprogress' //引入进度条组件 cnpm install nprogress --save
    import 'nprogress/nprogress.css' 
    Vue.use(Router)
复制代码

2.路由懒加载

/**
    *@parma {String} name 文件夹名称
    *@parma {String} component 视图组件名称
    */
    const getComponent = (name,component) => () => import(`@/views/${name}/${component}.vue`);
复制代码

3.路由配置

const myRouter=new Router({
          routes: [
            {
              path: '/',
              redirect: '/home',
              component: getComponent('login','index')
            },
            {
              path: '/login',
              name: 'login',
              component: getComponent('login','index')
            },
            {
              path: '/',
              component:getComponent('layout','Layout'),
              children:[{
                path:'/home',
                name:'home',
                component: getComponent('home','index'),
                meta:{title:'首页'}
              },
              {
                path:'/icon',
                component: getComponent('icons','index'),
                name:'icon',
                meta:{title:'自定义图标'}
              },
              {
                path:'/editor',
                component: getComponent('component','editor'),
                name:'editor',
                meta:{title:'富文本编译器'}
              },
              {
                path:'/countTo',
                component: getComponent('component','countTo'),
                name:'countTo',
                meta:{title:'数字滚动'}
              },
              {
                path:'/tree',
                component: getComponent('component','tree'),
                name:'tree',
                meta:{title:'自定义树'}
              },
              {
                path:'/treeTable',
                component: getComponent('component','treeTable'),
                name:'treeTable',
                meta:{title:'表格树'}
              },
              {
                path:'/treeSelect',
                component: getComponent('component','treeSelect'),
                name:'treeSelect',
                meta:{title:'下拉树'}
              },
              {
                path:'/draglist',
                component: getComponent('draggable','draglist'),
                name:'draglist',
                meta:{title:'拖拽列表'}
              },
              {
                path:'/dragtable',
                component: getComponent('draggable','dragtable'),
                name:'dragtable',
                meta:{title:'拖拽表格'}
              },
              {
                path:'/cricle',
                component: getComponent('charts','cricle'),
                name:'cricle',
                meta:{title:'饼图'}
              },
            ]
            }
          ]
        })
复制代码

4.本项目存在一个token,来验证权限问题,因此进入页面的时候需要判断是否存在token,如果不存在则跳转到登陆页面

//判断是否存在token
    myRouter.beforeEach((to,from,next)=>{
      NProgress.start()
      if (to.path !== '/login' && !store.state.token) {
         next('/login')     //跳转登录
         NProgress.done()   // 结束Progress
      }
      next()
    })
    myRouter.afterEach(() => {
      NProgress.done() // 结束Progress
    })
复制代码

5.导出路由

export default myRouter
复制代码

八、axios引入并封装

1.接口处理我选择的是axios,由于它遵循promise规范,能很好的避免回调地狱。现在我们开始安装

cnpm install axios -S
复制代码

2.在 src 目录下新建文件夹命名为 api ,里面新建两个文件,一个是 api.js ,用于接口的整合,另一个是 request.js ,根据相关业务封装axios请求。

  • request.js

    1.引入依赖

import axios from "axios";
    import router from "../router/router";
    import {
        Loading 
    } from "element-ui";
    import {messages} from '../assets/js/common.js' //封装的提示文件
    import store from '../store/store' //引入vuex
复制代码

2.编写axios基本设置

axios.defaults.timeout = 60000;                         //设置接口超时时间
    axios.defaults.baseURL = process.env.BASE_URL;          //根据环境设置基础路径
    axios.defaults.headers.post["Content-Type"] =
        "application/x-www-form-urlencoded;charset=UTF-8";  //设置编码
    let loading = null;                                     //初始化loading
复制代码

3.编写请求拦截,也就是说在请求接口前要做的事情

/*
 *请求前拦截
 *用于处理需要请求前的操作
 */
axios.interceptors.request.use(
    config => {
        loading = Loading.service({
            text: "正在加载中......",
            fullscreen: true
        });
        if (store.state.token) {
            config.headers["Authorization"] = "Bearer " + store.state.token;
        }
        return config;
    },
    error => {
        return Promise.reject(error);
    }
);
复制代码

4.编写请求响应拦截,用于处理数据返回操作

/*
     *请求响应拦截
     *用于处理数据返回后的操作
     */
    axios.interceptors.response.use(
        response => {
            return new Promise((resolve, reject) => {
                //请求成功后关闭加载框
                if (loading) {
                    loading.close();
                }
                const res = response.data;
                if (res.err_code === 0) {
                    resolve(res)
                } else{
                    reject(res)
                }
            })
        },
        error => {
            console.log(error)
            //请求成功后关闭加载框
            if (loading) {
                loading.close();
            }
            //断网处理或者请求超时
            if (!error.response) {
                //请求超时
                if (error.message.includes("timeout")) {
                    console.log("超时了");
                    messages("error", "请求超时,请检查互联网连接");
                } else {
                    //断网,可以展示断网组件
                    console.log("断网了");
                    messages("error", "请检查网络是否已连接");
                }
                return;
            }
            const status = error.response.status;
            switch (status) {
                case 500:
                    messages("error", "服务器内部错误");
                    break;
                case 404:
                    messages(
                        "error",
                        "未找到远程服务器"
                    );
                    break;
                case 401:
                    messages("warning", "用户登陆过期,请重新登陆");
                    localStorage.removeItem("token");
                    setTimeout(() => {
                        router.replace({
                            path: "/login",
                            query: {
                                redirect: router.currentRoute.fullPath
                            }
                        });
                    }, 1000);
                    break;
                case 400:
                    messages("error", "数据异常,详情请咨询聚保服务热线");
                    break;
                default:
                    messages("error", error.response.data.message);
            }
            return Promise.reject(error);
        }
    );
复制代码

5.请求相关的事情已经完成,现在开始封装get,post请求

/*
     *get方法,对应get请求
     *@param {String} url [请求的url地址]
     *@param {Object} params [请求时候携带的参数]
     */
    export function get(url, params) {
        return new Promise((resolve, reject) => {
            axios
                .get(url, {
                    params
                })
                .then(res => {
                    resolve(res);
                })
                .catch(err => {
                    reject(err);
                });
        });
    }
    /*
     *post方法,对应post请求
     *@param {String} url [请求的url地址]
     *@param {Object} params [请求时候携带的参数]
     */
    export function post(url, params) {
        return new Promise((resolve, reject) => {
            axios
                .post(url, params)
                .then(res => {
                    resolve(res);
                })
                .catch(err => {
                    reject(err);
                });
        });
    }
复制代码
  • api.js

    封装好axios的业务逻辑之后自然要开始,运用,首先引入 get 以及 post 方法

import {get,post} from './request';
复制代码

接下来开始封装接口,并导出

//登陆
    export const  login=(login)=>post('/api/post/user/login',login)
    //上传
    export const  upload=(upload)=>get('/api/get/upload',upload)
复制代码

那我们如何调用接口呢?以登陆页面为例。

import { login } from "@/api/api.js"; //引入login
复制代码
/**
    * @oarma {Object} login 接口传递的参数
    */
    login(login)
    .then(res => {
      //成功之后要做的事情
    })
    .catch(err => {
      //出错时要做的事情
    });
复制代码

接口相关的逻辑已经处理完。

九、vuex引入

由于vue项目中组件之间传递数据比较复杂,因此官方引入了一个全局状态管理的东东,也就是现在要说的vuex,vuex能更好的管理数据,方便组件之间的通信。

现在在store文件夹下面新建四个文件 state.js , mutations.js , getter.js , action.js 。

  • state.js

    state就是Vuex中的公共的状态, 我是将state看作是所有组件的data, 用于保存所有组件的公共数据.

const state = {
        token: '',//权限验证
        tagsList: [], //打开的标签页个数,
        isCollapse: false, //侧边导航是否折叠
    }
    export default state //导出
复制代码
  • mutations.js

    我将mutaions理解为store中的methods, mutations对象中保存着更改数据的回调函数,该函数名官方规定叫type, 第一个参数是state, 第二参数是payload, 也就是自定义的参数.改变state的值必须经过mutations

const mutations = {
        //保存token
        COMMIT_TOKEN(state, object) {
            state.token = object.token;
        },
        //保存标签
        TAGES_LIST(state, arr) {
            state.tagsList = arr;
        },
        IS_COLLAPSE(state, bool) {
            state.isCollapse = bool;
        }
    }
    export default mutations
复制代码
  • getter.js

    我将getters属性理解为所有组件的computed属性,也就是计算属性。vuex的官方文档也是说到可以将getter理解为store的计算属性, getters的返回值会根据它的依赖被缓存起来,且只有当它的依赖值发生了改变才会被重新计算。

const getters={
        //你要计算的属性
    }
    export default getters
复制代码
  • action.js

    actions 类似于 mutations,不同在于:

    1.actions提交的是mutations而不是直接变更状态

    2.actions中可以包含异步操作, mutations中绝对不允许出现异步

    3.actions中的回调函数的第一个参数是context, 是一个与store实例具有相同属性和方法的对象

const actions={
    
    }
    export default actions
复制代码
  • store.js

    store.js是vuex模块整合文件,由于刷新页面会造成vuex数据丢失,所以这里引入了一个vuex数据持久话插件,将state里面的数据保存到localstorage。

    安装 vuex-persistedstate

npm install vuex-persistedstate --save
复制代码
import Vue from 'vue'
    import Vuex from 'vuex'
    import state from "./state";
    import mutations from "./mutations";
    import actions from "./actions";
    import getters from "./getters";
    //引入vuex 数据持久化插件
    import createPersistedState from "vuex-persistedstate"
    Vue.use(Vuex)
    
    export default new Vuex.Store({
      state,
      mutations,
      actions,
      getters,
      plugins: [createPersistedState()]
    })
复制代码

至此vuex引入完毕,如同学们还有不明白的可以去翻阅vuex文档。

十、首页布局介绍

现在我们开始进行页面的布局。首先我们来分析下首页的情况

  1. 侧边栏
  2. 顶部栏
  3. 内容部分
    首先我们在 view 文件夹下面新建一个 layout 文件夹,里面再添加一个 layout.vue ,以及 compentents 文件夹。
  • 侧边栏

    在compentents文件夹下面新建一个 Aside.vue 文件,实现路由跳转相关的逻辑,运用了element导航菜单的路由模式,如有不明白的可以去ElementUI导航菜单去看看。

<template>
      <div class="aside">
        <el-menu
          :default-active="onRoutes"
          class="el-menu-vertical-demo"
          @open="handleOpen"
          @close="handleClose"
          :collapse="isCollapse"
          active-text-color="#bdb7ff"
          router
        >
          <template v-for="item in items">
            <template v-if="item.subs">
              <el-submenu :index="item.index" :key="item.index">
                <template slot="title">
                  <i :class="item.icon"></i>
                  <span slot="title">{{ item.title }}</span>
                </template>
                <template v-for="subItem in item.subs">
                  <el-submenu v-if="subItem.subs" :index="subItem.index" :key="subItem.index">
                    <template slot="title">{{ subItem.title }}</template>
                    <el-menu-item
                      v-for="(threeItem,i) in subItem.subs"
                      :key="i"
                      :index="threeItem.index"
                    >{{ threeItem.title }}</el-menu-item>
                  </el-submenu>
                  <el-menu-item v-else :index="subItem.index" :key="subItem.index">{{ subItem.title }}</el-menu-item>
                </template>
              </el-submenu>
            </template>
            <template v-else>
              <el-menu-item :index="item.index" :key="item.index">
                <i :class="item.icon"></i>
                <span slot="title">{{ item.title }}</span>
              </el-menu-item>
            </template>
          </template>
        </el-menu>
      </div>
    </template>
复制代码
import { mapState } from "vuex";
    export default {
      data() {
        return {
        //配置目录
          items: [
            {
              icon: "el-icon-edit-outline",
              index: "home",
              title: "系统首页"
            },
            {
              icon: "el-icon-edit-outline",
              index: "icon",
              title: "自定义图标"
            },
            {
              icon: "el-icon-edit-outline",
              index: "component",
              title: "组件",
              subs: [
                {
                  index: "editor",
                  title: "富文本编译器"
                },
                {
                  index: "countTo",
                  title: "数字滚动"
                },
                {
                  index: "trees",
                  title: "树形控件",
                  subs: [
                    {
                      index: "tree",
                      title: "自定义树"
                    },
                    {
                      index: "treeSelect",
                      title: "下拉树"
                    }
                    // ,{
                    //   index:'treeTable',
                    //   title:'表格树',
                    // }
                  ]
                },
              ]
            },
            {
              icon: "el-icon-edit-outline",
              index: "draggable",
              title: "拖拽",
              subs: [
                {
                  index: "draglist",
                  title: "拖拽列表"
                },
                {
                  index: "dragtable",
                  title: "拖拽表格"
                }
              ]
            },
            {
              icon: "el-icon-edit-outline",
              index: "charts",
              title: "图表",
              subs: [
                {
                  index: "cricle",
                  title: "饼图"
                },
              ]
            },
            {
              icon: "el-icon-edit-outline",
              index: "7",
              title: "错误处理",
              subs: [
                {
                  index: "permission",
                  title: "权限测试"
                },
                {
                  index: "404",
                  title: "404页面"
                }
              ]
            },
          ]
        };
      },
      computed: {
        onRoutes() {
          return this.$route.path.replace("/", "");
        },
        ...mapState(["isCollapse"]) //从vuex里面获取菜单是否折叠
      },
      methods: {
        //下拉展开
        handleOpen(key, keyPath) {
          console.log(key, keyPath);
        },
        //下来关闭
        handleClose(key, keyPath) {
          console.log(key, keyPath);
        }
      }
    };
复制代码
  • 顶部栏

    在 view/compentents 文件夹下面新建一个 Header.vue

<template>
      <div class="head-container clearfix">
        <div class="header-left">
          <showAside :toggle-click="toggleClick"/>
        </div>
        <div class="header-right">
          <div class="header-user-con">
            <!-- 全屏显示 -->
            <div class="btn-fullscreen" @click="handleFullScreen">
              <el-tooltip effect="dark" :content="fullscreen?`取消全屏`:`全屏`" placement="bottom">
                <i class="el-icon-rank"></i>
              </el-tooltip>
            </div>
            <!-- 消息中心 -->
            <div class="btn-bell">
              <el-tooltip effect="dark" :content="message?`有${message}条未读消息`:`消息中心`" placement="bottom">
                <router-link to="/tabs">
                 <i class="el-icon-bell"></i>
                 </router-link>
              </el-tooltip>
              <span class="btn-bell-badge" v-if="message"></span>
            </div>
            <!-- 用户名下拉菜单 -->
            <el-dropdown class="avatar-container" trigger="click">
              <div class="avatar-wrapper">
                <img
                  src="https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=3266090804,66355162&fm=26&gp=0.jpg"
                  class="user-avatar"
                >
                {{username }}<i class="el-icon-caret-bottom"/>
              </div>
              <el-dropdown-menu slot="dropdown" class="user-dropdown">
                <router-link class="inlineBlock" to="/">
                  <el-dropdown-item>首页</el-dropdown-item>
                </router-link>
                <el-dropdown-item>个人设置</el-dropdown-item>
                <el-dropdown-item divided>
                  <span style="display:block;" @click="logout">退出登陆</span>
                </el-dropdown-item>
              </el-dropdown-menu>
            </el-dropdown>
          </div>
        </div>
      </div>
    </template>
复制代码
import showAside from "@/components/showAside.vue";//引入了一个侧边栏是否折叠的组件
    export default {
      // name:'header',
      components: {
        showAside
      },
      data() {
        return {
          fullscreen: false,
          name: "linxin",
          message: 2,
          username: "zyh"
        };
      },
      computed: {
        isCollapse: {
          get: function() {
            return this.$store.state.isCollapse;
          },
          set: function(newValue) {
            console.log(newValue);
            this.$store.commit("IS_COLLAPSE", newValue);//提交到vuex
          }
        }
      },
      methods: {
        toggleClick() {
          this.isCollapse = !this.isCollapse;
        },
        // 用户名下拉菜单选择事件
        logout(command) {
          this.$router.push("/login");
        },
        // 全屏事件
        handleFullScreen() {
          let element = document.documentElement;
          if (this.fullscreen) {
            if (document.exitFullscreen) {
              document.exitFullscreen();
            } else if (document.webkitCancelFullScreen) {
              document.webkitCancelFullScreen();
            } else if (document.mozCancelFullScreen) {
              document.mozCancelFullScreen();
            } else if (document.msExitFullscreen) {
              document.msExitFullscreen();
            }
          } else {
            if (element.requestFullscreen) {
              element.requestFullscreen();
            } else if (element.webkitRequestFullScreen) {
              element.webkitRequestFullScreen();
            } else if (element.mozRequestFullScreen) {
              element.mozRequestFullScreen();
            } else if (element.msRequestFullscreen) {
              // IE11
              element.msRequestFullscreen();
            }
          }
          this.fullscreen = !this.fullscreen;
        }
      }
    };
复制代码

现在在 src/components 文件夹下面新建一个 showAside.vue 组件

<template>
      <div class="clearfix">
        <div class="showAside pull-left" @click="toggleClick">
          <i class="el-icon-menu"></i>
        </div>
      </div>
    </template>
复制代码
export default {
      name: "showAside",
      props: {
        toggleClick: {
          type: Function,
          default: null
        }
      }
    };
复制代码
  • 顶部导航栏标签组件

    在 view/compentents 文件夹下面新建一个 Tags.vue

<template>
      <!-- 打开标签的容器 -->
      <div class="tags">
        <ul>
          <li
            class="tags-li"
            v-for="(item,index) in tagsList"
            :key="index"
            :class="{'active': isActive(item.path)}"
          >
            <router-link :to="item.path" class="tags-li-title">{{item.title}}</router-link>
            <span class="tags-li-icon" @click="closeTags(index)">
              <i class="el-icon-close"></i>
            </span>
          </li>
        </ul>
        <div class="tags-close-box">
          <el-dropdown @command="handleCommand">
            <el-button size="mini" type="primary">
              标签选项
              <i class="el-icon-arrow-down el-icon--right"></i>
            </el-button>
            <el-dropdown-menu size="small" slot="dropdown">
              <el-dropdown-item command="closeOther">关闭其他</el-dropdown-item>
              <!-- <el-dropdown-item command="all">关闭所有</el-dropdown-item> -->
            </el-dropdown-menu>
          </el-dropdown>
        </div>
      </div>
    </template>
复制代码
import { messages } from "@/assets/js/common.js";
    export default {
      created() {
        //判断标签里面是否有值 有的话直接加载
        if (this.tagsList.length == 0) {
          this.setTags(this.$route);
        }
      },
      computed: {
        //computed 方法里面没有set方法因此不能使用mapState,需要重新定义set方法
        tagsList: {
          get: function() {
            return this.$store.state.tagsList;
          },
          set: function(newValue) {
            this.$store.commit("TAGES_LIST", newValue);
            // this.$store.state.tagsList = newValue;
          }
        }
      },
      watch: {
        //监听路由变化
        $route(newValue, oldValue) {
          this.setTags(newValue);
        }
      },
      methods: {
        //选中的高亮
        isActive(path) {
          return path === this.$route.fullPath;
        },
        handleCommand(command) {
          if (command == "closeOther") {
            // 关闭其他标签
            const curItem = this.tagsList.filter(item => {
              return item.path === this.$route.fullPath;
            });
            this.tagsList = curItem;
          }
        },
        //添加标签
        setTags(route) {
          let isIn = this.tagsList.some(item => {
            //判断标签是否存在
            return item.path === route.fullPath;
          });
          //不存在
          if (!isIn) {
            // 判断当前的标签个数
            if (this.tagsList.length >= 10) {
              messages("warning", "当标签大于10个,请关闭后再打开");
            } else {
              this.tagsList.push({
                title: route.meta.title,
                path: route.fullPath,
                name: route.name
              });
              //存到vuex
              this.$store.commit("TAGES_LIST", this.tagsList);
            }
          }
        },
        closeTags(index) {
          console.log(this.tagsList.length);
          if (this.tagsList.length == 1) {
            messages("warning", "不可全都关闭");
          } else {
            //删除当前
            let tags = this.tagsList.splice(index, 1);
            this.$store.commit("TAGES_LIST", this.tagsList);
          }
        }
      }
    };
复制代码

接下来在 view/compentents 文件夹下面新建一个 Main.vue ,主要是将顶部导航标签栏以及内容部分结合起来。

<template>
        <div class="container">
          <tags />
          <div class="contents">
            <transition name="fade-transform" mode="out-in">
                <router-view></router-view>
            </transition>
          </div>
        </div>
    </template>
复制代码
import Tags from './Tags.vue'
    export default {
        components:{
          Tags
        }
    }
复制代码

相关组件写好,在layout组件中汇总

<template>
      <div class="wrapper">
        <Aside class="aside-container"/>
        <div class="main-container" :class="isCollapse==true?'container_collapse':''">
          <Header/>
          <Main/>
        </div>
      </div>
    </template>
复制代码
import Aside from "./components/Aside.vue";
    import Header from "./components/Header.vue";
    import Main from "./components/Main.vue";
    import { mapState } from "vuex";
    export default {
      name: "Layout",
      components: {
        Aside,
        Header,
        Main
      },
      computed: {
        ...mapState(["isCollapse"])
      }
    };
复制代码

至此首页布局已经规划完成,如有不太清楚的可以查看 项目地址

十一、结语

管理系统是多种多样的,每家公司都有不同的业务逻辑,本篇文章也只是抛砖引玉,还有许多需要修正改进的地方,如果同学们有更好的想法可以提出来希望大家一起完善本项目。

|-- undefined
|-- .env
|-- .env.prod
|-- .env.test
|-- .gitignore
|-- babel.config.js
|-- package-lock.json
|-- package.json
|-- README.md
|-- vue.config.js
|-- public
|   |-- favicon.ico
|   |-- index.html
|-- src
    |-- App.vue
    |-- element-variables.scss
    |-- main.js
    |-- api
    |   |-- api.js
    |   |-- request.js
    |-- assets
    |   |-- logo.png
    |   |-- css
    |   |   |-- normalize.css
    |   |   |-- public.css
    |   |-- icon
    |   |   |-- demo.css
    |   |   |-- demo_index.html
    |   |   |-- iconfont.css
    |   |   |-- iconfont.eot
    |   |   |-- iconfont.js
    |   |   |-- iconfont.svg
    |   |   |-- iconfont.ttf
        |   |   |-- iconfont.woff
        |   |   |-- iconfont.woff2
        |   |-- img
        |   |   |-- tou.jpg
        |   |-- js
        |   |   |-- common.js
        |   |-- scss
        |       |-- global.scss
        |-- components
        |   |-- showAside.vue
        |-- plugins
        |   |-- element.js
        |-- router
        |   |-- router.js
        |-- store
        |   |-- actions.js
        |   |-- getters.js
        |   |-- mutations.js
        |   |-- state.js
        |   |-- store.js
        |-- views
            |-- layout
            |   |-- Layout.vue
            |   |-- components
            |       |-- Aside.vue
            |       |-- Header.vue
            |       |-- Main.vue
            |       |-- Tags.vue
复制代码

最后项目目录文件结构

查看原文: 从0到1搭建Element的后台框架

  • organicduck
  • purplepanda
  • tinyostrich
  • yellowswan
  • heavyelephant
需要 登录 后回复方可回复, 如果你还没有账号你可以 注册 一个帐号。