gfs-react-dm
Version:
简化react和redux的繁杂流程,更简单的数据操作管理
459 lines (416 loc) • 18.6 kB
JavaScript
;
exports.__esModule = true;
exports.Sync = Sync;
exports.Control = Control;
exports.getControl = getControl;
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
var _model = require('./model');
var _gfsReactTools = require('gfs-react-tools');
var _gfsReactTools2 = _interopRequireDefault(_gfsReactTools);
require('./utils');
var _extend = require('extend');
var _extend2 = _interopRequireDefault(_extend);
/**
* 控制器
* @class Control
* */
var fetch = _gfsReactTools2['default'].fetch;
exports.fetch = fetch;
var controlList = {};
//todo 异步action和数据实现
//todo 异步ajax中间件
var curl = {
//use:
/**
* 删除store中某条数据
* @method del
* @param path {string} 需要被删除的属性地址,根据具体的对象结构,例如一个结构为:var data={name:'test',other:{age:18}}的对象,如果想删除age的值应该是这样:this.del('data.other.age')
* @param modelName {string} model名字,默认是绑定model之后的modelname
* @return Function
* @example
$Control(TestModel)
class TestControl {
delTest(data,dispatch,modelJson,model){
this.del('age')
}
}
*/
del: function del(path, data) {
var modelName = arguments.length <= 2 || arguments[2] === undefined ? this.__modelName : arguments[2];
path = path.indexOf('.') >= 0 ? path.split('.') : Array.prototype.concat.call([], path);
return this.dispatch({
type: this.getModelName('del', true, modelName), //`${DEFAULT}${DEFAULT_METHOD_FIX}${modelName}${DEFAULT_METHOD_FIX}del`,
path: path,
data: data
});
},
/**
* 更新store中某条数据,主要已合并为主,如果是想将新值覆盖旧值,请使用save方法
* @method update
* @param path {string} 需要被删除的属性地址,根据具体的对象结构,例如一个结构为:var data={name:'test',other:{age:18}}的对象,如果想修改age的值应该是这样:this.update('data.other.age',20)
* @param data {string | objaect} 需要合并的值
* @param modelName {string} model名字,默认是绑定model之后的modelname
* @return Function
* @example
$Control(TestModel)
class TestControl {
updateTest(data,dispatch,modelJson,model){
fetch('/test').then((data)=>{
this.update('age',data.age)
})
}
}
*/
update: function update(path, data) {
var modelName = arguments.length <= 2 || arguments[2] === undefined ? this.__modelName : arguments[2];
if (arguments.length == 1) {
data = arguments[0];
path = '';
} else {
path = path.indexOf('.') >= 0 ? path.split('.') : Array.prototype.concat.call([], path);
}
return this.dispatch({
type: this.getModelName('update', true, modelName), //`${DEFAULT}${DEFAULT_METHOD_FIX}${modelName}${DEFAULT_METHOD_FIX}update`,
path: path,
data: data
});
},
/**
* 更新store中某条数据,可自定义合并规则
* @method updateWith
* @param data {object} 需要合并的值
* @param merge {function} 自定义合并规则方法
* @param modelName {string} model名字,默认是绑定model之后的modelname
* @return Function
* @example
$Control(TestModel)
class TestControl {
updateTest(data,dispatch,modelJson,model){
return
fetch('/test').then((data)=>{
this.updateWith({
names:['test','test1','test2']
},function merger(prev,next){
if( Immutable.List.isList(prev) && Immutable.List.isList(next)){
return next
}
if(prev && prev.mergeWith){
return prev.mergeWith(merger,next)
}
return next
})
})
}
}
*/
updateWith: function updateWith(data, merge) {
var modelName = arguments.length <= 2 || arguments[2] === undefined ? this.__modelName : arguments[2];
if (data && data.modelName) {
modelName = data.modelName.toLowerCase();
data = data.data;
}
return this.dispatch({
type: this.getModelName('updateWith', true, modelName), //`${DEFAULT}${DEFAULT_METHOD_FIX}${modelName}${DEFAULT_METHOD_FIX}update`,
merge: merge || null,
data: data
});
},
/**
* 插入store中某条数据
* @method insert
* @param path {string} 需要被删除的属性地址,根据具体的对象结构,例如一个结构为:var data={name:'test',other:{age:18}}的对象,如果想要在data中新增一些字段应该这样:this.insert({sex:'男'})
* @param data {string | object} 需要保存的值,新的值会覆盖之前的值
* @param isImmutable {boolean} 是否将值转换为Immutable类型,默认为false,如果更新的值为object类型建议设置为true
* @param modelName {string} model名字,默认是绑定model之后的modelname
* @return Function
* @example
$Control(TestModel)
class TestControl {
insertTest(data,dispatch,modelJson,model){
fetch('/test').then((data)=>{
this.insert({
sex:'男'
})
})
}
}
*/
insert: function insert(path, data) {
var isImmutable = arguments.length <= 2 || arguments[2] === undefined ? false : arguments[2];
var modelName = arguments.length <= 3 || arguments[3] === undefined ? this.__modelName : arguments[3];
if (arguments.length == 1) {
data = arguments[0];
path = '';
} else {
path = path.indexOf('.') >= 0 ? path.split('.') : Array.prototype.concat.call([], path);
}
return this.dispatch({
type: this.getModelName('update', true, modelName), //`${DEFAULT}${DEFAULT_METHOD_FIX}${modelName}${DEFAULT_METHOD_FIX}update`,
path: path,
data: data,
isImmutable: isImmutable
});
},
/**
* 保存store中某条数据
* @method save
* @param path {string} 跟update一样
* @param data {string | object} 需要保存的值,新的值会覆盖之前的值
* @param isImmutable {boolean} 是否将值转换为Immutable类型,默认为false,如果更新的值为object类型建议设置为true
* @param modelName {string} model名字,默认是绑定model之后的modelname
* @return Function
* @example
$Control(TestModel)
class TestControl {
saveTest(data,dispatch,modelJson,model){
fetch('/test').then((data)=>{
this.save('age',data.age)
})
}
}
*/
save: function save(path, data) {
var isImmutable = arguments.length <= 2 || arguments[2] === undefined ? false : arguments[2];
var modelName = arguments.length <= 3 || arguments[3] === undefined ? this.__modelName : arguments[3];
return this.dispatch({
type: this.getModelName('save', true, modelName), //`${DEFAULT}${DEFAULT_METHOD_FIX}${modelName}${DEFAULT_METHOD_FIX}save`,
path: path.indexOf('.') >= 0 ? path.split('.') : Array.prototype.concat.call([], path),
data: data,
isImmutable: isImmutable
});
}
};
//任意类型参数
/**
* 异步操作,<strong style="color:red">IE9以下不建议使用</strong>,Sync是一个装饰器(Decorator),用于装饰Control类中的方法,将原有的方法变成一个异步成功调用后执行结果方法,被装饰的方法需要返回数据或false,决定是否更新store刷新节点。
* - 由Sync装饰后的方法,其作用域为Control,依然可以调用类中其他方法
* - Sync参数error可以为Control中xxxError命名的方法替代,“xxx”命名规则必须与Sync装饰的方法名一致
* - 被装饰后的方法在View中调用时传入的参数将已第二个为准,第一个参数将永远是异步执行后的结果
* - 被装饰的方法名要和Model类中方法名对应
* @method Sync
* @param anywhere {object|string} 参数为一个字符串时,anywhere为url,当方法拥有2个参数,第一个参数作为url,第二个参数为object类型
* @param anywhere.dataType {string} 数据返回类型 默认为json
* @param anywhere.asyn {boolean} 是否为异步请求,默认为true
* @param anywhere.method {string} 数据请求方式,默认为GET,可选值有:POST、GET、OPTION、DEL、PUT
* @param anywhere.timeout {number} 请求超时时间,可选填
* @param anywhere.credentials {object} 跨域是是否要包含cookie值,可选值:include
* @param anywhere.error {function} 请求失败回调,可选
* @param anywhere.header {object} 包含的请求头,可选
* @param anywhere.body {object} 需要传递给服务端的属性字段值,可选
* @param anywhere.cache {boolean} 请求数据是否缓存
* @return function
* @example
* import {Sync,Control} from 'gfs-react-mvc'
*
* class TestControl{
* constructor(){}
* //这里由于@为文档关键符号,所以下面将以$代替
* $Sync('/test',{
* method:'get'
* })
* save(data){
* //此处data是异步请求后服务器返回的结果
* if(data){
* //返回数据更新页面节点信息
* return data
* }
* //不做任何改变
* return false
* }
* $Sync('/del',{
* method:'get'
* })
* del(data){
* //此处data是异步请求后服务器返回的结果
* if(data){
* //返回数据更新页面节点信息
* return {
* //手动指定model对应方法
* type:this.getModelName('del'),
* data:data
* }
* }
* //不做任何改变
* return false
* }
* //也可直接使用
* $Sync()
* update(data){
* //此处data是异步请求后服务器返回的结果
* if(data){
* //返回数据更新页面节点信息
* return {
* //手动指定model对应方法
* type:this.getModelName('update'),
* data:data
* }
* }
* //不做任何改变
* return false
* }
* }
* */
function Sync(anywhere) {
var url = '';
var opts = {};
var error = null;
//修正参数
if (typeof anywhere === 'string') {
url = anywhere;
} else if (typeof anywhere === 'object') {
url = anywhere.url;
opts = anywhere;
}
if (arguments.length >= 2) {
opts = arguments[1];
}
//todo error作用域无法指向target,target对象丢失,强制制定为undefined,解决作用域丢失问题
if (opts.error) {
error = opts.error;
}
return function (target, name, descriptor) {
var fn = (function () {
var success = arguments.length <= 0 || arguments[0] === undefined ? function () {} : arguments[0];
var fn = success;
return function () {
var args = Array.prototype.slice.call(arguments);
var methodArg = args[args.length - 1] || {};
if (typeof methodArg === 'object' && methodArg.url) {
url = methodArg.url;
}
return function (dispatch) {
if (opts && typeof opts.method == 'undefined') {
opts.method = 'get';
}
if (url) {
fetch(url, _extend2['default'](opts, methodArg.url || methodArg.body ? methodArg : {})).then(function () {
var result = fn.apply(target, Array.prototype.slice.call(arguments).concat(args));
if (result instanceof Function) {
result(dispatch);
} else if (result && typeof result === 'object') {
dispatch(_extend2['default'](result || {}, {
type: '' + target.__modelName + _model.DEFAULT_METHOD_FIX + (result.type ? result.type : name),
data: result.data ? result.data : {}
}));
}
}, error || target[name + 'Error']);
} else {
var result = fn.apply(target, args);
if (result && typeof result === 'object') {
dispatch(_extend2['default'](result || {}, {
type: '' + target.__modelName + _model.DEFAULT_METHOD_FIX + (result.type ? result.type : name),
data: result.data ? result.data : {}
}));
}
}
};
};
})(descriptor.value);
descriptor.value = fn;
descriptor.enumerable = true;
return descriptor;
};
}
/**
* 此方法是一个装饰器,只能用于类,被装饰后的类会变成对象列表(JSON)格式,主要目的是传递给组件使用,通过redux connect连接。
* 被装饰的类将成为一个控制器,处理异步数据逻辑或业务逻辑,将数据传递给视图或服务器
* @method Control
* @param modelName {object} 实体类对象
* @param loadingbar {Loadingbar} 废弃
* @param mock {Mock} 废弃
* @return object
* @example
* import {Sync,Control} from 'gfs-react-mvc'
* import TestModel from '../model/TestModel'
* //这里由于@为文档关键符号,所以下面将以$代替
* //@Control(TestModel)
* class TestControl{
* constructor(){}
* $action
* changeAge(){
*
*
* fetch('/test.json'[,params]).then((data)=>{
* //control中默认提供update、del、insert、save四种操作数据方法,会根据不同的control名生成,如下根据testControl生成的方法testControlUpdate
*
* dispatch(this.testControlUpdate('age','ajax改变的age:'+data.age) )
* })
*
* }
* //不建议使用下列方式
* //这里由于@为文档关键符号,所以下面将以$代替
* $Sync('/test',{
* method:'get'
* })
* $action
* save(data){
* //此处data是异步请求后服务器返回的结果
* if(data){
* //返回数据更新页面节点信息
* return data
* }
* //不做任何改变
* return false
* }
* }
* */
function Control(model, loadingbar, mock) {
if (model === undefined) model = {};
if (arguments.length === 2) {
_gfsReactTools2['default'].addLoadingBar(loadingbar);
}
if (arguments.length === 3) {
_gfsReactTools2['default'].addMock(mock);
}
return function (target) {
//target = extend(target,curl)
//let name = ''//target.name||''
// let control = new target()
//循环遍历方法,将返回
//将方法的作用域改成对象本身
//postmodel.toJS()
var t = new target();
var p = {};
for (var item in t) {
(function (item) {
var fnName = item;
p[fnName] = (function () {
var args = Array.prototype.slice.call(arguments);
var _this = this;
return function (dispatch, store) {
var modelJson = store()[model.modelName];
_this.dispatch = dispatch;
return t[fnName].apply(t, args.concat([dispatch, modelJson && typeof modelJson.toJS != 'undefined' ? modelJson.toJS() : null, modelJson || null]));
};
}).bind(t);
})(item);
// p[item] = t[item] instanceof Function ? t[item].bind(t ) : t[item]
}
model.controls = p;
controlList[model.modelName] = model;
for (var cItem in curl) {
target.prototype[cItem] = curl[cItem];
}
target.prototype.__modelName = target.__modelName = model.modelName;
/**
* 获取model方法名全名,在未传任何值时返回方法前缀
* @method getModelName
* @param actionName {string} default='',方法名,可选
* @param isDefault {boolean} 是否获取系统中提供的方法名,默认false,可选
* @param modelName {string} model名字,可选
* @return string
*/
target.prototype.getModelName = target.getModelName = function () {
var actionName = arguments.length <= 0 || arguments[0] === undefined ? '' : arguments[0];
var isDefault = arguments.length <= 1 || arguments[1] === undefined ? false : arguments[1];
var modelName = arguments.length <= 2 || arguments[2] === undefined ? target.__modelName : arguments[2];
return '' + (isDefault ? _model.DEFAULT + _model.DEFAULT_METHOD_FIX : '') + modelName + _model.DEFAULT_METHOD_FIX + actionName;
};
return model;
//todo 解决对象私有属性访问,同样是对象丢失造成
//return {...target.prototype}
};
}
function getControl(modelName) {
return controlList[modelName.toLowerCase()];
}