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安裝
- 安裝
npm install vue-router
- 新建 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;
配置前置路由攔截器動態設置每個頁面的瀏覽器頁簽名稱
- 修改 main.js
import Vue from 'vue'
import VueRouter from 'vue-router'
import router from './router'
new Vue({
  router,
  render: h => h(App)
}).$mount('#app')
- 修改 App.vue
<template>
  <div id="app">
    <router-view />
  </div>
</template>
- 重啟項目,分別訪問如下地址可以查看頁面效果
VueCli安裝
如果項目是使用vue-cli創建的,則可以使用下面的命令直接生成上述代碼及兩個示例路由。它也會覆蓋你的 App.vue,因此請確保在項目中運行以下命令之前備份這個文件
vue add router
動態生成左側菜單
添加layout組件
- 修改路由文件
首先我們要創建好 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 文件夾下的文件結構:

- 新建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>
- 修改 App.vue
只保留 router-view
<template>
  <div>
    <router-view/>
  </div>
</template>
現在我們打開頁面看到如下效果

修改頁面樣式
我們首頁雖然已經展示到了 appcontent 組件中,但是樣式並不是我們想要的效果。現在去修改src\layouts\index.vue文件,添加如下代碼
<template>
    <div class="app-wrapper">
      <div class="sidebar-container">
          左側菜單
      </div>
      <div class="main-container">
          <div class="header-main">頭部面包屑</div>
          <AppContent class="app-main" />
      </div>
    </div>
</template>
<script>
import AppContent from "./components/AppContent.vue";
export default {
  components: {
    AppContent,
  }
}
</script>
<style lang="less" scoped>
.app-wrapper {
  position: relative;
  height: 100%;
  width: 100%;
  .sidebar-container {
    -webkit-transition: width 0.28s;
    transition: width 0.28s;
    width: 200px !important;
    background-color: #304156;
    height: 100%;
    position: fixed;
    font-size: 0px;
    top: 0;
    bottom: 0;
    left: 0;
    z-index: 1001;
    overflow: hidden;
    -webkit-box-shadow: 2px 0 6px rgb(0 21 41 / 35%);
    box-shadow: 2px 0 6px rgb(0 21 41 / 35%);
    & > div {
      width: 211px !important;
    }
  }
  .main-container {
    min-height: 100%;
    -webkit-transition: margin-left 0.28s;
    transition: margin-left 0.28s;
    margin-left: 200px;
    position: relative;
  }
  .main-container {
    -webkit-transition: margin-left 0.28s;
    transition: margin-left 0.28s;
    position: fixed;
    width: calc(100vw - 210px);
    top: 50px;
    right: 0;
    bottom: 0;
    left: 0;
    .header-main {
      position: fixed;
      height: 50px;
      width: calc(100% - 200px);
      right: 0;
      top: 0;
      display: flex;
      align-items: center;
      border-bottom: 1px solid #ddd;
      padding-left: 15px;
      box-sizing: border-box;
    }
    .app-main {
      min-height: 100%;
      width: 100%;
      position: relative;
      overflow: hidden;
    }
  }
}
</style>
效果展示

引入左側菜單
- 新建 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>
- 注冊 ElMenu組件添加到src\layouts\index.vue中
<template>
  <div class="app-wrapper">
    <!-- 左側菜單 -->
    <ElMenu class="sidebar-container"/>
    <!-- 右側操作區域 -->
    <div class="main-container">
      <!-- 頭部面包屑 -->
      <div class="header-main">頭部面包屑</div>
      <!-- 內容展示區 -->
      <AppContent class="app-main" />
    </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>
- 此時打開頁面可以看到左側菜單

遞歸菜單組件
目前我們看到的只是一個寫死的菜單,我們想要的是根據 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 添加這個頁面的路由

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

添加頭部面包屑
基礎用法
我們想要在頭部添加一個如下的效果,可以很清晰的知道當前瀏覽的是哪個頁面

- 在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>
- 然后再 src\layouts\index.vue文件中引入HeaderNav組件
<template>
  <div>
    <div class="app-wrapper">
      <!-- 左側菜單 -->
      <ElMenu class="sidebar-container" />
      <!-- 右側操作區域 -->
      <div class="main-container">
        <!-- 頭部面包屑 -->
        <HeaderNav class="header-main" />
        <!-- 內容展示區 -->
        <AppContent class="app-main" />
      </div>
    </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,
  }
};
</script>
<style lang="less" scoped>
樣式省略。。。
</style>
- 此時我們的頁面效果是這樣的

是不是有點感覺了呢
- 接下只需要監聽頁面的變化去實時獲取最新的路由信息即可,然后循環遍歷顯示
實現代碼:
<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">
      <div class="app-wrapper">
        <div class="sidebar-container">
          <ElMenu />
        </div>
        <div class="main-container">
          <HeaderNav class="header-main" />
          <AppContent class="app-main" />
        </div>
      </div>
    </template>
    <!-- 如果在空白頁打開則不顯示框架 -->
    <template v-else>
      <AppContent />
    </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,
  },
  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>
圖片說明修改點



修改完成后查看頁面效果

現在可以看到點擊百度搜索菜單后在新頁簽打開了百度。
配置多環境地址
首先安裝 cross-env
npm i --save-dev cross-env
然后修改 package.json 文件中的 scripts 對象
{
  ......省略其他
    
  "scripts": {
    "serve": "cross-env VUE_APP_ENV=dev vue-cli-service serve",
    "build": "cross-env VUE_APP_ENV=production vue-cli-service build",
    "lint": "vue-cli-service lint"
  },
  ......省略其他
}
我們在啟動命令和打包命令前添加 cross-env 關鍵字,然后使用鍵值對的方式對一個變量賦值 VUE_APP_ENV=dev
然后新建src\utils\baseurl.js 文件,編寫如下代碼
// 封裝公共的請求api
const apiConfig = {
    // 開發環境
    dev: {
        fayongApi: "https://192.168.199.100"
    },
    // 生產環境
    production: {
        fayongApi: "https://appsh.yikongenomics.com"
    },
};
// 根據全局全局變量自動切換請求地址前綴
export default apiConfig[process.env.VUE_APP_ENV];
關鍵代碼:process.env.VUE_APP_ENV 通過這個可以獲取到輸入不同命令式設置的不同值。
最后我們在頁面中引入 baseurl 來查看當前獲取的環境地址
<template>
    <div>
        首頁
    </div>
</template>
<script>
import env from "../../utils/baseurl"
export default {
    data() {
      return{
      }  
    },
    mounted(){
        console.log(env.fayongApi);  //=> https://192.168.199.100
    }
}
</script>
此時獲取到的就是本地的一個地址,當我們打包后,這里就會自動變成線上地址,從而實現了多套環境的搭建和根據打包命令自動切換的功能。
配置代理和publicPath
在項目根目錄新建 vue.config.js。配置代碼如下
module.exports = {
    publicPath: "./",
    devServer: {
        disableHostCheck: true, //禁用主機檢查 
        proxy: {
            "/fayong": { // 設置以什么前綴開頭的請求用來代理
                target: "http://w79f7c.natappfree.cc/index", //要訪問的跨域的域名
                secure: false, // 使用的是http協議則設置為false,https協議則設置為true
                changOrigin: true, //開啟代理
                pathRewrite: {
                    "^/fayong": "",
                },
            }
        },
    },
};
然后重啟項目生效
簡單封裝一下 axios
首先安裝 axios
npm i axios --save
然后新建 src\utils\http.js,編寫如下代碼
// 引入axiox
import axios from 'axios'
// 創建axios實例
const service = axios.create()
// 請求攔截器
axios.interceptors.request.use(function (config) {
    // 在這里可以添加請求頭,請求token等信息
    return config;
}, function (error) {
    // 對請求錯誤做些什么
    return Promise.reject(error);
});
// 響應攔截器
service.interceptors.response.use(function (result) {
    // 判斷成功
    const { status, data } = result
    // 判斷接口返回狀態
    if (status === 200) {
        // 如果接口正常則返回接口給的數據
        return data
    } else {
        // 如果不正常則返回一個錯誤信息
        return Promise.reject('系統未知錯誤,請反饋給管理員')
    }
}, function (error) {
    // 返回錯誤信息
    return Promise.reject(error)
})
export default service
最后來使用一下
import http from '../../utils/http'
export default {
  data() {
    return {}
  },
  methods: {
    test() {
      http
        .get(`node/search/users?q=songzx`)
        .then((res) => {
          console.log(res)
        })
        .catch((err) => {
          console.log(err)
        })
    },
  },
}
查看控制台拿到的數據就是接口直接返回的數據

封裝全局Loading方法
在 main.js 中添加如下方法
/**
 * 配置全局loading提示框
 * 顯示loading this.showLoading()
 * 關閉loading this.hideLoading()
 */
Vue.prototype.loading = null
Vue.prototype.showLoading = function (msg = 'Loading') {
  Vue.prototype.loading = this.$loading({
    lock: true,
    text: msg,
    spinner: 'el-icon-loading',
    background: 'rgba(0, 0, 0, 0.7)'
  });
}
Vue.prototype.hideLoading = function(){
  Vue.prototype.loading.close();
}
