UNPKG

mutants-appfx

Version:

appfx module

467 lines (443 loc) 14.3 kB
import { action, observable, extendObservable, toJS } from 'mobx'; import DataProviderManager from '../DataProviderManager'; import mapper from 'automapper'; import moment from 'moment'; import _get from 'lodash/get'; import _setWith from 'lodash/setWith'; import _camelCase from 'lodash/camelCase'; const types = ['string', 'object', 'array', 'number', 'boolean', 'date', 'reference']; const getMeta = (obj) => { const meta = []; obj && Object.keys(obj).forEach(key => { let type; let fields; let defValue; if (typeof obj[key] === 'string') { type = types.indexOf(obj[key].toLowerCase()) > -1 ? obj[key].toLowerCase() : 'string'; } else if ('type' in obj[key] || 'Type' in obj[key]) { obj[key].type = obj[key].type || obj[key].Type; //debug(obj[key].type,obj[key].fields) type = obj[key].type === 'ref' ? 'reference' : obj[key].type; fields = type === 'obj' || type === 'object' || type === 'reference' || type === 'array' ? getMeta(obj[key].fields || obj[key].Fields) : undefined; defValue = obj[key].defValue || obj[key].DefValue; } else if (Array.isArray(obj[key])) { type = 'array'; if (obj[key].length > 0) { const arrobj = obj[key][0]; if (arrobj.type) { fields = getMeta(arrobj.fields); } else { fields = getMeta(arrobj); } } } else if (typeof obj[key] === 'object') { type = 'reference'; // 默认是引用类型 fields = getMeta(obj[key]); } else { console.warn('not support type', key); return; } meta.push({ key, type, fields, defValue }); }); return meta; } const findFields = (fields, path, i = 0) => { let key = path[i]; if (key.endsWith(']')) { key = key.substr(0, key.indexOf('[')) } const sub = fields.find(it => it.key === key); if (i + 1 < path.length) { return findFields(sub.fields, path, i + 1); } if (!sub) { throw new Error(key + ' not defined!'); } return sub.fields; } const createObj = (defineObj = {}, fields, cutRef = false) => { (fields || []).forEach(it => { switch (it.type) { case 'array': if (cutRef) { defineObj[it.key] = it.defValue || []; } else { extendObservable(defineObj, { [it.key]: it.defValue || [] }); } break; case 'reference': case 'object': if (cutRef && it.type === 'reference') { const idField = it.fields.find(it => it.key.toLowerCase() === 'id'); if (idField) { defineObj[it.key] = { [idField.key]: '' }; } else { defineObj[it.key] = {}; } } else { const props = createObj({}, it.fields, cutRef); // debug(defineObj,{ // [it.key]: props // },subDecs) extendObservable(defineObj, { [it.key]: it.type === 'reference' ? observable.ref(props) : props }); } break; case 'string': if (cutRef) { defineObj[it.key] = it.defValue !== undefined ? String(it.defValue || '') : null; } else { extendObservable(defineObj, { [it.key]: it.defValue !== undefined ? String(it.defValue || '') : null }); } break; case 'number': if (cutRef) { defineObj[it.key] = it.defValue !== undefined ? Number(it.defValue || 0) : null; } else { extendObservable(defineObj, { [it.key]: it.defValue !== undefined ? Number(it.defValue || 0) : null }); } break; case 'boolean': if (cutRef) { defineObj[it.key] = it.defValue !== undefined ? Boolean(it.defValue || false) : null; } else { extendObservable(defineObj, { [it.key]: it.defValue !== undefined ? Boolean(it.defValue || false) : null }); } break; case 'date': if (it.defValue === 'now') { if (cutRef) { defineObj[it.key] = Date.now; } else { extendObservable(defineObj, { [it.key]: Date.now }); } } else { if (cutRef) { defineObj[it.key] = it.defValue !== undefined ? moment(it.defValue).toDate() : null; } else { extendObservable(defineObj, { [it.key]: it.defValue !== undefined ? moment(it.defValue).toDate() : null }); } } break; default: if (cutRef) { defineObj[it.key] = it.defValue || null; } else { extendObservable(defineObj, { [it.key]: it.defValue || null }); } } }); return defineObj; } const createMapping = (fields, name) => { const dtom = mapper.createMap('dto', name); const mtod = mapper.createMap(name, 'dto'); //console.log('Type', name, fields); (fields || []).forEach(it => { switch (it.type) { case 'array': createMapping(it.fields, name + '_' + it.key); dtom.forMember(it.key, function () { const sourceValue = this.__sourceValue[it.key]; const destinationValue = this.__destinationValue[it.key]; if (sourceValue instanceof Array) { destinationValue.length = sourceValue.length; // 如果是数组就循环赋值 for (var i = 0; i < sourceValue.length; i += 1) { if (it.fields && it.fields.length > 0) { // map前必须定义结构 if (destinationValue.length <= i || !destinationValue[i]) { createMapping(it.fields, name + '_' + it.key); destinationValue[i] = createObj({}, it.fields); } mapper.map('dto', name + '_' + it.key, sourceValue[i], destinationValue[i]); } else { destinationValue[i] = sourceValue[i]; } } } else if (sourceValue !== undefined) { // 可以是一个对象,直接赋给数组的第一个元素上 destinationValue.length = 0; if (it.fields && it.fields.length > 0) { createMapping(it.fields, name + '_obj' + it.key); destinationValue[0] = createObj({}, it.fields); mapper.map('dto', name + '_obj' + it.key, sourceValue, destinationValue[0]); } else { destinationValue[0] = sourceValue; } } }); mtod.forMember(it.key, function () { const sourceValue = this.__sourceValue[it.key]; const destinationValue = this.__destinationValue[it.key]; if (Array.isArray(sourceValue)) { for (var i = 0; i < sourceValue.length; i += 1) { if (it.fields && it.fields.length > 0) { if (!destinationValue[i]) { createMapping(it.fields, name + '_' + it.key, {}); destinationValue[i] = createObj({}, it.fields, true); } mapper.map(name + '_' + it.key, 'dto', sourceValue[i], destinationValue[i]); } else { destinationValue[i] = sourceValue[i]; } } } else { destinationValue.length = 0; } }); break; case 'reference': case 'object': createMapping(it.fields, name + '_' + it.key); dtom.forMember(it.key, function () { let sourceValue = this.__sourceValue[it.key]; if (sourceValue) { mapper.map('dto', name + '_' + it.key, sourceValue, this.__destinationValue[it.key]); } else { // 对象支持把层级拉平赋值 //console.debug(it.key, this.__sourceValue) const destinationValue = this.__destinationValue[it.key]; const sourceKeys = Object.keys(this.__sourceValue); // ref引用类型支持 //debug(it.fields.map(it => it.key)); const flatValue = {}; //for (let key in it.fields.map(it => it.key)) { // if (!destinationValue.hasOwnProperty(key)) { // continue; // } // 把当前级别的subkey拉平成一个子对象 const flatKeys = sourceKeys.filter(key => key.toUpperCase().indexOf(it.key.toUpperCase()) > -1); flatKeys.forEach(key => { // 这里需要对大小写格式化 flatValue[_camelCase(key.substr(it.key.length))] = this.__sourceValue[key]; }); //} //debug(name + '_' + it.key, '=', flatValue) if (it.type === 'reference') { // destinationValue对象结构不是定义的结构导致map时获取props不对 const refVal = createObj({}, it.fields); //debug(refVal) mapper.map('dto', name + '_' + it.key, flatValue, refVal); this.__destinationValue[it.key] = refVal; } else { mapper.map('dto', name + '_' + it.key, flatValue, destinationValue); } } }); mtod.forMember(it.key, function () { let sourceValue = this.__sourceValue[it.key]; if (sourceValue) { mapper.map(name + '_' + it.key, 'dto', sourceValue, this.__destinationValue[it.key]); // } else { // this.__destinationValue[it.key] = sourceValue; } }); break; case 'string': [dtom, mtod].forEach(atob => atob.forMember(it.key, function () { if (this.__sourceValue.hasOwnProperty(it.key)) { this.__destinationValue[it.key] = this.__sourceValue[it.key]; // }else{ // this.__destinationValue[it.key] = ''; } })); break; case 'number': [dtom, mtod].forEach(atob => atob.forMember(it.key, function () { if (this.__sourceValue.hasOwnProperty(it.key)) { this.__destinationValue[it.key] = this.__sourceValue[it.key]; // }else{ // this.__destinationValue[it.key] = 0; } })); break; case 'boolean': [dtom, mtod].forEach(atob => atob.forMember(it.key, function () { if (this.__sourceValue.hasOwnProperty(it.key)) { this.__destinationValue[it.key] = !!this.__sourceValue[it.key]; // }else{ // this.__destinationValue[it.key] = 0; } })); break; case 'date': [dtom, mtod].forEach(atob => atob.forMember(it.key, function () { if (this.__sourceValue.hasOwnProperty(it.key)) { if (this.__sourceValue[it.key]) { // 支持字符串自动转换 if (mtod === atob) { this.__destinationValue[it.key] = moment(this.__sourceValue[it.key]).format(); } else { this.__destinationValue[it.key] = moment(this.__sourceValue[it.key]).toDate(); } } else { this.__destinationValue[it.key] = null; } // }else{ // this.__destinationValue[it.key] = 0; } })); break; default: [dtom, mtod].forEach(atob => atob.forMember(it.key, function () { if (this.__sourceValue.hasOwnProperty(it.key)) { this.__destinationValue[it.key] = this.__sourceValue[it.key]; // }else{ // this.__destinationValue[it.key] = null; } })); } }); } const fieldsSymbol = Symbol(); const dataKeySymbol = Symbol(); const idKeySymbol = Symbol(); export default class DataModel { constructor(dataInfo, dataKey) { this[fieldsSymbol] = getMeta(dataInfo); //debug('dataInfo', JSON.stringify(this[fieldsSymbol], null, 2)) this[dataKeySymbol] = dataKey; //debug(this[dataKeySymbol]); createMapping(this[fieldsSymbol], dataKey); createObj(this, this[fieldsSymbol], false); //console.log(this); } getId(){ return this[idKeySymbol]; } getValue(key) { return _get(this, key); } // 对象更新,要是简单类型直接赋值即可a.b=100 @action setValue(key, value) { const subObj = this.createValue(key, value); //debug('setValue', key); _setWith(this, key, subObj, (val, key, obj) => { //debug(val,key,obj,e) //return true; }); //debug(_get(this, key)) return subObj; } // 数组追加对象 @action pushValue(key, value) { const subObj = this.createValue(key, value); _get(this, key).push(subObj); return subObj; } // 插入一个对象,默认前插入 @action insertValue(key, value, index=0) { const subObj = this.createValue(key, value); _get(this, key).splice(index,0,subObj); return subObj; } createValue(key, value) { const fields = findFields(this[fieldsSymbol], key.split('.')); let subObj; if (fields) { subObj = createObj({}, fields); const paths = [this[dataKeySymbol], ...key.split('.')].map(key => key.endsWith(']') ? key.substr(0, key.indexOf('[')) : key); //debug('pushValue', key); mapper.map('dto', paths.join('_'), value, subObj); } else { // TODO 这里还需要类型转换 // 比如value是string,但是type是date,需要转换成Date subObj = value; } return subObj; } @action async load({ sysid, mid, id, ...other }, cacheData) { const dto = cacheData || await DataProviderManager.getData({ sysid, mid, id, ...other }); this[idKeySymbol] = id; this.merge(dto); } @action new() { this[idKeySymbol] = null; this.restore(); } @action merge(dto) { // map必须直接到this需要固定的结构 mapper.map('dto', this[dataKeySymbol], dto, this); } @action restore(initData) { this.merge(initData || createObj({}, this[fieldsSymbol])); } cut() { // 裁剪引用类型数据结构只保留ID const dto = createObj({}, this[fieldsSymbol], true); mapper.map(this[dataKeySymbol], 'dto', this.toJS(), dto); //debug(this.toJS(), '=>', dto); return dto; } async save({ sysid, mid, id, ...other }) { // todo send to server await DataProviderManager.saveData({ sysid, mid, id, ...other }, this.cut()); } toJS() { return toJS(this); } static async create({ sysid, mid, ...other }) { const dataInfo = (await DataProviderManager.getDataInfo({ sysid, mid, ...other })); return new DataModel(dataInfo, mid); } }