从0到1搭建Element的后台框架的方法步骤


Posted in Javascript onApril 10, 2019

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

一、初始化项目

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

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

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

cnpm install -g @vue/cli

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

vue --version

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

vue create vue-admin-project

随后会出现两个选项

从0到1搭建Element的后台框架的方法步骤

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

从0到1搭建Element的后台框架的方法步骤 

项目初始化成功

从0到1搭建Element的后台框架的方法步骤

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

从0到1搭建Element的后台框架的方法步骤

二、文件目录介绍与整理

整理前的初始目录

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

九、首页布局介绍

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

从0到1搭建Element的后台框架的方法步骤

  • 侧边栏
  • 顶部栏
  • 内容部分

首先我们在 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

最后项目目录文件结构

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
jquery.blockUI.js上传滚动等待效果实现思路及代码
Mar 18 Javascript
基于jquery编写的横向自适应幻灯片切换特效的实例代码
Aug 06 Javascript
Jqgrid设置全选(选择)及获取选择行的值示例代码
Dec 28 Javascript
利用Jquery实现可多选的下拉框
Feb 21 Javascript
打造个性化的功能强大的Jquery虚拟键盘(VirtualKeyboard)
Oct 11 Javascript
基于javascript实现右下角浮动广告效果
Jan 08 Javascript
JavaScript解析JSON格式数据的方法示例
Jan 24 Javascript
jQuery实现html table行Tr的复制、删除、计算功能
Jul 10 jQuery
使用JS编写的随机抽取号码的小程序
Aug 11 Javascript
详解Angular6 热加载配置方案
Aug 18 Javascript
layui自定义插件citySelect实现省市区三级联动选择
Jul 26 Javascript
ES6中Promise的使用方法实例总结
Feb 18 Javascript
详解vue.js移动端配置flexible.js及注意事项
Apr 10 #Javascript
小程序分享模块超级详解(推荐)
Apr 10 #Javascript
关于JavaScript 数组你应该知道的事情(推荐)
Apr 10 #Javascript
Vue中computed、methods与watch的区别总结
Apr 10 #Javascript
JavaScript 性能提升之路(推荐)
Apr 10 #Javascript
详解vue-cli3 中跨域解决方案
Apr 10 #Javascript
js中数组常用方法总结(推荐)
Apr 09 #Javascript
You might like
中国第一家无线电行
2021/03/01 无线电
Yii2实现ActiveForm ajax提交
2017/05/26 PHP
利用laravel搭建一个迷你博客实战教程
2017/08/13 PHP
php输出控制函数和输出函数生成静态页面
2019/06/27 PHP
javascript下判断一个对象是否具有指定名称的属性的的代码
2010/01/11 Javascript
jquery trim() 功能源代码
2011/02/14 Javascript
深入理解JavaScript系列(7) S.O.L.I.D五大原则之开闭原则OCP
2012/01/15 Javascript
js history对象简单实现返回和前进
2013/10/30 Javascript
自定义刻度jQuery进度条及插件
2015/09/02 Javascript
javascript设置页面背景色及背景图片的方法
2015/12/29 Javascript
基于JavaScript代码实现随机漂浮图片广告
2016/01/05 Javascript
JS回调函数简单用法示例
2017/02/09 Javascript
利用n工具轻松管理Node.js的版本
2017/04/21 Javascript
react在安卓中输入框被手机键盘遮挡问题的解决方法
2018/09/03 Javascript
elementUI select组件默认选中效果实现的方法
2019/03/25 Javascript
Ant Design Pro 下实现文件下载的实现代码
2019/12/03 Javascript
JS数组方法push()、pop()用法实例分析
2020/01/18 Javascript
在vue中使用inheritAttrs实现组件的扩展性介绍
2020/12/07 Vue.js
[01:00:22]DOTA2-DPC中国联赛定级赛 LBZS vs Magma BO3第三场 1月10日
2021/03/11 DOTA
跟老齐学Python之玩转字符串(2)更新篇
2014/09/28 Python
python 实现删除文件或文件夹实例详解
2016/12/04 Python
python中从for循环延申到推导式的具体使用
2019/11/29 Python
python GUI库图形界面开发之PyQt5树形结构控件QTreeWidget详细使用方法与实例
2020/03/02 Python
解决Jupyter notebook中.py与.ipynb文件的import问题
2020/04/21 Python
Python学习之路安装pycharm的教程详解
2020/06/17 Python
COS美国官网:知名服装品牌
2019/04/08 全球购物
幼儿教师国培感言
2014/02/19 职场文书
大课间体育活动方案
2014/03/12 职场文书
质量承诺书范文
2014/03/27 职场文书
2014年帮扶工作总结
2014/11/26 职场文书
晚会闭幕词
2015/01/28 职场文书
英文投诉信格式
2015/07/03 职场文书
美容院管理规章制度
2015/08/05 职场文书
生日祝酒词大全
2015/08/10 职场文书
开学第一周日记(三篇范文)
2019/08/23 职场文书
Typescript类型系统FLOW静态检查基本规范
2022/05/25 Javascript