28 changed files with 503 additions and 325 deletions
			
			
		@ -0,0 +1,21 @@ | 
				
			|||||||
 | 
					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, | 
				
			||||||
 | 
					} | 
				
			||||||
@ -0,0 +1,82 @@ | 
				
			|||||||
 | 
					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 | 
				
			||||||
 | 
					    }) | 
				
			||||||
 | 
					} | 
				
			||||||
@ -0,0 +1,17 @@ | 
				
			|||||||
 | 
					import DictOptions from './DictOptions' | 
				
			||||||
 | 
					import DictData from './DictData' | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default function(dict, dictMeta) { | 
				
			||||||
 | 
					  const label = determineDictField(dict, dictMeta.labelField, ...DictOptions.DEFAULT_LABEL_FIELDS) | 
				
			||||||
 | 
					  const value = determineDictField(dict, dictMeta.valueField, ...DictOptions.DEFAULT_VALUE_FIELDS) | 
				
			||||||
 | 
					  return new DictData(dict[label], dict[value], dict) | 
				
			||||||
 | 
					} | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/** | 
				
			||||||
 | 
					 * 确定字典字段 | 
				
			||||||
 | 
					 * @param {DictData} dict | 
				
			||||||
 | 
					 * @param  {...String} fields | 
				
			||||||
 | 
					 */ | 
				
			||||||
 | 
					function determineDictField(dict, ...fields) { | 
				
			||||||
 | 
					  return fields.find(f => Object.prototype.hasOwnProperty.call(dict, f)) | 
				
			||||||
 | 
					} | 
				
			||||||
@ -0,0 +1,13 @@ | 
				
			|||||||
 | 
					/** | 
				
			||||||
 | 
					 * @classdesc 字典数据 | 
				
			||||||
 | 
					 * @property {String} label 标签 | 
				
			||||||
 | 
					 * @property {*} value 标签 | 
				
			||||||
 | 
					 * @property {Object} raw 原始数据 | 
				
			||||||
 | 
					 */ | 
				
			||||||
 | 
					export default class DictData { | 
				
			||||||
 | 
					  constructor(label, value, raw) { | 
				
			||||||
 | 
					    this.label = label | 
				
			||||||
 | 
					    this.value = value | 
				
			||||||
 | 
					    this.raw = raw | 
				
			||||||
 | 
					  } | 
				
			||||||
 | 
					} | 
				
			||||||
@ -0,0 +1,38 @@ | 
				
			|||||||
 | 
					import { mergeRecursive } from "@/utils/ruoyi"; | 
				
			||||||
 | 
					import DictOptions from './DictOptions' | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/** | 
				
			||||||
 | 
					 * @classdesc 字典元数据 | 
				
			||||||
 | 
					 * @property {String} type 类型 | 
				
			||||||
 | 
					 * @property {Function} request 请求 | 
				
			||||||
 | 
					 * @property {String} label 标签字段 | 
				
			||||||
 | 
					 * @property {String} value 值字段 | 
				
			||||||
 | 
					 */ | 
				
			||||||
 | 
					export default class DictMeta { | 
				
			||||||
 | 
					  constructor(options) { | 
				
			||||||
 | 
					    this.type = options.type | 
				
			||||||
 | 
					    this.request = options.request, | 
				
			||||||
 | 
					    this.responseConverter = options.responseConverter | 
				
			||||||
 | 
					    this.labelField = options.labelField | 
				
			||||||
 | 
					    this.valueField = options.valueField | 
				
			||||||
 | 
					    this.lazy = options.lazy === true | 
				
			||||||
 | 
					  } | 
				
			||||||
 | 
					} | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/** | 
				
			||||||
 | 
					 * 解析字典元数据 | 
				
			||||||
 | 
					 * @param {Object} options | 
				
			||||||
 | 
					 * @returns {DictMeta} | 
				
			||||||
 | 
					 */ | 
				
			||||||
 | 
					DictMeta.parse= function(options) { | 
				
			||||||
 | 
					  let opts = null | 
				
			||||||
 | 
					  if (typeof options === 'string') { | 
				
			||||||
 | 
					    opts = DictOptions.metas[options] || {} | 
				
			||||||
 | 
					    opts.type = options | 
				
			||||||
 | 
					  } else if (typeof options === 'object') { | 
				
			||||||
 | 
					    opts = options | 
				
			||||||
 | 
					  } | 
				
			||||||
 | 
					  opts = mergeRecursive(DictOptions.metas['*'], opts) | 
				
			||||||
 | 
					  return new DictMeta(opts) | 
				
			||||||
 | 
					} | 
				
			||||||
@ -0,0 +1,51 @@ | 
				
			|||||||
 | 
					import { mergeRecursive } from "@/utils/ruoyi"; | 
				
			||||||
 | 
					import dictConverter from './DictConverter' | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const options = { | 
				
			||||||
 | 
					  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', | 
				
			||||||
 | 
					    }, | 
				
			||||||
 | 
					  }, | 
				
			||||||
 | 
					  /** | 
				
			||||||
 | 
					   * 默认标签字段 | 
				
			||||||
 | 
					   */ | 
				
			||||||
 | 
					  DEFAULT_LABEL_FIELDS: ['label', 'name', 'title'], | 
				
			||||||
 | 
					  /** | 
				
			||||||
 | 
					   * 默认值字段 | 
				
			||||||
 | 
					   */ | 
				
			||||||
 | 
					  DEFAULT_VALUE_FIELDS: ['value', 'id', 'uid', 'key'], | 
				
			||||||
 | 
					} | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/** | 
				
			||||||
 | 
					 * 映射字典 | 
				
			||||||
 | 
					 * @param {Object} response 字典数据 | 
				
			||||||
 | 
					 * @param {DictMeta} dictMeta 字典元数据 | 
				
			||||||
 | 
					 * @returns {DictData} | 
				
			||||||
 | 
					 */ | 
				
			||||||
 | 
					function responseConverter(response, dictMeta) { | 
				
			||||||
 | 
					  const dicts = response.content instanceof Array ? response.content : response | 
				
			||||||
 | 
					  if (dicts === undefined) { | 
				
			||||||
 | 
					    console.warn(`no dict data of "${dictMeta.type}" found in the response`) | 
				
			||||||
 | 
					    return [] | 
				
			||||||
 | 
					  } | 
				
			||||||
 | 
					  return dicts.map(d => dictConverter(d, dictMeta)) | 
				
			||||||
 | 
					} | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export function mergeOptions(src) { | 
				
			||||||
 | 
					  mergeRecursive(options, src) | 
				
			||||||
 | 
					} | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default options | 
				
			||||||
@ -0,0 +1,33 @@ | 
				
			|||||||
 | 
					import Dict from './Dict' | 
				
			||||||
 | 
					import { mergeOptions } from './DictOptions' | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default function(Vue, options) { | 
				
			||||||
 | 
					  mergeOptions(options) | 
				
			||||||
 | 
					  Vue.mixin({ | 
				
			||||||
 | 
					    data() { | 
				
			||||||
 | 
					      if (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(this.dict) | 
				
			||||||
 | 
					      this.dict.init(this.$options.dicts).then(() => { | 
				
			||||||
 | 
					        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) | 
				
			||||||
 | 
					          } | 
				
			||||||
 | 
					        }) | 
				
			||||||
 | 
					      }) | 
				
			||||||
 | 
					    }, | 
				
			||||||
 | 
					  }) | 
				
			||||||
 | 
					} | 
				
			||||||
					Loading…
					
					
				
		Reference in new issue