溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

從0到1搭建Element的后臺(tái)框架的方法步驟

發(fā)布時(shí)間:2020-10-19 02:27:37 來源:腳本之家 閱讀:203 作者:zyhing 欄目:web開發(fā)

由于最近公司要開發(fā)一個(gè)后臺(tái)管理系統(tǒng),查閱了很多vue框架,本人覺得element簡(jiǎn)潔,方便,于是選擇它作為我們的首選框架,并分享給大家,如果您覺得有需要改進(jìn)的地方可以提出來一起探討,Github地址。本文篇幅比較長(zhǎng),希望同學(xué)們可以耐心的讀下去,如有不懂可以下方留言

一、初始化項(xiàng)目

首先全局安裝的vue框架,這里是用的npm包管理工具來安裝的,如果你的網(wǎng)不是很好的話可以先安裝淘寶鏡像 npm install -g cnpm -registry=https://registry.npm.taobao.org,然后通過cnpm來安裝

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

其次開始安裝vue腳手架,當(dāng)前是第三版本vue-cli 3.x

cnpm install -g @vue/cli

安裝完成后,你還可以用這個(gè)命令來檢查其版本是否正確 (3.x):

vue --version

安裝腳手架后開始創(chuàng)建我們的項(xiàng)目

vue create vue-admin-project

隨后會(huì)出現(xiàn)兩個(gè)選項(xiàng)

從0到1搭建Element的后臺(tái)框架的方法步驟

選擇第二項(xiàng)并繼續(xù),并選擇自己需要配置的功能,完成后并繼續(xù),然后開始生成項(xiàng)目

從0到1搭建Element的后臺(tái)框架的方法步驟 

項(xiàng)目初始化成功

從0到1搭建Element的后臺(tái)框架的方法步驟

接下來按照上面的提示運(yùn)行 cd app以及啟動(dòng)本地服務(wù)器 npm run serve,當(dāng)運(yùn)行完成之后會(huì)提示你打來本地端口 http://localhost:8080,會(huì)出現(xiàn)歡迎頁(yè)面,此時(shí)代表你的vue項(xiàng)目初始化完成。

從0到1搭建Element的后臺(tái)框架的方法步驟

二、文件目錄介紹與整理

整理前的初始目錄

|-- vue-admin-project 
 |-- .gitignore   //git項(xiàng)目忽視文件
 |-- babel.config.js  //babel 配置文件
 |-- package-lock.json  //記錄安裝包的具體版本號(hào)
 |-- package.json   //包的類型
 |-- README.md 
 |-- public    //項(xiàng)目打包后的目錄
 | |-- favicon.ico
 | |-- index.html
 |-- src     //項(xiàng)目開發(fā)目錄
  |-- App.vue   //主入口文件
  |-- main.js   //主入口文件
  |-- router.js   //vue-router文件
  |-- store.js   //vuex
  |-- assets //靜態(tài)文件
   |-- 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  //狀態(tài)管理文件夾 
   |-- store.js
  |-- views
   |-- About.vue
   |-- Home.vue

三、開發(fā)環(huán)境與線上環(huán)境配置

vue-cli 3.0x與vue-cli 2.0x最主要的區(qū)別是項(xiàng)目結(jié)構(gòu)目錄精簡(jiǎn)化,這也帶來了許多問題,很多配置需要自己配置,由于2.0x版本中直接在 cofig/文件夾下面配置開發(fā)環(huán)境與線上環(huán)境,3.0x則需要自己配置。

首先配置開發(fā)環(huán)境,在項(xiàng)目根目錄下新建一個(gè)文件 .env文件。

 NODE_ENV="development"    //開發(fā)環(huán)境
 BASE_URL="http://localhost:3000/" //開發(fā)環(huán)境接口地址

接下來我們配置線上環(huán)境,同樣在項(xiàng)目根目錄新建一個(gè)文件 .env.prod這就表明是生產(chǎn)環(huán)境。

 NODE_ENV="production"    //生產(chǎn)環(huán)境
 BASE_URL="url" //生產(chǎn)環(huán)境的地址

現(xiàn)在我們?nèi)绾卧陧?xiàng)目中判斷當(dāng)前環(huán)境呢?

我們可以根據(jù) process.env.BASE_URL來獲取它是線上環(huán)境還是開發(fā)環(huán)境,后面會(huì)有運(yùn)用

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

至此,我們成功的配置好了開發(fā)環(huán)境與線上環(huán)境。

四、vue.config.js配置

講到 vue.config.js項(xiàng)目配置文件,又不得不說下3.x和2.x的區(qū)別,2.x里面webpack相關(guān)的配置項(xiàng)直接在項(xiàng)目的 build/webpack.base.conf.js里面配置,而3.x完全在 vue.config.js中配置,這使得整個(gè)項(xiàng)目看起來更加簡(jiǎn)潔明了,項(xiàng)目運(yùn)行速度更快。

由于項(xiàng)目初始化的時(shí)候沒有 vue.config.js配置文件,因此我們需要在項(xiàng)目根目錄下新建一個(gè) vue.config.js配置項(xiàng)。

在這個(gè)配置項(xiàng)里面,本項(xiàng)目主要是配置三個(gè)東西,第一個(gè)就是目錄別名 alias,另一個(gè)是項(xiàng)目啟動(dòng)時(shí)自動(dòng)打開瀏覽器,最后一個(gè)就是處理引入的全局scss文件。當(dāng)然有 vue.config.js的配置遠(yuǎn)遠(yuǎn)不止這幾項(xiàng),有興趣的同學(xué)可以去看看vue.config.js具體配置,具體代碼如下。

 let path=require('path');
 function resolve(dir){
  return path.join(__dirname,dir)
 }
 module.exports = {
  chainWebpack: config => {
   //設(shè)置別名
   config.resolve.alias
   .set('@',resolve('src'))
  },
  devServer: {
   open:true //打開瀏覽器窗口
  },
  //定義scss全局變量
  css: {
   loaderOptions: {
    sass: {
    data: `@import "@/assets/scss/global.scss";`
    }
   }
   }
 }

五、ElementUI引入

開始安裝ElementUI

vue add element

接下來兩個(gè)選項(xiàng),第一個(gè)是全部引入,第二個(gè)是按需引入,我選擇第一個(gè) Fully import,大家可以按照自己的項(xiàng)目而定。接下來會(huì)詢問是否引入scss,這里選擇是,語言選擇zh-cn。

接下來會(huì)提示安裝成功,并在項(xiàng)目首頁(yè)有一個(gè)element樣式的按鈕。

六、vue-router路由介紹入

路由管理也是本項(xiàng)目核心部分。

1.引入文件

 import Vue from 'vue'
 import Router from 'vue-router'
 import store from '../store/store' //引入狀態(tài)管理
 import NProgress from 'nprogress' //引入進(jìn)度條組件 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:'首頁(yè)'}
    },
    {
    path:'/icon',
    component: getComponent('icons','index'),
    name:'icon',
    meta:{title:'自定義圖標(biāo)'}
    },
    {
    path:'/editor',
    component: getComponent('component','editor'),
    name:'editor',
    meta:{title:'富文本編譯器'}
    },
    {
    path:'/countTo',
    component: getComponent('component','countTo'),
    name:'countTo',
    meta:{title:'數(shù)字滾動(dòng)'}
    },
    {
    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.本項(xiàng)目存在一個(gè)token,來驗(yàn)證權(quán)限問題,因此進(jìn)入頁(yè)面的時(shí)候需要判斷是否存在token,如果不存在則跳轉(zhuǎn)到登陸頁(yè)面

 //判斷是否存在token
 myRouter.beforeEach((to,from,next)=>{
  NProgress.start()
  if (to.path !== '/login' && !store.state.token) {
   next('/login')  //跳轉(zhuǎn)登錄
   NProgress.done() // 結(jié)束Progress
  }
  next()
 })
 myRouter.afterEach(() => {
  NProgress.done() // 結(jié)束Progress
 })

5.導(dǎo)出路由

export default myRouter

七、axios引入并封裝

1.接口處理我選擇的是axios,由于它遵循promise規(guī)范,能很好的避免回調(diào)地獄?,F(xiàn)在我們開始安裝

 cnpm install axios -S

2.在 src目錄下新建文件夾命名為 api,里面新建兩個(gè)文件,一個(gè)是 api.js,用于接口的整合,另一個(gè)是 request.js,根據(jù)相關(guān)業(yè)務(wù)封裝axios請(qǐng)求。

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基本設(shè)置

 axios.defaults.timeout = 60000;       //設(shè)置接口超時(shí)時(shí)間
 axios.defaults.baseURL = process.env.BASE_URL;   //根據(jù)環(huán)境設(shè)置基礎(chǔ)路徑
 axios.defaults.headers.post["Content-Type"] =
  "application/x-www-form-urlencoded;charset=UTF-8"; //設(shè)置編碼
 let loading = null;          //初始化loading

3.編寫請(qǐng)求攔截,也就是說在請(qǐng)求接口前要做的事情

 /*
 *請(qǐng)求前攔截
 *用于處理需要請(qǐng)求前的操作
 */
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.編寫請(qǐng)求響應(yīng)攔截,用于處理數(shù)據(jù)返回操作

 /*
  *請(qǐng)求響應(yīng)攔截
  *用于處理數(shù)據(jù)返回后的操作
  */
 axios.interceptors.response.use(
  response => {
   return new Promise((resolve, reject) => {
    //請(qǐng)求成功后關(guān)閉加載框
    if (loading) {
     loading.close();
    }
    const res = response.data;
    if (res.err_code === 0) {
     resolve(res)
    } else{
     reject(res)
    }
   })
  },
  error => {
   console.log(error)
   //請(qǐng)求成功后關(guān)閉加載框
   if (loading) {
    loading.close();
   }
   //斷網(wǎng)處理或者請(qǐng)求超時(shí)
   if (!error.response) {
    //請(qǐng)求超時(shí)
    if (error.message.includes("timeout")) {
     console.log("超時(shí)了");
     messages("error", "請(qǐng)求超時(shí),請(qǐng)檢查互聯(lián)網(wǎng)連接");
    } else {
     //斷網(wǎng),可以展示斷網(wǎng)組件
     console.log("斷網(wǎng)了");
     messages("error", "請(qǐng)檢查網(wǎng)絡(luò)是否已連接");
    }
    return;
   }
   const status = error.response.status;
   switch (status) {
    case 500:
     messages("error", "服務(wù)器內(nèi)部錯(cuò)誤");
     break;
    case 404:
     messages(
      "error",
      "未找到遠(yuǎn)程服務(wù)器"
     );
     break;
    case 401:
     messages("warning", "用戶登陸過期,請(qǐng)重新登陸");
     localStorage.removeItem("token");
     setTimeout(() => {
      router.replace({
       path: "/login",
       query: {
        redirect: router.currentRoute.fullPath
       }
      });
     }, 1000);
     break;
    case 400:
     messages("error", "數(shù)據(jù)異常");
     break;
    default:
     messages("error", error.response.data.message);
   }
   return Promise.reject(error);
  }
 );

5.請(qǐng)求相關(guān)的事情已經(jīng)完成,現(xiàn)在開始封裝get,post請(qǐng)求

 /*
  *get方法,對(duì)應(yīng)get請(qǐng)求
  *@param {String} url [請(qǐng)求的url地址]
  *@param {Object} params [請(qǐng)求時(shí)候攜帶的參數(shù)]
  */
 export function get(url, params) {
  return new Promise((resolve, reject) => {
   axios
    .get(url, {
     params
    })
    .then(res => {
     resolve(res);
    })
    .catch(err => {
     reject(err);
    });
  });
 }
 /*
  *post方法,對(duì)應(yīng)post請(qǐng)求
  *@param {String} url [請(qǐng)求的url地址]
  *@param {Object} params [請(qǐng)求時(shí)候攜帶的參數(shù)]
  */
 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的業(yè)務(wù)邏輯之后自然要開始,運(yùn)用,首先引入 get以及 post方法

import {get,post} from './request';

接下來開始封裝接口,并導(dǎo)出

 //登陸
 export const login=(login)=>post('/api/post/user/login',login)
 //上傳
 export const upload=(upload)=>get('/api/get/upload',upload)

那我們?nèi)绾握{(diào)用接口呢?以登陸頁(yè)面為例。

import { login } from "@/api/api.js"; //引入login
 /**
 * @oarma {Object} login 接口傳遞的參數(shù)
 */
 login(login)
 .then(res => {
  //成功之后要做的事情
 })
 .catch(err => {
  //出錯(cuò)時(shí)要做的事情
 });

接口相關(guān)的邏輯已經(jīng)處理完。

八、vuex引入

由于vue項(xiàng)目中組件之間傳遞數(shù)據(jù)比較復(fù)雜,因此官方引入了一個(gè)全局狀態(tài)管理的東東,也就是現(xiàn)在要說的vuex,vuex能更好的管理數(shù)據(jù),方便組件之間的通信。

現(xiàn)在在store文件夾下面新建四個(gè)文件 state.js, mutations.js, getter.js, action.js。

state.js

state就是Vuex中的公共的狀態(tài), 我是將state看作是所有組件的data, 用于保存所有組件的公共數(shù)據(jù).

 const state = {
  token: '',//權(quán)限驗(yàn)證
  tagsList: [], //打開的標(biāo)簽頁(yè)個(gè)數(shù),
  isCollapse: false, //側(cè)邊導(dǎo)航是否折疊
 }
 export default state //導(dǎo)出

mutations.js

我將mutaions理解為store中的methods, mutations對(duì)象中保存著更改數(shù)據(jù)的回調(diào)函數(shù),該函數(shù)名官方規(guī)定叫type, 第一個(gè)參數(shù)是state, 第二參數(shù)是payload, 也就是自定義的參數(shù).改變state的值必須經(jīng)過mutations

 const mutations = {
  //保存token
  COMMIT_TOKEN(state, object) {
   state.token = object.token;
  },
  //保存標(biāo)簽
  TAGES_LIST(state, arr) {
   state.tagsList = arr;
  },
  IS_COLLAPSE(state, bool) {
   state.isCollapse = bool;
  }
 }
 export default mutations

getter.js

我將getters屬性理解為所有組件的computed屬性,也就是計(jì)算屬性。vuex的官方文檔也是說到可以將getter理解為store的計(jì)算屬性, getters的返回值會(huì)根據(jù)它的依賴被緩存起來,且只有當(dāng)它的依賴值發(fā)生了改變才會(huì)被重新計(jì)算。

 const getters={
  //你要計(jì)算的屬性
 }
 export default getters

action.js

actions 類似于 mutations,不同在于:

1.actions提交的是mutations而不是直接變更狀態(tài)

2.actions中可以包含異步操作, mutations中絕對(duì)不允許出現(xiàn)異步

3.actions中的回調(diào)函數(shù)的第一個(gè)參數(shù)是context, 是一個(gè)與store實(shí)例具有相同屬性和方法的對(duì)象

 const actions={
 
 }
 export default actions

store.js

store.js是vuex模塊整合文件,由于刷新頁(yè)面會(huì)造成vuex數(shù)據(jù)丟失,所以這里引入了一個(gè)vuex數(shù)據(jù)持久話插件,將state里面的數(shù)據(jù)保存到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 數(shù)據(jù)持久化插件
 import createPersistedState from "vuex-persistedstate"
 Vue.use(Vuex)
 
 export default new Vuex.Store({
  state,
  mutations,
  actions,
  getters,
  plugins: [createPersistedState()]
 })

至此vuex引入完畢,如同學(xué)們還有不明白的可以去翻閱vuex文檔。

九、首頁(yè)布局介紹

現(xiàn)在我們開始進(jìn)行頁(yè)面的布局。首先我們來分析下首頁(yè)的情況

從0到1搭建Element的后臺(tái)框架的方法步驟

  • 側(cè)邊欄
  • 頂部欄
  • 內(nèi)容部分

首先我們?cè)?view文件夾下面新建一個(gè) layout文件夾,里面再添加一個(gè) layout.vue,以及 compentents文件夾。

側(cè)邊欄

在compentents文件夾下面新建一個(gè) Aside.vue文件,實(shí)現(xiàn)路由跳轉(zhuǎn)相關(guān)的邏輯,運(yùn)用了element導(dǎo)航菜單的路由模式,如有不明白的可以去ElementUI導(dǎo)航菜單去看看。

 <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: "系統(tǒng)首頁(yè)"
   },
   {
    icon: "el-icon-edit-outline",
    index: "icon",
    title: "自定義圖標(biāo)"
   },
   {
    icon: "el-icon-edit-outline",
    index: "component",
    title: "組件",
    subs: [
    {
     index: "editor",
     title: "富文本編譯器"
    },
    {
     index: "countTo",
     title: "數(shù)字滾動(dòng)"
    },
    {
     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: "錯(cuò)誤處理",
    subs: [
    {
     index: "permission",
     title: "權(quán)限測(cè)試"
    },
    {
     index: "404",
     title: "404頁(yè)面"
    }
    ]
   },
   ]
  };
  },
  computed: {
  onRoutes() {
   return this.$route.path.replace("/", "");
  },
  ...mapState(["isCollapse"]) //從vuex里面獲取菜單是否折疊
  },
  methods: {
  //下拉展開
  handleOpen(key, keyPath) {
   console.log(key, keyPath);
  },
  //下來關(guān)閉
  handleClose(key, keyPath) {
   console.log(key, keyPath);
  }
  }
 };

頂部欄

view/compentents文件夾下面新建一個(gè) 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>首頁(yè)</el-dropdown-item>
    </router-link>
    <el-dropdown-item>個(gè)人設(shè)置</el-dropdown-item>
    <el-dropdown-item divided>
     <span  @click="logout">退出登陸</span>
    </el-dropdown-item>
    </el-dropdown-menu>
   </el-dropdown>
   </div>
  </div>
  </div>
 </template>
 import showAside from "@/components/showAside.vue";//引入了一個(gè)側(cè)邊欄是否折疊的組件
 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;
  }
  }
 };

現(xiàn)在在 src/components文件夾下面新建一個(gè) 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
  }
  }
 };

頂部導(dǎo)航欄標(biāo)簽組件

view/compentents文件夾下面新建一個(gè) Tags.vue

<template>
  <!-- 打開標(biāo)簽的容器 -->
  <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">
    標(biāo)簽選項(xiàng)
    <i class="el-icon-arrow-down el-icon--right"></i>
   </el-button>
   <el-dropdown-menu size="small" slot="dropdown">
    <el-dropdown-item command="closeOther">關(guān)閉其他</el-dropdown-item>
    <!-- <el-dropdown-item command="all">關(guān)閉所有</el-dropdown-item> -->
   </el-dropdown-menu>
   </el-dropdown>
  </div>
  </div>
 </template>
 import { messages } from "@/assets/js/common.js";
 export default {
  created() {
  //判斷標(biāo)簽里面是否有值 有的話直接加載
  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: {
  //監(jiān)聽路由變化
  $route(newValue, oldValue) {
   this.setTags(newValue);
  }
  },
  methods: {
  //選中的高亮
  isActive(path) {
   return path === this.$route.fullPath;
  },
  handleCommand(command) {
   if (command == "closeOther") {
   // 關(guān)閉其他標(biāo)簽
   const curItem = this.tagsList.filter(item => {
    return item.path === this.$route.fullPath;
   });
   this.tagsList = curItem;
   }
  },
  //添加標(biāo)簽
  setTags(route) {
   let isIn = this.tagsList.some(item => {
   //判斷標(biāo)簽是否存在
   return item.path === route.fullPath;
   });
   //不存在
   if (!isIn) {
   // 判斷當(dāng)前的標(biāo)簽個(gè)數(shù)
   if (this.tagsList.length >= 10) {
    messages("warning", "當(dāng)標(biāo)簽大于10個(gè),請(qǐng)關(guān)閉后再打開");
   } 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", "不可全都關(guān)閉");
   } else {
   //刪除當(dāng)前
   let tags = this.tagsList.splice(index, 1);
   this.$store.commit("TAGES_LIST", this.tagsList);
   }
  }
  }
 };

接下來在 view/compentents文件夾下面新建一個(gè) Main.vue,主要是將頂部導(dǎo)航標(biāo)簽欄以及內(nèi)容部分結(jié)合起來。

 <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
  }
 }

相關(guān)組件寫好,在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"])
  }
 };

至此首頁(yè)布局已經(jīng)規(guī)劃完成,如有不太清楚的可以查看項(xiàng)目地址

十、結(jié)語

管理系統(tǒng)是多種多樣的,每家公司都有不同的業(yè)務(wù)邏輯,本篇文章也只是拋磚引玉,還有許多需要修正改進(jìn)的地方,如果同學(xué)們有更好的想法可以提出來希望大家一起完善本項(xiàng)目。

|-- 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

最后項(xiàng)目目錄文件結(jié)構(gòu)

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持億速云。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI