JAVA_SSM框架+MINIUI实现网页报表

最近两日新学了一下报表。
为了代码复用,也为了加深自己的流程印象。
将自己理解写在这里,仅供诸君参考。
如有错误,请见谅,并指出,谢谢。

1.对报表的理解:

报表含列,列有自己的Header,Field等配置参数,下面贴出MINI UI的API文档
这里写图片描述

2.实验流程

2.1报表模型

我这两天实现的表的格式基本上是

C1C2C3C4
数据库中没有的列有行数据数据库中有的列直接获得数据数据库中有的列直接获得数据数据库中没有的列行数据需要计算
数据库中没有的列有行数据数据库中有的列直接获得数据数据库中有的列直接获得数据数据库中没有的列行数据需要计算
数据库中没有的列有行数据数据库中有的列直接获得数据数据库中有的列直接获得数据数据库中没有的列行数据需要计算
附加行( 数据库中没有的列注意自定义对应)附加行( 数据库中没有的列注意自定义对应)附加行 ( 数据库中没有的列注意自定义对应)附加行( 数据库中没有的列注意自定义对应)

要添加自定义列在取表头时添加,要添加行在取数据时添加,注意字段对应。
对于C1基本上数据库里是没有列的(取不到field和header,数据需要取,此列基本上是描述列,静态的,一般只有一两列),作为C2.C3,由于是同一类(可以先写SQL取得列数据的集合,再写SQL利用前一集合取得的field循环获取对应的数据),所以数据库里一般有此列需要的数据。C4是汇总列。自己添加列后在前台做业务实现。

2.2逻辑实现

应该是如此的流程:
①获取列属性(C2,C3),表中没有的在业务层里加(C1,C4,在数据库里定义了列对象为自定义的COLUMN类型,再一层层加到LIST里返回一个LIST)
到前台JSON解析为:[{header:xx,field:xx,……},
{header:xx,field:xx,……},
{header:xx,field:xx,……}]
②获取列对应数据,依据列的Field在数据库中找寻,Field一定要相匹配
其中注意,前台用TreeGrid实现,其中TreeGrid的PARENTCODE属性为层级关系,也就是树的节点依据.
③其他实现的业务在BUSINESS和前台实现

3.代码如下:

3.1 Dao层

XML

select  COLUMN_NAME as header,           --列的名称
        COLUMN_CODE as field,            --列的字段,此值要与后面取出来的数据字段对应
        '120'       as width,            --列的宽度
        'center'    as align,            --列的 
        'center'    as headerAlign,
        'currency'  as dataType,
       COLUMN_CODE  as name  
 from RPTCOLUMN t where t.report_code = 'RPT1'
查询表数据:
SELECT T1.ROW_CODE     AS ROW_CODE,      -- 树的行字段,前台可以通过此字段指定单元格所在行,PS:根据Field制定单元格所在列
       T1.ORD          AS ORD,           -- ORD,暂时  
       T1.ITEM_DESC    AS ITEM_DESC,     -- 第一列表头数据   记得在SERVICE加表头与之对应       
       T1.PARENT_CODE  AS PARENT_CODE,   -- 所使用的层级描述,树的层级关系
      <foreach collection="headers" index="index" item="item">       
            ,NVL(SUM(CASE WHEN T2.COLUMN_CODE= #{item.name} THEN T2.DATA END),0) AS "${item.field}"  
      </foreach>                         --利用循环取得,要显示的表头字段,我设置中文的话显示有错误 最好不要用中文,这里为了方便取name为1,2   这里取得1,2的数据 。collection="headers"为接口定义中利用查询的表头作为参数 做数据的取得

FROM RPTCOLUMN T1LEFT JOIN RPTDATA T2 ON T1.REPORT_CODE = 'RPT1'AND T1.ROW_CODE = T2.ROW_CODE WHERE T1.REPORT_CODE = 'RPT1'GROUP BY T1.ROW_CODE ,T1.ORD,T1.ITEM_DESC,T1.PARENT_CODE ORDER BY T1.ORD

接口

public interface Rpt1Mapper {
    List<Column> queryRpt1Header(HashMap<String,String> param);
    //表头
    List<HashMap<String,String>> queryRpt1ListByHeader(@Param("headers") List<Column> headers,@Param("record") HashMap<String,String> record);
    //表数据
}

3.2 BUSINESS层

接口

public interface ReportBusiness {
    List<Column> queryRpt1Header(HashMap<String,String> param);
    //表头
    List<HashMap<String,String>> queryRpt1ListByHeader(HashMap<String,String> record); 
    //表数据
}

实现

@Component(value = "RSTReportBiz")
public class ReportBusinessImpl implements ReportBusiness {
    @Autowired
    private Rpt1Mapper  rpt1Mapper ;

    @Override
    public List<Column> queryRpt1Header(HashMap<String, String> param) {
        // 表头处理
        List<Column> allCols =new ArrayList<Column>();   
        Column firstCol = new Column();                   //设置返回List的集合中的数据 first 
        firstCol.setHeader("C1");
        firstCol.setAlign("left");
        firstCol.setField("ITEM_DESC");
        firstCol.setWidth("430");
        firstCol.setHeaderAlign("center");
        firstCol.setName("ITEM_DESC");

        List<Column> cols = rptRpt1Mapper.queryRpt1Header(param);

        Column lastCol = new Column();                       //设置返回List的集合中的数据 total
        lastCol.setHeader("TOTAL");
        lastCol.setAlign("right");
        lastCol.setField("TOTAL");
        lastCol.setWidth("300");
        lastCol.setHeaderAlign("center");
        lastCol.setName("TOTAL");

        if (cols == null || cols.size() == 0) {
            return null;
        } else {
            allCols.add(firstCol);                           //传入自己设置的Item first
            allCols.addAll(cols);                            //传入cols的Item
            allCols.add(lastCol);
            return allCols;
        }
    }
    @Override
    public List<HashMap<String, String>> queryRpt1ListByHeader(HashMap<String, String> record) {
        // 表数据处理
        List<HashMap<String, String>> result =new ArrayList<HashMap<String, String>>();
        HashMap<String, String> h1 =new HashMap<String, String>();   
        h1.put("ROW_CODE", "4");                                      //行数据的行数
        h1.put("ORD", "17");
        h1.put("ITEM_DESC", "第四行");                               //第一列的值字段要对应
        h1.put("PARENT_CODE", "");                                    //有层级关系再加,我这里没有

        HashMap<String, String> h2 =new HashMap<String, String>();
        h2.put("ROW_CODE", "5");
        h2.put("ORD", "17");
        h2.put("ITEM_DESC", "第五行");
        h2.put("PARENT_CODE", "");

        List<Column> columns = rptRpt1Mapper.queryRpt1Header(record);  
        //先取出表头以传入queryRpt1ListByHeader,即Dao层XML里queryRpt1ListByHeader查询表数据中用到的循环         集合headers
        if (columns != null && columns.size() > 0) {            
            result.addAll(rptRpt1Mapper.queryRpt1ListByHeader(columns, record)); //取出数据表中的数据
            result.add(h1);                                                      //我所要加的行
            result.add(h2);
            return result;
        } else {
            return null;
        }
    }
}

3.3 Control层

@Controller
public class ReportController {
    @Autowired
    private ReportBusiness ReportBiz;

    @RequestMapping(value = "/showRpt1View")
    public String showRpt1View(){
        return "alm/rptrst/Rpt1View";
    }

    //表头
    @RequestMapping(value = "/queryC001RstHeader")
    @ResponseBody
    public AjaxObj queryC001RstHeader(@RequestParam HashMap<String,String> record){
        List<Column> columns = RSTReportBiz.queryC001RstHeader(record);
        return new AjaxObj(0,null,columns);
    }
    //报表数据
    @RequestMapping(value = "/queryC001RstListByHeader")
    @ResponseBody
    public Pager<HashMap<String,String>> queryC001RstListByHeader(SystemContext systemContext,@RequestParam HashMap<String,String> record) {
        List<HashMap<String,String>> page = RSTReportBiz.queryC001RstListByHeader(record);
        return new Pager<HashMap<String,String>>(0, page);             //Pager是一个封装的分页对象这里不需要分页,所以传入参数需要分页的总数total为0
    }
}

3.4前台页面

<body>
    <div class="description"> 
        <h3>报表</h3>
    </div>
    <div class="mini-toolbar" style="padding: 0px; border: 0;background:#fff">
        <div id="form1">
            <table style="width: 100%;">
                <tr>
                    <td style="width: 100%;"></td>
                    <td style="white-space: nowrap; text-align: right;padding-right:10px">
                        报表日期:<input id="reportDt" name="reportDt" class="mini-combobox"                   style="width:110px"
                                    textField="TEXT" valueField="TEXT" valueFromSelect="true"
                                    url="<%=path %>/queryC001RstReportDtList"
                                    allowInput="true" dataField="data" onvaluechanged="onReportDtChanged"
                                    showNullItem="false" emptyText="请选择报表日期" NullItemText="请选择报表日期"
                                    />
                        报表货币:<input id="currency" name="currency" class="mini-combobox" style="width:70px"
                                    textField="dictText" valueField="dictValue"
                                    url="<%=path %>/queryDicyEntryByDictCode?dictCode=1018"
                                    required="true" allowInput="false" dataField="data"
                                    showNullItem="false" emptyText="请选择报表货币" NullItemText="请选择报表货币"
                                    onvaluechanged="search"/>
                        <a class="mini-button"
                            iconCls="icon-search" onclick="search()">查询</a>
                        <a class="mini-button"
                            iconCls="icon-download" onclick="exportExcel()">导出</a>
                    </td>
                </tr>
            </table>
        </div>
    </div>
    <div class="mini-fit" style="height: 100%; width: 100%">
        <div id="treegrid1" class="mini-datagrid"
            style="width: 99%; height: 100%;"
            url="<%=path %>/queryRpt1ListByHeader"
            treeColumn="ITEM_DESC" idField="ROW_CODE" parentField="PARENT_CODE"
            resultAsTree="false" expandOnLoad="true" showTreeIcon="false"
            showExpandButtons='false' virtualScroll="true" autoLoad="false"
            dataField="datas"  
            allowRowSelect="true" enableHotTrack="false" editNextOnEnterKey="true" 
            allowCellEdit="true" allowCellSelect="true" cellEditAction="cellclick">
            <div property="columns"></div>
        </div>
    </div>


<script type="text/javascript">
    mini.parse();
    var form = new mini.Form("#form1");          //这边表单传数据,但是我在SQL中没有演示
    var grid = mini.get("treegrid1");
    var reportDtCombo = mini.get("reportDt");
    var currencyCombo = mini.get("currency");
    reportDtCombo.select(0);
    currencyCombo.select(0);

    search();

    function search(){
        var formData = form.getData();
        var url = path + '/queryRpt1Header';
        myui.loadAjax(url,formData,function(o){
            var data = o.data;
            var header = [];
            var j = 0;
            //下面是演示如何在JS中添加表头(列数据对象),跟在BUSINESS中实现的业务相同,不过我COLUMN中没有设置editor,即可编辑属性,添加此属性,实现效果相同.自己代码中后台BUSINESS没有添加表头在前台添加的,因为要求要可以编辑
            header[j++]={"header":"C1",align:"left",field:"ITEM_DESC",name:"ITEM_DESC",width:"300",headerAlign:"left"};                                                     
            for(var i = 0;i<data.length;i++){
                header[j++] = {"header":data[i].header,align:"center",field:data[i].field,dataType:"currency",editor:{ type: "textbox",minWidth: "50"},width:"120",headerAlign:"center"};
            }
            header[j++]={"header":"TOTAL",align:"right",field:"TOTAL",width:"150",headerAlign:"center",dataType:"currency"};            
            grid.setColumns(header);
            grid.on("load",function(){
                grid.mergeColumns(["ITEM_DESC"]);
            });
            grid.load(formData);                            
        },function(e){
            alert(e.responseText);
        }); 
    }

    function onReportDtChanged(){
        var reportDt = reportDtCombo.getValue();
        if(reportDt == null || reportDt.length <= 0){
            reportDtCombo.select(0);
        }
        search();
    }

    grid.on("drawcell", function (e) {
        var record = e.record,
        column = e.column,
        field = e.field,
        value = e.value;    
        var val1;
        var val2;
        if(field == "TOTAL"){
            val1=isNaN(record[1]) ? 0 : record[1];                                        //Field为1的数据
            val2=isNaN(record[2]) ? 0 : record[2];                                        //Field为2的数据
            var html = parseFloat(val1) + parseFloat(val2);                               //更新此行TOTAL的数据 
            e.cellHtml = formatCurrency(html);   
        }
    });

    grid.on("cellbeginedit", function(e) {                                                //只有第四行的可以单元格可以编辑,而且只有Field为'1','2'的列可以编辑,也就是两个单元格列为C2,C3;行为第四行的单元格可以编辑
        var editor = e.editor;
        var record = e.record;
        if(record.ROW_CODE=="4"){

        }else{
            e.cancel = true;
        }
    });

    var fieldBool = true;
    grid.on("cellcommitedit", function(e) {
        var field = e.field;

        if (fieldBool) {
            fieldBool = false;
            var record = e.record;
            if(record.ROW_CODE == "4"){
                var data1 = grid.findRow(function(row) {                       //第一行
                    if (row.ROW_CODE == "1")
                        return true;
                });
                var updateRow = grid.findRow(function(row) {                  //需要更新的行
                    if (row.ROW_CODE == "5")
                        return true;
                });
                if(field == "1"){
                    var val1 = isNaN(data1[1]) ? 0 : data1[1];                 //第一行,Field为1的数据  我C2,C3列的Field分别为1和2
                    var val2 = isNaN(e.value) ? 0 : e.value;                   //取此单元格编辑的数据:e.value
                    var val = parseFloat(val1) - parseFloat(val2);
                    grid.updateRow(updateRow, {                                //调用更新行的方法
                        "1" : val
                    });
                }
                if(field == "2"){                                               //实现在Field为2的那一列的逻辑
                    var val1 = isNaN(data1[2]) ? 0 : data1[2];
                    var val2 = isNaN(e.value) ? 0 : e.value;
                    var val = parseFloat(val1) - parseFloat(val2);
                    grid.updateRow(updateRow, {
                        "2" : val
                    });
                }
            }   
        }
        fieldBool = true;
    });
</script>
</body>
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值