後端篇的時候已經對字典模塊進行了設計,相應的接口也已經完成。在先後端未分離的狀況下,由於頁面是由服務端渲染的,因此通常都會自定義一個字典標籤用於對字典數據的取值、渲染。該種狀況下,服務端很方便地對字典作緩存處理。先後端分離後,前端與後端都是經過接口進行交互的,因此維護字典的方式也會有所區別。前端
咱們簡單的分析一下字典組件應該具有的功能。vue
在後臺管理中,常見的使用字典的場景有三個:git
使用場景不一樣,對於前端來講,其實就是呈現方式的不一樣。因此咱們在作組件的時候,能夠先默認按場景分三種佈局。vuex
緩存的思路有不少種,這裏簡單講一下:數據庫
系統登陸後,一次性返回全部的字典數據,緩存在本地的cookies或vuex上;json
優勢:減輕服務器壓力後端
缺點:一次性返回,字典量多的話,可能會影響體驗api
不進行緩存,每次都調用接口獲取數據;緩存
優勢:無bash
缺點:頻繁請求,頁面中字典多的話,影響體驗
使用vuex,基於dictKey進行緩存,保證在同一個vue實例下,同一個key,只調用一次接口。
方案三是本框架採用的方式,也不能說是最優的。可是相對而已,可能會比前兩個方案會好一些。固然,除了這三個方案,確定還有別的方案,這裏就不討論了。
暫時定幾個經常使用的參數,後續可能還會有追加
參數名 | 類型 | 默認值 | 說明 |
---|---|---|---|
dictKey | String | undefined | 字典惟一編碼(表名_字段名) |
type | String | enum | 字典類型(enum->枚舉類字典類型,db->數據庫字典類型,local->本地字典類型) |
value | String, Number | undefined | 綁定的值 |
size | String | medium | 對應el-select的size,medium/small/mini |
mode | String | form | form->普通表單,list->列表頁,searchForm->搜索表單 |
先簡單說一下後端提供的接口
請求地址:
{{api_base_url}}/sys/dict/getByDictKey
數據類型:
application/json
請求示例:
{
"dictKey": "sys_role_role_type",
"type": "enum"
}
複製代碼
響應示例:
{
"code": 0, // 返回狀態碼0,成功
"msg": "經過字典惟一編碼查詢成功", // 消息描述
"data": {
"name": "角色類型",
"dictKey": "sys_role_role_type", // 字典惟一編碼
"items": [{
"name": "管理員",
"dictItemValue": 10
}, {
"name": "流程審覈員",
"dictItemValue": 20
}]
}
}
複製代碼
├── src
├── components/m
├── Dict
└── index.vue
├── store
├── modules
└── dict.js
├── getters.js
└── index.js
├── views
├── dashboard
└── index.vue
└── main.js
複製代碼
src/components/m/Dict/index.vue
字典組件
<template>
<div class="m-dict">
<!--表單佈局模式-->
<slot v-if="mode==='form'" v-bind:dict="dict">
<el-select :size="size" v-model="mValue" v-if="dict.items" @change="handleChange">
<el-option
v-for="item in dict.items"
:key="item.dictItemValue"
:label="item.name"
:value="item.dictItemValue">
</el-option>
</el-select>
</slot>
<!--列表佈局模式-->
<slot v-else-if="mode==='list'" v-bind:dict="dict">
<span v-for="item in dict.items" :key="item.dictItemValue">
<el-tag :type="type" size="mini" v-if="item.dictItemValue === value">{{ item.name }}</el-tag>
</span>
</slot>
<!--搜索表單佈局模式-->
<slot v-else-if="mode==='searchForm'" v-bind:dict="dict">
<el-select :size="size" v-model="mValue" v-if="dict.items" @change="handleChange">
<el-option label="全部" :value="undefined"></el-option>
<el-option
v-for="item in dict.items"
:key="item.dictItemValue"
:label="item.name"
:value="item.dictItemValue">
</el-option>
</el-select>
</slot>
</div>
</template>
<script>
import { mapGetters } from 'vuex'
export default {
name: 'MDict',
props: {
// 字典惟一編碼(表名_字段名)
dictKey: {
type: String,
default: undefined
},
// 字典類型(enum->枚舉類字典類型,db->數據庫字典類型,local->本地字典類型)
// 不傳的話,後端先查enum,再查db
type: {
type: String,
default: 'enum'
},
// 綁定的值
value: {
type: [String, Number],
default: undefined
},
size: { // medium/small/mini
type: String,
default: 'medium'
},
mode: { // form->普通表單,list->列表頁,searchForm->搜索表單
type: String,
default: 'form'
}
},
data() {
return {
mValue: this.value
}
},
computed: {
...mapGetters([
'dictMap'
]),
// 當前字典
dict() {
return this.dictMap[this.dictKey] || {}
}
},
watch: {
value(n) { // 監聽父組件值變更,子組件也要變更
this.mValue = n
}
},
created() {
if (!this.dictMap[this.dictKey]) {
// 這裏調用store/modules/dict.js/action->getByDictKey
this.$store.dispatch('dict/getByDictKey', {
dictKey: this.dictKey,
type: this.type
})
}
},
methods: {
// 子組件值變化要通知父組件
handleChange(value) {
this.$emit('input', value)
}
}
}
</script>
複製代碼
src/store/modules/dict.js
import request from '@/utils/request'
const getDefaultState = () => {
return {
// 字典map
dictMap: {}
}
}
const state = getDefaultState()
const mutations = {
// 保存字典項
SAVE_DICT_ITEM: (state, data) => {
var obj = {}
obj[data.dictKey] = data
// 須要拷貝一份,要否則數據變更監聽不到
state.dictMap = Object.assign({}, state.dictMap, obj)
},
// 移除字典項
DELETE_DICT_ITEM: (state, dictKey) => {
delete state.dictMap[dictKey]
}
}
const actions = {
// 獲取字典的action
getByDictKey({ commit }, data) {
return new Promise((resolve, reject) => {
if (state.dictMap[data.dictKey]) {
resolve()
} else {
// 防止同一個key屢次請求
commit('SAVE_DICT_ITEM', {
dictKey: data.dictKey,
items: []
})
// 這裏暫不用api.service.js
request({
url: '/sys/dict/getByDictKey',
method: 'post',
data
}).then(res => {
if (res.code === 0 && res.data) {
commit('SAVE_DICT_ITEM', res.data)
} else {
commit('DELETE_DICT_ITEM', data.dictKey)
}
resolve()
}).catch(error => {
commit('DELETE_DICT_ITEM', data.dictKey)
reject(error)
})
}
})
}
}
export default {
namespaced: true,
state,
mutations,
actions
}
複製代碼
src/store/getters.js
定義dictMap的get方法
const getters = {
sidebar: state => state.app.sidebar,
device: state => state.app.device,
token: state => state.user.token,
avatar: state => state.user.avatar,
name: state => state.user.name,
// 這裏追加dictMap的get方法,可使用mapGetters,詳見src/components/m/Dict/index.vue
dictMap: state => state.dict.dictMap
}
export default getters
複製代碼
src/store/index.js
這裏引入dict.js模塊,充分利用了require.contex
import Vue from 'vue'
import Vuex from 'vuex'
import getters from './getters'
// import app from './modules/app'
// import settings from './modules/settings'
// import user from './modules/user'
// 自動註冊vuex模塊
const files = require.context('./modules', true, /\.js$/)
var modules = {}
files.keys().forEach((routerPath) => {
const name = routerPath.replace(/^\.\/(.*)\.\w+$/, '$1')
const value = files(routerPath)
const fileModule = value.default
modules[name] = fileModule
}, {})
Vue.use(Vuex)
const store = new Vuex.Store({
modules: {
...modules
},
getters
})
export default store
複製代碼
src/main.js
主入口全局註冊自定義組件,這裏也用了require.context,代碼片斷
import Vue from 'vue'
// 處理自定義組件全局註冊
const files = require.context('./components/m', true, /\.vue$/)
files.keys().forEach((routerPath) => {
const componentName = routerPath.replace(/^\.\/(.*)\/index\.\w+$/, '$1')
const value = files(routerPath)
Vue.component('m-' + componentName.toLowerCase(), value.default)
}, {})
複製代碼
src/views/dashboard/index.vue
這裏提供了使用樣例:
自定義佈局
<m-dict v-model="form.roleType" dict-key="sys_role_role_type">
<template v-slot:default="{ dict }">
<el-select v-model="form.roleType" v-if="dict.items">
<el-option
v-for="item in dict.items"
:key="item.dictItemValue"
:label="item.name"
:value="item.dictItemValue">
</el-option>
</el-select>
</template>
</m-dict>
複製代碼
表單佈局模式
<m-dict mode="form" v-model="form.roleType" dict-key="sys_role_role_type"></m-dict>
複製代碼
列表佈局模式
<m-dict mode="list" v-model="form.roleType" dict-key="sys_role_role_type"></m-dict>
複製代碼
搜索表單佈局模式
<m-dict mode="searchForm" v-model="form.roleType" dict-key="sys_role_role_type"></m-dict>
複製代碼
本文對字典組件的封裝仍是比較粗糙,不過也基本上知足日常使用,如後續場景須要,再考慮繼續擴展。好比預留的local本地字典類型,若是本地存在字典配置,就能夠不走接口請求。目前該參數也未實現。