mutants-appfx
Version:
appfx module
467 lines (443 loc) • 14.3 kB
JavaScript
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
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;
}
// 数组追加对象
pushValue(key, value) {
const subObj = this.createValue(key, value);
_get(this, key).push(subObj);
return subObj;
}
// 插入一个对象,默认前插入
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;
}
async load({
sysid,
mid,
id,
...other
}, cacheData) {
const dto = cacheData || await DataProviderManager.getData({
sysid,
mid,
id,
...other
});
this[idKeySymbol] = id;
this.merge(dto);
}
new() {
this[idKeySymbol] = null;
this.restore();
}
merge(dto) {
// map必须直接到this需要固定的结构
mapper.map('dto', this[dataKeySymbol], dto, this);
}
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);
}
}