-----寫在前面-----
雖然自己寫了個仿站,但小林心里十分有數,這個玩意到時候可能都不好意思寫到簡歷上,果然前端越往后面學越覺得自己之前是弟弟。
現在在看6月份的簡歷,突然明白為啥公司們都不要我...就干兩個月,技術還沒多好,打擾了(;′⌒`)
把ElementUI的官網文檔過了一遍,感嘆作者也太🐮了...大神們造的輪子屬實給勁
正好看到了一個用標題所列的技術棧搭建的一個后台管理系統,clone下來看了一下發現屬實有點腦殼疼,不過問題不大,一天看懂一些,總歸是能吃的透透的嗷
-----2019.08.24(Day 1):目錄梳理、路由配置詳解、login.vue-----

和直接使用vue-cli以webpack模板生成的工程文件不同
作者把路由組件放在views文件夾下
api文件夾用於接收mock文件夾模擬出的數據並作為axios的請求地址(這個后面會詳解)
路由配置文件:
//省略路由的引入
let routes = [ { path: '/login', component: Login, name: '', hidden: true }, { path: '/404', component: NotFound, name: '', hidden: true }, //{ path: '/main', component: Main },
{ path: '/', component: Home, name: '', iconCls: 'el-icon-menu', leaf: true, //只有一個節點
children: [{ path: '/Index', component: Index, name: '首頁' }] }, { path: '/', component: Home, name: '', iconCls: 'el-icon-menu', //圖標樣式class
leaf: true, //只有一個節點
children: [ { path: '/Application', component: Application, name: '應用管理' } ] }, { path: '/', component: Home, name: '日志管理', iconCls: 'el-icon-menu', children: [ { path: '/SysLog', component: SysLog, name: '系統日志' }, { path: '/AdminLog', component: AdminLog, name: '管理員操作日志' }, { path: '/UserLog', component: UserLog, name: '用戶使用日志' } ] }, { path: '/', component: Home, name: '配置管理', iconCls: 'el-icon-menu', children: [ { path: '/GuidePageConfig', component: GuidePageConfig, name: '引導頁配置' }, { path: '/IntegralConfig', component: IntegralConfig, name: '積分規則配置' }, { path: '/SubscribeConfig', component: SubscribeConfig, name: '訂閱欄目配置' } ] }, { path: '/', component: Home, name: '權限管理', iconCls: 'el-icon-menu', children: [ { path: '/page4', component: Page4, name: '權限組管理' }, { path: '/Main', component: Main, name: '權限組權限管理' }, { path: '/Form', component: Form, name: '權限組人員管理' } ] }, { path: '/', component: Home, name: '賬號管理', iconCls: 'el-icon-menu', children: [ { path: '/user', component: user, name: '頁面4' }, { path: '/page5', component: Page5, name: '頁面5' } ] }, { path: '*', hidden: true, redirect: { path: '/404' } } ] export default routes
/login:登陸界面
/404:無效界面

/:通過指定不同的name屬性以及不同的嵌套組件來執行不同的跳轉(至子頁面)
這樣寫的好處是url不會是/page1/page1-1/page1-1-1,而是直接將最后一級路由拼上去。即/page1-1-1
iconCls: ElementUI為路由指定圖標的方式

hidden&leaf屬性:自定義屬性,用於后面配合指定路由是否渲染
login.vue
<el-form :model="ruleForm2" :rules="rules2" ref="ruleForm2" label-position="left" label-width="0px" class="demo-ruleForm login-container"
>
<h3 class="title">系統登錄</h3>
<el-form-item prop="account">
<el-input type="text" v-model="ruleForm2.account" auto-complete="off" placeholder="賬號"></el-input>
</el-form-item>
<el-form-item prop="checkPass">
<el-input type="password" v-model="ruleForm2.checkPass" auto-complete="off" placeholder="密碼"></el-input>
</el-form-item>
<el-checkbox v-model="checked" checked class="remember">記住密碼</el-checkbox>
<el-form-item style="width:100%;">
<el-button type="primary" style="width:100%;" @click.native.prevent="handleSubmit2" :loading="logining"
>登錄</el-button>
<!--<el-button @click.native.prevent="handleReset2">重置</el-button>-->
</el-form-item>
</el-form>


model:表單的數據對象,使用方式:


rules:驗證規則

label-position、label-width:表單域標簽的位置、表單域標簽的寬度
詳見 ElementUI-表單
登錄按鈕:使用native修飾符,否則無法監聽到原生click事件(被封裝過的),再阻止默認事件提交,並調用handleSubmit2()事件:


-----2019-08-26 Home.Vue、Index.Vue------

<el-dropdown>

trigger:觸發菜單展開的行為
divided:顯示分割線
<el-breadcrumb>面包屑導航
在這里是利用跟蹤路由變化來進行渲染新的面包屑導航項,例如進入一個二級路由界面后,$route.matched會被推入新的項,此時面包屑導航會多渲染出新的一項

菜單
<aside>-<el-menu>-<el-submenu>

菜單也是通過v-for渲染的,不渲染hidden為true的路由項(login.vue、404.vue等),為leaf屬性為true的一級菜單路由渲染其子路由
同時通過前面為路由自定義的iconCls屬性來添加菜單圖標
注意有兩個圖標

Index.Vue

沒啥好分析的,就是這里的echarts到時候需要看看文檔
-----2019.08.27 Mock邏輯(獲取用戶列表、登陸驗證邏輯) -----
獲取用戶列表的MOCK邏輯見這篇博文:Vue & Axios & Mock & Axios-Mock-Adapter:在Vue項目中模擬接口、請求以及數據
登陸驗證邏輯
api.js
export const requestLogin = params => { return axios.post(`${base}/login`, params).then(res => res.data); };
login.vue 中點擊登錄按鈕后的處理邏輯
handleSubmit2(ev) { var _this = this
this.$refs.ruleForm2.validate(valid => { if (valid) { this.logining = true
var loginParams = { username: this.ruleForm2.account, password: this.ruleForm2.checkPass }//即傳過去的參數包含填入的帳號與密碼
requestLogin(loginParams).then(data => { console.log(data);//{code: 200, msg: "請求成功", user: {…}}
this.logining = false
//NProgress.done();
let { msg, code, user } = data if (code !== 200) { this.$message({ message: msg, type: 'error' }) } else { sessionStorage.setItem('user', JSON.stringify(user)) this.$router.push({ path: '/Index' }) } }) } else { console.log('error submit!!') return false } }) }
Mock.js中的登陸相關邏輯
mock.onPost('/login').reply(config => { let { username, password } = JSON.parse(config.data); //解構賦值
return new Promise((resolve, reject) => { let user = null; setTimeout(() => { let hasUser = LoginUsers.some(u => { //LoginUsers是寫死的嗷
//some方法檢測數組中的元素是否符合指定條件,如果有一個滿足就會返回true
if (u.username === username && u.password === password) { user = JSON.parse(JSON.stringify(u)); user.password = undefined; return true; } }); //如果驗證通過
if (hasUser) { resolve([200, { code: 200, msg: '請求成功', user }]); //調用resolve方法,Promise變為操作成功狀態(fulfilled),執行then方法里面onfulfilled里的操作
//Promise.resolve方法會將這個對象轉為 Promise 對象,然后就立即執行thenable對象的then方法。
} else { resolve([200, { code: 500, msg: '賬號或密碼錯誤' }]); } }, 1000); }); });
大概的思路是這樣的:
(login.vue)點擊按鈕后將調用requestLogin()方法,賬號密碼以POST方式發送至代理的接口地址('/login')
(mock.js)mock攔截到請求,在onPost()方法中進行驗證,並根據驗證結果調用resolve()方法(傳入的參數不同),reply()中返回的這個Promise對象狀態變為成功(fulfilled)
隨后會調用login.vue中、定義在requestLogin()方法后的then()方法,解構賦值后根據code值判斷驗證的成功還是失敗

-----2019-08-28:查詢、新增、編輯、刪除操作、slot-scope&slot-----
⚠slot-scope是被廢棄的API(現在建議使用v-slot),但由於原作者在完成這個項目時使用的是2.2.2版本的Vue,正好這個用法我之前也沒學過就順便了解一下。
官方文檔地址:slot-scope v-slot

注意這個表格的代碼,借助ElementUI的幫助,可以直接在data屬性注入數據,然后在<el-table-column>中通過為prop設定對應鍵名來填充數據。
表格里的template就是每一行都相同的【編輯】、【刪除】按鈕,如何在點擊時直到是哪一行(的哪一項)需要進行操作?就是靠slot-scope
我們這里把scope整個打印出來看看

$idnex:0 這里點擊的是第一行的數據
column:該按鈕的列屬性,即第?列
row:

行屬性,包括行頭與單元格值
store:表格內部的狀態管理,這里略過
ElementUI官網的demo:Table-ElementUI
接下來是各個操作的詳解:
刪除:

關於this.$confirm:

具體參見MessageBox彈框
Mock.js中負責刪除邏輯的部分:

實際上就是把數據用fliter()方法走了一遍,並修改了全局的_Users變量--->然后再次調用getUser()方法--->用新的_Users渲染列表
⬆這個邏輯可以看前兩天的分析
編輯:這個復雜一些
首先是顯示編輯界面,並且將這一行數據的所有屬性復制一份保存下來


注意這里的 :visible.sync="editFormVisible",建議去學一下.sync的用法,這里指稍微提一下
這個寫法表達了對visible賦新值的意圖,跳過了使用$emit通知組件去修改的過程(ElementUI的組件都是被封裝過的哈記得)
提交編輯的邏輯函數:

在Mock.js中的功能函數:(邏輯就不用多說了吧)

新增的邏輯實現類似編輯,最后來看一哈 批量刪除 這個功能

<el-table :data="users" highlight-current-row v-loading="listLoading" @selection-change="selsChange" style="width: 100%;">

注意!這里的sels是所有當前被選中的項!

創建一個ids數組,並將被選中的項的id填入進去,並化為字符串形式,發送給mock.js中的batchRemoveUser()進行處理

-----2019-08-31 積分配置 訂閱欄目配置-----
喵喵喵??我發現這個作者后面幾個子頁面就是在偷懶了... 所有子界面有意義的就只有用戶列表和積分配置頁面,
其他不是復制的用戶列表就是無意義字符...但這個項目我還是學到了很有分量的東西的。
把積分配置界面魔改了一下

現在保存配置后會把積分的配置規則發到服務器端(模擬的)~
改動如下:
1.


這樣會把新的積分配置作為一個數組直接發送出去~
在api.js中新增一個接口:
export const IntegralConfig = params => { return axios.post(`${base}/user/IntegralConfig`, { params: params }) }
在mock.js中修改代理配置:
mock.onPost('/user/IntegralConfig').reply(config => { console.log(JSON.parse(config.data).params.nums); var [step1, step2, step3, step4] = JSON.parse(config.data).params.nums; console.log(step1, step2, step3, step4); return new Promise((resolve, reject) => { setTimeout(() => { resolve([ 200, { code: 200, msg: '積分規則變更成功' } ]); }, 500); }); });
引導頁、宣傳頁配置(這兩個模式是一樣的,就以宣傳頁配置為例)
<h1>宣傳頁配置 <el-switch style="display: block" v-model="GuidePageConfig" active-color="#13ce66" inactive-color="#ff4949" active-text="打開宣傳頁" inactive-text="關閉宣傳頁">
</el-switch>
</h1>
</el-col>
<el-col>
</el-col>
<el-form>
<el-upload action="https://jsonplaceholder.typicode.com/posts/" list-type="picture-card" :on-preview="handlePictureCardPreview" :on-remove="handleRemove" :file-list="fileList2">
<i class="el-icon-plus"></i>
</el-upload>
<el-dialog :visible.sync="dialogVisible">
<img width="100%" :src="dialogImageUrl" alt="">
</el-dialog>
關於el-upload的使用參見官網:el-upload


action:上傳到的服務器地址
list-type:文件列表類型
on-preview:點擊文件列表中已上傳的文件時的鈎子

在這里會使得對話框可見並展示大尺寸的圖片
on-remove:文件列表移除文件時的鈎子

