從0到1搭建Element的後臺框架

因爲最近公司要開發一個後臺管理系統,查閱了不少vue框架,本人以爲element簡潔,方便,因而選擇它做爲咱們的首選框架,並分享給你們,若是您以爲有須要改進的地方能夠提出來一塊兒探討,Github地址。本文篇幅比較長,但願同窗們能夠耐心的讀下去,若有不懂能夠下方留言javascript

1、目錄

目錄
1.目錄
2.初始化項目
3.文件目錄介紹與整理
4.開發環境與線上環境配置
5.vue.config.js配置
6.ElementUI引入
7.vue-router路由介紹
8.axios引入並封裝
9.vuex引入
10.首頁佈局介紹
11.結語

2、初始化項目

首先全局安裝vue腳手架,當前是第三版本vue-cli3.x,這裏是用的npm包管理工具來安裝的,若是你的網不是很好的話能夠先安裝淘寶鏡像 npm install -g cnpm -registry=https://registry.npm.taobao.org,而後經過cnpm來安裝css

cnpm install -g @vue/cli or npm install -g @vue/cli
複製代碼

安裝完成後,你還能夠用這個命令來檢查其版本是否正確 (3.x):html

vue --version
複製代碼

安裝腳手架後開始建立咱們的項目vue

vue create vue-admin-project
複製代碼

隨後會出現兩個選項java

alt
選擇第二項並繼續,並選擇本身須要配置的功能,完成後並繼續,而後開始生成項目

alt
項目初始化成功

alt
接下來按照上面的提示運行 cd app以及啓動本地服務器 npm run serve,當運行完成以後會提示你打來本地端口 http://localhost:8080,會出現歡迎頁面,此時表明你的vue項目初始化完成。

alt

3、文件目錄介紹與整理

整理前的初始目錄webpack

|-- 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文件夾下的目錄ios

|-- 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
複製代碼

4、開發環境與線上環境配置

vue-cli 3.0x與vue-cli 2.0x最主要的區別是項目結構目錄精簡化,這也帶來了許多問題,不少配置須要本身配置,因爲2.0x版本中直接在cofig/文件夾下面配置開發環境與線上環境,3.0x則須要本身配置。
首先配置開發環境,在項目根目錄下新建一個文件.env文件。c++

NODE_ENV="development"              //開發環境
   BASE_URL="http://localhost:3000/"   //開發環境接口地址
複製代碼

接下來咱們配置線上環境,一樣在項目根目錄新建一個文件.env.prod這就代表是生產環境。git

NODE_ENV="production"              //生產環境
   BASE_URL="url"   //生產環境的地址
複製代碼

如今咱們如何在項目中判斷當前環境呢?
咱們能夠根據process.env.BASE_URL來獲取它是線上環境仍是開發環境,後面會有運用es6

if(process.env.NODE_ENV='development'){
      console.log( process.env.BASE_URL) //http://localhost:3000/
   }else{
        console.log( process.env.BASE_URL) //url
   }
複製代碼

至此,咱們成功的配置好了開發環境與線上環境。

5、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";`
             }
           }
         }
   }
複製代碼

6、ElementUI引入

開始安裝ElementUI

vue add element
複製代碼

接下來兩個選項,第一個是所有引入,第二個是按需引入,我選擇第一個Fully import,你們能夠按照本身的項目而定。接下來會詢問是否引入scss,這裏選擇是,語言選擇zh-cn。
接下來會提示安裝成功,並在項目首頁有一個element樣式的按鈕。

7、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
複製代碼

8、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 => {
      //出錯時要作的事情
    });
複製代碼

接口相關的邏輯已經處理完。

9、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文檔。

10、首頁佈局介紹

如今咱們開始進行頁面的佈局。首先咱們來分析下首頁的狀況

首頁

  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"])
      }
    };
複製代碼

至此首頁佈局已經規劃完成,若有不太清楚的能夠查看項目地址

11、結語

管理系統是多種多樣的,每家公司都有不一樣的業務邏輯,本篇文章也只是拋磚引玉,還有許多須要修正改進的地方,若是同窗們有更好的想法能夠提出來但願你們一塊兒完善本項目。

|-- 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
複製代碼

最後項目目錄文件結構

相關文章連接
從0到1搭建element後臺框架之權限篇
[打包優化]從0到1搭建element後臺框架之優化架

相關文章
相關標籤/搜索