数据导入导出
数据的导入只要是以excel 或者 word文件方式导入;
模板制作是数据导入/导出的核心步骤.剩下的就是使用导入导出组件关联设计的导入/导出模板实现数据的导入/导出功能
模板管理
数据导出
table的导出
DataExport组件的应用
自定义导出的原理其实是与报表模板的原理是一致的.
选择模板
模板的设计
具体模板设计可参考报表的模板制作
- 编写导出脚本样例:
function Button1367_onClickScript(cxt: ScriptContext, btn: Button) {
btn.getPage().components.DataExport1.export(
{param1:'paramvalue1',param2:'paramvalue2'},
'导出的文件名')
//复杂参数可使用JSON字符串传递, 模板内接收并解析JSON
btn.getPage().components.DataExport1.export(
{ json: JSON.stringify({ aa: 'aa', bb: { cc: 'cc', dd: ['a', 'b'] } }) },
'你好中国');
}
数据导入
DataImport 导入组件的使用
- 后台处理地址
- word: core/word/wordin (word纯脚本方式导入)
- excel: core/excel/excelin (excel模板导入)
- excel: core/excel/scriptin (excel纯脚本方式处理导入)
- 组件的属性说明
json方式excel导入
注意事项:
导入的表必须在快搭表模型内
- 模板的设计说明
新建一个后缀名为.xls(兼容老版本excel 2003以前的版本)/.xlsx (兼容excel2003以后的版本)文件.Excel里面的列和样式按照需求添加
在首行上点击第一个单元格,右键,点击编辑批注,填写相应的批注, 批注格式为json,第一个单元格需要设置导入表的相关信息,导入表信息相关设置, 表信息设置的字段,分别为:
- TABLESTYPE:导入表类型
- TABLENAMEMAP:导入表名
- TABLEDEFAULTMAP:导入表的列
- TREERULES:导入规则
- MAPCOLUMN:导入表的数据列
- CHECKLIST:数据校验
- MULTCHECK:去重校验
- TOTALCHECKRULE:总的校验规则
- EXCELMULTCHECK:excel去重校验
- FILENAME:文件名,不需要带后缀
- 注意事项
- TABLESTYPE:导入表类型,现只支持两种,一个是单表,一个是树形表,默认导入单表,如需导入树形表则这样设置:”TABLESTYPE”:”F.TREETABLE”; 多个逗号隔开
- TABLENAMEMAP:导入表,设置格式为:”TABLENAMEMAP”:{“F”:”BD_ORG”,”A”:”BD_DEPT”};其中F代表表别名;BD_ORG代表表真正的名字;
- TABLEDEFAULTMAP:导入表的默认列,设置格式为:”TABLEDEFAULTMAP”:{“F.SYS_CREATOR”:”getCuruserid”};其中F代表导入表的别名, 与上述设置一致,SYS_CREATOR代表插入默认字段,getCuruserid,获取值 的方 法,不需要括号,在DataSource.groovy上编写;
- TREERULES:导入规则,针对树形,有两种格式,一种是SYS_LEV,一种是SYS_PARENTID,设置格式为:”TREERULES”:”F.SYS_LEV”, 导入树形表且规则是SYS_LEV时excel表格数据必须是按照树形的结构排好;多个逗号 隔 开,如果TABLESTYPE的为树形表,那么TREERULES不可缺少
- MAPCOLUMN:导入表的数据列设置格式为: “MAPCOLUMN”:{“机构代码”:{“FIELDNAME”:”F.OBJCODE”},”机构名称”:{“FIELDNAME”:”F.OBJNAME”}}, 其中:”机构代码”代表excel上显示的列名: {“FIELDNAME”:”F. OBJCODE”},指映射的数据库信息,可以设置的地段有3个:FIELDNAME,FIEDTYPE,FIEDSOURCE
- FIELDNAME:导入表的字段,设置格式为:”FIELDNAME”:”F.OOBJAME”,其中F为表的别名, OBJNAME为导入数据表的字段名,与上述设置的TABLENAMEMAP需要一致;
- FIEDTYPE:导入表列的类型,分为4种,为NORMAL,ID,TONUMBER,IDANDNORMAL,设置格式为:”FIEDTYPE”:”NORMAL”,NORMAL代表正常导入,即excel这列内容是什么就导入什么 ID代表这列导入的是根据内容去查 询 数 据库得出的ID,TONUMBER代表这列只有是否的选项,而数据存储则是0,1,其中0是否,1是是;IDANDNORMAL代表同时把这列的文字和ID插入, FIELDNAME需要设置为两列,且插入文字的那列必须要在第一位, 如: “FIELDNAME”:”F.OOBJAME,F.OOBJAMEID”,FIEDTYPE默认为NORMAL
- FIEDSOURCE:数据来源,针对FIEDTYPE是ID的类型,设置格式如下:”FIEDSOURCE”:” select rwid from bd_org where objanme=” 内置4个参数:userid、roleid、deptid、orgid,分别代表当前登录用户 的 id, 角色id,部门id,机构id,使用方法 :select ** from xx where xx = :userid
- SOURCEKEY:保存资源的key,避免相同值再去数据库查询,多个逗号隔开,如:”SOURCEKEY”:”F.ORGID”,默认值为当前列对应的数据库字段
- CHECKLIST:数据校验规则,设置格式入下:”CHECKLIST”:[“if(cxt.isBank(row.get(‘F.OBJNAME’))){‘机构名称不能为空’}”,”if(cxt.isBank(row.get(‘F.OBJCODE’))){‘机构代码不能为空’}”,
“checkDataIsEqual”]
校验规则分为两部分,一部分内置函数,一部分自己在DataCheck.groovy脚本上编写的函数,内置函数后续有说明
校验规则在DataCheck.groovy脚本上编写,checkDataIsEqual是函数名,至于函数参数有三个,第一个是当前行数据,类型为Map<String,Object> 第二个参数为错误信息,类型为List,第三个参数为excel 行 数,类型为int,在DataCheck上写的方法需要有这三个参数
MULTCHECK:去重校验,当校验的字段组合一致时不再进行插入,如:{“F”:”F.OBJCODE,F.OBJNAME”}
TOTALCHECKRULE:全部数据校验,后端会把所有的数据以及错误信息对象传到方法里面,如:[“checkdata”,”checksum”] 注意这些校验方法写在DataCheck.groovy脚本里面,定义的方法的应类似这样: checkdata (List<Map<String, Object>> map,Listlist),校验出错的信息就加入到list里面
EXCELMULTCHECK:excel去重校验,即excel同一列不允许出现相同的值,字符串类型,多个逗号隔开,如:”F.ORGID,F.SHOUJH”
FILENAME:文件名,不需要带上文件后缀,如:”FILENAME”:”xx”,自定义校验和获取数据源的groovy文件,存放在dataimport目录下面
编写完批注后保存文件 上传到[平台管理/模板管理]
*注意:文件保存时需要确定批注是否全部隐藏,如发现批注未隐藏需设置隐藏;
在数据校验中,如果有新增数据校验规则,需要在DataCheck.groovy中编写数据校验脚本,编码格式遵循java代码语法,可以引入各种包和文件,编写的校验规则需返回true,false 现有的数据校验规则有checkDataIsEqual(),注意编写校验规则参数设置需为Object数组对象,需要返回RetVo对象
在新增默认列,如果有新增的数据源,需要在DataSource.groovy中编写新增的默认的列的值,编码格式遵循java代码语法,可以引入各种包和文件,方法名必须以get开头 现有的默认列的值函数有:
- getOrgid():获取当前登录机构的id
- getCurOrgName():获取当前登录机构的名字
- getCurUserid():获取当前登录用户的id
- getCurUserName():获取当前登录用户的姓名
- getCurDeptid():获取当前登录用户部门的id
- getCurDeptName():获取当前登录用户部门的名字
- getCurDate():获取当前时间
内置函数说明,内置函数有两个对象,一个cxt,一个row,其中row.get()函数可以获取行的对象的值,比如row.get(‘F.OBJCODE’),就是获取F.OBJCODE这列的值其中cxt是一个类,可以通过类的方法来得到一些对字符串函数 的判断.现有的cxt部分内置函数有:
- isBank(String value):是否为空
- isNumber(String value):是否数字
- toNumber(String value):转为数字
- startWith(String source, String prefix):字符串source以prefix为开头
- endWith(String source, String suffix):字符串source以suffix结尾
- indexOf(String source, String str):字符串source包涵str的位置
- contains(String source, String str):字符串source是否包涵str字符
- toUpper(String source):字符串转大写
- toLow(String source):字符串转小写
- equals(String source, String tagSource):字符串source是否等于tagSource字符串
- equalsIgnoreCase(String source, String tagSource):字符串source是否等于tagSource字符串,忽略大小写
- 执行导入Excel
function Button1_onClickScript(cxt:ScriptContext,btn:Button){
btn.getPage().components.DataImport1.importData();
}
- 下载导入的模板文件
function Button1_onClickScript(cxt:ScriptContext,btn:Button){
btn.getPage().components.DataImport1.downloadTemplte();
脚本方式excel导入
function btnImport_onClickScript(cxt:ScriptContext,btn:Button){
btn.getPage().components.DataImport1.importData((err,res)=>{
if(Common.isNotEmpty(err)){
Common.errorMsg(err)
}else{
Common.successMsg(err);
}
},{hello:'123'})
}
- 纯excel脚本的导入
一般是客户提供的导入文件格式不是很有规范时候.系统需要使用自定义导入的Groovy脚本
- 通过传递参数到模板内
function btnImport_onClickScript(cxt:ScriptContext,btn:Button){ btn.getPage().components.DataImport1.importData((err,res)=>{ if(Common.isNotEmpty(err)){ Common.errorMsg(err) }else{ Common.successMsg('导入完成'); } },{hello:'123'}) }
word的导入
一般是客户提供的导入文件格式不是很有规范时候.比如客户提供了word 文件的数据文件导入系统需要使用自定义导入的脚本. 下面是一个word的导入Groovy脚本的例子:/** * office数据文件导入脚本 * * file MultipartFile 外部传进来的参数 */ import groovy.ui.SystemOutputInterceptor; import com.kdayun.z1.core.context.Context; import com.kdayun.z1.core.message.MessageUtil; import java.util.UUID; import org.apache.poi.hssf.usermodel.HSSFCell; import org.apache.poi.hssf.usermodel.HSSFDateUtil; import org.apache.poi.hssf.usermodel.HSSFWorkbook; import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.CellType; import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.usermodel.Workbook; import org.apache.poi.xssf.usermodel.XSSFWorkbook; import org.apache.poi.xwpf.extractor.XWPFWordExtractor; import org.apache.poi.xwpf.usermodel.XWPFDocument; import org.apache.poi.xwpf.usermodel.XWPFTable; import org.apache.poi.xwpf.usermodel.XWPFTableCell; import org.apache.poi.xwpf.usermodel.XWPFTableRow; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; import org.apache.commons.lang3.StringUtils; /** * 表头的索引 * 注意:从零开始 */ headerIndex=1 /** * 模板的列 * 注意:这个客户提供的word模板的类字段 ,可修改模板这里必须修改,不然通不过校验 * fields 列 * rows 数据行 * field 字段名(数据映射的字段名,无需更新设置成""或者null), regx:正则表达式校验合法 ,nullable: 允许为空 * colIndex:列的位置(正常情况与cell[1]相等多表导致这个值与cell[1]不等) * cell: 表头单元格的坐标 [row,col], data:文件内保存的字段名,用于校验表头是否与提供的一致 */ FIELDS_MAP= [ "fields":[ "序号": ["field":"" ,"regx":"" , "nullable":true,colIndex:0, cell:[0,0],data:null] ,"姓名": ["field":"XINGM" ,"regx":"" , "nullable":false,colIndex:1, cell:[0,1],data:null] ,"现机构及职务": ["field":"XIANDWJZW" ,"regx":"" , "nullable":false,colIndex:2, cell:[0,2],data:null] ,"性别": ["field":"XINGB" ,"regx":"" , "nullable":false,colIndex:3, cell:[0,3],data:null] ,"出生年月": ["field":"CHUSNY" ,"regx":"\\d{4}.\\d{1,2}" , "nullable":false,colIndex:4, cell:[0,4],data:null] ,"民族": ["field":"MINZ" ,"regx":"" , "nullable":false,colIndex:5, cell:[0,5],data:null] ,"籍贯": ["field":"JIG" ,"regx":"" , "nullable":false,colIndex:6, cell:[0,6],data:null] ,"参加工作时间": ["field":"CANJGZSJ" ,"regx":"\\d{4}.\\d{1,2}" , "nullable":false,colIndex:7, cell:[0,7],data:null] ,"加入中共时间": ["field":"JIARZGSJ" ,"regx":"\\d{4}.\\d{1,2}" , "nullable":false,colIndex:8, cell:[0,8],data:null] ,"全日学历": ["field":"QUANRXL" ,"regx":"" , "nullable":false,colIndex:9, cell:[1,9],data:null] ,"全日学位": ["field":"QUANRXW" ,"regx":"" , "nullable":false,colIndex:10, cell:[1,10],data:null] ,"全日毕业院校及专业": ["field":"QUANRBYYXJZY" ,"regx":"" , "nullable":false,colIndex:11, cell:[1,11],data:null] ,"在职学历": ["field":"ZAIZXL" ,"regx":"" , "nullable":true,colIndex:12, cell:[1,12],data:null] ,"在职学位": ["field":"ZAIZXW" ,"regx":"" , "nullable":true,colIndex:13, cell:[1,13],data:null] ,"在职毕业院校及专业": ["field":"ZAIZBYYXJZY" ,"regx":"" , "nullable":true,colIndex:14, cell:[1,14],data:null] ,"专业技术职务": ["field":"ZHUANYJSZW" ,"regx":"" , "nullable":true,colIndex:15, cell:[0,11],data:null] ,"任现职时间": ["field":"RENXZSJ" ,"regx":"" , "nullable":false,colIndex:16, cell:[0,12],data:null] ,"备注": ["field":"BEIZ" ,"regx":"" , "nullable":true,colIndex:17, cell:[0,13],data:null] ] ,"rows":[] ] INSERST_SQL="INSERT INTO CESB_RENYDR(RWID,XINGM,XIANDWJZW,XINGB,CHUSNY,MINZ,JIG,CANJGZSJ,JIARZGSJ,QUANRXL,QUANRXW,QUANRBYYXJZY,ZAIZXL,ZAIZXW,ZAIZBYYXJZY,ZHUANYJSZW,RENXZSJ,BEIZ, SYS_CREATOR,SYS_CREATETIME) VALUES (#{RWID},#{XINGM},#{XIANDWJZW},#{XINGB},#{CHUSNY},#{MINZ},#{JIG},#{CANJGZSJ},#{JIARZGSJ},#{QUANRXL},#{QUANRXW},#{QUANRBYYXJZY},#{ZAIZXL},# {ZAIZXW},#{ZAIZBYYXJZY},#{ZHUANYJSZW},#{RENXZSJ},#{BEIZ},#{SYS_CREATOR},#{SYS_CREATETIME})" SELECT_SQL="SELECT 1 FROM CESB_RENYDR WHERE XINGM=#{XINGM} AND CHUSNY=#{CHUSNY}" /** * 保存的原始数据 */ rawData=[] /** * 校验文件的格式合法性 * 文件的格式 后缀 * @return true 通过 则反之 */ def void checkImportFile() throws Exception { System.out.println("校验文件合法性..."); if(null==file) { throw new Exception("输入文件不允许为空") } } /** * 校验文件的文数据合法 * @return */ def void checkImportData() throws Exception { System.out.println("校验数据合法性..."); if(FIELDS_MAP.rows.size()==0) { throw new Exception("无数据可以导入") } checkFields(); checkRows(); } /** * 校验列 */ def checkFields() { for ( e in FIELDS_MAP.fields ) { if(StringUtils.isBlank(e.value.data)) { throw new Exception("导入的文件不存在数据列["+e.key+"]") } } } /** * 校验行 */ def checkRows() { for(int rowindex=0;rowindex<FIELDS_MAP.rows.size();rowindex++) { row=FIELDS_MAP.rows.get(rowindex); for ( e in FIELDS_MAP.fields ) { checkRowData(rowindex,row,e) } } } /** * 校验行的数据 */ def void checkRowData(Integer rowindex,ArrayList<String> row,def field) { value=row[field.value.colIndex]; cellTitle="行["+rowindex+"] 列["+field.value.colIndex+"]"+field.key +" "; regx=field.value.regx; nullable=field.value.nullable System.out.println("校验数据 "+cellTitle+" "+value); if(!nullable) { if(StringUtils.isBlank(value)) { throw new Exception( cellTitle+"的数据不允许为空") } strValue= value.toString(); if(StringUtils.isNotBlank(regx)) { if(!strValue.matches(regx).find()) { throw new Exception( cellTitle+"的数据格式不对") } } }else { if(StringUtils.isNotBlank(regx) && null!=value) { strValue= value.toString(); if(!strValue.matches(regx).find()) { throw new Exception( cellTitle+"的数据格式不对") } } } } /** * 读取数据文件的数据 * @return */ def getData() { System.out.println("读取文件数据..."); readWord7(); readFields(); readRows(); System.out.println("读取文件数据结果 row:"+FIELDS_MAP.rows.size()+" field:"+FIELDS_MAP.fields.size()); } /** * 检查是否存在行 */ def isExistsRow(variants) { List ret= cxt.exceSelectSql(SELECT_SQL,variants); return ret.size()>0; } def insertData(){ System.out.println("写入数据到数据库..."); variants=[] def user=Context.getInstance().securityService.getCurrentUser(); if(null==user) { throw new Exception("必须选登录才能导入数据") } for(int j;j<FIELDS_MAP.rows.size();j++) { def row=FIELDS_MAP.rows[j]; def vrow=[:]; variants.add(vrow); //给一个组件guid vrow.put("RWID",UUID.randomUUID().toString().replaceAll("-", "").toUpperCase()); vrow.put("SYS_CREATOR",user.getId()); vrow.put("SYS_CREATETIME",new Date()) for ( e in FIELDS_MAP.fields ) { if(StringUtils.isNotBlank(e.value.field)) { vrow.put(e.value.field,row[e.value.colIndex]); } } } System.out.println("写入数据到数据库:"+JSON.toJSONString(variants)); for(int j;j<FIELDS_MAP.rows.size();j++) { def row=FIELDS_MAP.rows[j]; try { if(isExistsRow(variants[j])) { throw new Exception("第"+j+"行数据已经存在,检查是否重复导入数据") }else { cxt.exceSelectSql(INSERST_SQL,variants[j]); } } catch(e) { throw new Exception("第["+j+"行]数据写入问题:"+e.toString()); } } } /** * 得到table单元格的值 */ def getCellValue(Integer rowIndex,Integer colIndex){ return rawData.get(rowIndex).get(colIndex); } /** * 读取数据列 */ def readFields() { if(rawData.size()==0) { throw new Exception( "无数据可以导入") } System.out.println("读取数据列..."); for ( e in FIELDS_MAP.fields ) { System.out.println("读取数据列"+JSON.toJSONString(e)); if(e.value.cell!=null) { rowIndex= e.value.cell[0] colIndex= e.value.cell[1] e.value.data=getCellValue(rowIndex,colIndex).replaceAll("\\s*", ""); } } } /** * 读取数据行 */ def readRows() { if(rawData.size()==0) { throw new Exception( "无数据可以导入") } System.out.println("读取数据行..."); for(int i;i<rawData.size();i++) { if(i>headerIndex) { def row=[]; FIELDS_MAP.rows.add(row); for ( e in FIELDS_MAP.fields ) { def rowIndex= i def colIndex= e.value.colIndex if(colIndex!=null) { row.add(getCellValue(rowIndex,colIndex)) } } } } System.out.println("读取数据列"+JSON.toJSONString(FIELDS_MAP)); } def readWord7(){ String text = null; InputStream inputStream =null; XWPFDocument doc =null; try { inputStream = file.getInputStream(); doc = new XWPFDocument(inputStream); List<XWPFTable> tables = doc.getTables(); for(int i=0;i<tables.size();i++) { List<XWPFTableRow> wordRows = tables.get(i).getRows(); for(int j=0;j<wordRows.size();j++) { XWPFTableRow wrodRow=wordRows.get(j); List<XWPFTableCell> tableCells = wrodRow.getTableCells(); List<String> r=new ArrayList<String>(); rawData.add(r) for(int n=0;n<tableCells.size();n++) { XWPFTableCell cell=tableCells.get(n); r.add(cell.getText()) } } } System.out.println("数据:"+JSON.toJSONString(rawData)); }finally { if (doc!=null) { try { doc.close(); } catch (IOException e) { e.printStackTrace(); } } } } /** * 开始导入 */ def void start() { System.out.println("开始导入..."); checkImportFile() getData(); checkImportData(); insertData(); } start();
执行导入Word
function Button1365_onClickScript(cxt:ScriptContext,btn:Button){
btn.getPage().components.DataImport1.importData();
}
- 下载导入的word模板文件
function Button1365_onClickScript(cxt:ScriptContext,btn:Button){
btn.getPage().components.DataImport1.downloadTemplte();
}
最后编辑:admin 更新时间:2024-10-17 08:28