第21章清爽夏日九宫格日记网
21.1 项目设计思路
21.1.1 功能阐述
显示九宫格日记列表模块
- 分页显示全部九宫格日记
- 分页显示我的日记
- 展开和收缩日记图
- 显示日记原图
- 对日记图片进行左转和右转
- 删除自己的日记等
写九宫格日记模块
- 填写日记信息
- 预览生成的日记图片
- 保存日记图片
用户模块
- 用户注册
- 用户登录
- 退出登录
- 找回密码
21.1.2 系统预览
后续放图
21.1.3 功能结构
访问者.png
A标记的部分,只有用户登录后,才可以操作的功能
21.1.4 文件夹组织结构
2.png
21.2 数据库和数据表设计
21.2.1 数据库设计
创建数据库,新建两张数据表tb_user(用户信息表)和tb_diary(日记表)
21.2.2 数据表设计
-
1.tab_user(用户信息表)
3.png
CREATE TABLE
tb_user(
idint(11) NOT NULL AUTO_INCREMENT,
usernamevarchar(50) NOT NULL COMMENT '用户名',
pwdvarchar(50) NOT NULL COMMENT '密码',
questionvarchar(45) NOT NULL COMMENT '密码提示问题',
answervarchar(45) NOT NULL COMMENT '提示问题答案',
cityvarchar(30) NOT NULL COMMENT '所在地',
PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-
2.tb_diary(日记表)
4.png
CREATE TABLE
tb_diary(
idint(11) NOT NULL AUTO_INCREMENT,
titlevarchar(60) DEFAULT NULL COMMENT '标题',
address varchar(50) DEFAULT NULL COMMENT '日记保存的地址',
writeTime datetime DEFAULT CURRENT_TIMESTAMP COMMENT '写日记时间',
userid int(10) DEFAULT NULL COMMENT '用户ID',
PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
在设置数据表tb_diary时,还需要为字段writeTime设置默认值,这里为CURRENT_TIMESTAMP,也就是当前时间
21.3 公共模块设计
21.3.1 编写数据库连接及操作的类
public class ConnDB {
//声明Connection对象的实例
public Connection conn=null;
//声明Statement对象的实例
public Statement statement=null;
//声明ResultSet对象的实例
public ResultSet resultSet=null;
private static String propFileName="connDB.properties";//指定资源文件的保存位置
private static Properties properties=new Properties();//创建并实例化Properties对象的实例
private static String dbClassName="com.mysql.jdbc.Driver";//定义保存数据库驱动的变量
private static String dbUrl="jdbc:mysql://127.0.0.1:3306/db_9griddiary"
+ "?user=root&password=hwp123456&useUnicode=true";
//构造方法
public ConnDB(){
try {
//将Properties文件读取到InputStream对象中
InputStream in = getClass().getResourceAsStream(propFileName);
//通过输入流对象加载properties文件
properties.load(in);
//获取数据库驱动
dbClassName=properties.getProperty("DB_CLASS_NAME",dbClassName);
dbUrl=properties.getProperty("DB_URL",dbUrl);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 连接数据库
* @return
*/
public static Connection getConnection() {
Connection connection=null;
try {
//装载数据库驱动
Class.forName(dbClassName).newInstance();
//建立与数据库URL中定义的数据库连接
connection=DriverManager.getConnection(dbUrl);
} catch (Exception e) {
e.printStackTrace();
}
if(connection==null){
System.out.println("数据库连接失败");
}
return connection;
}
/**
* 创建查询语句
* @param sql
* @return
*/
public ResultSet executeQuery(String sql){
try {
conn=getConnection();
statement=conn.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE,ResultSet.CONCUR_READ_ONLY);
resultSet=statement.executeQuery(sql);
} catch (Exception e) {
System.err.println(e.toString());
}
return resultSet;
}
/**
* 创建执行更新操作的方法
* @param sql
* @return
*/
public int executeUpdate(String sql) {
int resukt=0;
try {
conn=getConnection();
statement=conn.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE,ResultSet.CONCUR_READ_ONLY);
resukt=statement.executeUpdate(sql);
} catch (Exception e) {
e.printStackTrace();
}
return resukt;
}
/**
* 创建关闭数据库连接的方法
*/
public void close(){
try {
if(resultSet!=null){
resultSet.close();
}
if(statement!=null){
statement.close();
}
if(conn!=null){
conn.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
21.3.2 编写保存分页代码的JavaBean
public class MyPagination {
public List<Diary> list=null;
private int recordCount; //保存记录总数的变量
private int maxPage; //保存最大页数的变量
private int pageSize; //保存每页显示的记录数的变量
/**
* 初始化分页信息
* @param list
* @param Page
* @param pageSize
* @return
*/
public List<Diary> getInitPage(List<Diary> list,int Page,int pageSize){
List<Diary> newList=new ArrayList<>();
this.list=list;
recordCount=list.size();
this.pageSize=pageSize;
this.maxPage=getMaxPage();
try {
for(int i=(Page-1)*pageSize;i<=Page*pageSize;i++){
try {
if(i>=recordCount){break;}
} catch (Exception e) {
e.printStackTrace();
}
newList.add(list.get(i));
}
} catch (Exception e) {
e.printStackTrace();
}
return newList;
}
/**
* 获取指定页数据
* @param Page
* @return
*/
public List<Diary> getAppointPage(int Page){
List<Diary> newList=new ArrayList<>();
try {
for(int i=(Page-1)*pageSize;i<=Page*pageSize-1;i++){
try {
if(i>=recordCount){break;}
} catch (Exception e) {
e.printStackTrace();
}
newList.add(list.get(i));
}
} catch (Exception e) {
e.printStackTrace();
}
return newList;
}
/**
* 获取最大记录数
* @return
*/
private int getMaxPage() {
int maxPage=(recordCount%pageSize==0)?(recordCount/pageSize):(recordCount/pageSize+1);
return maxPage;
}
/**
* 获取总记录数
* @return
*/
public int getRecordSize(){
return recordCount;
}
/**
* 获取页数
* @param str
* @return
*/
public int getPage(String str){
if(str==null){
str="0";
}
int Page=Integer.parseInt(str);
if(Page<1){
Page=1;
}else{
if(((Page-1)*pageSize+1)>recordCount){
Page=maxPage;
}
}
return Page;
}
/**
* 输出记录导航
* @param Page 当前页数
* @param url URL地址
* @param para
* @return
*/
public String printCtrl(int Page,String url,String para){
String strHtml="<table width='100%' border='0' cellspacing='0' cellpadding='0'><tr> <td height='24' align='right'>当前页数:【"+Page+"/"+maxPage+"】 ";
try{
if(Page>1){
strHtml=strHtml+"<a href='"+url+"&Page=1"+para+"'>第一页</a> ";
strHtml=strHtml+"<a href='"+url+"&Page="+(Page-1)+para+"'>上一页</a>";
}
if(Page<maxPage){
strHtml=strHtml+"<a href='"+url+"&Page="+(Page+1)+para+"'>下一页</a> <a href='"+url+"&Page="+maxPage+para+"'>最后一页 </a>";
}
strHtml=strHtml+"</td> </tr> </table>";
}catch(Exception e){
e.printStackTrace();
}
return strHtml;
}
}
21.3.3 配置解决中文乱码的过滤器
通常有两种方法解决程序中中文乱码
- 1.通过编码字符串处理类,对需要的内容进行转码
- 2.配置过滤器(推荐使用)
新建CharacterEncodingFilter类,实现Filter接口,成为一个Servlet过滤器
public class CharacterEncodingFilter implements Filter {
private String encoding=null; //定义编码格式变量
private FilterConfig filterConfig; //定义过滤器配置对象
@Override
public void destroy() {
encoding=null;
filterConfig=null;
}
//过滤器的接口方法,用于执行过滤业务
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
if(encoding!=null){
request.setCharacterEncoding(encoding);//设置请求的编码
response.setContentType("text/html;charset="+encoding);
}
chain.doFilter(request, response);
}
@Override
public void init(FilterConfig arg0) throws ServletException {
this.filterConfig=arg0;//初始化过滤器配置对象
this.encoding=filterConfig.getInitParameter("encoding");//获取配置文件中指定的编码格式
}
}
在web.xml中为CharacterEncodingFilter过滤器配置,关键代码如下
<filter>
<!-- 指定过滤器类型 -->
<filter-name>CharacterEncodingFilter</filter-name>
<filter-class>com.wgh.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
<!-- 设置过滤器对应的请求方式 -->
<dispatcher>REQUEST</dispatcher>
<dispatcher>FORWARD</dispatcher>
</filter-mapping>
21.3.4 编写实体类
public class Diary {
private int id = 0;// 日记ID号
private String title = "";// 日记标题
private String address = "";// 日记图片地址
private Date writeTime = null;// 写日记的时间
private int userid = 0;// 用户ID
private String username = "";// 用户名
//省略所有的Setter()和Getter()方法
}
21.4 主界面设计
21.4.1 主界面概述
- Banner信息栏,主要用于显示网站的Logo
- 导航栏,主要用于显示网站的导航信息及欢迎信息,其中导航条目将根据是否是登录而显示不同的内容
- 主显示区,主要用于分页显示九宫格日记列表
- 版权信息栏,主要用于显示版权信息
21.4.2 让采用DIV+CSS布局的页面内容居中
- 1.在页面的<body>标记下方添加一个<div>标记(使用<div>标记将页面内容括起来),并设置其id属性
<div id="box">
</div>
- 2.设置CSS样式
body{
margin:0px; //外边距
padding:0px; //内边距
font-size:9pt; //设置字体大小
}
#box{
margin:0 auto auto auto; //设置外边距
width:800px; //宽度,其实这里宽度应该适配,写为auto
clear:both; //设置两侧边均不可以有浮动内容
background-color:#FFFFFF; //设置背景颜色
}
在JSP页面中,一定要包含以下代码,否则页面内容将不居中
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
21.4.3 主界面的实现过程
21.5 用户模块设计
21.5.1 用户模块概述
注册模块、登录模块截图
微信截图_20191021091724.png
微信截图_20191021091750.png
21.5.2 实现Ajax重构
- 1.创建一个单独的JS文件,名称为AjaxRequest.js,并且在该文件中编写重构Ajax所需的代码
var net=new Object(); //定义一个全局的变量
//编写构造函数
net.AjaxRequest=function(url,onload,onerror,method,params){
this.req=null;
this.onload=onload;
this.onerror=(onerror) ? onerror : this.defaultError;
this.loadDate(url,method,params);
}
//编写用于初始化XMLHttpRequest对象并指定处理函数,最后发送HTTP请求的方法
net.AjaxRequest.prototype.loadDate=function(url,method,params){
if (!method){
method="GET"; //设置默认的请求方式为GET
}
if (window.XMLHttpRequest){ //非IE浏览器
this.req=new XMLHttpRequest(); //创建XMLHttpRequest对象
} else if (window.ActiveXObject){//IE浏览器
this.req=new ActiveXObject("Microsoft.XMLHTTP"); //创建XMLHttpRequest对象
}
if (this.req){
try{
var loader=this;
this.req.onreadystatechange=function(){
net.AjaxRequest.onReadyState.call(loader);
}
this.req.open(method,url,true); // 建立对服务器的调用
if(method=="POST"){ // 如果提交方式为POST
this.req.setRequestHeader("Content-Type","application/x-www-form-urlencoded"); // 设置请求的内容类型
this.req.setRequestHeader("x-requested-with", "ajax"); //设置请求的发出者
}
this.req.send(params); // 发送请求
}catch (err){
this.onerror.call(this); //调用错误处理函数
}
}
}
//重构回调函数
net.AjaxRequest.onReadyState=function(){
var req=this.req;
var ready=req.readyState; //获取请求状态
if (ready==4){ //请求完成
if (req.status==200 ){ //请求成功
this.onload.call(this);
}else{
this.onerror.call(this); //调用错误处理函数
}
}
}
//重构默认的错误处理函籹
net.AjaxRequest.prototype.defaultError=function(){
alert("错误数据\n\n回调状态:" + this.req.readyState + "\n状态: " + this.req.status);
}
- 2.在需要应用Ajax页面中应用以下语句包括AjaxRequest.js的JS文件
<script language="javascript" src="JS/AjaxRequest.js"></script>
- 3.在应用Ajax的页面中编写错误处理的方法、实例化Ajax对象的方法和回调函数
21.5.3 用户注册的实现
1.设计用户注册页面
- 1.创建register.jsp页面,并在该页面上添加一个<div>标记,设置其id属性为register,并设置该<div>标记的style属性
<div id="register" style="width:663; height:421; background-color:#546B51; padding:4px; position:absolute; z-index:11;display:none;">
- 2.用户注册页面所涉及的表单及表单元素
|名称|元素类型|重要属性|含义|
|---------|---------|---------|---------|
|form1|form|action="" method="post"|表单|
|user|text|onBlur="checkUser(this.value)"|用户名|
|pwd|password|onBlur="checkPwd(this.value)"|密码|
|repwd|password|onBlur="checkRepwd(this.value)"|确认密码|
|email|text|onBlur="checkEmail(this.value)"|E-mail地址|
|province|select|id="province" onChange="getCity(this.value)"|省份|
|city|select|id="city"|市县|
|question|text|onBlur=“checkQuestion(this.value,this.form.answer.value)”|密码提示问题|
|answer|text|onBlur="checkQue(this.form.question.value,this.value)"|提示问题答案|
|btn_sumbit|buttom|value="提交" onClick="save()"|提交、按钮|
|btn_reset|button|value="重置" onClick="form_reset(this.form)"|重置、按钮|
|btn_close|button|value="关闭" onClick="Myclose('register')"|关闭、按钮| - 3.实现级联显示选择所在地省份和市县的下拉列表
- 4.编写自定义的JavaScript函数Regopen(),用于居中显示用户注册页面
//显示用户注册页面
function Regopen(divID){
getProvince(); //获取省和直辖市
var notClickDiv=document.getElementById("notClickDiv"); //获取id为notClickDiv的层
notClickDiv.style.display='block'; //设置层显示
document.getElementById("notClickDiv").style.width=document.body.clientWidth;
document.getElementById("notClickDiv").style.height=document.body.clientHeight;
divID=document.getElementById(divID); //根据传递的参数获取操作的对象
divID.style.display='block'; //显示用户注册页面
divID.style.left=(document.body.clientWidth-663)/2; //设置页面的左边距
divID.style.top=(document.body.clientHeight-441)/2; //设置页面的顶边距
}
- 5.在网站导航栏中设置用于显示用户注册页面的超链接,并在其onclick事件中调用Regopen()函数
<a href="#" onclick="Regopen('register')">注册</a>
- 6.编写自定义的JavaScript函数Myclose(),用于隐藏用户注册页面
//隐藏用户注册页面
function Myclose(divID){
document.getElementById(divID).style.display='none'; //隐藏用户注册页面
//设置id为notClickDiv的层隐藏
document.getElementById("notClickDiv").style.display='none';
}
2.验证输入信息的有效性
- 1.在验证输入信息的有效性,首先需要定义一下6个JavaScript全局变量
<script language="javascript">
var flag_user=true; //记录用户是否合法
var flag_pwd=true; //记录密码是否合法
var flag_repwd=true; //确认密码是否通过
var flag_email=true; //记录e-mail地址是否合法
var flag_question=true; //记录密码提示问题是否输入
var flag_answer=true; //记录提示问题答案是否输入
</script>











网友评论