十年河東,十年河西,莫欺少年窮
學無止境,精益求精
之所以寫這篇博客,主要是鞏固下vue搭建過程。
我們最終的目的是搭建一個類似於這種效果的后台管理系統。
項目源碼中有引入vue公交車及axios
npm install vue-bus
npm install axios --save
項目源碼地址:https://download.csdn.net/download/wolongbb/14028534
下面內容比較瑣碎,可以通過下載項目結合着看,也可以不看,直接使用項目。
1、查看本機環境是否可以使用vue腳手架
chenwolong@LAPTOP-P5GVS4UM MINGW64 /d/Project/Vue/vueTest $ node -v v12.18.4 chenwolong@LAPTOP-P5GVS4UM MINGW64 /d/Project/Vue/vueTest $ npm -v 6.14.9 chenwolong@LAPTOP-P5GVS4UM MINGW64 /d/Project/Vue/vueTest $ webpack -v webpack-cli 4.2.0 webpack 5.9.0 chenwolong@LAPTOP-P5GVS4UM MINGW64 /d/Project/Vue/vueTest $ vue -V 2.9.6
node -v 用於查看本機 node 環境,如果沒有 node 環境,需要安裝 nodejs,具體可參考:https://www.runoob.com/nodejs/nodejs-install-setup.html
那么如何安裝 npm 呢?其實不用安裝。
首先我們了解下,npm是什么東東?npm其實是Node.js的包管理工具(package manager)。
其實npm已經在Node.js安裝的時候順帶安裝好了。我們在命令提示符或者終端輸入npm -v
,應該看到上述類似的輸出。
如果你想升級Npm,則可以使用如下指令安裝
【升級npm(可選操作)】
npm install -g npm
webpack 是一個打包工具,如果你的電腦沒有安裝 webpack,則需要通過如下指令安裝
【全局安裝 webpack】
npm install webpack -g
【webpack 4.X 開始,需要安裝 webpack-cli 依賴】
npm install webpack webpack-cli -g
vue -V 用於查看vue-cli腳手架的版本號,如果你的電腦沒有安裝,請執行如下指令安裝
npm install -g vue-cli
安裝好了環境依賴,我們就可以通過腳手架來搭建我們的項目了。
2、通過vue腳手架搭建快速搭建vue項目
執行如下指令:
vue init webpack vue-test
在執行過程中,vue會讓用戶做出相關的配置選擇,如下:
chenwolong@LAPTOP-P5GVS4UM MINGW64 /d/Project/Vue/vueTest $ vue init webpack vue-test ? Project name (vue-test) --項目名稱是否叫vue-test 直接回車,進行下一步 ? Project name vue-test ? Project description (A Vue.js project) --項目描述是否是 A Vue.js project 直接回車,進行下一步 ? Project description A Vue.js project ? Author (chenwolong <chenwolong@163.com>) --作者 直接回車,進入下一步 ? Author chenwolong <chenwolong@163.com> ? Vue build (Use arrow keys) ? Vue build standalone ? Install vue-router? (Y/n) y --是否安裝Vue路由,輸入 y 然后回車,進行下一步 ? Install vue-router? Yes ? Use ESLint to lint your code? (Y/n) y --是否安裝EsLine代碼規范 輸入 y 然后回車,進行下一步 ? Use ESLint to lint your code? Yes ? Pick an ESLint preset (Use arrow keys) ? Pick an ESLint preset Standard ? Set up unit tests (Y/n) n --是都建立單元測試 輸入 n 不安裝單元測試,進入下一步 ? Set up unit tests No ? Setup e2e tests with Nightwatch? (Y/n) --是否安裝e2e測試 輸入 n 不安裝 進入下一步,這時所有配置項執行完畢,腳手架開始下載項目模板 ? Setup e2e tests with Nightwatch? Yes ? Should we run `npm install` for you after the project has been created? (recom ? Should we run `npm install` for you after the project has been created? (recom mended) npm vue-cli · Generated "vue-test". # Installing project dependencies ...
待上述指令執行完畢后,我們進入 d/Project/Vue/vueTest 目錄,執行
npm run dev
瀏覽器中輸入:http://localhost:8082/
如果可以正常運行,說明你的vue項目搭建成功。那么下一步,我們就可以結合antd進行大刀闊斧的改革了。
3、vue結合antd
引入antd,執行如下指令
npm install ant-design-vue --save
執行完畢后,在項目main.js中全局引入antd,並在初始項目中的HelloWorld.vue 中引入一個button 按鈕,測試下是否引入成功。
main.js變更如下:

// The Vue build version to load with the `import` command // (runtime-only or standalone) has been set in webpack.base.conf with an alias. import Vue from 'vue' import App from './App' import router from './router' import Antd from 'ant-design-vue';/* 全局引入ant */ import 'ant-design-vue/dist/antd.css'; /* 全局引入ant樣式文件*/ Vue.config.productionTip = false Vue.use(Antd); /* 使用antd */ /* eslint-disable no-new */ new Vue({ el: '#app', router, components: { App }, template: '<App/>' })
HelloWorld.vue 變更如下,引入了antd 的button 按鈕:

<template>
<div>
<a-button type="danger">
Danger
</a-button>
</div>
</template>
執行
npm run dev
發現報錯了,eslint 校驗出錯,我們使用簡單粗暴的辦法,直接關閉eslint格式校驗。
在config/index.js 中找到 :useEslint: true, 將true 改為 false即可。繼續執行 npm run dev
看到如下界面,則意味着ant引入成功
成功引入ant后,我們來修改我們的路由,找到 src/route/index.js 修改index.js,變更如下:

import Vue from 'vue' import Router from 'vue-router' import index from '../view/index' import system from '../view/system/system' import product from '../view/product/product' Vue.use(Router) const originalPush = Router.prototype.push Router.prototype.push = function push(location) { return originalPush.call(this, location).catch(err => err) } export default new Router({ routes: [ { path: '/', name: 'index', component: index }, { path: '/index', name: 'index', component: index, children: [ { path: '', component: system }, { path: '/system', component: system }, { path: '/product', component: product } ] }, { path: '*', name: '404', component: () => import('@/view/404.vue'), } ] })
通過路由的代碼可知,我們需要在src下面建一個名稱為view的文件夾,而后在view中分別建立如下文件夾/文件,如下:
下面逐個進行分析,入口路由為 view/index.vue,下面來看index.vue的代碼

<template> <a-layout id="components-layout-demo-top-side"> <!--頭部--> <site-header v-bind:topmenu="topmenu"></site-header> <a-layout-content style="padding: 0 3px"> <a-layout style="padding: 24px 0; background: #fff"> <!--左側菜單--> <site-menu></site-menu> <a-layout-content :style="{ padding: '0 24px', minHeight: '680px' }"> <!--內容填充--> <router-view /> </a-layout-content> </a-layout> </a-layout-content> </a-layout> </template> <script> export default { name: "index", data() { return { topmenu:{system:'系統管理',diangui:'商品管理',dianchi:'訂單管理',dianzhuang:'圖表管理'} } } }; </script> <style> #components-layout-demo-top-side .logo { width: 120px; height: 31px; background: rgba(255, 255, 255, 0.2); margin: 16px 28px 16px 0; float: left; } .footer{ text-align: center; height: 35px; padding: 8px 40px; } </style>
在index.vue中,我們使用了自定義組件 site-header 和 site-menu,我們需要在components文件夾中創建相關組件,如下:
文件 components/header.vue 代碼如下:

<template> <div> <a-layout-header class="header"> <div class="logo"> <!-- 這里放你公司的logo圖標--> <!-- <img src=" ../../static/pic/logo5.png" style="margin-bottom: 35px" /> --> </div> <a-menu theme="dark" mode="horizontal" :default-selected-keys="['0']" :style="{ lineHeight: '64px' }" > <a-menu-item style="font-size: 17px" disabled="true"> </a-menu-item> <a-menu-item style="font-size: 17px" disabled="true"> </a-menu-item> <a-menu-item style="font-size: 17px" disabled="true"> </a-menu-item> <a-menu-item style="font-size: 17px" disabled="true"> </a-menu-item> <a-menu-item style="font-size: 17px" disabled="true"> </a-menu-item> <a-menu-item style="font-size: 17px" disabled="true"> </a-menu-item> <a-menu-item style="font-size: 17px" disabled="true"> </a-menu-item> <a-menu-item style="font-size: 17px" disabled="true"> </a-menu-item> <a-menu-item style="font-size: 17px" disabled="true"> </a-menu-item> <a-menu-item key="0" @click="clickEve(0)" style="font-size: 17px"> {{ this.topmenu.system }} </a-menu-item> <a-menu-item key="1" @click="clickEve(1)" style="font-size: 17px"> {{ this.topmenu.dianchi }} </a-menu-item> <a-menu-item key="2" @click="clickEve(2)" style="font-size: 17px" disabled="true" > {{ this.topmenu.diangui }} </a-menu-item> <a-menu-item key="3" @click="clickEve(3)" style="font-size: 17px" disabled="true" > {{ this.topmenu.dianzhuang }} </a-menu-item> <a-menu-item style="font-size: 17px" disabled="true"> </a-menu-item> <a-menu-item style="font-size: 17px" disabled="true"> </a-menu-item> <a-menu-item style="font-size: 17px" disabled="true"> </a-menu-item> <a-menu-item style="font-size: 17px" disabled="true"> </a-menu-item> <a-menu-item style="font-size: 17px" disabled="true"> </a-menu-item> <a-menu-item style="font-size: 17px" disabled="true"> </a-menu-item> <a-menu-item style="font-size: 17px" disabled="true"> </a-menu-item> <a-menu-item style="font-size: 17px" disabled="true"> </a-menu-item> <a-menu-item style="font-size: 17px" disabled="true"> <a-tooltip> <template slot="title"> 點我退出登錄 </template> <div class="custom-icons-list" @click="loginout"> <panda-icon :style="{ fontSize: '32px' }" /> </div> </a-tooltip> </a-menu-item> </a-menu> </a-layout-header> </div> </template> <script> const HeartSvg = { template: ` <svg width="1em" height="1em" fill="currentColor" viewBox="0 0 1024 1024"> <path d="M923 283.6c-13.4-31.1-32.6-58.9-56.9-82.8-24.3-23.8-52.5-42.4-84-55.5-32.5-13.5-66.9-20.3-102.4-20.3-49.3 0-97.4 13.5-139.2 39-10 6.1-19.5 12.8-28.5 20.1-9-7.3-18.5-14-28.5-20.1-41.8-25.5-89.9-39-139.2-39-35.5 0-69.9 6.8-102.4 20.3-31.4 13-59.7 31.7-84 55.5-24.4 23.9-43.5 51.7-56.9 82.8-13.9 32.3-21 66.6-21 101.9 0 33.3 6.8 68 20.3 103.3 11.3 29.5 27.5 60.1 48.2 91 32.8 48.9 77.9 99.9 133.9 151.6 92.8 85.7 184.7 144.9 188.6 147.3l23.7 15.2c10.5 6.7 24 6.7 34.5 0l23.7-15.2c3.9-2.5 95.7-61.6 188.6-147.3 56-51.7 101.1-102.7 133.9-151.6 20.7-30.9 37-61.5 48.2-91 13.5-35.3 20.3-70 20.3-103.3 0.1-35.3-7-69.6-20.9-101.9z" /> </svg> `, }; const PandaSvg = { template: ` <svg viewBox="0 0 1024 1024" width="1em" height="1em" fill="currentColor"> <path d="M99.096 315.634s-82.58-64.032-82.58-132.13c0-66.064 33.032-165.162 148.646-148.646 83.37 11.91 99.096 165.162 99.096 165.162l-165.162 115.614zM924.906 315.634s82.58-64.032 82.58-132.13c0-66.064-33.032-165.162-148.646-148.646-83.37 11.91-99.096 165.162-99.096 165.162l165.162 115.614z" fill="#6B676E" p-id="1143" /> <path d="M1024 561.548c0 264.526-229.23 429.42-512.002 429.42S0 826.076 0 561.548 283.96 66.064 512.002 66.064 1024 297.022 1024 561.548z" fill="#FFEBD2" p-id="1144" /> <path d="M330.324 842.126c0 82.096 81.34 148.646 181.678 148.646s181.678-66.55 181.678-148.646H330.324z" fill="#E9D7C3" p-id="1145" /> <path d="M644.13 611.098C594.582 528.516 561.55 512 512.002 512c-49.548 0-82.58 16.516-132.13 99.096-42.488 70.814-78.73 211.264-49.548 247.742 66.064 82.58 165.162 33.032 181.678 33.032 16.516 0 115.614 49.548 181.678-33.032 29.18-36.476-7.064-176.93-49.55-247.74z" fill="#FFFFFF" p-id="1146" /> <path d="M611.098 495.484c0-45.608 36.974-82.58 82.58-82.58 49.548 0 198.194 99.098 198.194 165.162s-79.934 144.904-148.646 99.096c-49.548-33.032-132.128-148.646-132.128-181.678zM412.904 495.484c0-45.608-36.974-82.58-82.58-82.58-49.548 0-198.194 99.098-198.194 165.162s79.934 144.904 148.646 99.096c49.548-33.032 132.128-148.646 132.128-181.678z" fill="#6B676E" p-id="1147" /> <path d="M512.002 726.622c-30.06 0-115.614 5.668-115.614 33.032 0 49.638 105.484 85.24 115.614 82.58 10.128 2.66 115.614-32.944 115.614-82.58-0.002-27.366-85.556-33.032-115.614-33.032z" fill="#464655" p-id="1148" /> <path d="M330.324 495.484m-33.032 0a33.032 33.032 0 1 0 66.064 0 33.032 33.032 0 1 0-66.064 0Z" fill="#464655" p-id="1149" /> <path d="M693.678 495.484m-33.032 0a33.032 33.032 0 1 0 66.064 0 33.032 33.032 0 1 0-66.064 0Z" fill="#464655" p-id="1150" /> </svg> `, }; const HeartIcon = { template: ` <a-icon :component="HeartSvg" /> `, data() { return { HeartSvg, }; }, }; const PandaIcon = { template: ` <a-icon :component="PandaSvg" /> `, data() { return { PandaSvg, }; }, }; export default { components: { HeartIcon, PandaIcon, }, props: ["topmenu"], created() { console.log(this.topmenu); }, data: { index: "234", }, methods: { clickEve(params) { console.log("params"); }, loginout() { this.$router.push({ path: "/", query: {} }); }, }, }; </script> <style scoped> .custom-icons-list >>> .anticon { margin-right: 6px; margin-top: 10px; padding-top: 18x; } </style>
文件 components/leftmenu.vue 代碼如下:

<template> <div> <a-layout-sider width="200" style="background: #fff; height: 100vh" v-if="menuparams == 0" > <a-menu mode="inline" :default-selected-keys="selectedkeys" :default-open-keys="openkeys" :selectedKeys="NowSelectedKeys" style="height: 100%" @click="menuClick" > <a-sub-menu key="admin" > <span slot="title"><a-icon type="user" />系統設置</span> <a-menu-item key="main" @click="Redireact()" >管理員列表 </a-menu-item> <a-menu-item key="rolelst" @click="Redireact" >角色管理</a-menu-item > </a-sub-menu> <a-sub-menu key="main1"> <span slot="title"><a-icon type="setting" />電池OTA升級</span> <a-menu-item key="dianchiota" @click="Redireact" > OTA固件列表 </a-menu-item> <a-menu-item key="dianchiotalst" @click="Redireact" > OTA升級日志 </a-menu-item> </a-sub-menu> </a-menu> </a-layout-sider> <a-layout-sider width="200" style="background: #fff; height: 100vh" v-if="menuparams == 1" > <a-menu mode="inline" :default-selected-keys="selectedkeys" :default-open-keys="openkeys" :selectedKeys="NowSelectedKeys" :openKeys="NowopenKeys" style="height: 100%" > <a-sub-menu key="dianchi"> <span slot="title"><a-icon type="user" />充放電圖表</span> <a-menu-item key="cdsigvol" @click="Redireact" >單體電壓</a-menu-item > <a-menu-item key="elcsoc" @click="Redireact" >電流電量</a-menu-item > <a-menu-item key="batteryheart" @click="Redireact" title="充放電圖表模塊" >電池心跳</a-menu-item > </a-sub-menu> </a-menu> </a-layout-sider> </div> </template> <script> export default { data() { return { menuparams: 0, selectedkeys: ["cdsigvol", "main"], openkeys: ["dianchi", "admin"], NowSelectedKeys: ["cdsigvol", "main"], }; }, created() { var that = this; this.$bus.on("pasdddrameterName", function (params) { that.menuparams = params; that.NowSelectedKeys = ["cdsigvol", "main"]; //console.log(that.selectedkeys); if (that.menuparams == 0) { that.$router.push({ path: "/main" }); } if (that.menuparams == 1) { that.$router.push({ path: "/cdsigvol" }); } }); }, methods: { Redireact(item) { this.NowSelectedKeys = []; this.NowSelectedKeys.push(item.key); this.$router.push({ path: "/" + item.key }); }, }, }; </script>
寫完了組件,在components文件夾中增加一個js,componentsRegist.js的代碼如下,

// 組件註冊
import siteheader from './header'
import sitemenu from './leftmenu'
export default {
install (Vue) {
Vue.component('site-header', siteheader); // 頂部
Vue.component('site-menu', sitemenu); // 頂部
}
}
然后在main.js中全局注冊

// The Vue build version to load with the `import` command // (runtime-only or standalone) has been set in webpack.base.conf with an alias. import Vue from 'vue' import App from './App' import router from './router' import Antd from 'ant-design-vue';/* 全局引入ant */ import 'ant-design-vue/dist/antd.css'; /* 全局引入ant樣式文件*/ import components from './components/componentsRegist.js' /* 全局引入組件注冊JS*/ Vue.config.productionTip = false Vue.use(Antd); /* 使用antd */ Vue.use(components); /* 使用自定義組件 */ new Vue({ el: '#app', router, components: { App }, template: '<App/>' })
view/404.vue代碼如下:

<template> <div> <h1> 訪問的頁面未找到... </h1> </div> </template>
view/system/system代碼如下:

<template> <div> <a-card hoverable="true" title="" headStyle="text-align:left;color:#606266;font-size:14px" bodyStyle="border:none" > <a slot="extra" href="#" style="float: left"> <a-breadcrumb separator=">"> <a-breadcrumb-item>系統設置</a-breadcrumb-item> <a-breadcrumb-item>管理員列表 </a-breadcrumb-item> </a-breadcrumb> </a> <div> <a-row align="middle" class="arow"> <a-col :span="6"> <a-checkable-tag v-model="checked" @change="handleChange"> 姓名 </a-checkable-tag> <a-input placeholder="請輸入用戶名" style="width: 180px" /> </a-col> <a-col :span="6"> <a-checkable-tag v-model="checked" @change="handleChange"> 性別 </a-checkable-tag> <a-select default-value="0" style="width: 180px"> <a-select-option value="0">請選擇</a-select-option> <a-select-option value="1"> 男 </a-select-option> <a-select-option value="2"> 女 </a-select-option> </a-select> </a-col> <a-col :span="6"> <a-checkable-tag v-model="checked" @change="handleChange"> 年齡 </a-checkable-tag> <a-input-number :min="1" :max="100" style="width: 180px" placeholder="請輸入年齡" /> </a-col> <a-col :span="6"> <a-button type="primary"> 查詢 </a-button> <a-button type="danger"> 新增 </a-button> </a-col> </a-row> <a-table :columns="columns" :data-source="data" :scroll="{ x: 1300 }" style="margin-top:20px;"> <a slot="action" href="javascript:;">編輯</a> </a-table> </div> </a-card> </div> </template> <script> import moment from "moment"; import "moment/locale/zh-cn"; import locale from "ant-design-vue/es/date-picker/locale/zh_CN"; const columns = [ { title: "姓名", width: 100, dataIndex: "name", key: "name", fixed: "left", }, { title: "性別", width: 100, dataIndex: "sex", key: "sex", fixed: "left" }, { title: "年齡", dataIndex: "age", key: "1" }, { title: "職稱", dataIndex: "staff", key: "2" }, { title: "聯系方式", dataIndex: "tel", key: "3" }, { title: "聯系地址", dataIndex: "address", key: "4" }, { title: "郵箱", dataIndex: "email", key: "5" }, { title: "角色", dataIndex: "role", key: "6" }, { title: "狀態", dataIndex: "status", key: "7" }, { title: "創建日期", dataIndex: "createdate", key: "8" }, { title: "操作", key: "operation", fixed: "right", width: 100, scopedSlots: { customRender: "action" }, }, ]; const data = [ { key: "1", sex:"男", name: "陳工", age: 30, staff:"程序猿", tel:"18137070152", address:"中國蘇州", email: "163.com", role:"超級管理員", status:"正常", createdate:"2020-08-08" }, { key: "2", sex:"男", name: "邱工", age: 30, staff:"程序猿", tel:"18852585698", address:"中國蘇州", email: "163.com", role:"超級管理員", status:"正常", createdate:"2020-08-08" }, { key: "3", sex:"男", name: "劉工", age: 30, staff:"前端", tel:"18565985236", address:"中國蘇州", email: "163.com", role:"超級管理員", status:"正常", createdate:"2020-08-08" }, { key: "4", sex:"男", name: "姚工", age: 30, staff:"測試", tel:"15225285869", address:"中國蘇州", email: "163.com", role:"超級管理員", status:"正常", createdate:"2020-08-08" }, { key: "5", sex:"女", name: "李工", age: 30, staff:"程序猿", tel:"18137070152", address:"中國蘇州", email: "163.com", role:"超級管理員", status:"正常", createdate:"2020-08-08" }, { key: "6", sex:"女", name: "黃工", age: 30, staff:"程序猿", tel:"18137070152", address:"中國蘇州", email: "163.com", role:"超級管理員", status:"正常", createdate:"2020-08-08" }, { key: "7", sex:"女", name: "張工", age: 30, staff:"程序猿", tel:"18137070152", address:"中國蘇州", email: "163.com", role:"超級管理員", status:"正常", createdate:"2020-08-08" }, { key: "8", sex:"女", name: "諸葛工", age: 30, staff:"程序猿", tel:"18137070152", address:"中國蘇州", email: "163.com", role:"超級管理員", status:"正常", createdate:"2020-08-08" }, { key: "9", sex:"女", name: "司馬工", age: 30, staff:"程序猿", tel:"18137070152", address:"中國蘇州", email: "163.com", role:"超級管理員", status:"正常", createdate:"2020-08-08" }, { key: "10", sex:"男", name: "歐陽工", age: 30, staff:"程序猿", tel:"18137070152", address:"中國蘇州", email: "163.com", role:"超級管理員", status:"正常", createdate:"2020-08-08" }, { key: "11", sex:"男", name: "陳工", age: 30, staff:"程序猿", tel:"18137070152", address:"中國蘇州", email: "163.com", role:"超級管理員", status:"正常", createdate:"2020-08-08" }, ]; export default { name: "Echarts", data() { return { locale, checked: false, dateFormat: "YYYY/MM/DD", monthFormat: "YYYY/MM", data, columns, }; }, methods: { moment, onChange(dates, dateStrings) { console.log("From: ", dates[0], ", to: ", dates[1]); console.log("From: ", dateStrings[0], ", to: ", dateStrings[1]); }, handleChange(checked) { console.log(checked); }, }, mounted() {}, }; </script> <style scoped> .img { width: 100%; height: auto; } .arow { text-align: left; } .arowLat { text-align: left; margin-top: 25px; } </style>
截止到現在,我們的站點就構造完成了。
運行下站點,看下效果
主要就是記錄一下,沒啥太大的意義。
@天才卧龍的博客