从零搭建项目开发框架-15用户管理界面设计与开发

这一节讲用户管理界面设计。先想想用户管理都要做什么,新增、修改、删除、查询用户基本信息,密码重置、加锁、解锁用户,查询用户登录记录,姑且这么多,如果还有额外的要求,后期都可以加进去。

1、新建公共引用taglib.jsp

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>

页面开发中肯定要用到各种表达式语言,所以就用公共的jsp引进来,包括JSTL里面的C标签,ftm函数标签,还有Spring的form标签,后续还会用到自定义标签。

2、各页面的编写

这里面一共包括四个页面:
1、index.jsp,管理页面
2、add.jsp,新增页面
3、update.jsp,修改页面
4、login.jsp,用户的登录信息

都不是很复杂,下面主要讲特殊一点的知识点。

3、自定义函数式标签

开发中有些页面的引用可能用在好多地方,这时候就需要自定义一些函数式标签,常用的功能提前写好,用到的时候以标签的形式展示。比如用户管理界面用户状态字段,数据库存储的是1正常,2锁定,页面展示的时候就需要显示为汉字,更好看一些的话,正常是绿色,锁定是红色,加一些样式,这时候标签的好处就能体现出来了。

webtag.tld

在/webapp/WEB-INF/下面新建标签配置文件webtag.tld

<?xml version="1.0" encoding="UTF-8" ?>

<taglib xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd"
    version="2.0">
    <tlib-version>1.0</tlib-version>
    <short-name>critc</short-name>
    <uri>http://www.critc.com/jsp/jstl/tags</uri>

    <function>
        <description>系统管理-用户管理获取用户状态</description>
        <name>getUserStatus</name>
        <function-class>com.critc.plat.util.web.WebTag</function-class>
        <function-signature>java.lang.String getUserStatus(java.lang.Integer)</function-signature>
    </function>
    
</taglib>

这里面首先定义标签的版本号<tlib-version>,缩写名<short-name>,及URI<uri>,这样在jsp页面里面就可以引用该标签了。

这里面定义了一个function,里面四个配置项,<description>标签的描述,<name>函数名,<function-class>函数对应的类,需要写全路径,<function-signature>函数签名,需要写清楚调用的是类里面的哪个方法,返回值是什么,这里面返回值和参数都需要时包装类型,不能是原生类型。

WebTag.java

public class WebTag {

    /**
     * 获取用户状态,系统管理-用户管理用到
     *
     * @param user_status
     * @return
     */
    public static String getUserStatus(Integer user_status) {
        if (user_status == 1)
            return "<span class=\"label label-success arrowed\">正常</span>";
        else if (user_status == 2)
            return "<span class=\"label label-warning arrowed\">已锁定</span>";
        else
            return "";
    }

}

taglib.jsp增加引用

<%@ taglib prefix="critc" uri="http://www.critc.com/jsp/jstl/tags"%>

index.jsp中的具体使用

${critc:getUserStatus(sysUser.status)}

很简单吧,而且可以重复使用。

4、SiteMesh中自定义Tag

实际开发中页面会有自定义的JS和CSS,这些JS和CSS需要按照格式要求放到指定位置,比如CSS放到header中,JS放到公共引用js的后面,这时候就需要自定义Tag了。

SiteMesh3.xml

 <!-- 自定义标签 -->
    <content-processor>
        <tag-rule-bundle class="com.critc.plat.core.tagrules.ScriptTagRuleBundle" />
        <tag-rule-bundle class="com.critc.plat.core.tagrules.CssTagRuleBundle" />
    </content-processor>

增加这样两句话,自定义位置包括javascript和css

ScriptTagRuleBundle.java和CssTagRuleBundle.java

public class ScriptTagRuleBundle implements TagRuleBundle {
    @Override
    public void install(State defaultState, ContentProperty contentProperty, SiteMeshContext siteMeshContext) {
        defaultState.addRule("critc-script",
                new ExportTagToContentRule(siteMeshContext, contentProperty.getChild("critc-script"), false));
    }

    @Override
    public void cleanUp(State defaultState, ContentProperty contentProperty, SiteMeshContext siteMeshContext) {
    }
}

这一段的代码非常简单,实现TagRuleBundle接口,增加一个Tag 规则即可,这里定义为critc-script

decorator.jsp

<%@include file="../common/scripts.jspf" %>
<sitemesh:write property='critc-script'/>

这里就和定位body和header一样,<sitemesh:write property='critc-script'/>这句话就可以页面定义的javascript放到最后。

index.jsp

<critc-script>
    <script type="text/javascript">
      ///省略
        </script>
</critc-script>

这里就和普通javascript定义一样,只不过在最外层包了一下<critc-script>,原理一看就明白了。

5、自定义操作成功界面和失败界面

每个按钮操作成功后,比如新增、修改,需要告诉用户操作成功了,然后跳转回原先界面。一版这种场景有以下几种方式:
1、alert以下或页面上方、下方弹出一个tip,提示操作成功了,然后列表页面刷新一下,常用于页面无刷新的架构,比如easyUI、ext等
2、跳转至成功页面,提示操作成功,3秒后返回列表页面,常用于互联网界面。
这里选择第二种方式。

SysUserController.java

 @RequestMapping("/add")
    public String add(HttpServletRequest request, HttpServletResponse response, SysUser sysUser) {
        sysUser.setStatus(1);
        int flag = sysUserService.add(sysUser);
        if (flag == 0)
            return "forward:/error.htm?msg=" + StringUtil.encodeUrl("用户信息新增失败");
        else if (flag == 2)
            return "forward:/error.htm?msg=" + StringUtil.encodeUrl("用户账号已存在");
        else
            return "forward:/success.htm?msg=" + StringUtil.encodeUrl("用户信息新增成功");
    }

操作成功或失败,跳往对应界面,同时把操作结果,以msg的形式带过去了。

更好的方式是以代码或数字码的方式传过去,在SccessController或ErrorController再解析这些代码具体对应的中文是什么。

SuccessController.java

@Controller
@RequestMapping("/")
public class SuccessController {

    /**
     * 成功处理操作
     * @param request
     * @param response
     * @return
     */
    @RequestMapping("/success")
    public ModelAndView success(HttpServletRequest request, HttpServletResponse response) {
        ModelAndView mv = new ModelAndView();
        mv.setViewName("/plat/common/success");
        mv.addObject("msg", StringUtil.decodeUrl(request.getParameter("msg")));
        mv.addObject("backUrl", StringUtil.decodeUrl(request.getParameter("backUrl")));
        return mv;
    }
}

这一块代码很简单啊,就是获取msgbackUrl,并跳转success.jsp界面

success.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ include file="../common/taglib.jsp" %>
<head>
    <title>${webTitle }-操作成功</title>
</head>
<body>
<div class="breadcrumbs  breadcrumbs-fixed" id="breadcrumbs">
    <ul class="breadcrumb">
        <li><i class="ace-icon fa fa-home home-icon"></i> <a href="${dynamicServer}/index.htm">首页</a></li>
        <li class="active">操作结果</li>
    </ul>
</div>

<div class="page-content">
    <div class="alert alert-success" style="text-align: center;">
        <h4>
            <i class="fa fa-check-circle"></i> ${msg }
        </h4>
        <a href="${backUrl}">如果你的浏览器没有自动跳转,请点击此链接</a>
        <script type="text/javascript">
                            setTimeout(function() {
                                location.href = "${backUrl}";
                            }, 2000);

        </script>
    </div>
</div>
</body>
</html>

具体效果

具体效果.png

6、弹出框提示

效果如图

在删除用户、锁定等操作时,一定要提示用户,这种情况下就需要提示框了。

bootbox.confirm("你确定要删除该用户吗?", function (result) {
                if (result) {
                    window.location = "delete.htm?id=" + id + "&backUrl=${backUrl}";
                }
            })

这里整合了bootbox,只需要一句话即可,还是很方便的。

7、SysUserSearchVO.java的作用域

写java代码的时候定义SysUserSearchVO这个类,用于获取查询条件,具体怎么用的呢,很多人会有疑问。

index请求方法

 /**
     * 进入用户管理界面
     *
     * @return
     */
    @RequestMapping("/index")
    public ModelAndView index(HttpServletRequest request, HttpServletResponse response, SysUserSearchVO sysUserSearchVO) {
        ModelAndView mv = new ModelAndView();
        int recordCount = sysUserService.count(sysUserSearchVO);// 获取查询总数
        String url = createUrl(sysUserSearchVO);
        PageNavigate pageNavigate = new PageNavigate(url, sysUserSearchVO.getPageIndex(), recordCount);//定义分页对象
        List<SysUser> list = sysUserService.list(sysUserSearchVO);
        mv.addObject("pageNavigate", pageNavigate);// 设置分页的变量
        mv.addObject("list", list);// 把获取的记录放到mv里面
        mv.addObject("listRole", sysRoleService.list());// 角色列表
        mv.setViewName("/plat/sys/user/index");// 跳转至指定页面
        BackUrlUtil.createBackUrl(mv, request, url);// 设置返回url
        return mv;
    }

这里面SysUserSearchVO作为一个参数,由SpringMVC自动进行参数的包装,就是说进入这个方法,SysUserSearchVO的各个参数都已经赋值了。

页面传值

 // 查询方法
        var searchUser = function () {
            var url = "index.htm?";
            if ($("#txtUsername").val() != '')
                url += "username=" + $("#txtUsername").val();
            if ($("#txtRealname").val() != '')
                url += "&realname=" + $("#txtRealname").val();
            if ($("#cmbStatus").val() != '')
                url += "&status=" + $("#cmbStatus").val();
            if ($("#cmbRoleId").val() != '')
                url += "&role_id=" + $("#cmbRoleId").val();
            window.location = encodeURI(url);
        }

页面的传值是通过拼请求的url,来实现的。包括分页的链接也都是这种写法。原理就是不管是查询还是分页,把所有参数都写到url的参数里面,这样不管怎么跳转,参数是不会变的。一定要牢记,页面查询一定不要用用form的post方法,这样分页就无法post了。

8、表单校验

这里表单校验用的是jquery-validator,用起来还是比较简单的。

引用jquery-validator

页面校验对应js

$("#userForm").validate({
            errorElement: "label",
            errorClass: "valiError",
            errorPlacement: function (error, element) {
                error.appendTo($("#" + element.attr('id') + "Tip"));
            },
            rules: {
                username: {
                    required: true,
                    minlength: 4,
                    maxlength: 20,
                    remote: {
                        url: "checkUserExist.htm", //后台处理程序
                        type: "post", //数据发送方式
                        //dataType: "json", //接受数据格式
                        data: { //要传递的数据
                            username: function () {
                                return $("#username").val();
                            }
                        }
                    }
                },
                realname: {
                    required: true,
                    maxlength: 20
                },
                mobile: {
                    required: true,
                    maxlength: 11
                },
                roleId: {
                    required: true
                }
            },
            messages: {
                username: {
                    remote: "账号已存在!"
                }
            },
            submitHandler: function (form) {
                form.submit();
            }
        });

这里面在rules定义校验规则,比如长度、是否为空、email、数字、ajax异步校验等等。

提示信息

jquery-validator校验表单如果出错,默认是在该表单后面显示错误信息,但是ACE里面需要设置一个默认的div,来定位错误信息的输出。
生效代码是这一段

errorElement: "label",
            errorClass: "valiError",
            errorPlacement: function (error, element) {
                error.appendTo($("#" + element.attr('id') + "Tip"));
            },

错误信息的标签用label,样式class是valiError,错误的位置是当前表单的id+Tip,比如当前表单的id是username,则错误提示的显示位置为usernameTip,所以在每个表单后面加了一句话<label id="usernameTip"></label>

jquery-validator功能非常丰富,还可以ajax异步校验、自定义校验方法等等,可以参见网上的教程
http://www.cnblogs.com/linjiqin/p/3431835.html

9、查看登录历史

用户列表里面有一个链接,登录历史,点击该链接,弹出框,显示用户的登录历史。

这里需要利用ajax的post的方法,请求整个页面,加载进来放到bootstrap的model里面。
先看看具体代码实现:

viewLoginHis

 var viewLoginHis = function (id, title) {
            $.post('searchUserLogin.htm', {
                userId: id
            }, function (html) {
                $("#dialog-viewLogin").html(html);
                var dialog = $("#dialog-viewLogin").removeClass('hide').dialog({
                    title: "【" + title + "】登录历史",
                    title_html: false,
                    width: 1000,
                    minHeight: 500,
                    position: {my: "center", at: "center", of: window},
                    modal: true,
                    buttons: [
                        {
                            text: "返回",
                            "class": "btn btn-minier btn-center",
                            click: function () {
                                $(this).dialog("close");
                            }
                        }
                    ]
                });
            });
        }

这是点击链接的js代码,post请求url

searchUserLogin java方法

/**
     * 用户登录信息
     *
     * @param request
     * @param response
     */
    @RequestMapping("/searchUserLogin")
    public ModelAndView searchUserLogin(HttpServletRequest request, HttpServletResponse response, SysUserloginSearchVO sysUserloginSearchVO) {
        ModelAndView mv = new ModelAndView();
        int recordCount = sysUserLoginService.count(sysUserloginSearchVO);// 获取查询总数
        String url = createUserLoginUrl(sysUserloginSearchVO);
        PageNavigate pageNavigate = new PageNavigate(url, sysUserloginSearchVO.getPageIndex(), recordCount);//
        List<SysUserLogin> list = sysUserLoginService.list(sysUserloginSearchVO);
        mv.addObject("pageNavigate", pageNavigate);// 设置分页的变量
        mv.addObject("list", list);// 把获取的记录放到mv里面
        mv.setViewName("/plat/sys/user/login");// 跳转至指定页面
        return mv;
    }

这一块和普通的请求controller代码没什么两样。

login.jsp

<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8" %>
<%@ include file="../../common/taglib.jsp" %>

<c:if test="${empty list}">
    <div class="row">
        <div class="col-xs-12">暂无数据</div>
    </div>
</c:if>
<c:if test="${!empty list && list.size() > 0}">
<div class="row">
    <div class="col-xs-12">
        <table id="simple-table" class="table table-striped table-bordered table-hover">
            <thead>
            <tr>
                <th width=40>#</th>
                <th width=140>登录时间</th>
                <th width="120">登录IP</th>
                <th>终端</th>
                <th>浏览器类型</th>
                <th>浏览器版本</th>
            </tr>
            </thead>
            <tbody>
            <c:forEach items="${list }" var="sysUserLogin" varStatus="st">
                <tr>
                    <td>${st.index+1 }</td>
                    <td><fmt:formatDate value="${sysUserLogin.loginDate }" pattern="yyyy-MM-dd HH:mm"/></td>
                    <td>${sysUserLogin.loginIp }</td>
                    <td>${sysUserLogin.terminal }</td>
                    <td>${sysUserLogin.explorerType }</td>
                    <td>${sysUserLogin.explorerVersion}</td>
                </tr>
            </c:forEach>
            </tbody>
        </table>
    </div>
</div>
<div class="row">
    <div class="col-xs-12" id="pageNavForward">${ pageNavigate.pageModel}</div>
</div>
</c:if>
<script type="text/javascript">
    $(function() {
        $("#pageNavForward").on('click', 'a', function(e) {
            var $link = $(this);
            var $container = $link.closest('.ui-dialog-content');
            $container.load( $link.attr('href'));
            return false;
        });
        $("#forward_div").css("max-height", $(window).height() - 300);
    })
</script>

SiteMesh3.xml增加过滤项

searchUserLogin.htm这个请求是不需要被装饰的,所以需要过滤掉

    <mapping path="/sys/user/searchUserLogin.htm" exclue="true" />

查看登录历史效果


效果.png

最终整体效果图如下:

用户管理.png

注意:

这一块和java开发还不一样,需要掌握的技能点比较多,主要是ACE框架的使用,div、css、html等相关代码的编写都需要了解,不能把自己仅仅定位于写java代码,要全面发展,前端的技能也都要掌握,这样才能游刃有余!

源码下载

本阶段详细源码

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,590评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,808评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,151评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,779评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,773评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,656评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,022评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,678评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,038评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,756评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,411评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,005评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,973评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,053评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,495评论 2 343

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,442评论 25 707
  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,571评论 18 399
  • 曾几何时,自己一直有个文学梦,也想做个文学青年。六年前的那场考试,把这个梦想摔得粉碎。自从出来工作,因为种种原因,...
    平凡的素识阅读 85评论 0 0
  • 突然间,想要写点什么;但又不知从何写起,我努力克制自己的负面情绪,不想影响他,不想要他多想。我已经很习惯有他...
    土耳其的一场梦阅读 162评论 0 1