若依(ruoyi)字典管理插件实现思路探究
Posted siweidetu
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了若依(ruoyi)字典管理插件实现思路探究相关的知识,希望对你有一定的参考价值。
一个UI表单的构成,避免不了下拉框,多选框等标签,在开发这些标签时,通常会请求后台接口获取字典值进行动态渲染。定制化开发虽然实现简单,但会产生大量重复工作,解决这类问题的思路有哪些?文章对若依字典管理插件实现思路进行了探究,以此来开阔思路。
探究过程如下:
- 界面设计
- 数据库设计
- 开发用例
- 源码分析
一、界面设计
界面截图如下:
功能提供了字典类型及字典键值的管理
二、数据库设计
SYS_DICT_TYPE
SYS_DICT_DATA
使用到SYS_DICT_TYPE,SYS_DICT_DATA两张表,定义了字典类型,及对应字典键值,两者是一对多的关系,通过dict_type关联。
三、开发用例
以用户性别为例,
功能配置:
字典名称:用户性别 |
字典类型:sys_user_sex |
字典数据
|
页面开发:
- 引入字典类型
export default
name: "User",
dicts: ['sys_normal_disable', 'sys_user_sex'],
components:
Treeselect
,
......
- 标签使用字典值
<el-col :span="12">
<el-form-item label="用户性别">
<el-select v-model="form.sex" placeholder="请选择性别">
<el-option v-for="dict in dict.type.sys_user_sex" :key="dict.value" :label="dict.label"
:value="dict.value"></el-option>
</el-select>
</el-form-item>
</el-col>
实现效果:
可以发现,通过灵活的功能配置,vue页面只要引入对应的字典类型,就可以使用字典数据,完成标签的动态渲染,接下来对源码进行分析,探究其实现过程。
四、源码分析
在开发用例中,发现vue自定义属性:dicts: ['sys_user_sex'],申明了vue实例需要引入的字典类型,那问题来了,vue实例是怎么通过这个自定义属性获取对应字典信息呢?
为了回答这个问题,需要先了解一下main.js的使用,参考文章vue项目中main.js使用方法详解,
文中提到“main.js是我们的入口文件,主要作用是初始化vue实例,并引入所需要的插件”,根据提示在main.js中找到与字典插件相关的所有代码段:
import getDicts from "@/api/system/dict/data";
// 字典标签组件
import DictTag from '@/components/DictTag'
// 字典数据组件
import DictData from '@/components/DictData'
// 全局方法挂载
Vue.prototype.getDicts = getDicts
// 全局组件挂载
Vue.component('DictTag', DictTag)
DictData.install()
按照这些插件加载的先后顺序进行分析
-
import getDicts from "@/api/system/dict/data":一个接口方法,根据字典类型查询字典数据信息。
// 根据字典类型查询字典数据信息 export function getDicts(dictType) return request( url: '/system/dict/data/type/' + dictType, method: 'get' )
-
import DictTag from '@/components/DictTag':字典标签组件,根据传入标签值,回显其对应的标签中文名,常用于数据列表中,返回的是标签值,通过DictTag组件可以回显对应的中文描述。
<template> <div> <template v-for="(item, index) in options"> <template v-if="values.includes(item.value)"> <span v-if="item.raw.listClass == 'default' || item.raw.listClass == ''" :key="item.value" :index="index" :class="item.raw.cssClass" > item.label </span > <el-tag v-else :disable-transitions="true" :key="item.value" :index="index" :type="item.raw.listClass == 'primary' ? '' : item.raw.listClass" :class="item.raw.cssClass" > item.label </el-tag> </template> </template> </div> </template> <script> export default name: "DictTag", props: options: type: Array, default: null, , value: [Number, String, Array], , computed: values() if (this.value !== null && typeof this.value !== 'undefined') return Array.isArray(this.value) ? this.value : [String(this.value)]; else return []; , , ; </script> <style scoped> .el-tag + .el-tag margin-left: 10px; </style>
-
import DictData from '@/components/DictData':提供了字典数据组件安装方法。
import Vue from 'vue'
import DataDict from '@/utils/dict'
import getDicts as getDicts from '@/api/system/dict/data'
function install()
Vue.use(DataDict,
metas:
'*':
labelField: 'dictLabel',
valueField: 'dictValue',
request(dictMeta)
return getDicts(dictMeta.type).then(res => res.data)
,
,
,
)
export default
install,
其中 import DataDict from '@/utils/dict' 引入了字典数据组件,是整个实现的核心,是这次分析的重点,涉及以下插件:
其中index.js 是入口程序,实现逻辑如下:
- 引入了Dict,DictOptions 插件
import Dict from './Dict' import mergeOptions from './DictOptions'
- 合并配置参数
mergeOptions(options),将DictData的metas 与 DictOptions的metas配置信息进行合并
metas: '*': labelField: 'dictLabel', valueField: 'dictValue', request(dictMeta) return getDicts(dictMeta.type).then(res => res.data) , , | metas: '*': /** * 字典请求,方法签名为function(dictMeta: DictMeta): Promise */ request: (dictMeta) => console.log(`load dict $dictMeta.type`) return Promise.resolve([]) , /** * 字典响应数据转换器,方法签名为function(response: Object, dictMeta: DictMeta): DictData */ responseConverter, labelField: 'label', valueField: 'value', , , |
- 通过mixin创建混入对象,将Dict组件 “混合”到各个使用组件本身的选项中。
什么是mixin?可查看博文对vue中mixin的理解。
其中data()方法 “const dict = new Dict()” 负责Dict组件创建,created() 方法中“this.dict.init(this.$options.dicts)”,将vue页面上定义的dicts数组传进去,组装数据,请求后端,获取对应字典数据。
import Dict from './Dict'
import mergeOptions from './DictOptions'
export default function(Vue, options)
mergeOptions(options)
Vue.mixin(
data()
if (this.$options === undefined || this.$options.dicts === undefined || this.$options.dicts === null)
return
const dict = new Dict()
dict.owner = this
return
dict
,
created()
if (!(this.dict instanceof Dict))
return
// options 如果配置onCreated ,则执行options.onCreated
options.onCreated && options.onCreated(this.dict)
this.dict.init(this.$options.dicts).then(() =>
//options 如果配置onReady ,则执行options.onReady方法
options.onReady && options.onReady(this.dict)
this.$nextTick(() =>
this.$emit('dictReady', this.dict)
if (this.$options.methods && this.$options.methods.onDictReady instanceof Function)
this.$options.methods.onDictReady.call(this, this.dict)
)
)
,
)
对this.dict.init(this.$options.dicts)代码进行分析:其根据传入的options配置信息,生成字典元数据配置信息dictMeta,然后创建回调方法执行数组const ps = [],根据dictMeta加入对应字典类型的loadDict方法,通过Promise.all(ps)发送多个请求并根据请求顺序获取和使用字典数据。
import Vue from 'vue'
import mergeRecursive from "@/utils/ruoyi";
import DictMeta from './DictMeta'
import DictData from './DictData'
const DEFAULT_DICT_OPTIONS =
types: [],
/**
* @classdesc 字典
* @property Object label 标签对象,内部属性名为字典类型名称
* @property Object dict 字段数组,内部属性名为字典类型名称
* @property Array.<DictMeta> _dictMetas 字典元数据数组
*/
export default class Dict
constructor()
this.owner = null
this.label =
this.type =
init(options)
if (options instanceof Array)
options = types: options
const opts = mergeRecursive(DEFAULT_DICT_OPTIONS, options)
if (opts.types === undefined)
throw new Error('need dict types')
const ps = []
this._dictMetas = opts.types.map(t => DictMeta.parse(t))
this._dictMetas.forEach(dictMeta =>
const type = dictMeta.type
Vue.set(this.label, type, )
Vue.set(this.type, type, [])
if (dictMeta.lazy)
return
ps.push(loadDict(this, dictMeta))
)
return Promise.all(ps)
/**
* 重新加载字典
* @param String type 字典类型
*/
reloadDict(type)
const dictMeta = this._dictMetas.find(e => e.type === type)
if (dictMeta === undefined)
return Promise.reject(`the dict meta of $type was not found`)
return loadDict(this, dictMeta)
/**
* 加载字典
* @param Dict dict 字典
* @param DictMeta dictMeta 字典元数据
* @returns Promise
*/
function loadDict(dict, dictMeta)
return dictMeta.request(dictMeta)
.then(response =>
const type = dictMeta.type
let dicts = dictMeta.responseConverter(response, dictMeta)
if (!(dicts instanceof Array))
console.error('the return of responseConverter must be Array.<DictData>')
dicts = []
else if (dicts.filter(d => d instanceof DictData).length !== dicts.length)
console.error('the type of elements in dicts must be DictData')
dicts = []
dict.type[type].splice(0, Number.MAX_SAFE_INTEGER, ...dicts)
dicts.forEach(d =>
Vue.set(dict.label[type], d.value, d.label)
)
return dicts
)
这样字典管理插件实现思路就基本清楚了,希望对你有所帮助。
以上是关于若依(ruoyi)字典管理插件实现思路探究的主要内容,如果未能解决你的问题,请参考以下文章