从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 相关文章推荐
JS getStyle获取最终样式函数代码
Apr 01 Javascript
非常强大的 jQuery.AsyncBox 弹出对话框插件
Aug 29 Javascript
利用jQuery插件扩展识别浏览器内核与外壳的类型和版本的实现代码
Oct 22 Javascript
判断文件是否正在被使用的JS代码
Dec 21 Javascript
Javascript学习笔记之 函数篇(一) : 函数声明和函数表达式
Jun 24 Javascript
JQuery使用index方法获取Jquery对象数组下标的方法
May 18 Javascript
原生js实现图片轮播特效
Dec 18 Javascript
一览画面点击复选框后获取多个id值的方法
May 30 Javascript
Vue概念及常见命令介绍(1)
Dec 08 Javascript
jQuery实现的checkbox级联选择下拉菜单效果示例
Dec 26 Javascript
vue地址栏直接输入路由无效问题的解决
Nov 15 Javascript
Vue实现购物车基本功能
Nov 08 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
php下使用无限生命期Session的方法
2007/03/16 PHP
PHP 中的批处理的实现
2007/06/14 PHP
用php+javascript实现二级级联菜单的制作
2008/05/06 PHP
PHP 远程文件管理,可以给表格排序,遍历目录,时间排序
2009/08/07 PHP
php+ajax导入大数据时产生的问题处理
2014/06/11 PHP
基于ThinkPHP+uploadify+upload+PHPExcel 无刷新导入数据
2015/09/23 PHP
PHP错误Warning:mysql_query()解决方法
2015/10/24 PHP
jQuery 表单验证插件formValidation实现个性化错误提示
2009/06/23 Javascript
利用javascript/jquery对上传文件格式过滤的方法
2009/07/25 Javascript
js异步加载的三种解决方案
2013/03/04 Javascript
js时间日期格式化封装函数
2014/12/02 Javascript
JavaScript实现N皇后问题算法谜题解答
2014/12/29 Javascript
jquery判断至少有一个checkbox被选中的方法
2015/06/05 Javascript
jQuery实现图片左右滚动特效
2020/04/20 Javascript
Web前端框架Angular4.0.0 正式版发布
2017/03/28 Javascript
[js高手之路]HTML标签解释成DOM节点的实现方法
2017/08/31 Javascript
微信小程序 swiper组件构建轮播图的实例
2017/09/20 Javascript
Vue实现购物车场景下的应用
2017/11/27 Javascript
webpack热模块替换(HMR)/热更新的方法
2018/04/05 Javascript
JS实现同一DOM元素上onClick事件与onDblClick事件并存的解决方法
2018/06/07 Javascript
JavaScript实现仿Clock ISO时钟
2018/06/29 Javascript
Python中lambda的用法及其与def的区别解析
2014/07/28 Python
python实现忽略大小写对字符串列表排序的方法
2014/09/25 Python
理解Python中的With语句
2015/02/02 Python
Python解析xml中dom元素的方法
2015/03/12 Python
python实现微信小程序自动回复
2018/09/10 Python
在Pandas中给多层索引降级的方法
2018/11/16 Python
python读取文件名并改名字的实例
2019/01/07 Python
Pandas DataFrame中的tuple元素遍历的实现
2019/10/23 Python
python爬虫基础知识点整理
2020/06/02 Python
什么是Python变量作用域
2020/06/03 Python
电子商务个人自荐信
2013/12/12 职场文书
复核员上岗演讲稿
2014/01/05 职场文书
英语教师自荐信
2014/05/26 职场文书
教师党员个人自我剖析材料
2014/09/29 职场文书
go goth封装第三方认证库示例详解
2022/08/14 Golang