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


    一、目录

    目录
    1.目录
    2.初始化项目
    3.文件目录介绍与整理
    4.开发环境与线上环境配置
    5.vue.config.js配置
    6.ElementUI引入
    7.vue-router路由介绍
    8.axios引入并封装
    9.vuex引入
    10.首页布局介绍
    11.结语

    二、初始化项目

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

    cnpm install -g @vue/cli or npm install -g @vue/cli

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

    vue --version

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

    vue create vue-admin-project

    随后会出现两个选项

    1.png
    选择第二项并继续,并选择自己需要配置的功能,完成后并继续,然后开始生成项目

    2.png
    项目初始化成功

    3.png
    接下来按照上面的提示运行cd app以及启动本地服务器npm run serve,当运行完成之后会提示你打来本地端口http://localhost:8080,会出现欢迎页面,此时代表你的vue项目初始化完成。

    5.png

    三、文件目录介绍与整理

    整理前的初始目录

    |-- 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中配置,这使得整个项目看起来更加简洁明了,项目运行速度更快。<br/>
    由于项目初始化的时候没有vue.config.js配置文件,因此我们需要在项目根目录下新建一个vue.config.js配置项。<br/>
    在这个配置项里面,本项目主要是配置三个东西,第一个就是目录别名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 <br/>

    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文档。

    十、首页布局介绍

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

    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"])
          }
        };

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

    十一、结语

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

    |-- vue-admin-project
    |-- .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

    最后项目目录文件结构

    转载:https://segmentfault.com/a/1190000018943236#articleHeader0

  • 相关阅读:
    HDU 1573: X问题
    HDU 1370: Biorhythms
    Break Number --AtCoder
    Cat Snuke and a Voyage --AtCoder
    Fennec VS. Snuke --AtCoder
    Splitting Pile --AtCoder
    Sharing Cookies --AtCoder
    GLB串
    派(Dispatch)
    你知道那棵杨树底下有什么吗
  • 原文地址:https://www.cnblogs.com/liupengfei13346950447/p/10881622.html
Copyright © 2020-2023  润新知