28 changed files with 503 additions and 325 deletions
			
			
		@ -0,0 +1,21 @@
				@@ -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 @@
				@@ -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 @@
				@@ -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 @@
				@@ -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 @@
				@@ -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 @@
				@@ -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 @@
				@@ -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