保姆級別的vue + ElementUI 搭建後臺管理系統教程

X南川發表於2021-11-19

vue + ElementUI 搭建後臺管理系統記錄

本文件記錄了該系統從零配置的完整過程

專案原始碼請訪問:https://gitee.com/szxio/vue2Admin,如果感覺對你有幫助,請點一個小星星,O(∩_∩)O

新建專案

vue create vueadmin

安裝 less-loader

安裝

這裡是一個小坑,安裝 less-loader 時推薦安裝指定版本,如果安裝預設高版本會導致專案出錯

cnpm i less-loader@6.0.0 -D

使用

<style lang="less" scoped>
div{
  b{
    span{
      color: red;
    }
  }
}
</style>

引入 ElementUI

安裝

cnpm i element-ui -S

配置

import Vue from 'vue'
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
import App from './App.vue'

Vue.use(ElementUI);

Vue.config.productionTip = false

new Vue({
  render: h => h(App)
}).$mount('#app')

使用

<template>
  <div>
    <el-row>
      <el-button>預設按鈕</el-button>
      <el-button type="primary">主要按鈕</el-button>
      <el-button type="success">成功按鈕</el-button>
      <el-button type="info">資訊按鈕</el-button>
      <el-button type="warning">警告按鈕</el-button>
      <el-button type="danger">危險按鈕</el-button>
    </el-row>
  </div>
</template>

配置 VueRouter

npm安裝

  1. 安裝
npm install vue-router
  1. 新建 scr/router/index.js,並新增如下程式碼
import Vue from "vue";
import VueRouter from "vue-router";

Vue.use(VueRouter);

const routes = [
  {
    path: "/",
    name: "首頁",
    component: () => import("../views/Home.vue"),
  },
  {
    path: "/about",
    name: "About",
    component: () => import("../views/About.vue"),
  },
];

const router = new VueRouter({
  mode: "history",
  base: process.env.BASE_URL,
  routes,
});

// 前置路由攔截器
router.beforeEach((to, from, next) => {
  // 設定當前頁簽名稱
  document.title = to.name;
  next();
});

export default router;

配置前置路由攔截器動態設定每個頁面的瀏覽器頁簽名稱

  1. 修改 main.js
import Vue from 'vue'
import VueRouter from 'vue-router'
import router from './router'

new Vue({
  router,
  render: h => h(App)
}).$mount('#app')
  1. 修改 App.vue
<template>
  <div id="app">
    <router-view />
  </div>
</template>
  1. 重啟專案,分別訪問如下地址可以檢視頁面效果

VueCli安裝

如果專案是使用vue-cli建立的,則可以使用下面的命令直接生成上述程式碼及兩個示例路由。它也會覆蓋你的 App.vue,因此請確保在專案中執行以下命令之前備份這個檔案

vue add router

動態生成左側選單

新增layout元件

  1. 修改路由檔案

首先我們要建立好 router 路由,修改 src\router\index.js 檔案

import Vue from "vue";
import VueRouter from "vue-router";
import Layouts from "../layouts";

Vue.use(VueRouter);

const routes = [
  {
    path: "",
    redirect: "home",
    component: Layouts,
    children: [
      {
        path: "/home",
        meta: { title: "首頁", icon: "el-icon-s-home" },
        component: () => import("../views/home"),
      },
      {
        path: "system",
        meta: { title: "系統管理", icon: "el-icon-s-home" },
        component: Layouts,
        children: [
          {
            path: "item1",
            meta: { title: "使用者管理", icon: "el-icon-s-home" },
            component: () => import("../views/system/item1"),
          },
          {
            path: "item2",
            meta: { title: "產品管理", icon: "el-icon-s-home" },
            component: () => import("../views/system/item2"),
          },
        ],
      },
    ],
  },
];

const router = new VueRouter({
  mode: "history",
  base: process.env.BASE_URL,
  routes,
});

// 前置路由攔截器
router.beforeEach((to, from, next) => {
  // 設定當前頁簽名稱
  document.title = to.meta.title;
  next();
});

export default router;

程式碼說明:

  • path:路由地址
  • redirect:重定向到指定路由
  • component:頁面對應的元件
  • children:設定子路由,二級選單
  • meta:頁面的補充,用來宣告頁面的名稱和圖示等

我們將所有的頁面都放在根路由的 children 下面,如果下面的選單沒有配置 children 屬性,則表示該選單是一級選單,如果設定了則表示二級選單,可以多級巢狀。上面的路由對應的修改views 資料夾下的檔案結構:

  1. 新建src\layouts\index.vue

這個檔案用來配置專案頁面的外殼,左側的選單和頂部的麵包屑都會在該資料夾中

頁面結構分成三大部分:

  • 左側選單
  • 頂部麵包屑
  • 內容展示區域

對應成程式碼結構如下

<template>
  <div>
    <div>左側選單</div>
    <div>
      <div>頭部麵包屑</div>
      <div>內容展示區</div>
    </div>
  </div>
</template>

我們既然要將頁面在內容展示區顯示,所以我們對應的建立專門用來展示頁面的元件。

所以接下來新建 src\layouts\components\AppContent.vue 元件。元件程式碼如下

<template>
    <div>
        <router-view/>
    </div>
</template>

沒有看錯,很簡單,只要放置一個 router-view 標籤即可。然後將 AppContent 元件註冊到 layouts\index.vue

<template>
  <div>
    <div>左側選單</div>
    <div>
      <div>頭部麵包屑</div>
      <div>
        <AppContent />
      </div>
    </div>
  </div>
</template>

<script>
import AppContent from "./components/AppContent.vue";
export default {
  components: {
    AppContent,
  },
};
</script>
  1. 修改 App.vue

只保留 router-view

<template>
  <div>
    <router-view/>
  </div>
</template>

現在我們開啟頁面看到如下效果

修改頁面樣式

我們首頁雖然已經展示到了 appcontent 元件中,但是樣式並不是我們想要的效果。現在去修改src\layouts\index.vue檔案,新增如下程式碼

<template>
  <div>
      <!-- 左側選單 -->
      <div class="left-menu-main">左側選單</div>
      <!-- 右側操作區域 -->
      <div class="right-view-main">
          <!-- 頭部麵包屑 -->
          <div class="header">頭部麵包屑</div>
          <!-- 內容展示區 -->
          <AppContent class="content" />
      </div>
  </div>
</template>

<script>
import AppContent from "./components/AppContent.vue";
export default {
  components: {
    AppContent,
  }
};
</script>

<style scoped>
.left-menu-main {
  transition: width 0.28s;
  width: 210px !important;
  background-color: #304156;
  height: 100%;
  position: fixed;
  top: 0;
  bottom: 0;
  left: 0;
  z-index: 1001;
  overflow: auto;
}

.header {
  position: fixed;
  height: 50px;
  width: calc(100vw - 210px);
  top: 0;
  right: 0;
  display: flex;
  align-items: center;
  border-bottom: 1px solid #ddd;
  padding-left: 15px;
  box-sizing: border-box;
}
.content {
  position: fixed;
  overflow: auto;
  height: calc(100vh - 50px);
  width: calc(100vw - 210px);
  bottom: 0;
  right: 0;
}
</style>

效果展示

引入左側選單

  1. 新建 src\layouts\components\ElMenu\index.vue 元件,初始化程式碼
<template>
  <div>
    <el-menu
      default-active="2"
      class="el-menu-vertical-demo"
      background-color="#545c64"
      text-color="#fff"
      active-text-color="#ffd04b"
    >
      <!-- 可展開選單 -->
      <el-submenu index="1">
        <template slot="title">
          <i class="el-icon-location"></i>
          <span>導航一</span>
        </template>
        <el-menu-item index="3">
          <i class="el-icon-document"></i>
          <span slot="title">導航三</span>
        </el-menu-item>
      </el-submenu>
      <!-- 點選選單 -->
      <el-menu-item index="2">
        <i class="el-icon-menu"></i>
        <span slot="title">導航二</span>
      </el-menu-item>
    </el-menu>
  </div>
</template>
  1. 註冊 ElMenu 元件新增到 src\layouts\index.vue
<template>
  <div>
    <!-- 左側選單 -->
    <ElMenu class="left-menu-main"/>
    <!-- 右側操作區域 -->
    <div class="right-view-main">
      <!-- 頭部麵包屑 -->
      <div class="header">頭部麵包屑</div>
      <!-- 內容展示區 -->
      <AppContent class="content" />
    </div>
  </div>
</template>

<script>
import AppContent from "./components/AppContent.vue";
import ElMenu from "./components/ElMenu/index.vue";
export default {
  components: {
    AppContent,
    ElMenu,
  },
};
</script>

<style lang="less" scoped>
...和上面一樣,這裡省略
</style>
  1. 此時開啟頁面可以看到左側選單

遞迴選單元件

目前我們看到的只是一個寫死的選單,我們想要的是根據 router 檔案自動生成對應的選單,那麼應該怎麼做呢?

首先左側選單的每一項都可以當做一個元件,然後獲取到 router 中的所有選單,迴圈展示每一項選單即可,那麼就開始做吧!

新建 src\layouts\components\ElMenu\MenuItem.vue 元件,用來展示每一項的選單名稱

修改 src\layouts\components\ElMenu\index.vue 頁面,引入 router.js 獲取定義的路由資料,並且引入 MenuItem 元件去迴圈展示每一項選單

<template>
  <div>
    <el-menu
      :default-active="$route.path"
      class="el-menu-vertical-demo"
      background-color="#545c64"
      text-color="#fff"
      active-text-color="#ffd04b"
    >
      <MenuItem
        v-for="(route, index) in routersList"
        :key="index"
        :item="route"
        :fatherPath="route.path"
      ></MenuItem>
    </el-menu>
  </div>
</template>

<script>
import routers from "../../../router";
import MenuItem from "./MenuItem.vue";
export default {
  components: {
    MenuItem,
  },
  data() {
    return {
      routersList: [],
    };
  },
  mounted() {
    // 獲取所有定義的一級選單和多級選單
    this.routersList = routers.options.routes[0].children;
  }
};
</script>

程式碼說明:

el-menu 標籤中我們定義了 :default-active="$route.path" ,這個含義表示預設選中當前路由選單,如果是子級選單會自動展開並選中,這是因為下面的程式碼中我們會將每一個頁面的 path 作為選單的 index

另外程式碼中我們遍歷 MenuItem 元件時傳遞了每個選單的物件 item 和每個選單的路徑 fatherPaht ,現在我們要到 MenuItem 元件去根據這個兩個屬性做遞迴展示根選單和多級選單結構。來到 MenuItem 元件中,編寫如下程式碼

<template>
  <div>
    <!-- 根選單 -->
    <router-link tag="span" :to="resolvePath()" v-if="!item.children">
      <el-menu-item :index="resolvePath()">
        <i :class="item.meta.icon"></i>
        <span slot="title">{{ item.meta.title }}</span>
      </el-menu-item>
    </router-link>

    <!-- 可展開選單 -->
    <el-submenu :index="resolvePath()" v-else>
      <template slot="title">
        <i :class="item.meta.icon"></i>
        <span slot="title">{{ item.meta.title }}</span>
      </template>
      <!-- 這裡遞迴去展示多級選單 -->
      <menu-item
        v-for="(route, index) in item.children"
        :key="index"
        :item="route"
        :fatherPath="resolvePath(route.path)"
      >
      </menu-item>
    </el-submenu>
  </div>
</template>

<script>
// 引入path用來處理路徑
import path from "path";

export default {
  // 做元件遞迴時必須定義一個name。然後遞迴時的元件名就是這裡的name值
  name: "MenuItem",
  props: {
    // 上一級的路由資訊
    item: {
      type: Object,
      default: null,
    },
    // 上一級的路徑
    fatherPath: {
      type: String,
      default: "",
    },
  },
  data() {
    return {};
  },
  methods: {
    resolvePath(routePath = "") {
      return path.resolve(this.fatherPath, routePath);
    },
  },
};
</script>

程式碼說明

  • 在做元件遞迴時,必須要在遞迴元件內宣告 name 屬性,屬性值就是當前元件的名稱,這樣才能實現多級巢狀迴圈效果。

另外在ElementUI 中的選單分成兩種型別,分別如下

<el-menu-item index="4">
    <i class="el-icon-setting"></i>
    <span slot="title">導航四</span>
</el-menu-item>
  • 這種的表示根選單
<el-submenu index="1">
    <template slot="title">
        <i class="el-icon-location"></i>
        <span>導航一</span>
    </template>
    <el-menu-item index="4">
        <i class="el-icon-setting"></i>
        <span slot="title">導航四</span>
    </el-menu-item>
</el-submenu>
  • 這種的表示一個可展開的選單,我們根據路由有沒有 children 來判斷這個選單是否有子選單

在根選單外層新增了一個 router-link 實現了點選選單跳轉到不同頁面

現在我們來檢視效果

選單上出現了一個根選單和一個二級選單

新增路由自動完成選單巢狀

現在我們已經完成了一個二級選單的展示,那麼我們新增一個三級路由會不會自動出現三級選單呢?

首先新建一個測試頁面,在資料夾 item2 下面新建一個 item2-1,並且在裡面新增一個 index.vue 檔案,如下圖:

然後去 src\router\index.js 新增這個頁面的路由

新增完成後可以發現產品管理選單自動變成了一個可展開的選單,展開后里面有一個類別列表選單

新增頭部麵包屑

基礎用法

我們想要在頭部新增一個如下的效果,可以很清晰的知道當前瀏覽的是哪個頁面

  1. layouts 資料夾新增 HeaderNav 元件,元件地址: src\layouts\components\HeaderNav.vue,新增如下初始程式碼
<template>
  <div>
    <el-breadcrumb separator="/">
      <el-breadcrumb-item :to="{ path: '/' }">首頁</el-breadcrumb-item>
      <el-breadcrumb-item><a href="/">活動管理</a></el-breadcrumb-item>
      <el-breadcrumb-item>活動列表</el-breadcrumb-item>
      <el-breadcrumb-item>活動詳情</el-breadcrumb-item>
    </el-breadcrumb>
  </div>
</template>
  1. 然後再 src\layouts\index.vue 檔案中引入 HeaderNav 元件
<template>
  <div>
    <template>
      <!-- 左側選單 -->
      <ElMenu class="left-menu-main" />
      <!-- 右側操作區域 -->
      <div class="right-view-main">
        <!-- 頭部麵包屑 -->
        <HeaderNav class="header" />
        <!-- 內容展示區 -->
        <AppContent class="content" />
      </div>
    </template>
  </div>
</template>

<script>
import AppContent from "./components/AppContent.vue";
import ElMenu from "./components/ElMenu/index.vue";
import HeaderNav from "./components/HeaderNav.vue";
export default {
  components: {
    AppContent,
    ElMenu,
    HeaderNav,
  }
};
</script>

<style lang="less" scoped>
樣式省略。。。
</style>
  1. 此時我們的頁面效果是這樣的

是不是有點感覺了呢

  1. 接下只需要監聽頁面的變化去實時獲取最新的路由資訊即可,然後迴圈遍歷顯示

實現程式碼:

<template>
  <div>
    <el-breadcrumb separator-class="el-icon-arrow-right">
      <el-breadcrumb-item
        v-for="(route, index) in breadcrumbItems"
        :key="index"
      >
        <i :class="route.icon"></i>
        <span>{{ route.title }}</span>
      </el-breadcrumb-item>
    </el-breadcrumb>
  </div>
</template>

<script>
export default {
  data() {
    return {
      breadcrumbItems: [],
    };
  },
  mounted() {
    this.geBreadcrumbItems(this.$route);
  },
  methods: {
    geBreadcrumbItems(route) {
      // 獲取當前頁面的路由組
      this.breadcrumbItems = route.matched;
      // 從下標為1的位置開始獲取路由,去除了最外層定義的根路由資訊,並且獲取到的陣列裡面只有meta資料,方便我們取值
      this.breadcrumbItems = this.breadcrumbItems
        .map((item) => item.meta)
        .splice(1);
    },
  },
  watch: {
    $route: function (newVal) {
      this.geBreadcrumbItems(newVal);
    },
  },
};
</script>

效果展示

新增首頁快速入口

我們已經實現了基本效果,但是我們還想在麵包屑的首位新增首頁的連線,點選首頁文字快速跳轉到到首頁

修改 src\layouts\components\HeaderNav.vue 程式碼為如下

<template>
  <div>
    <el-breadcrumb separator-class="el-icon-arrow-right">
      <el-breadcrumb-item
        v-for="(route, index) in breadcrumbItems"
        :key="index"
      >
        <!-- 判斷面包屑是否有path屬性,如果有則顯示router-link標籤 -->
        <router-link v-if="route.path" :to="route.path">
          <i :class="route.icon"></i>
          <span>{{ route.title }}</span>
        </router-link>
        <!-- 如果沒有path屬性則不跳轉 -->
        <template v-else>
          <i :class="route.icon"></i>
          <span>{{ route.title }}</span>
        </template>
      </el-breadcrumb-item>
    </el-breadcrumb>
  </div>
</template>

<script>
export default {
  data() {
    return {
      breadcrumbItems: [],
    };
  },
  mounted() {
    this.geBreadcrumbItems(this.$route);
  },
  methods: {
    geBreadcrumbItems(route) {
      // 獲取當前頁面的路由組
      this.breadcrumbItems = route.matched;
      // 從下標為1的位置開始獲取路由,去除了最外層定義的根路由資訊,並且獲取到的陣列裡面只有meta資料,方便我們取值
      this.breadcrumbItems = this.breadcrumbItems
        .map((item) => item.meta)
        .splice(1);

      // 判斷當前頁面是否已經是首頁
      let nowPath = route.path;
      // 如果當前頁面不是首頁,則在麵包屑的首位置新增一個首頁連結
      if (nowPath !== "/home") {
        this.breadcrumbItems.unshift({
          title: "首頁",
          icon: "el-icon-s-home",
          path: "/home",
        });
      }
    },
  },
  watch: {
    $route: function (newVal) {
      this.geBreadcrumbItems(newVal);
    },
  },
};
</script>

修改之後頁面效果是當我們進入非首頁頁面時,麵包屑前面會有一個首頁的快速入口,當進入首頁時不會展示首頁連線

個性化選單配置

配置獨立頁面

現在我們看到的頁面都巢狀在左側選單和麵包屑下面,但是有些頁面時不能在這個巢狀頁面的,例如登入頁面。那麼我們怎麼通過配置路由來實現這樣的效果呢?

首先新增登入頁面,新建 src\views\login\index.vue,編寫如下程式碼

<template>
    <div>
        登入頁面
    </div>
</template>

新增完登入頁面後前往 src\router\index.js 檔案新增路由資訊,如下圖

我們在登入頁面的路由資訊中的增加一個 oneself:true 的標識,用來標識這個頁面時獨自開啟的,不需要巢狀在選單下

新增完路由後找到 src\layouts\index.vue 頁面修改為如下程式碼

<template>
  <div>
    <!-- 如果不是獨自頁面則讓頁面巢狀在選單下 -->
    <template v-if="!isOneself">
      <!-- 左側選單 -->
      <ElMenu class="left-menu-main" />
      <!-- 右側操作區域 -->
      <div class="right-view-main">
        <!-- 頭部麵包屑 -->
        <HeaderNav class="header" />
        <!-- 內容展示區 -->
        <AppContent class="content" />
      </div>
    </template>

    <!-- 如果是獨自開啟頁面,則只展示純頁面 -->
    <div v-else>
      <AppContent />
    </div>
  </div>
</template>

<script>
import AppContent from "./components/AppContent.vue";
import ElMenu from "./components/ElMenu/index.vue";
import HeaderNav from "./components/HeaderNav.vue";
export default {
  components: {
    AppContent,
    ElMenu,
    HeaderNav,
  },
  data() {
    return {
      isOneself: false,
    };
  },
  mounted() {
    // 獲取當前路由是否是獨自開啟的
    this.isOneself = this.$route.meta.oneself;
  },
  watch: {
    // 監聽路由變化,實時獲取路由資訊
    $route: function (newVal) {
      this.isOneself = newVal.meta.oneself;
    },
  },
};
</script>

<style lang="less" scoped>
css省略。。。
</style>

修改完成後檢視頁面效果

效果很明顯,點選了登入後左側的選單和麵包屑都沒有了,瀏覽器只會展示登入頁面資訊。

到這裡我們會發現登入頁面作為了一個選單項顯示到了左側選單中,這個問題怎麼解決呢?

配置隱藏選單

找到 src\router\index.js 檔案,為登入頁面新增一個 hide:true ,如下圖,這個屬性用來表示這個頁面不在左側選單中顯示

新增完成後找到 src\layouts\components\ElMenu\MenuItem.vue 檔案,在根標籤上新增一個 v-if 判斷,用來判斷當前選單是否需要被渲染

由於這個功能所新增的程式碼極少,所以就不貼程式碼了。修改完之後檢視頁面

通過動畫可以看到登入頁面已經不在選單中展示,修改頁面地址也會正常的在新頁面中開啟。

配置外部連線

現在我們配置的地址只能配置我們專案中的地址,那麼我需要點選選單直接開啟百度怎麼做呢?

首先新增路由資訊如下

此時我們點選選單並不能正常的開啟百度

這是因為我們並沒有判斷頁面的 path 型別。

接下來新建 src\layouts\components\ElMenu\MenuLink.vue,編寫如下程式碼

下面程式碼的含義是定義了一個動態元件,根據父元件傳遞過來的路徑型別顯示不同的元件

<template>
  <component :is="type" v-bind="linkProps(to)">
    <slot />
  </component>
</template>

<script>
export default {
  props: {
    // 接收從父元件傳遞過來的頁面地址
    to: {
      type: String,
      required: true,
    },
  },
  computed: {
    isExternal() {
      return /^(https?:|mailto:|tel:)/.test(this.to);
    },
    type() {
      // 根據路徑判斷元件型別,如果是外部連線則用a標籤
      if (this.isExternal) {
        return "a";
      }
      // 如果不是外部連線則用router-link元件包裹
      return "router-link";
    },
  },
  methods: {
    // 繫結元件屬性
    linkProps(to) {
      // 如果是外部連線則設定a標籤的href地址為傳遞過來的地址,並且設定在新標籤開啟
      if (this.isExternal) {
        return {
          href: to,
          target: "_blank",
          style: {
            "text-decoration": "none",
          },
        };
      }
      // 如果是內部地址則設定router-link的to屬性值,以及tag屬性值為span
      return {
        to: to,
        tag: "span",
      };
    },
  },
};
</script>

然後找到 src\layouts\components\ElMenu\MenuItem.vue 檔案,引入剛剛新建 MenuLink 元件

修改程式碼如下

<template>
  <!-- 判斷當前頁面是否顯示,如果hide為true,則不渲染該選單 -->
  <div v-if="!item.meta.hide">
    <!-- 根選單 -->
    <MenuLink :to="resolvePath()" v-if="!item.children">
      <el-menu-item :index="resolvePath()">
        <i :class="item.meta.icon"></i>
        <span slot="title">{{ item.meta.title }}</span>
      </el-menu-item>
    </MenuLink>

    <!-- 可展開選單 -->
    <el-submenu :index="resolvePath()" v-else>
      <template slot="title">
        <i :class="item.meta.icon"></i>
        <span slot="title">{{ item.meta.title }}</span>
      </template>
      <!-- 這裡遞迴去展示多級選單 -->
      <menu-item
        v-for="(route, index) in item.children"
        :key="index"
        :item="route"
        :fatherPath="resolvePath(route.path)"
      >
      </menu-item>
    </el-submenu>
  </div>
</template>

<script>
// 引入path用來處理路徑
import path from "path";
import MenuLink from "./MenuLink.vue";

export default {
  // 做元件遞迴時必須定義一個name。然後遞迴時的元件名就是這裡的name值
  name: "MenuItem",
  components: {
    MenuLink,
  },
  props: {
    // 上一級的路由資訊
    item: {
      type: Object,
      default: null,
    },
    // 上一級的路徑
    fatherPath: {
      type: String,
      default: "",
    },
  },
  data() {
    return {};
  },
  methods: {
    // 判斷路徑是否是外部地址
    isExternal(path) {
      return /^(https?:|mailto:|tel:)/.test(path);
    },
    // 解析頁面地址
    resolvePath(routePath = "") {
      // 判斷當前頁面地址是否為外部地址
      if (this.isExternal(routePath)) {
        return routePath;
      }
      // 判斷從父元件傳遞過來的地址是否為外部地址
      if (this.isExternal(this.fatherPath)) {
        return this.fatherPath;
      }
      // 格式化頁面地址
      return path.resolve(this.fatherPath, routePath);
    },
  },
};
</script>

圖片說明修改點

修改完成後檢視頁面效果

現在可以看到點選百度搜尋選單後在新頁籤開啟了百度。

相關文章