ExtJs十四(ExtJs Mvc图片管理之四)

现在来实现排序的问题。要实现远程排序,首先要清楚的是排序信息是如何提交到服务器的,而这个,利用FireBug相当简单。

在VS,切换到PicManager.js文件,找到filestore的定义,先将renmoteSort修改为true。然后添加sorters配置项,代码如下:

            sorters: [
                { property: "modify", direction: "DESC" }
            ],

这段代码的意思就是默认排序的字段为modify,排序方向为顺序排序。

那么,这个方法要怎么写呢?首先,要让该方法知道,它要提取的字段包括那些,只有符合要求的字段才会被提取。其次,就是要将要处理的字符串传递给该方法。最后,如果处理过程找不到符合要求的排序信息,就返回一个默认的排序,因而,需要给该方法传递一个默认值。

最后,要考虑方法怎么返回排序信息。新的LINQ版本支持动态查询,它的排序信息可以是以下格式:

it.字段1 排序方向1, it.字段2 排序方向2,...

因而,方法只需要返回以上形式组成的字符串就可以了。

目标确定了,切换到MyFunction.cs来完成这个方法。在MyFunction.cs内创建一个名为ProcessSorterString的静态方法,代码如下:

public static string ProcessSorterString(string[] fields, string sortinfo,string defaultSort)
        {
            if (string.IsNullOrEmpty(sortinfo)) return defaultSort;
            JArray ja = JArray.Parse(sortinfo);
            string result = "";
            foreach (JObject c in ja)
            {
                string field = (string)c["property"];
                if (fields.Contains(field))
                {
                    result += string.Format("it.{0} {1},", field, (string)c["direction"] == "ASC" ? "" : "DESC");
                }
            }
            if (result.Length > 0)
            {
                result = result.Substring(0, result.Length - 1);
            }
            else
            {
                result = defaultSort;
            }
            return result;
        }

代码中,先检查要处理的排序信息是否为空或空字符串,如果是,返回默认值。接着,将字符串转换为JArray对象,然后一个个提取排序对象,如果在指定的字段数组内包含该字段,就根据格式组合字符串。最后,检查组合的字符串是否有符合要求的排序信息,如果没有,返回默认值。

在实现前,先打开管理NuGet程序包窗口,搜索DynamicQuery,然后安装Dynamic Expression API程序包以实现动态排序。如果是使用实体框架,其内部已经包含了动态查询,不需要安装该包,在这里,因为返回的是FileInfo集合,没有使用到实体框架,因而要安装该程序包。

现在切换到File控制器,修改代码如下以实现排序功能:现在切换到File控制器,修改代码如下以实现排序功能:

public JObject List()
        {
            bool success = false;
            string msg = "";
            JArray ja = new JArray();
            int total = 0;
            try
            {
                int start = 0;
                int.TryParse(Request["start"], out start);
                string path = Request["path"] ?? "/";
                string sort = Request["sort"] ?? "";
                sort = Helper.MyFunction.ProcessSorterString(new string[] { "filename", "modify", "size" }, sort, "it.LastWriteTime ASC");
                DirectoryInfo dir = new DirectoryInfo(Server.MapPath(root + path));
                total = dir.GetFiles().Count();
                var q = dir.GetFiles().Select(m => new
                {
                    filename = m.Name,
                    modify = m.LastWriteTime,
                    size = m.Length
                }).AsQueryable().OrderBy(sort).Skip(start).Take(50);
                foreach (var c in q)
                {
                    ja.Add(new JObject { 
                        new JProperty("path",path),
                        new JProperty("filename",c.filename),
                        new JProperty("modify",c.modify.ToString("yyyy-MM-dd hh:mm")),
                        new JProperty("size",c.size)
                    });
                }
                success = true;

            }
            catch (Exception e)
            {
                msg = e.Message;
            }
            return Helper.MyFunction.WriteJObjectResult(success, total, msg, ja);
        }

现在,要在客户端加一个排序菜单以实现排序功能。切换到PicManager.js文件,找到me.items的定义,在图片文件的配置项中添加tbar配置项,用来放置一个分页工具条,并在工具条上放一个SplitButton用来实现排序功能。在SplitButton下定义一个由6个子菜单组成的菜单。这6个子菜单必须是单选的,也就是一次只能选择一个。具体代码如下:

                tbar: {
                    xtype: "pagingtoolbar",
                    pageSize: 20, displayInfo: true, store: me.filestore,
                    items: [
                        '-',
                        { xtype: "splitbutton", iconCls: "sort", tooltip: "排序", text: "排序",
                            scope: me,
                            menu: {
                                items: [
                                    {
                                        text: '文件名顺序', group: 'sort', checked: false, checkHandler: me.onSort, scope: me,
                                        fieldname: "filename", sortdir: "ASC"
                                    },
                                    {
                                        text: '文件名降序', group: 'sort', checked: false, checkHandler: me.onSort, scope: me,
                                        fieldname: "filename", sortdir: "DESC"
                                    },
                                    {
                                        text: '修改日期顺序', group: 'sort', checked: false, checkHandler: me.onSort, scope: me,
                                        fieldname: "modify", sortdir: "ASC"
                                    },
                                    {
                                        text: '修改日期降序', checked: true, group: 'sort', checkHandler: me.onSort, scope: me,
                                        fieldname: "modify", sortdir: "DESC"
                                    },
                                    {
                                        text: '文件大小顺序', group: 'sort', checked: false, checkHandler: me.onSort, scope: me,
                                        fieldname: "size", sortdir: "ASC"
                                    },
                                    {
                                        text: '文件大小降序', group: 'sort', checked: false, checkHandler: me.onSort, scope: me,
                                        fieldname: "size", sortdir: "DESC"
                                    }
                                ]
                            }
                        }
                    ]
                }

注意代码中子菜单的定义。每个子菜单都有一个group配置项,且它们的值是相同的,这样就可将6个子菜单组合为一组了。配置项checked是必不可少的,该配置项决定了菜单的是一个单选功能的子菜单。因为刚才在Store的定义中,默认情况下是以修改日期降序排序的,因而该子菜单的checked的值被设置为true。还有一个地方比较特别,就是把子菜单相关的字段和排序方向都以配置项形式定义好了,这样在编写onSort方法的时候,处理起来就很方便,以下就是onSort的代码:

        onSort: function (item, checked) {
        var me = this;
        if (checked) {
            me.filestore.sort({ property: item.fieldname, direction: item.sortdir });
            me.filestore.load();
        }
    },

因为每个子菜单都包含了字段和排序方向信息,因而,这里就不需要做判断了,直接调用Store的sort方法进行排序就行了。重新设置排序后,调用load方法重新就可以了。这里唯一要注意的地方是,子菜单在取消选择和选择的时候都会触发该方法,因而需要检查checked的值,当它为true的时候才进行处理。

在定义排序按钮的时候使用iconCls配置项为图片添加了一个图片,因而要在app.css中添加它的样式,代码如下:

.sort
{
     background:url("../images/sort.png")!important;
}

好了,现在生成一下解决方案,然后刷新一下页面,

至此,排序功能就实现了。

现在考虑一下视图的选择问题,在操作系统中,一般都可以使用拖动的方式选择文件,这个功能相当实用,而在Ext JS,要实现该功能也很简单,只有使用Ext JS包中的用户插件Ext.ux.DataView.DragSelector就可简单实现。

先在解决方案ExtJS\ux目录下创建一个DataView目录,然后在Ext JS包中examples\ux\DataView目录下,将DragSelector.js文件复制到该目录。为什么要这样?因为动态加载是根据类名来找文件的,注意Ext.ux.DataView.DragSelector的类名,在ux目录下,多了一个DataView,因而需要添加DataView目录。

在DataView目录下还有一个DragSelector.css文件,定义了DragSelector要用到的一些样式,把文件里的样式复制到app.css就行了。

切换到PicManager.js文件,先在layout配置项下加一个requires配置项,来声明该类需要使用到DragSelector类,代码如下:

requires: ["Ext.ux.DataView.DragSelector"],

接着在me.dataview的定义中添加以下代码来创建插件:

            plugins: [
                Ext.create('Ext.ux.DataView.DragSelector', {})
            ],

因为DragSelector类没有定义别名,所以不能使用xtype进行定义,只能直接创建了。

刷新一下浏览器,然后在视图中任意点按下鼠标左键,然后拖动鼠标,通过拖动方式选择图片了。

现在来完成图片的删除功能。先在分页工具条上添加一个删除按钮,代码如下:

{ iconCls: "picture-delete", handler: me.onDelete, scope: me, disabled: true, tooltip: "删除图片" }

注意,目前按钮的状态是禁用状态的。因而需要在视图选择了图片的时候开启它。同文件夹删除按钮一样,这里也不能使用id,只能使用查询方式获取按钮。在dataview的定义中,添加以下代码监听视图的selectionchange事件:

            listeners: {
                scope: me,
                selectionchange: me.onPictureSelect
            }

接着完成onPictureSelect方法,代码如下:

    onPictureSelect: function (model, sels) {
        this.down("button[tooltip=删除图片]").setDisabled(sels.length == 0);
    },

因为没有合适的上一层组件,因而只能从组件本身开始往下找了。

现在完成删除图片的onDelete方法,代码如下:

    onDelete: function () {
        var me = this,
            rs = me.dataview.getSelectionModel().getSelection();
        if (rs.length > 0) {
            content = ["确定删除以下图片?"];
            for (var i = 0; ln = rs.length, i < ln; i++) {
                content.push(rs[i].data.filename);
            }
            Ext.Msg.confirm("删除图片", content.join("<br/>"), function (btn) {
                if (btn == "yes") {
                    var me = this,
                        store = me.dataview.store, 
                        rs = me.dataview.getSelectionModel().getSelection();
                    store.remove(rs);
                    store.sync({
                        success: function (e, opt) {
                            this.store.commitChanges();
                        },
                        failure: function (e, opt) {
                            this.store.rejectChanges()
                            Ext.Msg.alert("发生错误", e.exceptions[0].error);
                        },
                        scope: me.dataview
                    });
                }
            }, me)
        } else {
            Ext.Msg.alert('删除记录', '请选择要删除的记录。');
        }
    }

代码与之前删除操作的代码没什么不同,因而有兴趣,可以研究一下将这些代码统一起来,这样就不用粘贴复制了。这里的焦点还是使用Store的remove方法删除数据,然后调用sync同步,如果服务器端删除成功,就调用commitChanges方法确认修改,否则调用rejectChanges方法取消删除。如果是希望在删除后重新加载页面,可以将commitChanges方法修改为load方法,重新加载数据。

现在切换到File控制器完成删除操作,方法与文件夹的删除差不多,代码如下:

public JObject Delete()
        {
            bool success = false;
            string msg = "";
            JArray ja = null;
            try
            {
                string data = Request["data"] ?? "";
                if (string.IsNullOrEmpty(data))
                {
                    msg = "错误的提交数据。";
                }
                else
                {
                    ja = JArray.Parse(data);
                    if (ja.Count > 0)
                    {
                        foreach(JObject jo in ja)
                        {
                            string path = Server.MapPath(root + (string) jo["path"] + (string) jo["filename"]);
                            FileInfo file = new FileInfo(path);
                            if (file.Exists)
                            {
                                file.Delete();
                            }
                        }
                        success = true;
                    }
                    else
                    {
                        msg = "错误的提交数据。";
                    }
                }
            }
            catch (Exception e)
            {
                msg = e.Message;
            }
            return Helper.MyFunction.WriteJObjectResult(success, 0, msg, ja);
        }

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏我和未来有约会

CaseStudy(showcase)数据篇-加载图片

做silvelight也有一段时间了,相册、游戏,刚刚完成的showcase这个小程序算是一个阶段了。这里就以showcase这个项目来做一下CaseStudy...

19690
来自专栏Urahara Blog

Joomla V3.7.0 核心组件SQL注入漏洞分析

20640
来自专栏Java 源码分析

SpringBoot 笔记 ( 二 ):自定义配置

SpringBoot 笔记 ( 二 ) 1. 配置文件 SpringBoot使用一个全局的配置文件,配置文件名是固定的: application.propert...

40660
来自专栏java架构师

解决ASP.NET中的各种乱码问题

经常发现有人被乱码困扰着,而我感觉比较幸运,很少为此烦恼过。 在这篇博客中,我将把我想到的一些与乱码有关的经验总结出来,供大家参考。 页面显示乱码问题 在一...

38460
来自专栏hotqin888的专栏

DOC文件中法规对标系统完成

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/hotqin888/article/det...

8010
来自专栏高性能服务器开发

libevent源码深度剖析十 支持I/O多路复用技术

(1)libevent源码深度剖析一 序 (2)libevent源码深度剖析二 Reactor模式 (3)libevent源码深度剖析三 libevent基本使...

13010
来自专栏Android干货园

Android JS相互调用详解

版权声明:本文为博主原创文章,转载请标明出处。 https://blog.csdn.net/lyhhj/article/details/49...

23510
来自专栏令仔很忙

学生信息管理系统问题集锦

 1)、数据库的ODBC配置出现错误,没有配置好,与数据库的连接没有连接好,就会出现这样的问题

10220
来自专栏量化投资与机器学习

【精心解读】关于Jupyter Notebook的28个技巧

Jupyter具有很强的可扩展性,支持许多编程语言,可以很容易地托管在计算机上或几乎所有的服务器上,只需要拥有ssh或http访问权限。 最重要的是,它是完全免...

2.1K70
来自专栏技术最杂谈

Fiddler应用之FiddlerScript

Fiddler是一款强大的HTTP抓包工具,它能记录所有客户端和服务器的http和https请求,允许你监视,设置断点,甚至修改输入输出数据. 使用Fiddle...

90060

扫码关注云+社区

领取腾讯云代金券