【化学药品管理平台——Servlet+Jsp实现 0101】环境搭建和前端页面

前言

   从17年暑假学习JavaWeb到现在,终于断断续续学完了各种基础,和几个框架。由于平时需要顾着科研,学习技术的时间非常少,所以过程比较艰难。

   时间都是挤出来的。没有错,时间是有的,只是看大家愿不愿意去挤。

   博主本人是生命科学学院的在读研究生,于是第一个项目就写了一个药品管理的网页应用。模仿的是一个在高校用的比较多,目前感觉开发的不错的一个药品管理方面的网站。

   为了将学习过的技术投入应用,这个管理平台我写了三个版本,除了本篇的Servlet+Jsp,还有后面用SSHSSM框架写的两个版本。

   项目源码连接(旧的源码有很多瑕疵)

   项目源码链接(新)

   本项目将分三个方面讲解:前端页面,后台用户,后台库存。同时准备写三个篇幅。

 

开发环境

   服务器:apache-tomcat-7.0.52

   数据库:MySQL Server 5.5

   IDE:Eclipse

数据库

   本来应该放在下一篇讲,因为我先讲前端,要涉及到后端传来的一些对象,所以列一个数据库表结构。此项目只有两个表,一个user用户,一个agents药品,一对多的关系。

   

 

前端页面

主页home.jsp

   前端总共六个jsp页面,本篇只介绍head.jsp和agents_list.jsp,其他在后面的篇幅中会有讲解。

   

   主要页面是agents_list.jsp,分为三部分,页面顶端,左侧显示其他信息部分(暂时没有开发),和右侧入库功能及展示药品信息部分。通过设置div的float属性来设计页面布局。页面样式主要由bootstrap完成。页面功能和和特效由Javascript和Jquery来实现。

   前端页面所有可能引入的指令标签、外部样式表和外部js如下。

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
<script type="text/javascript" src="${pageContext.request.contextPath }/js/jquery-1.11.3.min.js"></script>
<script type="text/javascript" src="${pageContext.request.contextPath }/js/jquery.validate.js"></script>
<script type="text/javascript" src="${pageContext.request.contextPath }/js/bootstrap.min.js"></script>
<link href="${pageContext.request.contextPath }/css/bootstrap.min.css" rel="stylesheet">
<link href="${pageContext.request.contextPath }/css/agents_list.css" rel="stylesheet">
</head>

一、页面顶端

   先说页面顶端吧,一个head.jsp,动态包含在其他几个页面中。head.jsp包含四个按钮,分别连接到首页、库存展示、登陆功能和退出功能。

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
	
    <!-- bootstrap导航条 -->
    <ul class="nav nav-tabs">
	<li style="position: relative; left:30%">
	    <a href="${pageContext.request.contextPath }/home.jsp">首页</a>
	</li>
		
	<!-- 想要安排两个相邻的按钮,可以将其相对位置设置为一样的 -->
	<li style="position: relative; left:30%">
	    <a href="${pageContext.request.contextPath }/agents?method=findAgentsByPage&currPage=1">库存</a>
	</li>
		
	<!-- 登录和注册 -->
	<c:if test="${empty user }">
	    <li style="position: relative; left:70%"><a href="${pageContext.request.contextPath }/user?method=loginUI">登录</a></li>
	    <li style="position: relative; left:70%"><a href="${pageContext.request.contextPath }/user?method=registUI">注册</a></li>
	</c:if>
	<!-- 用户名和退出登录 -->
	<c:if test="${not empty user }">
	    <li style="position: relative; left:68%"><a href="javascript:void(0);">${user.uname }_${user.ugrade }:您好</a></li>
	    <li style="position: relative; left:68%"><a href="${pageContext.request.contextPath }/user?method=logout">退出</a></li>
	</c:if>
    </ul>
</body>
</html>

   四个按钮是用bootstrap的li标签写的,用relative属性设定相对位置,设定相同参数的相对位置,则会有两个按钮先后排列的效果。

   首页即普通a标签连接,取出部署的应用程序名,设置绝对路径。

   库存a标签是一个servlet,同时传递了要执行的方法参数method和一个currPage参数,其作用会在讲后台的时候介绍。

   下面是jstl的c:if标签判断session域中是否有EL表达式获取的user对象,即是否登陆过。若未登陆,则最后两个按钮完成登陆和注册功能;若已登陆,则完成显示用户名和退出登陆的功能。

二、入库

   入库及药品展示都在agents_list.jsp中,代码部分从前往后分别是动态包含页面顶端,一个div左侧页面,一个div右侧页面。下面主要看右侧页面的药品入库部分的代码。

   入库的div首先在agents_list.css设置为隐藏(display:none),点击入库按钮触发点击事件,可以让入库表单下滑出来。点击表单中的取消按钮,可以让入库表单上滑隐藏。

入库表单隐藏

入库表单滑出

<!-- jsp代码:隐藏的入库表单 -->
<div id="agentsLoad" style="float: left; width: 76%;">
    <form id="formId" action="${pageContext.request.contextPath }/agents?method=add" method="post">
	<div class="form-group">
	    <table style="border-collapse:separate; border-spacing:6px;">
		<tr>
		    <th>品名<input type="text" class="form-control" name="aname"/></th>
		    <th>品牌<input type="text" class="form-control" name="aboard"/></th>
		    <th>货号<input type="text" class="form-control" name="aNo"/></th>
		    <th>CAS<input type="text" class="form-control" name="aCAS"/></th>
		    <th>供应商<input type="text" class="form-control" name="asup"/></th>
						
		</tr>
		<tr>
		    <th>数量<input type="text" class="form-control" name="count"/></th>
		    <th>单位<input type="text" class="form-control" name="aunit"/></th>	
		    <th>规格<input type="text" class="form-control" name="aspec"/></th>
		    <th>存放地<input type="text" class="form-control" name="astore"/></th>
		    <th>
			<input type="submit" class="btn btn-default" value="提交" style="margin: 20px 0 0 0"/>
			<input type="button" class="btn btn-default" value="取消" onclick="fadeOut()" style="margin: 20px 0 0 2px"/>
		    </th>
		</tr>
	    </table>
	</div>
			
    </form>
</div>
/* css代码:隐藏入库表单 */
#agentsLoad{
    display: none;
    background-color: white;
    position: absolute;
    top: 108px; 
    z-index: 100;
}
<script type="text/javascript">
    /* js代码:入库表单的滑入滑出 */
    function fadeIn(){
	$("#agentsLoad").slideDown("slow");
    }
	
    function fadeOut(){
	$("#agentsLoad").slideUp("slow");
    }
</script>

三、药品展示

   药品展示由单页的药品数据列表和分页显示按钮组成,先说药品列表。

   (1)药品列表

   用table显示药品,th为表头,tr为行。同样,利用jstl的c:forEach标签和El,循环显示多列后台传来的药品数据。首先获取一个参数为agents的list,然后将var设为单个agents。再将agents的每个属性传入每个tr的td中。

<!-- 药品显示列表 -->
<table class="table" id="atable">
<thead>
    <tr>
	<th>品名</th>
	<th>图片</th>
	<th>品牌</th>
	<th>货号</th>
	<th>CAS</th>
	<th>规格</th>
	<th>数量</th>
	<th>单位</th>
	<th>存放地</th>
	<th>供应商</th>
	<th>入库人</th>
	<th>更新时间</th>
	<th>损耗</th>
    </tr>
</thead>


<!-- jstl的forEach语句展示EL获得从后台传来的数据 -->
<tbody>
    <c:forEach items="${ab.list }" var="a">
    <tr>
	<td>${a.aname }</td>
	<!-- 鼠标悬停放大图片,以及点击添加图片 -->
	<td class="picsTd">
	    <div id="picsMagnify">
		<img width="100%" alt="点击此处添加图片" src="${pageContext.request.contextPath }/${a.aimg}" onclick="picsAdd(this)">
		<!-- 此处放置一个隐藏的input标签,准备给点击事件传递agents的aname和aid属性 -->
		<input name="${a.aname }" value="${a.aid }" style="display: none;" />
	    </div>
	</td>
	<td>${a.aboard}</td>
	<td>${a.aNo}</td>
	<td>${a.aCAS}</td>
	<td>${a.aspec}</td>
	<td>${a.count}</td>
	<td>${a.aunit}</td>
	<td>${a.astore}</td>
	<td>${a.asup}</td>
	<td>${a.user.uname}</td>
	<td>${a.adate}</td>
	<!-- 点击修改药品损耗情况 -->
	<td>
	    <button type="button" class="btn btn-default" name="${a.aid }" value="${a.count}" onclick="showDiv(this)" >损耗</button>
	    <%-- <button type="button" name="${a.aname }" value="${a.aid }" onclick="picsAdd(this)">添加图片</button> --%>
	</td>
    </tr>
    </c:forEach>
<tbody>
</table>

   (2)放大添加图片

   .picsTd中的图片展示有一个细节,即鼠标悬停可放大图片,点击可添加图片。

.picsTd{position:relative; width:45px; height:45px}

#picsMagnify{
    width: 45px;
    height: 30px;
    background-color: #FFFFFF;
    position:absolute;
    left:0px;
    top:0px;
    z-index:10;
}
#picsMagnify:hover{
    background-color: #FFFFFF;
    width:300px; height:200px; 
    left:-50px;
    top:-50px;
    z-index:100;
    box-shadow:0px 0px 5px 0px #000
}

   .picsTd设置列表图片的div大小,#picsMagnify设置鼠标非悬停状态的图片大小,#picsMagnify:hover设置鼠标悬停状态图片大小。想要图片悬停前后缩放比例不变的办法就是,设置<img width="100%">,然后上级标签固定了width或height尺寸,所以#picsMagnify和#picsMagnify:hover其实只需要设置width或者height就行。设置z-index可以使放大后的图片显示在页面顶层。

   点击放大后的图片可以添加或更新图片。在<img>后面设置一个隐藏的<input>,通过点击事件来传递input中的参数到图片添加弹出层#picsDiv。

<td class="picsTd">
    <div id="picsMagnify">
	<img width="100%" alt="点击此处添加图片" src="${pageContext.request.contextPath }/${a.aimg}" onclick="picsAdd(this)">
	<!-- 此处放置一个隐藏的input标签,准备给点击事件传递agents的aname和aid属性 -->
	<input name="${a.aname }" value="${a.aid }" style="display: none;" />
    </div>
</td>
    <!-- 图片添加弹出层 -->
    <div id="picsDiv">
    	<form id="picsForm" action="${pageContext.request.contextPath }/addImage" method="post" enctype="multipart/form-data">
    	    <div id="picsInner">
    		<!-- js修改html内容区 -->
    	    </div>
    	    <input type="file" name="aimg" />
    	    <div class="btn_div">
                <input type="submit" class="btn btn-default" value="确定" />
                <input type="button" class="btn btn-default" value="取消" onclick="picsCl()" />
            </div>
    	</form>
    </div>
<script type="text/javascript">
    function picsAdd(obj) {  
	/* 显示隐藏的图片添加区 */
	document.getElementById("bg").style.display ="block";  
	document.getElementById("picsDiv").style.display ="block";
	/* 通过点击事件获得的this对象,来回显当前agents对象的aname和aid*/
	document.getElementById("picsInner").innerHTML = "<input type='text' class='form-control' name='aname' value='"+obj.nextElementSibling.name+"' readonly='readonly'/><br/>";
	document.getElementById("picsInner").innerHTML += "<input type='hidden' class='form-control' name='aid' value='"+obj.nextElementSibling.value+"'/>";
    }
	
    /* 取消图片添加弹出层 */
    function picsCl(){  
	document.getElementById("bg").style.display ="none";  
	document.getElementById("picsDiv").style.display ="none";
    }  
</script>

   点击事件获取this当前对象,然后获取当前对象的下一个兄弟元素nextElementSibling,修改弹出层#picsDiv的html内容,将兄弟元素的属性赋给弹出层#picsDiv的元素。至此,弹出层效果如下。

   (3)弹出层

   对于弹出层的设计,没有使用bootstrap框架,而是简单地用css做的。因为一开始的想法是,不论前端后端,都从最基本的功能开始设计,然后再用框架做,后来还是想把重心放在后端,弹出层这里就没有再进行优化了。

   看下面css代码,#bg是弹出层背景,#picsDiv和#show分别是图片添加和损耗修改弹出层表单。

#bg{   
    display: none;   
    position: absolute;   
    top: 0%;   
    left: 0%;   
    width: 100%;   
    height: 100%;   
    background-color: black;   
    z-index:1001;   
    -moz-opacity: 0.3;  		/* 提供给mozilla firefox的css属性 */   
    opacity:.30;   				/* 兼容IE FF */
    filter: alpha(opacity=30);  /* IE相应的是 filter:alpha */ 
}  
/* 图片添加弹出层表单 */ 
#picsDiv{   
    display: none;   
    position: absolute;   
    top: 25%;   
    left: 32%;   
    width: 33%;   
    height: auto;   
    padding: 8px;  
    border-radius:12px;  
    border: 2px solid #ddd;   
    background-color: white;   
    z-index:1002;   
    overflow: auto;  
} 
/* 损耗修改弹出层表单 */
#show{   
    display: none;   
    position: absolute;   
    top: 25%;   
    left: 32%;   
    width: 33%;   
    height: auto;   
    padding: 8px;
    border-radius:12px;  
    border: 2px solid #ddd;   
    background-color: white;   
    z-index:1002;   
    overflow: auto;  
}

   初始的时候,三个部分的display属性都是none,即背景和表单都不显示。当发生相应的点击事件之后,其display属性都变为block,即显示出来。而且,由于z-index属性,背景和表单覆盖在了整个页面的顶端,且表单在最顶端,背景占了整个屏幕。这样就达到了弹出层的效果。点击表单取消按钮,display属性重新变为none。

<script type="text/javascript">
    function picsAdd(obj) {  
	/* 显示隐藏的图片添加区 */
	document.getElementById("bg").style.display ="block";  
	document.getElementById("picsDiv").style.display ="block";
	/* 此处代码省略 */
    }
	
    /* 取消图片添加弹出层 */
    function picsCl(){  
	document.getElementById("bg").style.display ="none";  
	document.getElementById("picsDiv").style.display ="none";
    }  
</script>

   (4)损耗功能

   列表单行最后一列td是损耗按钮,点击可以对入库的药品数量进行减操作。同样,点击事件将agents的aid和count传入到损耗更新弹出层#show。弹出层将显示该agents的已有数量,和将损耗的数量。

<td>
    <button type="button" class="btn btn-default" name="${a.aid }" value="${a.count}" onclick="showDiv(this)" >损耗</button>
</td>
    <div id="show">
    	 损耗<hr />
        <form id="showForm" action="${pageContext.request.contextPath }/agents?method=update" method="post">
            <div id="cal">
            	<!-- js修改html内容区 -->
            </div>
            <div class="btn_div">
                <input type="submit" class="btn btn-default" value="确定" />
                <input type="button" class="btn btn-default" value="取消" onclick="hideDiv()" />
                <input type="reset" class="btn btn-default" value="清空"  />
            </div>
        </form>
    </div>

   这里的损耗数量用validate做了一个表单验证,控制其输入范围在0到已有数量中。这里有一个问题,如果简单的直接设置一个range验证,页面刷新后,第一次点击某个agents的损耗按钮之后,在弹出层点击取消的话,之后不论点击哪一个损耗按钮,出现的表单验证范围都是对于第一个agents的。

   所以,在损耗弹出层点击取消按钮之后,需要取消当前的表单验证。试了很多方法,只有下面这种成功了。即先对#showForm建立表单验证,再给#dif添加验证规则。当点击取消按钮的时候,将#dif的验证规则remove掉。这样一来,每次弹出的损耗表单验证就会是对应当前对象的。

<script type="text/javascript">
    function showDiv(obj) {   
        /* 显示隐藏的损耗修改区 */
        document.getElementById("bg").style.display ="block";  
        document.getElementById("show").style.display ="block";
        /* 通过点击事件获得的this对象,来回显当前agents对象的名称和数量*/
        document.getElementById("cal").innerHTML = "已有数量<input type='text' class='form-control' name='oCount' value='"+obj.value+"' readonly='readonly'/><br/>";
        document.getElementById("cal").innerHTML += "损耗数量<input type='text' class='form-control' id='dif' name='dif' /><br/>";
        document.getElementById("cal").innerHTML += "<input type='hidden' name='oAid' value='"+obj.name+"'/>";
	    	
        /* 先建立表单验证 */
        $("#showForm").validate({
	    rules:{	
				
	    },
	    messages:{
				
            }
        });	
	    
        /* 再添加验证规则 */
        $("#dif").rules("add",{
            range:[0, obj.value],
	    messages:{
	    	range:"请输入{0}~{1}的数量",
	    }
	})
	   
    }  
	
    /* 取消损耗更新弹出层 */
    function hideDiv(){  
	document.getElementById("bg").style.display ="none";  
	document.getElementById("show").style.display ="none";
	/* 取消弹出层的同时,移除表单验证规则,若不移除,则一直是第一次点击打开的agents对象对应的表单验证规则 */
	$("#dif").rules("remove");
    }  
</script>

   (5)分页查询功能

   分页查询由所有页码和上一页下一页按钮组成。上一页下一页比较简单,先判断后台传来的ab.currPage是否等于1或是否等于ab.totalPage,即当前页是否是首页或最后一页。若是首页或最后一页,则将首页和最后一页的a标签设置为死链接,不可点击;若不是则可点击完成正常跳转功能。

   对于所有页码,若页数过多,则设置forEach语句的begin=ab.currPage-5、end=ab.currPage+4,即只显示当前页前五页到后四页的页码。

   之后,判断每个页码是否是当前页,若是则不可点击,若不是则可完成按页查询功能。

<!--分页查询 -->
<div style="width:380px;margin:0 auto;margin-top:20px;">
    <ul class="pagination" style="text-align:center; margin-top:10px;">
	<!-- 判断当前页是否是首页  -->
	<c:if test="${ab.currPage == 1 }">
	    <li class="disabled">
		<a href="javascript:void(0)" aria-label="Previous"><span aria-hidden="true">«</span></a>
	    </li>
	</c:if>
		
	<c:if test="${ab.currPage != 1 }">
	    <li>
		<a href="${pageContext.request.contextPath}/agents?method=findAgentsByPage&currPage=${ab.currPage-1}" aria-label="Previous"><span aria-hidden="true">«</span></a>
	    </li>
	</c:if>
		
	<!-- 展示所有页码 -->
	<c:forEach begin="${ab.currPage-5>0?ab.currPage-5:1 }" end="${ab.currPage+4>ab.totalPage?ab.totalPage:ab.currPage+4 }" var="n">
	    <!-- 判断是否是当前页 -->
	    <c:if test="${ab.currPage==n }">
		<li class="active"><a href="javascript:void(0)">${n }</a></li>
	    </c:if>
	    <c:if test="${ab.currPage!=n }">
		<li><a href="${pageContext.request.contextPath}/agents?method=findAgentsByPage&currPage=${n}">${n }</a></li>
	    </c:if>
	</c:forEach>
		
	<!-- 判断是否是最后一页 -->
	<c:if test="${ab.currPage == ab.totalPage }">
            <li class="disabled">
		<a href="javascript:void(0)" aria-label="Next">
		    <span aria-hidden="true">»</span>
		</a>
	    </li>
	</c:if>
	<c:if test="${ab.currPage != ab.totalPage }">
	    <li>
		<a href="${pageContext.request.contextPath}/agents?method=findAgentsByPage&currPage=${ab.currPage+1}" aria-label="Next">
		    <span aria-hidden="true">»</span>
		</a>
	    </li>
	</c:if>		
    </ul>
</div>
<!-- 分页结束-->

结尾

   至此,本项目前端部分介绍完了。不知道这种将页面分节式介绍大家好不好理解,看博客的时候,可以先下载此项目代码,将代码先熟悉一遍,再看此文。

   内容不多,但是讲解起来还挺需要考量的。博主着急先将第一篇博客写完发布出来,也是想激励自己加快进度写后面的篇幅。希望大家多多支持我在技术方面的努力,比不上科班出身的同行,但要执着追求。各位有什么看法和意见,请多留言。

 

 

 

 

 

 

 

 

 

 

 

 

 

posted @ 2018-06-30 00:27  XD_Yangf  阅读(24)  评论(0编辑  收藏  举报