通过实例学习Fireasy开发(中篇)

      本文目录

通过实例学习Fireasy开发(上篇)

通过实例学习Fireasy开发(下篇)

通过实例学习Fireasy开发(补充)

上篇我们介绍了进行Fireasy开发的前期准备,接下来,我们将通过两个小功能来演示怎么进行业务开发。

      一、部门列表页面

部门是树型结构,Fireasy采用了00010001这样的编码来支持树结构实体的快速开发。

1、 修改Dept类(使用Dept_Ex分部类文件),在类名称上添加EntityTreeMappingAttribute特性,InnerSign指定存储00010001编码的属性,在Dept类里是使用No作为这一标识的。

    //如果要启用实体验证,请使用以下特性,并在 DeptMetadata 中定义验证特性。
    [MetadataType(typeof(DeptMetadata))]
    [EntityTreeMapping(InnerSign="No")]
    public partial class Dept
    {
    }

编码每一层默认使用4位编码,如果要改成2位,可以将SignLength属性设置为2。

2、 修改Dept类,使它实现ITreeNode<>接口,目的是返回json的时候可以使用子节点等这样的属性,后面会介绍。

    //如果要启用实体验证,请使用以下特性,并在 DeptMetadata 中定义验证特性。
    [MetadataType(typeof(DeptMetadata))]
    [EntityTreeMapping(InnerSign="No")]
    public partial class Dept: ITreeNode<Dept>
    {
        public System.Collections.Generic.List<Dept> Children { get; set; }

        System.Collections.IList ITreeNode.Children
        {
            get { return Children; }
            set { Children = (System.Collections.Generic.List<Dept>)value; }
        }

        public bool HasChildren { get; set; }

        public bool IsLoaded { get; set; }
    }

3、 打开DeptList.aspx文件,将datagrid替换成treegrid,修改loadData函数如下:

        //加载数据
        function loadData() {
            $(‘#dg‘).treegrid({
                url: ‘DeptList.ajx/GetDepts‘,
                fit: true,//自动填充
                idField: ‘id‘,
                treeField: ‘text‘,
                columns: [[

                    { field: ‘text‘, title: ‘名称‘, width: 150, halign: ‘center‘, align: ‘left‘, sortable: true },
                    { field: ‘no‘, title: ‘编码‘, width: 150, halign: ‘center‘, align: ‘left‘, sortable: true }
                ]]
            })
        }

treeField属性指定作为节点控制的字段,我们使用text作为其字段。

4、treegrid的返回json是一个树结构,且展开子节点时是用id参数把当前节点的主键值传递过去的。在DeptList.aspx.cs文件中,修改GetDepts方法如下:

        /// <summary>
        /// 根据查询条件获取部门。
        /// </summary>
        /// <param name="id">当前的父ID。</param>
        /// <param name="keyword">关键字</param>
        /// <returns></returns>
        [ExceptionBehavior(true)]
        public List<Dept> GetDepts(string id, string keyword)
        {
            //添加一个json转换器
            ServiceContext.Current.Converters.Add(new DeptJsonConverter());

            var result = new List<Dept>();

            using (var context = new DbContext())
            {
                //查询出父部门
                Dept parent = null;
                if (!string.IsNullOrEmpty(id))
                {
                    parent = context.Depts.Get(id);
                    if (parent == null)
                    {
                        return result;
                    }
                }

                //创建一个Dept的树实体持久化对象
                var persister = context.CreateTreePersister<Dept>();

                //查询出它的所有孩子
                foreach (var child in persister.QueryChildren(parent).ToList())
                {
                    child.Children = new List<Dept>();

                    //再判断一下这个孩子是否还有孩子
                    child.HasChildren = persister.HasChildren(child);
                    result.Add(child);
                }

                return result;
            }
        }

在每展开一个节点的时候,treegrid都会来请求该方法,id即当前节点的主键值。Fireasy提供了EntityTreePersister这样一个类,可以方便的查询出下级孩子,插入节点,移动节点等。

DeptJsonConverter类是一个json格式的转换类,我们查看其基类TreeNodeJsonConverter可得知,它实则是提供treegrid所需的json数据格式,如:

{ id: "", text: "", children: [], state: "closed", attributes: { } }

它的构造方法是传入一个字典类型,指定其他属性值,比如 text、no、attributes 等等。id不用指定,在基类里已经输出了,因为ITreeNode接口有一个Id属性的(如果实体类型的主键不是ID或不是string类型,要想办法实现它)。

        private class DeptJsonConverter : TreeNodeJsonConverter<Dept>
        {
            public DeptJsonConverter()
                : base(new Dictionary<string, Func<Dept, object>>
                    {
                        { "text", s => s.Name },
                        { "no", s => s.No },
                        { "attributes", s => new { no = s.No } }
                    })
            {
            }
        }

好了,DeptList页面的修改就完成了,如果你现在迫不急待的想看到结果如何,可以在TB_Dept表里加入三条数据(注意NO的编码规则):

浏览DeptList.aspx页面,如果人品较好的话,应该能看到树结构数据了:

      二、部门编辑页面

DeptEdit.aspx看似简单,其实要处理的地方很多,0^0,我们需要让部门创建到某一个节点下,或是从某一节点移动到某一节点,这里有几个细节的地方必须得说明一下。

首先,得介绍一下Html扩展。Fireasy提供了类似MVC的HTML扩展标记,并由Fireasy.EasyUI组件提供了丰富的easyui扩展,比如TextBox、ComboBox、ComboTree、DateBox、NumberBox等等,具体可以查看Fireasy.EasyUI组件中的类。

那么,在DeptEdit.aspx(以后的所有Edit页面都会存在)中,看这一段代码:

    <% var Html = new HtmlHelper<WebApplication.Data.Model.Dept>(); %>
    <div data-options="region:‘center‘,border:false">
        <table class="form-body">

            <tr>
                <td class="addon">ID</td>
                <td><%= Html.TextBox(s => s.Id) %></td>
            </tr>
            <tr>
                <td class="addon">编码</td>
                <td><%= Html.TextBox(s => s.No) %></td>
            </tr>
            <tr>
                <td class="addon">名称</td>
                <td><%= Html.TextBox(s => s.Name) %></td>
            </tr>
            <tr>
                <td class="addon">排序</td>
                <td><%= Html.TextBox(s => s.OrderNo) %></td>
            </tr>
        </table>
    </div>

第一行定义了一个Html变量,它是基于实体类型的HTML扩展,在使用EasyUI扩展的时候,就可以把相应的属性引用进来了。使用基于实体类型的HTML扩展还有一个好处,实体的验证规则,如必填、长度、小数位数等,都是通过实体类型的*Metadata类来定义的,可以看*_EX.cs文件。

DeptEdit页面需要增加一个域,用来选择上级单位。随便把No删掉,把排序改成NumberBox。

    <% var Html = new HtmlHelper<WebApplication.Data.Model.Dept>(); %>
    <div data-options="region:‘center‘,border:false">
        <table class="form-body">
            <tr>
                <td class="addon">上级部门</td>
                <td><%= Html.ComboTree("cboParent").MarkNoClear() %></td>
            </tr>
            <tr>
                <td class="addon">名称</td>
                <td><%= Html.TextBox(s => s.Name) %></td>
            </tr>
            <tr>
                <td class="addon">排序</td>
                <td><%= Html.NumberBox(s => s.OrderNo) %></td>
            </tr>
        </table>
    </div>

修改js代码,加入一个loadDepts函数,沿用DeptList.aspx.cs中的方法,只是需要增加两个参数,targetId和currentId,targetId是combobox中选中的节点,即当前部门的父部门,currentId是当前修改的部门ID。GetDepts方法的修改呆会再介绍。

        var pid = ‘<%= Request.QueryString["parentId"] %>‘;

        //加载模块
        function loadDepts() {
            $(‘#cboParent‘).combotree({
                url: "DeptList.ajx/GetDepts?targetId=" + pid + ‘&currentId=‘ + id,
                panelWidth: 260,
                onLoadSuccess: function () {
                    $(‘#cboParent‘).combotree(‘setValue‘, pid);
                }
            })
        }

pid在添加部门的时候是从DeptList.aspx,修改DeptList.aspx的addInfo函数如下:

        //添加信息
        function addInfo() {
            var row = $(‘#dg‘).treegrid(‘getSelected‘);
            var parentId = row == null ? ‘‘ : row.id;
            common.showDialog(‘DeptEdit.aspx?parentId=‘ + parentId, ‘部门‘, 500, 300, function () {
                $(‘#dg‘).treegrid(‘reload‘);
            });
        }

返回到DeptEdit.aspx,修改loadInfo函数,在新增和修改场景下加入loadDepts函数:

        //加载信息
        function loadInfo() {
            if (id != ‘‘) {
                $.getJSON(‘DeptEdit.ajx/GetDept?id=‘ + id, function (data) {
                    common.processResult(data, function () {
                        $(‘#form1‘).form(‘load‘, data);
                        loadDepts();
                    });
                });

                $(‘#btnSaveAndNew‘).remove();
            }
            else {
                loadDepts();
            }
        }

你可能注意到了,在修改部门的时候,pid并没有从DeptList.aspx中传入,所以修改部门的时候pid为空,loadDepts将读到所有的数据,它不会选定当前部门的父节点。因此,我们需要对GetDept方法做一些修改,目的是能返回当前部门的上级部门ID:

        /// <summary>
        /// 根据ID获取部门。
        /// </summary>
        /// <param name="id">信息ID。</param>
        /// <returns></returns>
        public object GetDept(string id)
        {
            using (var context = new DbContext())
            {
                var info = context.Depts.Get(id);
                if (info != null)
                {
                    //创建一个Dept的树实体持久化对象
                    var persister = context.CreateTreePersister<Dept>();
                    //找以它的上一个模块
                    var parentId = persister.RecurrenceParent(info).FirstOrDefault().AssertNotNull(s => s.Id);
                    return info.Extend(new { ParentId = parentId });
                }

                return null;
            }
        }

好了,返回到DeptEdit.aspx页面中,修改loadInfo函数:

        //加载信息
        function loadInfo() {
            if (id != ‘‘) {
                $.getJSON(‘DeptEdit.ajx/GetDept?id=‘ + id, function (data) {
                    common.processResult(data, function () {
                        $(‘#form1‘).form(‘load‘, data);

                        if (data.ParentId != undefined) {
                            pid = data.ParentId;
                        }

                        loadDepts();
                    });
                });

                $(‘#btnSaveAndNew‘).remove();
            }
            else {
                loadDepts();
            }
        }

现在,不要忘了DeptList.aspx.cs中的GetDepts方法,刚刚我们增加了两个参数targetId和currentId。

targetId是目标节点,它可能处于某一层下,我们需要在返回的json数据中,逐级的把这些节点加载出来,这样,页面那边才选得中这个节的。

currentId是当前节点,目的是在修改部门的时候,不能让它选择到它下面的节点,不然移动节点将发生异常。

        /// <summary>
        /// 根据查询条件获取部门。
        /// </summary>
        /// <param name="id">当前的父ID。</param>
        /// <param name="targetId">目标节点,返回的json中要能看到它。</param>
        /// <param name="currentId">当前部门的节点,不要列出它的孩子。</param>
        /// <param name="keyword">关键字</param>
        /// <returns></returns>
        [ExceptionBehavior(true)]
        public List<Dept> GetDepts(string id, string targetId, string currentId, string keyword)
        {
            //添加一个json转换器
            ServiceContext.Current.Converters.Add(new DeptJsonConverter());

            var result = new List<Dept>();

            using (var context = new DbContext())
            {
                //查询出父部门
                Dept parent = null;
                if (!string.IsNullOrEmpty(id))
                {
                    parent = context.Depts.Get(id);
                    if (parent == null)
                    {
                        return result;
                    }
                }

                //创建一个Dept的树实体持久化对象
                var persister = context.CreateTreePersister<Dept>();

                foreach (var child in persister.QueryChildren(parent).ToList())
                {
                    //排除当前模块
                    if (currentId == child.Id)
                    {
                        continue;
                    }

                    child.Children = new List<Dept>();
                    child.HasChildren = persister.HasChildren(child);
                    result.Add(child);
                }

                //如果要显示到子模块
                if (!string.IsNullOrEmpty(targetId))
                {
                    var target = context.Depts.Get(targetId);
                    if (target != null)
                    {
                        //得到父级ID列表 0:父 1:祖父 2: 曾祖父 .....
                        var parents = persister.RecurrenceParent(target).Select(s => s.Id).ToList();

                        //递归展开一下级
                        result.Expand(parents, (childId) => GetDepts(childId, targetId, currentId, keyword), parents.Count - 1, currentId);
                    }
                }

                return result;
            }
        }

RecurrenceParent得到目标节点的父列表,那么我们从根祖父这一级一直往下递归展开。Expand是一个扩展方法。

现在,我们来看看改造后的DeptEdit.aspx页面的运行效果。我们在DeptList.aspx中选择一个子节点办公室,点击添加按钮。

弹出的DeptEdit.aspx中,上级部门自动填充到了办公室,并且选定了它。

最后,我们完成部门保存的代码调整。

在DeptEdit.aspx中修改saveInfo函数,增加一个parentId参数。

        //保存信息
        function saveInfo(isNew) {
            if (!$(‘#form1‘).form(‘validate‘)) {
                return;
            }

            var postData = new Object();

            //将表单填充的内容序列化为json
            var data = $(‘#form1‘).form(‘save‘);
            postData["info"] = JSON.stringify(data);
            var parentId = $(‘#cboParent‘).combotree(‘getValue‘);

            common.showProcess();
            $.post(‘DeptEdit.ajx/SaveDept?parentId=‘ + parentId + ‘&id=‘ + id, postData, function (result) {
                common.processResult(result, function () {
                    if (isNew) {
                        $(‘#form1‘).form(‘clear‘);
                    }

                    id = isNew ? ‘‘ : result.data;
                    common.setReturnValue(true);
                });
            });
        }

相应的,DeptEdit.aspx.cs文件中的SaveDept方法也要作调整如下:

        /// <summary>
        /// 保存部门。
        /// </summary>
        /// <param name="id">id。</param>
        /// <param name="parentId">父部门ID。</param>
        /// <param name="info">要保存的数据。</param>
        /// <returns>id</returns>
        public object SaveDept(string id, string parentId, Dept info)
        {
            using (var context = new DbContext())
            {
                var persister = context.CreateTreePersister<Dept>();
                var parent = persister.First(parentId);

                if (string.IsNullOrEmpty(id))
                {
                    info.Id = Guid.NewGuid().ToString();

                    if (parent == null)
                    {
                        persister.Create(info);
                    }
                    else
                    {
                        persister.Insert(info, parent, EntityTreePosition.Children);
                    }
                }
                else
                {
                    persister.Update(info, s => s.Id == id);

                    //需要取出NO,不然无法移动
                    info.No = persister.First(id).No;

                    persister.Move(info, parent, EntityTreePosition.Children);
                }

                return Result.Success("保存成功。", info.Id);
            }
        }

至此,部门管理的功能就完成了,由于篇幅太长(预想中没有这么长),员工管理将在下篇中介绍。

时间: 2024-10-08 03:18:34

通过实例学习Fireasy开发(中篇)的相关文章

通过实例学习Fireasy开发(补充)

      本文目录 通过实例学习Fireasy开发(上篇) 通过实例学习Fireasy开发(中篇) 通过实例学习Fireasy开发(下篇)       通过实例学习Fireasy开发(补充) 前面的功能已经开发完成了,但是这里专门用一章来进行补充.       一.EasyUI验证 不知道你有没有发现,我们虽然在EmployeeMetada里加了验证特性RequiredAttribute.StringLengthAttribute,但是页面并没有在data-options里生成validTyp

通过实例学习Fireasy开发(下篇)

      本文目录 通过实例学习Fireasy开发(上篇) 通过实例学习Fireasy开发(中篇)       通过实例学习Fireasy开发(下篇) 通过实例学习Fireasy开发(补充)       一.员工列表页面 EmployeeList.aspx页面的需求是这样的,左边是部门树,右边是员工列表,点击部门节点后右边显示该部门下面的员工,同时提供岗位.生日期间查询. 对table:#dg处进行改造,加下一个treegrid列表: <div data-options="region:

通过实例学习Fireasy开发(上篇)

Fireasy一直在发布新版本,但是怎么用,到底好不好用,估计大家都有这样的疑惑.所以,今天花点时间通过一个简单的示例一步一步来介绍fireasy的用法. 首先有必要介绍一下Fireasy的组件构成: Fireasy.Common 公共组件库,主要包含缓存管理.日志管理.序列化.动态编译.扩展方法等. Fireasy.Data 数据库组件库,提供数据库的操作,语法.批量插入.数据库构架等. Fireasy.Data.Entity 实体组件库,ORMapper.LINQ解析.数据上下文.树实体持久

彩票历史记录分析工具 -- 通过实例学习wpf开发

前言 虽然本人对彩票不感兴趣,仍然有不少人对此情有独钟.他们花大量时间精力去分析彩票的历史记录,企图发现规律,为下一次投注做指导,希望“赢的“”概率增大.不管研究历史记录是否有意义,我用软件实现了对彩票的分析,手工分析彩票几天工作量,现在一秒可以实现. 执行程序,点我下载! 程序界面 处理原理分析: 程序实际上是对六合彩分析(彩票种类很多,本文只处理一种).数据格式如下: 2010001 11 13 22 16 21 182010002 22 28 16 5 14 262010003 5 14

2016年1月25日 《1024伐木累》-小白篇之开发网站,三天!(中篇-2奇怪的IE)-总章节十一

往期回顾:  老王的“先见之明”,解决了困扰耗仔三人的大难题.顺利安装完开发工具,大家投入紧张的工作.航空部领导的突然闯入,IE不兼容,页面错乱,摆在三人面前的形势依然严峻.第一次见这阵仗的耗仔,又会怎么办? 2016-02-01<1024伐木累>-小白篇之开发网站,三天!(中篇-2奇怪的IE) # region 总章节11 IE出了问题,来不及找原因,屋内就涌进一堆领导,你一言我一语,围着三人叽叽喳喳说个不停. 此时的耗仔头都大了,这到底是要干啥,刚开始没几个小时,再这么折腾下去,还怎么工作

wampServer 中篇 PHP开发测试环境 使用设置

wampserver 安装完成后,桌面出现一个红色图标. 双击启动. 右下角显示启动状态.红色没能启动.启动成功为绿色.     默认菜单是英文,可以改成简体中.          改完后菜单简体中文的样子.正常菜单(左键),右键菜单.     mySql控制台菜单,密码默认为空,直接回车.      常用菜单 开发测试目录,默认在安装目录下的 www 下, localhost 显示最下面,phpmyadmin 管理查看数据库 我的 wampserver 默认安装在 D 盘,本地网站WWW目录,

中篇: php 微信支付 基于Thinkphp3.2开发

⑤ 微信支付接口的使用 a.微信公众平台文档:https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421141115 b.微信支付开发文档:https://pay.weixin.qq.com/wiki/doc/api/index.html c.下载微信支付DEMO https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=7_1 d.解压并改名为WxPay放到项目中 e.公众号支付

利用HTML5开发Android笔记(中篇)

资源来自于www.mhtml5.com 杨丰盛老师成都场的PPT分享 一个很简明的demo 可以作为入门基础 学习的过程中做了点笔记 整理如下 虽然内容比较简单 但是数量还是比较多的 所以分了3篇 (上)包括Android设备多分辨率的问题,Android中构建HTML5应用程序基础 (中)包括Android与JS之间的互动,Android处理JS的警告对话框等,Android中的调试 (下)包括本地储存在Android中的应用,地理位置的应用,离线应用的构建 —————————————————

Hive UDF开发实例学习

1. 本地环境配置 必须包含的一些包. http://blog.csdn.net/azhao_dn/article/details/6981115 2. 去重UDF实例 http://blog.csdn.net/lifuxiangcaohui/article/details/41548667 http://www.cnblogs.com/end/archive/2012/10/12/2721543.html