UNPKG

18.6 kBJavaScriptView Raw
1'use strict';
2
3exports.__esModule = true;
4exports.Sync = Sync;
5exports.Control = Control;
6exports.getControl = getControl;
7
8function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
9
10var _model = require('./model');
11
12var _gfsReactTools = require('gfs-react-tools');
13
14var _gfsReactTools2 = _interopRequireDefault(_gfsReactTools);
15
16require('./utils');
17
18var _extend = require('extend');
19
20var _extend2 = _interopRequireDefault(_extend);
21
22/**
23 * 控制器
24 * @class Control
25 * */
26var fetch = _gfsReactTools2['default'].fetch;
27exports.fetch = fetch;
28var controlList = {};
29//todo 异步action和数据实现
30//todo 异步ajax中间件
31var curl = {
32 //use:
33 /**
34 * 删除store中某条数据
35 * @method del
36 * @param path {string} 需要被删除的属性地址,根据具体的对象结构,例如一个结构为:var data={name:'test',other:{age:18}}的对象,如果想删除age的值应该是这样:this.del('data.other.age')
37 * @param modelName {string} model名字,默认是绑定model之后的modelname
38 * @return Function
39 * @example
40 $Control(TestModel)
41 class TestControl {
42 delTest(data,dispatch,modelJson,model){
43 this.del('age')
44 }
45 }
46 */
47 del: function del(path, data) {
48 var modelName = arguments.length <= 2 || arguments[2] === undefined ? this.__modelName : arguments[2];
49
50 path = path.indexOf('.') >= 0 ? path.split('.') : Array.prototype.concat.call([], path);
51
52 return this.dispatch({
53 type: this.getModelName('del', true, modelName), //`${DEFAULT}${DEFAULT_METHOD_FIX}${modelName}${DEFAULT_METHOD_FIX}del`,
54 path: path,
55 data: data
56 });
57 },
58 /**
59 * 更新store中某条数据,主要已合并为主,如果是想将新值覆盖旧值,请使用save方法
60 * @method update
61 * @param path {string} 需要被删除的属性地址,根据具体的对象结构,例如一个结构为:var data={name:'test',other:{age:18}}的对象,如果想修改age的值应该是这样:this.update('data.other.age',20)
62 * @param data {string | objaect} 需要合并的值
63 * @param modelName {string} model名字,默认是绑定model之后的modelname
64 * @return Function
65 * @example
66 $Control(TestModel)
67 class TestControl {
68 updateTest(data,dispatch,modelJson,model){
69 fetch('/test').then((data)=>{
70 this.update('age',data.age)
71 })
72 }
73 }
74 */
75 update: function update(path, data) {
76 var modelName = arguments.length <= 2 || arguments[2] === undefined ? this.__modelName : arguments[2];
77
78 if (arguments.length == 1) {
79 data = arguments[0];
80 path = '';
81 } else {
82 path = path.indexOf('.') >= 0 ? path.split('.') : Array.prototype.concat.call([], path);
83 }
84
85 return this.dispatch({
86 type: this.getModelName('update', true, modelName), //`${DEFAULT}${DEFAULT_METHOD_FIX}${modelName}${DEFAULT_METHOD_FIX}update`,
87 path: path,
88 data: data
89 });
90 },
91 /**
92 * 更新store中某条数据,可自定义合并规则
93 * @method updateWith
94 * @param data {object} 需要合并的值
95 * @param merge {function} 自定义合并规则方法
96 * @param modelName {string} model名字,默认是绑定model之后的modelname
97 * @return Function
98 * @example
99 $Control(TestModel)
100 class TestControl {
101 updateTest(data,dispatch,modelJson,model){
102 return
103 fetch('/test').then((data)=>{
104 this.updateWith({
105 names:['test','test1','test2']
106 },function merger(prev,next){
107 if( Immutable.List.isList(prev) && Immutable.List.isList(next)){
108 return next
109 }
110 if(prev && prev.mergeWith){
111 return prev.mergeWith(merger,next)
112 }
113 return next
114 })
115 })
116
117 }
118 }
119 */
120 updateWith: function updateWith(data, merge) {
121 var modelName = arguments.length <= 2 || arguments[2] === undefined ? this.__modelName : arguments[2];
122
123 if (data && data.modelName) {
124 modelName = data.modelName.toLowerCase();
125 data = data.data;
126 }
127 return this.dispatch({
128 type: this.getModelName('updateWith', true, modelName), //`${DEFAULT}${DEFAULT_METHOD_FIX}${modelName}${DEFAULT_METHOD_FIX}update`,
129 merge: merge || null,
130 data: data
131 });
132 },
133 /**
134 * 插入store中某条数据
135 * @method insert
136 * @param path {string} 需要被删除的属性地址,根据具体的对象结构,例如一个结构为:var data={name:'test',other:{age:18}}的对象,如果想要在data中新增一些字段应该这样:this.insert({sex:'男'})
137 * @param data {string | object} 需要保存的值,新的值会覆盖之前的值
138 * @param isImmutable {boolean} 是否将值转换为Immutable类型,默认为false,如果更新的值为object类型建议设置为true
139 * @param modelName {string} model名字,默认是绑定model之后的modelname
140 * @return Function
141 * @example
142 $Control(TestModel)
143 class TestControl {
144 insertTest(data,dispatch,modelJson,model){
145
146 fetch('/test').then((data)=>{
147 this.insert({
148 sex:'男'
149 })
150 })
151
152 }
153 }
154 */
155 insert: function insert(path, data) {
156 var isImmutable = arguments.length <= 2 || arguments[2] === undefined ? false : arguments[2];
157 var modelName = arguments.length <= 3 || arguments[3] === undefined ? this.__modelName : arguments[3];
158
159 if (arguments.length == 1) {
160 data = arguments[0];
161 path = '';
162 } else {
163 path = path.indexOf('.') >= 0 ? path.split('.') : Array.prototype.concat.call([], path);
164 }
165
166 return this.dispatch({
167 type: this.getModelName('update', true, modelName), //`${DEFAULT}${DEFAULT_METHOD_FIX}${modelName}${DEFAULT_METHOD_FIX}update`,
168 path: path,
169 data: data,
170 isImmutable: isImmutable
171 });
172 },
173 /**
174 * 保存store中某条数据
175 * @method save
176 * @param path {string} 跟update一样
177 * @param data {string | object} 需要保存的值,新的值会覆盖之前的值
178 * @param isImmutable {boolean} 是否将值转换为Immutable类型,默认为false,如果更新的值为object类型建议设置为true
179 * @param modelName {string} model名字,默认是绑定model之后的modelname
180 * @return Function
181 * @example
182 $Control(TestModel)
183 class TestControl {
184 saveTest(data,dispatch,modelJson,model){
185
186 fetch('/test').then((data)=>{
187 this.save('age',data.age)
188 })
189
190 }
191 }
192 */
193 save: function save(path, data) {
194 var isImmutable = arguments.length <= 2 || arguments[2] === undefined ? false : arguments[2];
195 var modelName = arguments.length <= 3 || arguments[3] === undefined ? this.__modelName : arguments[3];
196
197 return this.dispatch({
198 type: this.getModelName('save', true, modelName), //`${DEFAULT}${DEFAULT_METHOD_FIX}${modelName}${DEFAULT_METHOD_FIX}save`,
199 path: path.indexOf('.') >= 0 ? path.split('.') : Array.prototype.concat.call([], path),
200 data: data,
201 isImmutable: isImmutable
202 });
203 }
204};
205
206//任意类型参数
207/**
208 * 异步操作,<strong style="color:red">IE9以下不建议使用</strong>,Sync是一个装饰器(Decorator),用于装饰Control类中的方法,将原有的方法变成一个异步成功调用后执行结果方法,被装饰的方法需要返回数据或false,决定是否更新store刷新节点。
209 * - 由Sync装饰后的方法,其作用域为Control,依然可以调用类中其他方法
210 * - Sync参数error可以为Control中xxxError命名的方法替代,“xxx”命名规则必须与Sync装饰的方法名一致
211 * - 被装饰后的方法在View中调用时传入的参数将已第二个为准,第一个参数将永远是异步执行后的结果
212 * - 被装饰的方法名要和Model类中方法名对应
213 * @method Sync
214 * @param anywhere {object|string} 参数为一个字符串时,anywhere为url,当方法拥有2个参数,第一个参数作为url,第二个参数为object类型
215 * @param anywhere.dataType {string} 数据返回类型 默认为json
216 * @param anywhere.asyn {boolean} 是否为异步请求,默认为true
217 * @param anywhere.method {string} 数据请求方式,默认为GET,可选值有:POST、GET、OPTION、DEL、PUT
218 * @param anywhere.timeout {number} 请求超时时间,可选填
219 * @param anywhere.credentials {object} 跨域是是否要包含cookie值,可选值:include
220 * @param anywhere.error {function} 请求失败回调,可选
221 * @param anywhere.header {object} 包含的请求头,可选
222 * @param anywhere.body {object} 需要传递给服务端的属性字段值,可选
223 * @param anywhere.cache {boolean} 请求数据是否缓存
224 * @return function
225 * @example
226 * import {Sync,Control} from 'gfs-react-mvc'
227 *
228 * class TestControl{
229 * constructor(){}
230 * //这里由于@为文档关键符号,所以下面将以$代替
231 * $Sync('/test',{
232 * method:'get'
233 * })
234 * save(data){
235 * //此处data是异步请求后服务器返回的结果
236 * if(data){
237 * //返回数据更新页面节点信息
238 * return data
239 * }
240 * //不做任何改变
241 * return false
242 * }
243 * $Sync('/del',{
244 * method:'get'
245 * })
246 * del(data){
247 * //此处data是异步请求后服务器返回的结果
248 * if(data){
249 * //返回数据更新页面节点信息
250 * return {
251 * //手动指定model对应方法
252 * type:this.getModelName('del'),
253 * data:data
254 * }
255 * }
256 * //不做任何改变
257 * return false
258 * }
259 * //也可直接使用
260 * $Sync()
261 * update(data){
262 * //此处data是异步请求后服务器返回的结果
263 * if(data){
264 * //返回数据更新页面节点信息
265 * return {
266 * //手动指定model对应方法
267 * type:this.getModelName('update'),
268 * data:data
269 * }
270 * }
271 * //不做任何改变
272 * return false
273 * }
274 * }
275 * */
276
277function Sync(anywhere) {
278
279 var url = '';
280 var opts = {};
281 var error = null;
282 //修正参数
283 if (typeof anywhere === 'string') {
284 url = anywhere;
285 } else if (typeof anywhere === 'object') {
286 url = anywhere.url;
287 opts = anywhere;
288 }
289
290 if (arguments.length >= 2) {
291 opts = arguments[1];
292 }
293 //todo error作用域无法指向target,target对象丢失,强制制定为undefined,解决作用域丢失问题
294 if (opts.error) {
295 error = opts.error;
296 }
297
298 return function (target, name, descriptor) {
299
300 var fn = (function () {
301 var success = arguments.length <= 0 || arguments[0] === undefined ? function () {} : arguments[0];
302
303 var fn = success;
304
305 return function () {
306
307 var args = Array.prototype.slice.call(arguments);
308 var methodArg = args[args.length - 1] || {};
309
310 if (typeof methodArg === 'object' && methodArg.url) {
311 url = methodArg.url;
312 }
313
314 return function (dispatch) {
315 if (opts && typeof opts.method == 'undefined') {
316 opts.method = 'get';
317 }
318 if (url) {
319 fetch(url, _extend2['default'](opts, methodArg.url || methodArg.body ? methodArg : {})).then(function () {
320 var result = fn.apply(target, Array.prototype.slice.call(arguments).concat(args));
321 if (result instanceof Function) {
322 result(dispatch);
323 } else if (result && typeof result === 'object') {
324 dispatch(_extend2['default'](result || {}, {
325 type: '' + target.__modelName + _model.DEFAULT_METHOD_FIX + (result.type ? result.type : name),
326 data: result.data ? result.data : {}
327 }));
328 }
329 }, error || target[name + 'Error']);
330 } else {
331 var result = fn.apply(target, args);
332 if (result && typeof result === 'object') {
333 dispatch(_extend2['default'](result || {}, {
334 type: '' + target.__modelName + _model.DEFAULT_METHOD_FIX + (result.type ? result.type : name),
335 data: result.data ? result.data : {}
336 }));
337 }
338 }
339 };
340 };
341 })(descriptor.value);
342
343 descriptor.value = fn;
344 descriptor.enumerable = true;
345 return descriptor;
346 };
347}
348
349/**
350 * 此方法是一个装饰器,只能用于类,被装饰后的类会变成对象列表(JSON)格式,主要目的是传递给组件使用,通过redux connect连接。
351 * 被装饰的类将成为一个控制器,处理异步数据逻辑或业务逻辑,将数据传递给视图或服务器
352 * @method Control
353 * @param modelName {object} 实体类对象
354 * @param loadingbar {Loadingbar} 废弃
355 * @param mock {Mock} 废弃
356 * @return object
357 * @example
358 * import {Sync,Control} from 'gfs-react-mvc'
359 * import TestModel from '../model/TestModel'
360 * //这里由于@为文档关键符号,所以下面将以$代替
361 * //@Control(TestModel)
362 * class TestControl{
363 * constructor(){}
364 * $action
365 * changeAge(){
366 *
367 *
368 * fetch('/test.json'[,params]).then((data)=>{
369 * //control中默认提供update、del、insert、save四种操作数据方法,会根据不同的control名生成,如下根据testControl生成的方法testControlUpdate
370 *
371 * dispatch(this.testControlUpdate('age','ajax改变的age:'+data.age) )
372 * })
373 *
374 * }
375 * //不建议使用下列方式
376 * //这里由于@为文档关键符号,所以下面将以$代替
377 * $Sync('/test',{
378 * method:'get'
379 * })
380 * $action
381 * save(data){
382 * //此处data是异步请求后服务器返回的结果
383 * if(data){
384 * //返回数据更新页面节点信息
385 * return data
386 * }
387 * //不做任何改变
388 * return false
389 * }
390 * }
391 * */
392
393function Control(model, loadingbar, mock) {
394 if (model === undefined) model = {};
395
396 if (arguments.length === 2) {
397 _gfsReactTools2['default'].addLoadingBar(loadingbar);
398 }
399
400 if (arguments.length === 3) {
401 _gfsReactTools2['default'].addMock(mock);
402 }
403
404 return function (target) {
405 //target = extend(target,curl)
406 //let name = ''//target.name||''
407 // let control = new target()
408 //循环遍历方法,将返回
409 //将方法的作用域改成对象本身
410 //postmodel.toJS()
411 var t = new target();
412 var p = {};
413 for (var item in t) {
414 (function (item) {
415 var fnName = item;
416 p[fnName] = (function () {
417 var args = Array.prototype.slice.call(arguments);
418 var _this = this;
419 return function (dispatch, store) {
420 var modelJson = store()[model.modelName];
421 _this.dispatch = dispatch;
422 return t[fnName].apply(t, args.concat([dispatch, modelJson && typeof modelJson.toJS != 'undefined' ? modelJson.toJS() : null, modelJson || null]));
423 };
424 }).bind(t);
425 })(item);
426 // p[item] = t[item] instanceof Function ? t[item].bind(t ) : t[item]
427 }
428 model.controls = p;
429 controlList[model.modelName] = model;
430
431 for (var cItem in curl) {
432 target.prototype[cItem] = curl[cItem];
433 }
434 target.prototype.__modelName = target.__modelName = model.modelName;
435 /**
436 * 获取model方法名全名,在未传任何值时返回方法前缀
437 * @method getModelName
438 * @param actionName {string} default='',方法名,可选
439 * @param isDefault {boolean} 是否获取系统中提供的方法名,默认false,可选
440 * @param modelName {string} model名字,可选
441 * @return string
442 */
443 target.prototype.getModelName = target.getModelName = function () {
444 var actionName = arguments.length <= 0 || arguments[0] === undefined ? '' : arguments[0];
445 var isDefault = arguments.length <= 1 || arguments[1] === undefined ? false : arguments[1];
446 var modelName = arguments.length <= 2 || arguments[2] === undefined ? target.__modelName : arguments[2];
447
448 return '' + (isDefault ? _model.DEFAULT + _model.DEFAULT_METHOD_FIX : '') + modelName + _model.DEFAULT_METHOD_FIX + actionName;
449 };
450 return model;
451 //todo 解决对象私有属性访问,同样是对象丢失造成
452 //return {...target.prototype}
453 };
454}
455
456function getControl(modelName) {
457
458 return controlList[modelName.toLowerCase()];
459}
\No newline at end of file