1 /**
  2  * 明细表操作的相关方法
  3  * @namespace
  4  */
  5 Sucang.detail={
  6 xtemps:{},
  7 /**
  8  * @description 1,默认在最后插入一行<br>
  9  * 2,当CheckBox选中后,在最后选中项的前面插入一行.<br>
 10  * 3,如果指定Index则插入到index行的位置,**一次只能插入一行**
 11  * @param {formPrefix} String 子表单的前缀编号,见FormInfo.indexFlag
 12  * @param {index} Integer|Object|null //如果是数字型,则指行的顺序即可从0开始计数
 13  * @param {callback} Function|Object 添加明细行完成后的回调函数或者填充初始化值{name:$v1,name2:$v2,callback:function(rowIndex,id){ ... }} 或者 function(rowIndex,id){ ... }
 14  * @return {objTr} HTMLTrElement 返回新增加行的TR对象
 15  */
 16 Add:function(id,index,_callback){
 17 	var domlist = Ext.getDom(id+'List');
 18 	if(domlist.getAttribute('data-readonly')=='true') return null;
 19 	var xtemp = Sucang.detail.xtemps[id+'_XTemp'];
 20 	if(!xtemp){
 21 	   xtemp=Sucang.detail.xtemps[id+'_XTemp']=new Ext.XTemplate(window[id+'Temp']);
 22 	}
 23 	window[id+'RowNum']+=1;
 24 	var selRow=null;
 25 	var firstRow = false;
 26 	if(typeof(index)=='number'){
 27 		//var rows=Ext.query('input[type=checkbox][_rowindex=true]',id+'List');如果HTML有两行tr则下面的append无法添加成功
 28 		//为解决明细行重复部分会有多行,所以不能用tr的行数来判断,应该用checkbox的个数来判断
 29 		var rows=Ext.getDom(id+'List').rows;
 30 		if(index<0){
 31 			firstRow = true;
 32 			selRow=rows[0];
 33 		}else{ //如果大于显示的行数,则默认添加至最后
 34 			if(index<rows.length) selRow=rows[index+1];//取指定行号的下一行为插入的位置
 35 		}
 36 	}else if(!Ext.isEmpty(index) && index.tagName=='INPUT'){
 37 		selRow = Sucang.util.getParentObject(selRow, 'tr');
 38 	}else{
 39 		var chks=Ext.query("input[_rowindex=true]",domlist);
 40 		Ext.each(chks,function(obj,i){
 41 			if(obj.checked) selRow=obj;
 42 		});//获取最后一项选中的值.
 43 		if(selRow!=null) selRow = Sucang.util.getParentObject(selRow, 'tr');
 44 	} //end.if
 45 	var dataObj={};
 46 	if(typeof(_callback)=='object'){//将初始化数据传给模板模型参数变量
 47 		dataObj=_callback;
 48 		_callback=null;
 49 		if(typeof(dataObj.callback)=='function'){
 50 			_callback=dataObj.callback;
 51 			delete dataObj.callback;
 52 		}
 53 	}//这里初始值的填充由于文本值由模板确定即可,
 54 	//如果select和checkbox类型的选中是生成带<tpl if判断条件的Ext.XTemplate语句
 55 	var defaultVal = window[id + 'DefaultValue'];
 56 	if(defaultVal && typeof(defaultVal)=='object') $.extend(dataObj, defaultVal);
 57 	dataObj.index=window[id+'RowNum'];
 58 	var row=null;
 59 	if(!Ext.isEmpty(selRow)){
 60 		row=xtemp[firstRow?'insertBefore':'insertAfter'](selRow,dataObj,false);
 61 	}else{
 62 		row=xtemp.append(id+'List',dataObj,false);
 63 	}//end.if
 64 	window._row=row;
 65 	if(window[id+'WithScript']){
 66 		var js = xtemp.apply(dataObj).replace(/{/gi,'{').replace(/}/gi,'}');
 67 		Sucang.util.evalScripts(js);
 68 	}
 69 	if(window[id+'WithOrder']) this.reorder(id);
 70 	if(SDate)SDate.addDetailRow(row);
 71 	if(Sucang.formular) Sucang.formular.addDetailRow(row);//对动态增加的明细监听公式计算
 72 	if(Sucang.autocomplete) Sucang.autocomplete._init(row);//扫描是否有浏览框启用autoComplete功能
 73 	var oForm=YrwUtils.getParentObject(row,'form');
 74 	if(oForm!=null && typeof(Validation._valid[oForm.id])=='object')
 75 		Validation.addBlurEvent(row);
 76 	if(typeof(_callback)=='function') _callback(dataObj.index,id);
 77 	return row;
 78 },
 79 /**
 80  * @description 根据子表id删除当前选中的行
 81  * @param {formPrefix} String 子表单的前缀编号,见FormInfo.indexFlag
 82  * @return {rowindexs} Array 返回值为删除了的行索引号
 83  */
 84 Del:function(id){
 85 	var domlist = Ext.getDom(id+'List');
 86 	if(domlist.getAttribute('data-readonly')=='true') return null;
 87 	//var tags=document.getElementsByName(id+'RowIndex');
 88 	var tags=Ext.query("input[_rowindex=true]", domlist);
 89 	var ids=[];
 90 	//alert(id+'deletes');
 91 	var delids=DWRUtil.getValue(id+'deletes');
 92 	for(var i=0,j=0;i<tags.length;i++){
 93 		var tag=tags[i];
 94 		if(!tag.name.startsWith(id+'RowIndex'))continue;
 95 		if(tag.tagName.toLowerCase()=='input' && tag.checked){
 96 			ids[j++]=tag;
 97 			if(tag.value.startsWith("_"))delids+=","+tag.value.substring(1);//保存准备删除的字段.
 98 		}
 99 	}//end for.
100 	if(delids.startsWith(",")) delids=delids.substring(1);
101 	DWRUtil.setValue(id+'deletes',delids);
102 	var hasDel = ids.length>0;
103 	var arRowindex=[];
104 	for(var i=0;i<ids.length;i++){
105 		var tag = ids[i];
106 		var pos = tag.id.indexOf('RowIndex_')+9;
107 		arRowindex[i] = parseInt(tag.id.substring(pos,tag.id.length-1));
108 		var pTr = Sucang.util.getParentObject(tag,'tr');
109 		if(!pTr){
110 			console.log('Sucang.detail.DeoRow:tag.id' + tag.id + '选择标识符必须在tr标签内.');
111 			continue;
112 		}
113 		var row=new Ext.Element(pTr);
114 		if(SDate)SDate.delDetailRow(row.dom);
115 		if(Sucang.formular) Sucang.formular.delDetailRow(row.dom);
116 		row.remove();
117 	}
118 	if(hasDel && Sucang.formular) Sucang.formular.reCal();//删除后重新计算
119 	if(window[id+'WithOrder']) this.reorder(id);
120 	return arRowindex;
121 },
122 /**
123  * @description 选中所有明细行,如Sucang.detail.selectAll('F34',this);
124  * @param {formPrefix} String 子表单的前缀编号,见FormInfo.indexFlag
125  * @param {obj} boolean|Object 当为true/false时表示全选中或全不选,object时指向input.checked的值
126  */
127 selectAll:function(prefix,obj){
128 	var chks=Ext.query("input[_rowindex=true]",Ext.getDom(prefix+'List'));
129 	prefix=Ext.isEmpty(prefix)?'RowIndex':prefix+'RowIndex';
130 	var bl=true;
131 	if(typeof(obj)=='boolean') bl=obj;
132 	else bl=Ext.isEmpty(obj)?true:obj.checked;
133 	Ext.each(chks,function(chk,i){
134 		if(chk.name.indexOf(prefix)>=0)chk.checked=bl;
135 	});
136 },
137 /**
138  * @description 清除子表单明细行的唯一标识符ID,用于提交时会重新保存数据,相当于复制了当前所有明细行数据
139  * @param {formPrefix} String 子表单的前缀编号,见FormInfo.indexFlag
140  */
141 clearRowId:function(prefixId){
142 	var ids=[];
143 	var chks=Ext.query("input[_rowindex=true]",Ext.getDom(prefixId+'List'));
144 	for(var i=0;i<chks.length;i++){
145 		var val=chks[i].value;
146 		if(val.charAt(0)!='_') continue;
147 		var id=chks[i].name;
148 		id=id.substring(id.indexOf('_')+1,id.length-1);
149 		chks[i].value=id;
150 		//alert(chks[i].value);
151 	}
152 	return true;
153 },
154 /**
155  * @description 返回明细行的行号
156  * @param {formPrefix} String F+表单的前缀编号,见FormInfo.indexFlag,如:F103,F18
157  * @param {blSelected} boolean 可以为空,true或false,空时表示获取所有行号,true表示只获取选中,false获取未选中的行号。version>2.201有该选项可用
158  * @return {ids} Array<Number>
159  */
160 getRowIndexs:function(prefixId, blSelected){
161 	var ids=[];
162 	var chks=Ext.query("input[_rowindex=true]",Ext.getDom(prefixId+'List'));
163 	for(var i=0;i<chks.length;i++){
164 		var id=chks[i].name;
165 		if(typeof(blSelected)=='boolean'){
166 			if(blSelected===true && !chks[i].checked) continue;//表示获取选中时,未选中的则跳过
167 			if(blSelected===false && chks[i].checked) continue;//表示获取未选中时,选中的复选框则跳过
168 		}
169 		id=id.substring(id.indexOf('_')+1,id.length-1);
170 		ids.push(parseInt(id,10));
171 	}
172 	//alert(Ext.encode(ids));
173 	return ids;
174 },
175 /**
176  * @description 返回明细行的ID值的排列顺序,以便下次编辑是保持顺序。如果为数字表示新增行
177  * @param {formPrefix} String F+表单的前缀编号,见FormInfo.indexFlag,如:F103,F18
178  * @return {ids} Array<String> 明细表行字段按数组顺序排列,数组中保存的是行号
179  */
180 getRowIds:function(prefixId){
181 	var ids=[];
182 	var chks=Ext.query("input[_rowindex=true]",Ext.getDom(prefixId+'List'));
183 	for(var i=0;i<chks.length;i++){
184 		var id=chks[i].value;
185 		if(id.charAt(0)=='_') id=id.substring(1);
186 		ids.push(id.length==32?id:parseInt(id,10));
187 	}
188 	//alert(Ext.encode(ids));
189 	return ids;
190 },
191 /**
192  * @description 根据行顺序号获取行的索引号,正常情况下是相等的,当行有中间增加或删除时,行顺序和行索引号就不对应.
193  * @param {prefix} String 明细表单的前缀
194  * @param {order} Number 行的顺序号
195  * @return {rowIndex} Number 如果顺序号超出显示明细的行数,则返回-1
196  */
197 getRowIndexByOrder:function(prefix,order){
198 	var ret = -1;
199 	if(typeof(order) != 'number' || order<0){
200 		console.log('getRowIndexByOrder:参数order只能为数字且必须大于0');
201 		return ret;
202 	}
203 	var dom = Ext.getDom(prefix+'List');
204 	if(!dom){
205 		console.log('getRowIndexByOrder:前缀为'+prefix+'的明细表不存在.');
206 		return ret;
207 	}
208 	var rows = dom.rows;
209 	if(rows.length<=order) return -1;
210 	var ar = Ext.query('input[_rowindex=true]', rows[order]);
211 	if(ar.length > 0){
212 		var id = ar[0].id;
213 		id = id.substring(id.indexOf('_') + 1, id.length - 1);
214 		ret = parseInt(id,10);
215 	}
216 	return ret;
217 },
218 /**
219  * @description 有明细表数据时提交前的检查和准备工作
220  * @private
221  */
222 checkSubmit:function(){
223 	var oTables=Ext.query("tbody[class*=detailTable]");
224 	for(var i=0;i<oTables.length;i++){
225 		var chks=Ext.query("input[_rowindex=true]",oTables[i]);
226 		if(chks.length<=0) continue;
227 		var name1=chks[0].name;
228 		var orderObject=new Object();
229 		var prefix=name1.substring(0,name1.indexOf('RowIndex'));//获取F17RowIndex_2中的F17前缀
230 		var startIndex=0;
231 		var ar0=Ext.query('div[class=pageNav]',oTables[i].parentNode);//如果有分页则带上分页后的排序索引号
232 		if(ar0.length>0){
233 			var info=ar0[0].getAttribute('info');
234 			if(info!=null) startIndex=Ext.decode(info).startRecordNum;
235 		}
236 		Ext.each(chks,function(chk,i){
237 			chk.checked=true;
238 			var hideChk=Ext.get(chk.id+'chkVal');
239 			if(hideChk) hideChk.remove();//提交的时候需要对隐藏复选框域进行删除,否则会有重复字段名提交
240 			orderObject[chk.value]=(startIndex+i);//chk.parentNode.parentNode.rowIndex获取复选框当前所在TR的行号
241 		});//提交明细表前需要将所有Check都选中
242 		Sucang.setValue(prefix+'orderids',Ext.encode(orderObject));
243 	}//end.for
244 },
245 /**
246  * @description 分解明细表字段的名称
247  * @param {fieldName} String|Object 明细表中的字段名或明细表中的Browser按钮对象,名称一般为:F45_2_name1
248  * @return {obj} Object {prefix:$前缀,rowindex:$该字段所在行号,name:$字段名}
249  * @example Sucang.detail.getName('F45_2_name1');返回值为{prefix:'F45',rowindex:2,name:'name1'}
250  */
251 getName:function(obj){
252 	if(typeof(obj)=='object' && obj.id && obj.id.endsWith('btn')) obj=obj.id.substring(0,obj.id.length-3);
253     var name=(typeof(obj)=='string')?obj:obj.name;
254     var prefix=name.substring(0,name.indexOf('_'));
255     name=name.substring(name.indexOf('_')+1);
256     var rowindex=name.substring(0,name.indexOf('_'));
257     name=name.substring(name.indexOf('_')+1);
258     //fields_${loopIndex}_defaultValue
259     obj={prefix:prefix,rowindex:parseInt(rowindex,10),name:name};
260 	//alert('names:'+Ext.encode(obj));
261     return obj;
262 },
263 /**
264  * @description 返回$fieldName列的数据以数组类型
265  * @param {fieldName} String|Object 明细表中的字段名或Input输入框的对象
266  * @param {isNumber} boolean 表示是否将数据转换为数字型数组返回,可省略。默认为字符串
267  * @return {vals} Array<String>
268  * @example Sucang.page.getColumnValue('F45_2_name1');将返回name1字段列的所有数据 
269  */
270 getColumnValue:function(obj,isNumber){
271 	var name=null;
272 	if(typeof(obj)=='object') name=obj.name;
273 	else{
274 		name=obj;
275 		//obj=Ext.getDom(name);
276 	}
277 	var ar0=[];
278 	//if(obj==null) return ar0;//可以不存在的对象,只要格式符号命名规范
279 	var names=this.getName(name);
280 	var data=Ext.query('input[name$='+names.name+'],textarea[name$='+names.name+'],select[name$='+names.name+']', Ext.getDom(names.prefix+'List'));
281 	var reg=new RegExp(names.prefix+'_\\d+_'+names.name);//这里不能加;ig参数,否则下面不能多次使用.test方法测试
282 	for(var i=0,j=data.length;i<j;i++){
283 		if(!reg.test(data[i].name)) continue;//表示主表和子表有重名
284 		var val=data[i].value;
285 		if(isNumber){
286 			var n=parseFloat(val);
287 			ar0.push(isNaN(n)?0:n);
288 		}else ar0.push(val);
289 	}
290 	return ar0;
291 },
292 /**
293  * @description 返回$rowIndex行的数据以键值对类型的对象
294  * @param {fieldName} string|Object 明细表中的字段名或Input输入框的对象
295  * @param {withText} Boolean 表示是否返回带文本的用于行数据复制用
296  * @return {vals} Object
297  * @example Sucang.page.getRecordValue('F45_2_name1');将返回第2行的所有字段的数据 
298  */
299 getRecordValue:function(obj,withText){//返回$obj的行记录数据以对象map类型
300 	var name=null;
301 	if(typeof(obj)=='object') name=obj.name;
302 	else{
303 		name=obj;
304 		obj=Ext.getDom(name);
305 	} 
306 	var names=this.getName(name);
307 	var sel='[name^='+names.prefix+"_"+names.rowindex+"_"+']';
308 	var data=Ext.query('input'+sel+',textarea'+sel+',select'+sel,Ext.getDom(names.prefix+'List'));
309 	var obj={};
310 	var plen=names.prefix.length+names.rowindex.toString().length+2;
311 	for(var i=0,j=data.length;i<j;i++){
312 		var val=data[i].value;
313 		var fieldName = data[i].name.substring(plen);
314 		if(data[i].tagName.toLowerCase()=='input'){
315 			if(data[i].type=='checkbox') val=data[i].checked?val:0;
316 			else if(data[i].type=='hidden' && withText){
317 				var dom = Ext.getDom(fieldName+'span');
318 				if(dom) obj[fieldName+'span'] = dom.innerHTML;
319 			}
320 		}
321 		obj[fieldName] = val;
322 	}
323 	return obj;
324 },
325 /**
326  * @description 设置行数据为只读,执行该操作后无法还原为编辑状态。
327  * @param {prefix} String //明细表前缀
328  * @param {rowindex} Number //行号,可能因为有删除行,行号不总是连续的。需要使用getRowIndexs(prefix)方法获取
329  * @param {noSaveForm} boolean //表示当前行数据不保存到服务器仅显示。
330  * 		值为true时会删除表单控件但不会影响公式的计算。如果参数为1则删除控件对象会影响公式计算。
331  * @param {renderData} Object //用于明细文本只读后,需要显示格式化的数据模板.例:{'字段名':'format ${value}后的值.',..}
332  * @return 执行成功返回true
333  */
334 setRowReadonly:function(prefix, rowindex,noSaveForm,renderData){
335 	var row=Ext.getDom(prefix+'RowIndex_'+rowindex+'_');
336 	if(row){
337 		var obj=row.parentNode;
338 		if(noSaveForm) obj.removeChild(row);//如果是只读且不保存到服务器的则删除行号标识符
339 		row=obj.parentNode;//获取父标签Tr的Dom对象
340 	}else{ console.log('明细表prefix='+prefix+',的'+rowindex+'行对象为空');return false;}
341 	if(row.getAttribute('data-readonly')=='true') return true;//表示已经设置过
342 	var sel='input[type=text]';
343 	sel+=',textarea[name^='+prefix+'_'+rowindex+'_]';
344 	sel+=',input[class*=bbox]';
345 	sel+=',select';
346 	var ar=Ext.query(sel,row);
347 	var isDel = (typeof(noSaveForm)=='number' && noSaveForm==1);
348 	var plen=(prefix+'_'+rowindex).length;
349 	Ext.each(ar,function(obj){
350 		var tagName=obj.tagName.toLowerCase();
351 		var bl=true;
352 		var fieldName=obj.id;
353 		var val = obj.value;
354 		if(tagName=='input'){
355 			if(obj.type.toLowerCase()=='text'){
356 				obj.type='hidden';
357 			}else{ //browser
358 				bl=false;
359 				fieldName=obj.id.substring(0,obj.id.length-3);
360 				obj.style.display='none';
361 			}
362 		}else if(tagName=='textarea'){
363 		 	obj.style.display='none';
364 		}else if(tagName=='select'){
365 			obj.style.display='none';
366 			val=obj.options[obj.selectedIndex].text;
367 		}else alert('tagName:'+tagName);
368 		var hand=Ext.getDom(fieldName+'Handler');
369 		if(hand) hand.style.display='none';
370 		hand=Ext.getDom(fieldName+'Msg');
371 		if(hand) hand.className=hand.className.replace(' validationFailed-icon','');
372 		if(bl && !Ext.isEmpty(val)){
373 			val = Ext.util.Format.htmlEncode(val);
374 			var f=fieldName.substring(plen+1);
375 			if(renderData && typeof(renderData[f])=='string'){
376 				val=String.format(renderData[f].replace('${value}','{0}'),val);
377 			}
378 			Ext.DomHelper.insertAfter(obj,{tag:'span',html: val});
379 		}
380 		if(isDel){//这里针对新主题时可能会异常
381 			if(!bl) obj.parentNode.removeChild(Ext.getDom(fieldName));//表示删除浏览框的隐藏域
382 			obj.parentNode.removeChild(obj); //当浏览框时删除的是按钮,非浏览框时删除的是表单控件对象
383 		}
384 	}); //end.each(ar);
385 	
386 	if(isDel){
387 		ar=Ext.query('input[type=hidden]',row);
388 		for(var i=0;i<ar.length;i++){
389 			ar[i].parentNode.removeChild(ar[i]);
390 		}
391 	}
392 	row.setAttribute('data-readonly','true');
393 },
394 /**
395  * @description 将指定行号或选中行的数据复制到后一行
396  * @param {prefix} String //明细表前缀
397  * @param {rowOrder} Number //行序号,从0开始计数,如果为null则默认复制选中行
398  * @param {data} Function|Object //参数格式同Add()方法的第三个参数
399  * @return 如果执行成功返回索引行号,否则返回-1
400  */
401 Copy:function(prefix,index,data){
402 	//获取选项中的行号
403 	if(typeof(index)=='number' && index>=0){
404 		var rows=Ext.getDom(prefix+'List').rows;
405 		if(rows.length>index){
406 			var ar = Ext.query('input[_rowindex=true]',rows[index]);
407 			if(ar.length>0) ar[0].checked=true;//使指定序号的行复选框选中
408 		}
409 	}
410 	var rowindex = -1;
411 	var ids = this.getRowIndexs(prefix, true);
412 	if(ids.length==1){//只选一行且仅一行
413 		rowindex = ids[0];
414 		var spanData={};
415 		var rowValues=this.getRecordValue(prefix + '_'+rowindex+'_name',true);//true表示获取只读显示文本的值
416 		for(var f in rowValues){
417 			var dom = Ext.getDom(prefix+'_'+rowindex+'_'+f+'span');
418 			if(dom) spanData[f+'span']=dom.innerHTML;
419 		}
420 		rowValues = Ext.apply(rowValues,spanData);
421 		rowValues = Ext.apply(rowValues,data);
422 		this.Add(prefix,null,rowValues);//增加一行并初始化数据
423 	}else alert('请选中其中一行后操作');
424 	return rowindex;
425 },
426 /**
427  * @description 检查指定明细行数据的唯一性,需要自定义检查脚本中调用是否需要检测
428  * @param {prefix} String //明细表前缀
429  * @param {fieldNames} String|Array //字段名或多个字段联合唯一性
430  * @return boolean|Number 如果都是唯一的则返回true,否则为行序号(不是行的索引号,有可能因为删除或中间插入而导致的显示行和索引号不一致)
431  */
432 checkUnique:function(prefix,fieldNames){
433 	if(Ext.isEmpty(fieldNames)){
434 		console.log('checkUniue的参数fieldNames不能为空.');
435 		return;
436 	}
437 	var ret = true;
438 	var ar = fieldNames;
439 	if(!Ext.isArray(fieldNames)) ar = [fieldNames];
440 	var dataRows = [];
441 	var isSingle = ar.length==1;//表示只有一个字段列
442 	var existsObj = {};
443 	for(var index = 0; index < ar.length; index++){
444 		var fieldName = ar[index];
445 		var cols = this.getColumnValue(prefix + '_0_' + fieldName);
446 		for(var i = 0; i < cols.length; i++ ){
447 			if(isSingle){
448 				if(Ext.isEmpty(cols[i])) continue;
449 				if(typeof(existsObj[cols[i]])=='boolean'){
450 					ret = i;
451 					break;
452 				}else{
453 					existsObj[cols[i]] = true;
454 				}
455 			}else{
456 				if(typeof(dataRows[i]) != 'object') dataRows[i] = {};
457 				dataRows[i][fieldName] = Sucang.trim(cols[i]);
458 			}
459 		} //end.for(cols);
460 	} //end.if(ar)
461 	if(!isSingle){
462 		existsObj = [];
463 		Ext.each(dataRows,function(row,n){
464 			var val = '';
465 			for(var i = 0;i < ar.length; i++){
466 				val += row[ar[i]];
467 			}
468 			if(existsObj.indexOf(val) < 0) existsObj.push(val);
469 			else{
470 				ret = n;
471 				return false;
472 			}
473 		});
474 	} //end.if(isSingle)
475 
476 	return ret;
477 }
478 /**
479  * @description 用于在布局中Ajax异步加载明细多行数据,根据sql或者url,返回值为JSONArray类型,并key和明细字段名对应
480  * @param {preifx} String 明细表的前缀ID
481  * @param {sqlid} String 布局中的自定义sqlid标识符或者直接指定在/为前缀的URL地址
482  * @param {params} String|Object 指定参数对象,如果只传一个参数,则Sql页面用value变量表示
483  * @param {data} Function|Object 回调函数格式{callback:function(rowindex,rowData){ .. },_clearAll:true}默认参数_clearAll=true表示填充明细前先清空明细表可以指定为false则不清空
484  *<br>如果是Object则每一行的上覆盖自定义数据可以指定callback属性来指定回调函数(每一行都会调用callback函数)
485  * @return void
486  */
487 ,filldata:function(prefix,sqlid,params,data){
488 	if(Ext.isEmpty(sqlid)) return null;
489 	var uri='/datainterfacelist.do?action=json&_result=detail&_type=validateajax&_n='+sqlid;
490 	if(sqlid.startsWith('/')) uri = sqlid;
491 	var dom = Sucang.getDom('layoutid');
492 	if(dom) uri+='&_refid='+dom.value;
493 	var fillFun = function(txt){
494 		var list1 = Sucang.parseJSON(txt);
495 		if(list1==null || !Ext.isArray(list1)){console.log('filldata.error:服务器返回数据格式或非数组格式\n'+txt);return;}
496 		if(Ext.isEmpty(data) || Ext.isEmpty(data._clearAll) || data._clearAll!=false){
497 			Sucang.detail.selectAll(prefix, true);
498 			Sucang.detail.Del(prefix);
499 		}
500 		if(data) delete data._clearAll;
501 		for(var i=0;i<list1.length;i++){
502 			var row = list1[i];
503 			if(typeof(data)=='object') row = Ext.apply(row,data);
504 			else if(typeof(data)=='function') row.callback=data;
505 			Sucang.detail.Add(prefix,null,row);
506 		}
507 	};
508 	return Sucang.util.Ajax(uri,params,fillFun,false);
509 },
510 /**
511  * @description 用于在编辑模式的明细前<s:rowid prefix="F1" value="_${ch.id}" withorder="true" /"> 如果自定义序号格式,请实现reorderCallback(i)回调函数
512  * @param {preifx} String[必须] -  明细表的前缀ID
513  * @param {textFun} Function[可选] - 用于序号的文本,默认为【0、】。参数定义为function(i)返回需要显示的文本
514  *
515  */
516 reorder:function(id,textFun){
517 	if(Ext.isEmpty(id)) return;
518 	var ar=Ext.query('span.detailOrder',id+'List');
519 	if(typeof(reorderCallback)=='function') txtFun = reorderCallback;
520 	if(typeof(textFun)!='function'){
521 		textFun=function(i){return (i+1)+'、';};
522 	}
523 	Ext.each(ar,function(span,i){
524 		span.innerHTML=textFun(i);
525 	});
526 }
527 };
528 
529 /** 明细表操作的相关方法Sucang.detail的缩写 */
530 var FormDetail=Sucang.detail;