吉特仓储管系统(开源WMS)--Web在线报表以及打印模板分享

  很早之前就想写这篇文章与大家分享一下自己在吉特仓储管理系统中开发打印和报表的功能,在GitHub(https://github.com/hechenqingyuan/gitwms)上公开下载的代码中很多人觉得在线设计报表这个功能比较不错,但是很多人也会有疑问。这边文章就简单讲解一下如何开发这个功能的,供大家学习参考,如果有任何疑问可以直接联系我,当然也有很多不足之处希望大家多多谅解和指点。

  一. 各种需求报表以及打印

    最开始之初在Web上做打印是每个打印也都会做一个页面,利用的是浏览器自身带的打印功能,当时做的也津津有味的感觉比较爽,但是后面做的打印页面多了想死的心都有,特别是遇到了复杂的打印。后面就想着用一个打印组件试试,这样开发打印可能方便很多,于是后面使用了lodop 打印组件(收费),这是一个非常不错的打印组件,刚开始觉得这个组件也挺不错的,后面用着也发现很多东西都有局限性。于是后面专门弄了一个在线报表设计组件(FastReport),相信很多人都使用过这个组件,可以很方便的做在线报表以及打印功能。

    在吉特仓储管理系统中涉及到打印的部分主要是如下部分:

    (1) 入库单 (2) 出库单[有些客户喜欢用作送货单] (3) 报损单 (4) 调拨单 (5) 销售订单 (6) 采购单 (7) 各种报表功能

    以单据为主的相关打印功能:

    

    另外一种是以表格为主的报表统计功能:

    

    还有一种以图表为主的报表功能:

    

  

  二. 如何统一报表和单据的打印

    使用FastReport 可以方便的做在线Web的打印,所以这里打印功能就不是问题了,无论是做单据的打印还是做报表的打印直接利用这个组件即可。在分析一下打印以及报表的过程:

    (1) 获取数据源:数据可能来自多方面的,比如入库单,出库单,以及自己定义的数据源

    (2) 数据显示到页面中:不同单据显示不一样,这里就需要不同的打印模板

    (3) 打印页面中内容:打印功能比较单一,打印显示的内容即可

    只要能够解决上面上个问题,那么做一个公共的打印功能就比较方便了,提供一个公共格式的数据源,根据公共格式的数据源能够在线设计不同形式的模板,打印显示的内容。

    不同的单据其字段是不一样的,所以在统计数据格式的时候可以使用DataTable ,DataTable是比较方便的能够解决这种问题的,至于打印模板无非就是一个在线设计的问题,FastReport已经完全解决了这种问题。最关键点就是提供数据源了, 在吉特仓储管理系统中数据源都是使用的实体模型, 相信各位做开发的也能够理解在开发过程中使用DataTable 带来的不便,单是在这里我们要反其道而行将实体模型转换为DataTable 。

  三. 分类处理

    本文章只做几个分类的处理,这个分类比较零散,这里以其中一个客户实际案例作为讲解。

    

    在报表的分类中我们定义了 入库单,出库单,盘点单,报表和施工单几个分类, 都是定义的枚举值。 我们可以根据具体的业务需求来修改这里的值。当然这里也制定了数据源提供的方式,主要是两种数据源:SQL 语句以及存储过程。其实这里我们有第三种数据源,那就是各种单据数据,因为系统中已经提供了各种单据数据访问的接口,我们没有必要去再次写SQL语句以及存储过程,存储过程和SQL语句只是给自定义报表来使用的,因为自定义报表不清楚系统是否已经提供了响应的接口。

    FastReport利用的数据源其实也就是DataSet,这里我们可以很方便的衔接起来。如果使用SQL语句或者存储过程我们就只需要将返回的结果集指定为DataSet,而其他的单据则只需要将List<T>集合转换为DataTable 即可。 这里就解决了字段不确定的问题,在技术上解决如下几个问题:

    (1) List<T> 转换为DataTable 的问题: 这种问题非常容易解决,有过.NET开发经验的应该都知道

    (2) SQL 语句中带有参数的问题: 这里规定SQL语句中可以带参数或者不带参数,如果有参数那必须使用@占位符

    (3) 存储过程中带有参数的问题:存储过程同样可以带参数或者不带参数

    (4) 调用SQL语句和存储过程执行问题: 如何确定调用的是SQL语句还是存储过程, 这里在数据源格式上做了分类,上图可以看得出

    

  四. SQL数据源

    使用SQL数据源遵循如下几个步骤:

    (1) 编写SQL语句,SQL语句中如果有参数则必须使用占位符参数

    (2) 添加占位符参数信息,占位符参数必须和SQL语句中的占位符参数一致

SELECT * FROM [dbo].[ConDetail] WHERE [email protected]

    假设我们定义报表的数据源如上,当然也可以不适用参数的。以上是客户实际案例中择选的,更多的细节就不透露,反正可以说明问题。

    

    有几个参数则添加几个参数,不能多也不能少,必须唯一对应,否则在执行的过程中就无法得到正确的数据。基本信息填好之后保存则可以生成一个报表的数据行,接下来要走的就是设计模板了。

    

    

    在线设计器打开之后就可以看到数据源中包含的所有数据字段了,这样就可以设计我们想要的报表格式。至于如何设计报表这里不做过的阐述,本文主要讲解设计这边功能的一个思路,报表工具的使用可以到网上查找相关的资料学习。设计好报表之后保存相关的设计即可,然后就可以打开预览查看了。

    

    打开预览页面可以看到相应的参数输入框,输入参数点击搜索即可展示报表的内容,完全根据自己的条件需要展示不同的数据。上面这个案例虽然有点简单,但是能够说明问题,在实际的客户过程中肯定不只是显示两列数据的,可能有多个语句更加复杂的操作在里面,但是都大同小异。

  五. 存储过程的使用

    存储过程相对SQL语句来说是一样的,这里唯一一个比较偏门的就是如何或者存储过程的参数问题,我们以入库单审核存储过程为例: Proc_AuditeInStorage

    

    数据源类型选择存储过程,在数据源中输入存储过程的名称,然后回车则会自动加载存储过程中的所有参数信息,然后自己将这些信息补充完整即可,比如显示的名称,页面显示的元素类型。

SELECT [SPECIFIC_CATALOG],[SPECIFIC_NAME],[ORDINAL_POSITION],[PARAMETER_MODE],[PARAMETER_NAME],[DATA_TYPE],[CHARACTER_MAXIMUM_LENGTH]
FROM [INFORMATION_SCHEMA].[PARAMETERS]
WHERE [SPECIFIC_NAME]=@SPECIFIC_NAME

    上面这段SQL语句是获取存储过程的相关参数信息的,如果有类似的功能需求可以参考利用一下这个SQL语句。SQL以及存储过程的相关执行都是依赖于Git.Framework.ORM 这个组件,当然你也可以使用其他的方式来实现。

  六. 单据打印的数据源

    单据打印的数据源有点特殊,他不需要自己写SQL或者存储过程来提供数据源,当然你执意要这么做也是没问题的。

public override DataSet GetPrint(string argOrderNum)
        {
            DataSet ds = new DataSet();
            InStorageEntity entity = new InStorageEntity();
            entity.SnNum = argOrderNum;
            entity = GetOrder(entity);
            if (entity != null)
            {
                List<InStorageEntity> list = new List<InStorageEntity>();
                list.Add(entity);
                DataTable tableOrder = list.ToDataTable();
                ds.Tables.Add(tableOrder);

                InStorDetailEntity detail = new InStorDetailEntity();
                detail.OrderSnNum = argOrderNum;
                List<InStorDetailEntity> listDetail = GetOrderDetail(detail);
                listDetail = listDetail.IsNull() ? new List<InStorDetailEntity>() : listDetail;
                DataTable tableDetail = listDetail.ToDataTable();
                ds.Tables.Add(tableDetail);
            }
            else
            {
                List<InStorageEntity> list = new List<InStorageEntity>();
                List<InStorDetailEntity> listDetail = new List<InStorDetailEntity>();
                DataTable tableOrder = list.ToDataTable();
                ds.Tables.Add(tableOrder);

                DataTable tableDetail = listDetail.ToDataTable();
                ds.Tables.Add(tableDetail);
            }
            return ds;
        }

入库单打印数据源提供

    入库单打印的数据都是List<T> ,我们这里需要将其转换为DataTable

public DataSet GetPrint(string SnNum)
        {
            DataSet ds = new DataSet();
            ConBookEntity entity = GetBook(SnNum);
            if (entity != null)
            {
                List<ConBookEntity> listBook = new List<ConBookEntity>();
                listBook.Add(entity);
                DataTable tableBook = listBook.ToDataTable();
                ds.Tables.Add(tableBook);

                List<ConDetailEntity> listDetail = GetDetailList(SnNum);
                listDetail = listDetail.IsNull() ? new List<ConDetailEntity>() : listDetail;
                DataTable tableDetail = listDetail.ToDataTable();
                ds.Tables.Add(tableDetail);

                List<BookMaterialEntity> listMaterial = GetMaterialList(SnNum);
                listMaterial = listMaterial.IsNull() ? new List<BookMaterialEntity>() : listMaterial;
                DataTable tableMaterial = listMaterial.ToDataTable();
                ds.Tables.Add(tableMaterial);
            }
            else
            {
                List<ConBookEntity> listBook = new List<ConBookEntity>();
                entity = new ConBookEntity();
                listBook.Add(entity);
                DataTable tableBook = listBook.ToDataTable();
                ds.Tables.Add(tableBook);

                List<ConDetailEntity> listDetail = null;
                listDetail = listDetail.IsNull() ? new List<ConDetailEntity>() : listDetail;
                DataTable tableDetail = listDetail.ToDataTable();
                ds.Tables.Add(tableDetail);

                List<BookMaterialEntity> listMaterial = null;
                listMaterial = listMaterial.IsNull() ? new List<BookMaterialEntity>() : listMaterial;
                DataTable tableMaterial = listMaterial.ToDataTable();
                ds.Tables.Add(tableMaterial);
            }

            return ds;
        }

施工单打印的数据源

    下载过github上代码看过的人,其实一看就明白这里都是套路,套路。 所有的单据都是这个套路,同时也遵循这个套路。

    在报表管理的页面中新建施工单打印的模板,报表类型要选择施工单,这里不能随意选一定要选择正确,相关的数据源都可以不指定,或者随意制定以下SQL或者存储过程即可。

    各种单据的打印我们需要提供的参数就是单据的唯一编号,这里是需要明确的,在吉特仓储系统中唯一编号使用的是GUID,其实这样可以很方便的解决这个问题。

; (function ($) {
    $.fn.CusReportDialog = function (options) {
        var defaultOption = {
            title:"选择打印模板",
            data: {},
            Mult: false,
            EventName: "click",
            callBack: undefined,
            ReportType:undefined
        };
        defaultOption = $.extend(defaultOption, options);

        var current=undefined;
        var target=$(this);

        var DataServer={
            Server: function () {
                var config = (function () {
                    var URL_GetList = "/Report/ManagerAjax/GetList";
                    return {
                        URL_GetList: URL_GetList
                    };
                })();

                //数据操作服务
                var dataServer = (function ($, config) {
                    //查询分页列表
                    var GetList=function(data,callback){
                        $.gitAjax({
                            url: config.URL_GetList,
                            data: data,
                            type: "post",
                            dataType: "json",
                            success: function (result) {
                                if(callback!=undefined && typeof callback=="function"){
                                    callback(result);
                                }
                            }
                        });
                    }

                    return {
                        GetList: GetList
                    }

                })($, config);
                return dataServer;
            },
            SetTable:function(result){
                current.find("#tabInfo").DataTable({
                    destroy: true,
                    data:result.Result,
                    paging:false,
                    searching:false,
                    scrollX: false,
                    bAutoWidth:true,
                    bInfo:false,
                    ordering:false,
                    columns: [
                        { data: ‘SnNum‘ ,render:function(data, type, full, meta){
                            return "<input type=‘checkbox‘ name=‘item_report‘ value=‘"+data+"‘ data-full=‘"+JSON.stringify(full)+"‘/>";
                        }},
                        { data: ‘ReportNum‘},
                        { data: ‘ReportName‘},
                        { data: ‘Remark‘}
                    ],
                    aoColumnDefs:[
                        { "sWidth": "15px",  "aTargets": [0] }
                    ],
                    oLanguage:{
                        sEmptyTable:"没有查询到任何数据"
                    }
                });
                var pageInfo=result.PageInfo;
                if(pageInfo!=undefined){
                    current.find("#myMinPager").minpager({ pagenumber: pageInfo.PageIndex, recordCount: pageInfo.RowCount, pageSize: pageInfo.PageSize, buttonClickCallback: DataServer.PageClick });
                }

                DataServer.BindEvent();
            },
            BindEvent:function(){
                if(defaultOption.Mult){
                    current.find("#tabInfo").find("input[name=‘item_all‘]").click(function(event) {
                        var flag=$(this).attr("checked");
                        if(flag){
                            current.find("#tabInfo").find("input[name=‘item_report‘]").attr("checked",true);
                        }else{
                            current.find("#tabInfo").find("input[name=‘item_report‘]").attr("checked",false);
                        }
                    });
                }
                else{
                    current.find("#tabInfo").find("input[name=‘item_all‘]").hide();
                    current.find("#tabInfo").find("input[name=‘item_report‘]").click(function(event) {
                        current.find("#tabInfo").find("input[name=‘item_report‘]").attr(‘checked‘, false);
                        $(this).attr("checked",true);
                    });
                }
            },
            GetSelect:function(){
                var list=[];
                current.find("#tabInfo").find("input[name=‘item_report‘]").each(function(i,item){
                    var flag=$(item).attr("checked");
                    if(flag){
                        var value=$(item).attr("data-full");
                        var item=JSON.parse(value);
                        list.push(item);
                    }
                });
                return list;
            }
        }

        var submit = function (v, h, f) {
            if (v == 1) {
                var list=DataServer.GetSelect();

                if (defaultOption.callBack != undefined && typeof (defaultOption.callBack) == "function") {
                    if(defaultOption.Mult){
                        defaultOption.callBack.call(target,list);
                    }else{
                        defaultOption.callBack.call(target,list[0]);
                    }
                }
            }
        };

        $(this).bind(defaultOption.EventName, function () {

            var Server=DataServer.Server();
            var search={};
            search["ReportType"]=defaultOption.ReportType;

            Server.GetList(search,function(result){

                var data=result.Result;
                if(data!=undefined && data.length>1){
                    $.jBox.open("get:/Report/Manager/Dialog", defaultOption.title, 650, 400, {
                        buttons: { "选择": 1, "关闭": 2 }, submit: submit, loaded: function (h) {
                            current=h;
                            DataServer.SetTable(result);
                        }
                    });
                }else{
                    defaultOption.callBack.call(target,data[0]);
                }
            });

        });

    };
})(jQuery);

单据打印模板的选择插件

    不同的单据再打印的时候可以选择不同的模板,这里可以使用这个插件来实现

    

$(‘#tabList‘).find(‘a.print‘).each(function(i,item){
                $(item).CusReportDialog({
                    ReportType:1,
                    callBack:function(result){
                        if(result!=undefined){
                            var SN=data[i].OrderSnNum;
                            var SnNum=result.SnNum;
                            var url="/Report/Manager/Show?SnNum="+SnNum+"&OrderNum="+SN;
                            window.location.href=url;
                        }
                    }
                });
            });

调用打印功能示例

  七. 模板设计过程中如何加载结构

    有个很显示的问题,在模板设计的过程中如果数据源为空,那模板设计就失去了设计的基础数据源结构,这样是毫无意义的。而很多情况下很多数据在后台过程中只有得到了数据才能得到结构,这是要命的问题。所以在这里走一定技巧性的处理,如果查询的数据源结果集为空,那么久默认添加一个空的数据进去,填充其结构,这里需要特别注意。

List<InStorageEntity> list = new List<InStorageEntity>();
List<InStorDetailEntity> listDetail = new List<InStorDetailEntity>();
DataTable tableOrder = list.ToDataTable();
ds.Tables.Add(tableOrder);

DataTable tableDetail = listDetail.ToDataTable();
ds.Tables.Add(tableDetail);

默认处理数据源结构

时间: 2024-10-08 08:16:40

吉特仓储管系统(开源WMS)--Web在线报表以及打印模板分享的相关文章

吉特仓储管系统(开源WMS)--分享两月如何做到10W+的项目

在此文开篇之处先特别申明,此文在有些人的眼中会有广告的嫌疑,但是本人不想将其作为一个广告宣传的文章,在此提到软件内容部分请大家予以谅解和包含,作为时间不算短的程序员给大家分享一些自己开发吉特仓储管理软件相关的经验和坑,当然还有一些自己从中获利的方式,不能说给大家指条明路吧,算是作为程序开发人的相互经验交流. 此文本来想写在国庆假期之前的,但是那段时间公司事情刚好很忙,所以没有来得及写此文.当时要搞Solr搜索引擎,因为自己不熟悉java程序所以在弄得过程中有些费力,而且自己本身也不是一心苦心专研

Web在线文件管理器(web os) KODExplorer

KODExplorer是款开源的Web在线文件管理.代码编辑器.它提供了类windows经典用户界面,一整套在线文件管理.文件预览.编辑.上传下载.在线解压缩.音乐播放功能.让你直接在浏览器端实现web开发.源码文件预览.网站部署的同时拥有与本地操作一样方便.快捷.安全的体验.

Linux -- 利用IPS(入侵防御系统) 构建企业Web安全防护网

一.IPS系统简介 (应用层上应用) 防火墙只在网络层上应用,IPS 和防火墙相比,检测及过滤功能更为强大,它通过串联在网络主干线路上,对防火 墙所不能过滤的攻击进行过滤.这样一个两级的过滤模式,可以最大地保证系统的安全.在 一些专业的机构,或对网络安全要求比较高的地方,IPS和其他审计跟踪产品结合,可以提 供针对网络信息资源全面的审计资料,这些资料对于攻击还原.入侵取证.异常事件识别. 网络故障排除等等都有很重要的作用. 作为串接部署的设备,IPS必须要确保用户业务不受影响,错误的阻断必定意味

手把手让你实现开源企业级web高并发解决方案(lvs+heartbeat+varnish+nginx+eAccelerator+memcached)

原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 .作者信息和本声明.否则将追究法律责任.http://freeze.blog.51cto.com/1846439/677348 此文凝聚笔者不少心血请尊重笔者劳动,转载请注明出处.违法直接人肉出电话 写大街上. http://freeze.blog.51cto.com/个人小站刚上线 http://www.linuxwind.com 有问题还可以来QQ群89342115交流. 今儿网友朋友说:freeze黔驴技穷了,博客也不更新,也

Brackets - 强大免费的开源跨平台Web前端开发工具IDE (HTML/CSS/Javascript代码编辑器)

Brackets 是一个免费.开源且跨平台的 HTML/CSS/JavaScript 前端 WEB 集成开发环境 (IDE工具).该项目由 Adobe 创建和维护,根据MIT许可证发布,支持 Windows.Linux 以及 OS X 平台. Brackets 的特点是简约.优雅.快捷!它没有很多的视图或者面板,也没太多花哨的功能,它的核心目标是减少在开发过程中那些效率低下的重复性工作,例如浏览器刷新,修改元素的样式,搜索功能等等.和 Sublime Text.Everedit 等通用代码编辑器

Python web开发:几个模板系统的性能对比(转)

http://blog.chedushi.com/archives/910 结论: 点评一下吧.django就是个渣,不多废话了.webpy的代码很简洁,可惜速度太慢了.bottle看起来快一点,不过也没有多出彩.tornado本身速度很快,不过模板--也就是如此吧.真的值得一用的,只有jinja2,mako,cheetah三个.速度都小于了5ms,单核每秒可以生成200个页面,16核机器上大概就能跑到3000req/s,性能比较高.jinja2的速度比较折衷,配置灵活,语法类似django是他

零基础如何系统学习Java Web

零基础如何系统学习Java Web? 我来给你说一说 你要下决心,我要转行做开发,这样你才能学成. 你要会打字,我公司原来有一个程序员,打字都是两个手一指禅,身为程序员你一指禅怎么写出的代码,半个月后被辞退了,当然我们还是朋友. 前两个条件都符合了你就可以学了,首先要了解web是什么,一般呢,java web开发无外乎就这么两大类,第一,互联网公司,第二,软件公司.对于互联网公司和软件公司还有一些差别,互联公司是面向广大网民的,会有专门的ui设计,前台开发,后台代码开发,ios开发,androi

RDIFramework.NET ━ 9.13 系统日志与系统异常管理 ━ Web部分

RDIFramework.NET ━ .NET快速信息化系统开发框架 9.13  系统日志与系统异常管理 -Web部分  一个软件在投入运行时不可能没有任何异常,在软件发生异常时及时的记录下来,也好我们及时对异常进行跟踪,以解决发生的异常,避免异常的再次发生.异常分为两种情况,一种为客户端发生的异常,另一种为服务端发生的异常.对于客户端发生的异常,通常都会及时的弹出,用户看到后可以反馈给我们.服务器端产生的异常会自动记录到数据库中,管理员可以通过框架异常管理进行查看操作. 框架日志管理对于框架的

在Win7系统中搭建Web服务器

局 域网Web服务器的主要功能是实现资源共享,同时借助于局域网服务器访问页面可有效的实现信息的同步.利用Web服务器,我们随时随地都可以将自己的信息 上传到服务器端,让其它关注你的用户能在第一时间内了解的思想和动态.下面笔者就如何在Windows7系统下搭建Web服务器与大家交流一下. 工具/原料 Windows7操作系统 方法/步骤 1 打开控制面板,选择并进入“程序”,双击“打开或关闭Windows服务”,在弹出的窗口中选择“Internet信息服务”下面所有地选项,点击确定后,开始更新服务