1 /**
  2  *
  3  * 流程UI
  4  *
  5  * @fileOverView 业务流基础组件
  6  * @author  <a href="mailto:zhang.gd@foxmail.com">Zhang Guangda</a>
  7  * @date    2012-10-25
  8  */
  9 define([
 10 		"core",
 11 		"component/login",
 12 		"component/dialog",
 13 		"component/process_components",
 14 		"component/process_container"
 15 	], function() {
 16 
 17 	/**
 18 	 * 业务流组件核心的html结构
 19 	 * @type {String}
 20 	 */
 21 	var _html = '<div attr="inner:progress"></div><div attr="inner:business"></div>',
 22 		_exceptionHtml = '<p class="we_cell_exception" attr="exception" style="display:none;"></p>',
 23 		_exceptionText = '系统异常,请稍后再试',
 24 		_exceptionTime = 1000,
 25 		_dialogTitle = '业务流程',
 26 		// _buttonPrev = '上一步',
 27 		_buttonPrev = false, // 暂时默认不开放上一步按钮
 28 		_buttonNext = '下一步',
 29 		_buttonEnd = '完成',
 30 		_dialogWidth = 600,
 31 		_content_class = 'we_pop_content_box',
 32 		_defaultProcessName = '业务流程',
 33 		_bizStyle = 'dialog',
 34 		bInUse = false; // 是否在使用中
 35 
 36 	var _isProcessPassed = function(cur, params) {
 37 		return this.processes[cur]
 38 			&& typeof this.processes[cur].beforeAction == "function"
 39 			&& !this.processes[cur].beforeAction.call(this.processQueue[cur], params);
 40 	};
 41 
 42 	/**
 43 	 * 业务流对话框组件
 44 	 * @lends $we.widget.process.ui
 45 	 */
 46 	$we.widget.reg("process.ui", {
 47 		/**
 48 		 * interfaces
 49 		 * @memberOf $we.widget.process.ui#
 50 		 */
 51 		interfaces: {
 52 			/**
 53 			 * 流程启动
 54 			 * @memberOf $we.widget.process.ui#
 55 			 */
 56 			start: function() {
 57 				if(!this._inited || bInUse) return;
 58 
 59 				/**
 60 				 * 设置是否独立流程
 61 				 */
 62 				if(typeof this.config.independent != "undefined") $we.process.setData("independent", this.config.independent);
 63 
 64 				// 进度条展示
 65 				this.progressBar.render();
 66 
 67 				// 第一个流程不存在上一步,所以设置一下button
 68 				this.setButton({
 69 					prev: false,
 70 					next: this.config.next
 71 				});
 72 				// 将第一个可以启动的流程启动
 73 				if (_isProcessPassed.call(this, this.cur)) this.notifies.goNext();
 74 				else this.processQueue[this.cur].process.start();
 75 
 76 				// 如果正在关闭中,则停止调用
 77 				if (this.bClosing) return;
 78 
 79 				// 打开对话框
 80 				this.open();
 81 
 82 				bInUse = true;
 83 			},
 84 			/**
 85 			 * 去往下一个流程
 86 			 * @memberOf $we.widget.process.ui#
 87 			 */
 88 			next: function() {
 89 				if(this.cur > this.total - 1 || this.nexting) return;
 90 				// 标识为正在前往下一个,将在回调中设回false
 91 				this.nexting = true;
 92 				// 调用当前业务流组件中的checkSucc,这里面处理具体是否可以往下个流程走的逻辑
 93 				if(!this.processQueue[this.cur].process.checkSucc() && this.nexting) this.notifies.pause();
 94 
 95 			},
 96 			/**
 97 			 * 去往上一个流程
 98 			 * @memberOf $we.widget.process.ui#
 99 			 */
100 			prev: function() {
101 				if (this.bClosing) return;
102 				if(this.cur == 0) return;
103 
104 				if(this.processQueue[this.cur].process.end)
105 					this.processQueue[this.cur].process.end();
106 
107 				if (this.processes[this.cur].hidden) {
108 					while (this.cur > 0) {
109 						--this.cur;
110 						if (_isProcessPassed.call(this, this.cur)) {
111 							continue;
112 						}
113 						break;
114 					}
115 				} else {
116 					while (this.cur > 0) {
117 						--this.cur;
118 						if (this.processes[this.cur].hidden || _isProcessPassed.call(this, this.cur)) {
119 							continue;
120 						}
121 						break;
122 					}
123 				}
124 
125 				// 重置按钮
126 				this.setButton({
127 					prev: this.config.prev,
128 					next: this.config.next,
129 					prevGray: this.config.prevGray,
130 					nextGray: this.config.nextGray
131 				});
132 
133 				// 如果上一个是最开始的,去掉prev
134 				if(this.cur == 0) this.setButton({
135 					prev: false
136 				});
137 
138 				if (this.cur === 0 && _isProcessPassed.call(this, this.cur)) {
139 					this.close();
140 					return;
141 				} else {
142 					this.processQueue[this.cur].process.start();
143 				}
144 					
145 				// 设置进度栏
146 				this.progressBar.setCur(this.barQueue[this.cur]);
147 
148 				this.position();
149 			},
150 			/**
151 			 * 设置按钮
152 			 * @memberOf $we.widget.process.ui#
153 			 * @param {object} config 配置 {<br />
154 			 * 	prev: {string} 按钮的名称, false 表示隐藏,<br />
155 			 * 	next: {string} 按钮的名称, false 表示隐藏<br />
156 			 * }
157 			 */
158 			setButton: function(config) {
159 				var btn = $(this.node.notice_button),
160 					p = btn.find("a:first"),
161 					c = btn.find("a:eq(1)"),
162 					n = btn.find("a:last");
163 
164 				if(config.prev === false) p.hide();
165 
166 				if(config.next === false) n.hide();
167 
168 				if(config.cancel) c.show();
169 				else c.hide();
170 
171 				if(config.prev) {
172 					p.html(config.prev);
173 					p.show();
174 				}
175 
176 				if(config.next) {
177 					n.html(config.next);
178 					n.show();
179 				}
180 
181 				if(config.prevGray) p.removeClass("we_button").addClass("we_button_gray");
182 				else if(config.prevGray === false) p.removeClass("we_button_gray").addClass("we_button");
183 
184 				if(config.nextGray) n.removeClass("we_button").addClass("we_button_gray");
185 				else if(config.nextGray === false) n.removeClass("we_button_gray").addClass("we_button");
186 			},
187 			/**
188 			 * 显示异常信息
189 			 * @memberOf $we.widget.process.ui#
190 			 * @param  {String} text 异常文案
191 			 * @param  {Integer} time 显示的时间
192 			 */
193 			showException: function(text, time) {
194 				text = $we.utils.setValue(text, _exceptionText);
195 				time = $we.utils.setValue(time, _exceptionTime);
196 				var exp = $(this.node.exception);
197 				exp.html(text);
198 				exp.slideDown();
199 				setTimeout(function() {
200 					exp.slideUp();
201 				}, time);
202 			},
203 			/**
204 			 * 关闭按钮
205 			 * @memberOf $we.widget.process.ui#
206 			 */
207 			close: function() {
208 				this.bClosing = true;
209 				try {
210 					this.processQueue[this.cur].process.end();
211 				} catch(e) {}
212 				$we.process.clearData();
213 				this.remove();
214 				bInUse = false;
215 			},
216 			/**
217 			 * 重新定位
218 			 * @memberOf $we.widget.process.ui#
219 			 */
220 			position: function() {
221 				var me = this;
222 				$we.dom.imageLoaded(this.node.root, function() {
223 					me._super.position();
224 				})
225 			}
226 		},
227 		/**
228 		 * notifies
229 		 * @memberOf $we.widget.process.ui-
230 		 */
231 		notifies: {
232 			/**
233 			 * 将interfaces的next暴露给下属组件
234 			 * @memberOf $we.widget.process.ui-
235 			 */
236 			next: function() {
237 				this.next();
238 			},
239 			/**
240 			 * 被通知前往下一个
241 			 * @memberOf $we.widget.process.ui-
242 			 * @param  {mixed} params 参数
243 			 */
244 			goNext: function(params) {
245 
246 				if (this.bClosing) return;
247 
248 				this.renderBottom("");
249 
250 				this.processes[this.cur] && typeof this.processes[this.cur].afterAction == "function" && this.processes[this.cur].afterAction.call(this, params);
251 
252 				// 如果是最后一个,直接关闭
253 				if(this.cur >= this.total - 1) {
254 					this.close();
255 					return;
256 				}
257 
258 				if(this.processQueue[this.cur].process.end) {
259 					try {
260 						this.processQueue[this.cur].process.end();	
261 					} catch (e) {}
262 				}
263 
264 				while (this.cur++ < this.total) {
265 					if (_isProcessPassed.call(this, this.cur, params)) continue;
266 					break;
267 				}
268 
269 				// 重置按钮
270 				this.setButton({
271 					prev: this.config.prev,
272 					next: this.config.next,
273 					prevGray: this.config.prevGray,
274 					nextGray: this.config.nextGray
275 				});
276 
277 				// 如果当前是最后一个,将下一步的按钮调整为结束的文字
278 				if(this.cur == this.total - 1) this.setButton({
279 					next: this.config.end
280 				});
281 
282 				if (this.cur === this.total) {
283 					this.close();
284 					return;
285 				} else {
286 					this.processQueue[this.cur].process.start(params);
287 				}
288 
289 				// 设置进度条
290 				this.progressBar.setCur(this.barQueue[this.cur]);
291 
292 
293 				this.position();
294 
295 				// 重置nexting为false
296 				this.nexting = false;
297 			},
298 			/**
299 			 * 被通知前往上一个
300 			 * @function
301 			 * @memberOf $we.widget.process.ui-
302 			 */
303 			goPrev: function() {
304 				this.prev();
305 			},
306 			/**
307 			 * 设置按钮
308 			 * @function
309 			 * @memberOf $we.widget.process.ui-
310 			 */
311 			setButton: function() {
312 				this.setButton.apply(this, arguments);
313 			},
314 			/**
315 			 * 被通知暂不走向下一个流程
316 			 * @memberOf $we.widget.process.ui-
317 			 */
318 			pause: function() {
319 				// 重置nexting为false
320 				this.nexting = false;
321 			},
322 			/**
323 			 * 被通知关闭
324 			 * @memberOf $we.widget.process.ui-
325 			 */
326 			close: function() {
327 				// 重置nexting为false
328 				this.nexting = false;
329 				this.close();
330 			},
331 			/**
332 			 * 被通知退出流程
333 			 * @memberOf $we.widget.process.ui-
334 			 */
335 			abort: function() {
336 				this.nexting = false;
337 				this.close();
338 			},
339 			/**
340 			 * 被通知重新定位
341 			 * @memberOf $we.widget.process.ui-
342 			 */
343 			position: function() {
344 				this.position();
345 			},
346 			/**
347 			 * 被通知显示异常
348 			 * @memberOf $we.widget.process.ui-
349 			 * @param {String} text 异常文字
350 			 * @param {Integer} time 异常显示时间
351 			 */
352 			showException: function(text, time) {
353 				this.showException(text, time);
354 			},
355 			/**
356 			 * 将interfaces的next暴露给下属组件
357 			 * @memberOf $we.widget.process.ui-
358 			 * @param {String} params 渲染底部的参数
359 			 */
360 			renderBottom: function(params) {
361 				this.renderBottom(params);
362 				this.position();
363 			},
364 			/**
365 			 * 显示进度条
366 			 * @memberOf $we.widget.process.ui-
367 			 */
368 			showProgressBar: function() {
369 				this.progressBar.render();
370 			},
371 			/**
372 			 * 隐藏进度条
373 			 * @memberOf $we.widget.process.ui-
374 			 */
375 			hideProgressBar: function() {
376 				this.progressBar.hide();
377 			}
378 		},
379 		/**
380 		 * 初始化函数
381 		 * @constructs
382 		 * @param  {array} processes {
383 		 *	name: '流程名称',
384 		 *	widget: '组件名称',
385 		 *	params: []
386 		 * }
387 		 * @param  {object} config    配置
388 		 */
389 		init: function(processes, config) {
390 
391 			// 标识开始初始化
392 			this._inited = false;
393 			if(!$.isArray(processes)) return;
394 
395 			this.config = {};
396 			if (typeof config == "object") {
397 				for (var i in config) {
398 					this.config[i] = config[i];
399 				}
400 			}
401 
402 			this.config.independent = this.config.independent;
403 			this.config.bizStyle = $we.utils.setValue(this.config.bizStyle, _bizStyle);
404 			this.config.flow = $we.utils.setValue(this.config.flow, "");
405 			this.config.title = $we.utils.setValue(this.config.title, _dialogTitle);
406 			this.config.title = '<i class="we_title_icon"></i>'+this.config.title;
407 			this.config.prev = $we.utils.setValue(this.config.prev, _buttonPrev);
408 			this.config.next = $we.utils.setValue(this.config.next, _buttonNext);
409 			this.config.prevGray = $we.utils.setValue(this.config.prevGray, false);
410 			this.config.nextGray = $we.utils.setValue(this.config.nextGray, false);
411 			this.config.end = $we.utils.setValue(this.config.end, _buttonEnd);
412 			this.config.width = $we.utils.setValue(this.config.width, _dialogWidth);
413 			this.config.class_notice_content = $we.utils.setValue(this.config.class_notice_content, _content_class);
414 
415 			var me = this;
416 
417 			this.config.mask = true;
418 			this.config.close = true;
419 			this.config.notice_content = _html;
420 			this.config.notice_button_config = [{
421 				name: this.config.prev,
422 				click: this.prev
423 			}, {
424 				name: "取消",
425 				type: 'cancel',
426 				click: this.close
427 			}, {
428 				name: this.config.next,
429 				click: function(e) {
430 					me.next();
431 					// IE 6 下,a链接会触发 onbeforeunload事件,导致jsonp请求被打断。需要干掉它的事件
432 					if ($.browser.msie && $.browser.version < 8) {
433 						e.preventDefault();
434 						e.stopPropagation();
435 					}
436 					
437 				}
438 			}];
439 
440 			// 总的流程数
441 			this.total = processes.length;
442 
443 			// if (this.total == 1) {
444 			// 	++this.total;
445 			// 	processes.push({
446 			// 		name: "结束流程",
447 			// 		widget: "process.end"
448 			// 	});
449 			// }
450 			
451 			// 渲染对话框
452 			switch (this.config.bizStyle) {
453 				case "dialog":
454 				this.extend('NoticeDialog', [this.config]);
455 				break;
456 
457 				case "div":
458 				this.extend('process.container', [this.config.el, this.config]);
459 				break;
460 			}
461 			
462 
463 			var curBar,
464 				processNames = [],
465 				// 流程名称,array
466 				processQueue = [],
467 				barQueue = [],
468 				// 将流程的object储存在此,这个object是通过$we.process.set new 出来的object
469 				pNode = this.node.business; // 每个流程的父节点
470 			// 将每个流程分别处理
471 			$(processes).each(function(index) {
472 				if(!this.widget) return;
473 
474 				processQueue.push($we.widget.add({
475 					name: this.widget,
476 					notifyTo: me
477 				}, pNode, this.params));
478 
479 				// 如果没有声明名称,我们认为他是隐藏的
480 				if(!this.name) this.hidden = true;
481 
482 				if(!this.hidden) processNames.push(this.name || _defaultProcessName);
483 
484 				curBar = processNames.length - 1;
485 				if (curBar < 0) curBar = 0;
486 				barQueue.push(curBar);
487 			});
488 
489 			// 储存 processQueue 到this 中去
490 			this.processQueue = processQueue;
491 			this.processes = processes;
492 			this.barQueue = barQueue;
493 
494 			// 设置进度条,将processNames传入其中
495 			this.progressBar = $we.widget.add("process.progress", this.node.progress, processNames);
496 
497 			// 当前流程设置为0
498 			this.cur = 0;
499 
500 			// 添加一个exception 浮层
501 			this.before(this.node.notice_content, _exceptionHtml);
502 
503 			/**
504 			 * 设置流程总长度
505 			 */
506 			$we.process.setData("length", this.total);
507 
508 			/**
509 			 * 设置流程的id
510 			 */
511 			if (!$we.process.getData("id"))
512 				$we.process.setData("id", $we.conf.RUNTIME.__RANDOMSEED.toString() + (+new Date).toString());
513 			/**
514 			 * 设置流程名称
515 			 */
516 			if (!$we.process.getData("flow"))
517 				$we.process.setData("flow", this.config.flow);
518 
519 			// 初始化完成
520 			this._inited = true;
521 		}
522 	});
523 
524 	return $we.widget.amd("process.ui");
525 
526 });