不知不覺已寫到第7篇。前面6篇都是探索性的實現(xiàn),是學(xué)習(xí)理論的過程,從這篇開始,開始用理論來指導(dǎo)實踐了。
接上篇,我們現(xiàn)在通過組件繼承實現(xiàn)Admin頁面內(nèi)導(dǎo)航。
動態(tài)導(dǎo)航
我們要的導(dǎo)航,是根據(jù)登錄用戶的權(quán)限,顯示不同的導(dǎo)航菜單。根據(jù)職能分工,菜單內(nèi)容的確定由后端來做更符合實踐,前端只需要實現(xiàn)動態(tài)渲染菜單即可。好在這是Vue擅長的。
在components目錄下新建Aside.vue。在Aside.vue中放置一個el-menu,我們先用一個靜態(tài)的占個位,然后實現(xiàn)成動態(tài)的。靜態(tài)的我們直接用ElemenUI自己的例子好了。
<template>
<el-row>
<el-col :span="24">
<h5>管理后臺</h5>
<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>導(dǎo)航一</span>
</template>
<el-menu-item-group>
<template slot="title">分組一</template>
<el-menu-item index="1-1">選項1</el-menu-item>
<el-menu-item index="1-2">選項2</el-menu-item>
</el-menu-item-group>
<el-menu-item-group title="分組2">
<el-menu-item index="1-3">選項3</el-menu-item>
</el-menu-item-group>
<el-submenu index="1-4">
<template slot="title">選項4</template>
<el-menu-item index="1-4-1">選項1</el-menu-item>
</el-submenu>
</el-submenu>
<el-menu-item index="2">
<i class="el-icon-menu"></i>
<span slot="title">導(dǎo)航二</span>
</el-menu-item>
<el-menu-item index="3" disabled>
<i class="el-icon-document"></i>
<span slot="title">導(dǎo)航三</span>
</el-menu-item>
<el-menu-item index="4">
<i class="el-icon-setting"></i>
<span slot="title">導(dǎo)航四</span>
</el-menu-item>
</el-menu>
</el-col>
</el-row>
</template>
<script>
export default {
};
</script>
<style scoped>
/*去掉最右邊的白框,個人習(xí)慣*/
.el-menu {
border-right: 0;
}
</style>
Admin.vue
<el-aside width="300px"><Aside /></el-aside>
name: 'App',
components: {
Aside,
},
修改下Admin.vue中的背景色,否則太難看
.el-aside {
background-color: #545c64;/*這里*/
color: #333;
text-align: center;
}
現(xiàn)在,npm run dev,正常的話,顯示效果已經(jīng)出來了!
下面來實現(xiàn)動態(tài)效果。
現(xiàn)在修改Aside.vue來實現(xiàn)自定義菜單
<template>
<el-row>
<el-col :span="24">
<h5>管理后臺</h5>
<el-menu
default-active="2"
class="el-menu-vertical-demo"
background-color="#545c64"
text-color="#fff"
active-text-color="#ffd04b">
<template v-for="menu in menus">
<el-submenu :index="menu.index" v-if="menu.items" :key="menu.index">
<template slot="title"><i :class="getIcon(menu.icon)"></i>{{menu.label}}</template>
<el-menu-item v-for="item in menu.items"
:index="item.index" @click="send(item)" :key="item.index">
<i :class="getIcon(item.icon)"></i>{{item.label}}
</el-menu-item>
</el-submenu>
<el-menu-item :index="menu.index" @click="send(menu)" :key="menu.index" v-else>
<i :class="getIcon(menu.icon)"></i>{{menu.label}}
</el-menu-item>
</template>
</el-menu>
</el-col>
</el-row>
</template>
<script>
/**
* 定義一個menus,體驗菜單的動態(tài)渲染
* 將來會通過props傳進(jìn)來
*/
const menus = [
{
index: '1',
label: '配置管理',
icon: 'document',
items: [
{
index: '11',
label: '網(wǎng)站配置',
url: 'web.json',
},
{
index: '12',
label: '數(shù)據(jù)庫配置',
url: 'core,json',
},
],
},
{
index: '2',
label: '用戶管理',
url: 'login.json',
redirect: true,
},
{
index: '3',
label: '統(tǒng)計信息',
url: 'status.json',
icon: 'location',
},
];
export default {
data() {
return { menus };
},
methods: {
send(item) {
this.$emit('redirect', item);
},
getIcon(icon = 'menu') {
return `el-icon-${icon}`;
},
},
};
</script>
<style scoped>
/*去掉菜單最右邊的白框,個人習(xí)慣*/
.el-menu {
border-right: 0;
text-align: left;
}
</style>
我們的菜單已經(jīng)是通過一個數(shù)組menus來自定義了,根據(jù)vue特性,修改menus菜單就會變化哦,現(xiàn)在我們先不測試這個,留待將來在測試。我們這個有更重要的,菜單點擊后會向Admin組件發(fā)布redirect消息,我們修改Admin組件,處理這個消息。
給Admin組件添加響應(yīng)消息的地方
<el-aside width="240px"><Aside @redirect="onRedirect"/></el-aside>
...
<el-main><component :config="view" v-bind:is="view.name" /></el-main>
...
<script>
export default {
data() {
return {
view: {
},
};
},
methods: {
submit() {
this.$emit('redirect', { url: '/login.json' });
},
onRedirect(action) {
// 冒泡
if (this.passUp(action)) return this.$emit('redirect', action);
// 本地處理
return this.$HttpSend(action).then((response) => {
this.view = response.view;
throw new Error('route');
}).catch(() => {});
},
passUp(action) {
return action && action.redirect;
},
},
};
</script>
給Admin組件添加幾個臨時組件方便效果展示
const Table = {
template: '<H1>Table</H1>',
};
const Form = {
template: '<H1>Form</H1>',
};
const Chart = {
template: '<H1>Chart</H1>',
};
export default {
components: {
Aside,
Table,
Form,
Chart,
},
data() {...
突發(fā)奇想,就想修改下Http.js(HttpSend參數(shù)格式變了)
/**
* 遠(yuǎn)程調(diào)用的核心方法
* @returns {Promise<T>}
*/
static HttpSend = (action = {}) => {
/** @type {boolean} */
const withValue = action && action.value;
const option = {
url: Http.createUrl((action) ? action.url : undefined),
};
if (action && action.post) {
option.method = 'post';
if (withValue) option.data = Qs.stringify(action.value);
option.headers = { 'Content-Type': 'application/x-www-form-urlencoded' };
} else {
if (withValue) option.params = JSON.parse(JSON.stringify(action.value));
option.method = 'get';
}
// 開啟跨域cookie
option.withCredentials = true;
option.paramsSerializer = params => Qs.stringify(params, { arrayFormat: 'brackets' });
return $http(option).then((response) => {
const data = response.data;
// 判斷返回結(jié)果信息
if ((function isError(v) {
if (typeof v !== 'object') return false;
if (v.status === false) return true;
return (v.status && Number(v.status) <= 0);
})(data)) return Http.onError(data, '操作無效');
return Http.onReceive(data);
}, error => Http.onError(error, '數(shù)據(jù)獲取失敗'))
.catch(error => Http.onError(error, '內(nèi)部錯誤'));
};
/**
* 觸發(fā)鉤子函數(shù)
* @type {Function}
* @param {Object} data
* @returns {Object}
*/
static onReceive = (data) => {
Http.receivers.forEach(receiver => receiver(data));
return data;
}
/**
* 插件安裝函數(shù)
*/
static install(Vue) {
this.vue = Vue;
this.createUrl = this.vue.createUrl || (url => url);
this.vue.prototype.$httpGet = (url, value = null) => this.HttpSend({ url, value });
this.vue.prototype.$HttpPost = (url, value = null) => this.HttpSend({ url, value, post: true });
this.vue.prototype.$HttpSend = Http.HttpSend;
this.vue.prototype.$addReceiver = receiver => this.receivers.push(receiver);
this.vue.prototype.$removeReceiver = id => this.receivers.splice(id, 1);
}
看看神奇的效果吧。原來的幾個按鈕,因為Http.js的修改,失效了,改起來很簡單,不會就問我。
這樣呢,App和Admin有一部分重復(fù)的功能,我們提取出來
<script>
export default {
data() {
return {
view: {
},
};
},
methods: {
onRedirect(action) {
// 冒泡
if (this.passUp(action)) return this.$emit('redirect', action);
// 本地處理
return this.$HttpSend(action).then((response) => {
this.view = response.view;
throw new Error('route');
}).catch(() => {});
},
passUp(action) {
return action && action.redirect;
},
},
};
</script>
取名叫Container.vue放在components下。
修改App.vue
import Father from './components/Container';
...
export default {
name: 'App',
extends: Father,
data() {
return {
trace: {
rows: [],
},
};
},
...
methods: {
passUp() {
return false;
},
},
};
</script>
修改Admin.vue
export default {
extends: Father,
...
methods: {
submit() {
this.$emit('redirect', { url: '/login.json' });
},
},
};
看看效果,媽呀,組件繼承就這么簡單?。?!