一、项目介绍
留言板项目:该留言板可以实现三个功能模块;通过用户访问该网站,无需登录直接进行留言,输入内容包括昵称和留言内容,之后点击发送,便可以通过js脚本自动获取昵称、留言内容和留言时间,传送给服务器。服务器接收该数据后,进行处理,保存到数据库。再通过脚本自动读取数据库分页打印到留言板主页,具体分多少页由记录数决定。此外还提供“刷新”功能,供留言后的用户刷新查看自己刚刚的留言。
- 刷新回到第一页;
- 对数据库的数据进行读取并分页打印到主页(一页只有5条数据库记录);
- 在主页输入“留言昵称”和“留言内容”,点击“发送”,会自动获取“发送时间”一同存取到数据库,“刷新”或者点击“首页”、“下一页”、“上一页”和“尾页”即可更新(即更新2的操作)。
二、项目涉及技术
页面设计主要通过html、css和javaScript的一个框架jQuery来实现;数据的传输主要通过jQuery ajax来实现;后台处理数据主要通过MVC模式来实现(javabean、JDBCUtil、接口和接口实现类、servlet、jsp)。此外,还需要一个用来保存数据的数据库,我使用mysql;最后还要利用一个服务器配置软件,我选择tomcat。
- 前端:html+css+jQuery(不使用jQuery也可以,可以使用javaScript);
- 数据传输:jQuery ajax;
- 后台:MVC模式;
- 数据库:mysql;
- 服务器配置:tomcat;
- 开发环境:PC端,win7系统,谷歌浏览器(未进行浏览器兼容性处理,未进行移动端设计,目前只适合以上开发环境)。
三、具体代码
1. 针对服务器进行配置
(1)对“server.xml”文件的配置
<Context path="test" docBase="d:/test" reloadable="true" workDir="d:/test/work" />
(2)建立目录结构:在d盘建立test文件夹。再在test文件夹下建立WEB-INF文件夹(这个文件夹命名一定是大写),在WEB-INF文件夹下建两个目录src和classes。最后在src文件夹下分级建gzmtu/xt/dzsw目录即可。
(3)可以先启动tomcat,进行测试,看看配置是否正确,不正确进行检查。之后可以关了tomcat,因为等一下还要建立新的目录和.java文件以及java文件的编译,这两个是需要重启tomcat的。
2.导入jQuery文件
想要导入jQuery文件,需要在“https://jquery.com/download/”进行下载,其中有发布版和开发板。发布版是经过压缩的,会比较小,适合实际运行阶段;开发版是未经压缩的,内含有注释,相对较大,适合应用开发阶段。
我下载的是以下版本。下载好后更名为“jquery.js”,保存在“d:/test/jQuery”目录下,需要先建立“jQuery”目录。
3. 数据库设计
(1)建立数据库
create database guestbook charset=utf8;
(2)引用数据库
use guestbook;
(3)建数据库表
create table message(
id int(11) NOT NULL auto_increment PRIMARY KEY,
name varchar(10) NOT NULL,
text varchar(500) NOT NULL,
postTime datetime NOT NULL
)auto_increment=1;
(4)向这个表插入数据
insert into message(name,text,postTime)
value('李华','欢迎来到留言板,我是此系统的开发人员,如
果您在操作过程中遇到什么问题,可以联系我的tel
(19023784335)。','2021-06-01 16:39:55');
4. 前台代码设计
(1)html文件:命名为index.html,采用utf-8编码,放置在“d:/test/”目录下。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>留言板</title>
<link rel="stylesheet" href="CSS/index.css" />
<script type="text/javascript" src="jQuery/jquery.js"></script>
<script type="text/javascript" src="javaScript/insertRecord.js"></script>
<script type="text/javascript" src="javaScript/listGuestBooks.js"></script>
<script type="text/javascript" src="javaScript/reload.js"></script>
</head>
<body>
<div class="main">
<div class="info">
<h3>留言板<button id="reload" style="width:50px;background:url('images/reload.png') no-repeat center"></button></h3>
<div id="message"></div>
</div>
<hr />
<div class="operate">
<h3>请输入留言信息<span id="remind"><span></h3>
<form action="" method="post">
<label for="name">姓名</label><br />
<input type="text" id="name" name="name" placeholder="请输入姓名..." required /><br />
<label for="text">留言内容</label><br />
<textarea id="text" name="text" cols="100" rows="10" placeholder="请输入留言内容..." required></textarea><br />
<input type="text" id="postTime" name="postTime" value="" />
<input type="button" id="send" value="发送" onclick="insertHandler();">
<input type="reset" id="reset">
</form>
</div>
</div>
</body>
</html>
解释:该文件主要提供留言板基本的内容信息,因为html的工作就是显示内容的。
(2)主页样式:命名为index.css,保存在“d:/test/CSS/”目录下。在主页链接这个CSS样式的时候需要注意路径问题。
* {
margin:0px;
padding:0px;
}
body {
width:100%;
height:100%;
}
.main {
width:800px;
height:100%;
margin:auto;
box-sizing:content-box;
border:1px solid gray;
}
.info, .operate {
margin:0px 20px;
}
.info {
height:70%;
position:relative;
}
.operate {
}
.info>h3 {
height:50px;
line-height:50px;
border-bottom:2px solid gray;
}
.operate>h3 {
height:50px;
line-height:50px;
}
.info>h3>button {
width:50px;
height:30px;
position:absolute;
top:10px;
right:10px;
}
label {
width:100%;
line-height:25px;
display:inline-block;
border:2px solid gray;
background-color:gray;
}
input[type="text"], textarea {
width:100%;
margin-top:5px;
margin-bottom:5px;
}
#postTime {
display:none;
}
input[type="button"] {
width:100%;
height:25px;
border:2px solid #ddd;
background:linear-gradient(to bottom,#fff,#ddd);
text-align:center;
}
input[type="button"]:hover {
background:linear-gradient(to bottom,#ddd,#fff);
}
input[type="reset"]{
display:none;
}
#remind {
font:10px "宋体";
color:gray;
}
解释:该css样式文件主要是对主页内容文件index.html进行修饰,让主页文件在通过浏览器解析后变得好看,符合大部分人的审美。
(3)脚本设计:共三个脚本,分别是insertRecord.js脚本、listGuestBooks.js脚本、reload.js脚本。需要在“d:/test/”目录下建立“javaScript”目录,用来存放脚本文件。
- insertRecord.js脚本:命名为insertRecord.js,以utf-8编码格式保存在“d:/test/javaScript”目录下。老规矩,在主页链接这个CSS样式的时候需要注意路径问题。
function insertHandler(){
//检查昵称、留言内容是否为空
if($('#name').val()==''){
alert('昵称不能为空!');
return false;
}
if($('#text').val()==''){
alert('留言内容不能为空!');
return false;
}
//设置时间
var localTime=new Date();
var year=localTime.getFullYear();
var month=localTime.getMonth()+1;
var date=localTime.getDate();
var hour=localTime.getHours();
var minutes=localTime.getMinutes();
var seconds=localTime.getSeconds();
if(Math.floor(month/10)!=1) month='0'+month;
if(Math.floor(date/10)==0) date='0'+date;
if(Math.floor(hour/10)==0) hour='0'+hour;
if(Math.floor(minutes/10)==0) minutes='0'+minutes;
if(Math.floor(seconds/10)==0) seconds='0'+seconds;
var postTime=year+'-'+month+'-'+date+' '+hour+':'+minutes+':'+seconds;
$('#postTime').val(postTime);
//异步传输
$.ajax({
type:'GET',
url:'/test/viewInsertRecord.do',
data:$('form').serialize()
});
//提醒用户
$(document).ajaxStart(function(){
$('#remind').text('正在发送请求...').show();
});
$(document).ajaxStop(function(){
$('#reset').trigger('click');
$('#remind').text('留言成功!').fadeOut(5000);
});
}
解释:该脚本主要是检验昵称、留言内容是否为空;获取表单提交时间;将昵称、留言内容和表单提交时间异步传输到处理数据的服务器,处理的映射为viewInsertRecord.do;最后当数据传输完成后对用户进行提醒,包括文字提醒和清空表单。
- listGuestBooks.js脚本
$(document).ready(function(){
$('#message').load('/test/list.jsp');
});
解释:该脚本主要是当浏览器读取完html结构后,加载list.jsp文件到主页的一个内容块上。而list.jsp文件就是对从数据库中获取到的数据进行分页打印的一个文件。
- reload.js脚本
$(document).ready(function(){
$('#reload').click(function(){
$('#message').load('/test/list.jsp');
});
});
解释:该脚本主要是提供刷新功能,当点击刷新按钮后,便可以重新读取list.jsp文件到主页。
5. 后台设计
(1)javabean:命名为Person.java,以utf-8编码进行保存到“d:/test/WEB-INF/src/gzmtu/xt/dzsw/entity”目录,需要先在“d:/test/WEB-INF/src/gzmtu/xt/dzsw/”目录下建“entity”文件夹。
package gzmtu.xt.dzsw.entity;
public class Person{
private int id;
private String name;
private String text;
private String postTime;
public Person(){}
public void setID(int id){
this.id=id;
}
public int getID(){
return id;
}
public void setName(String name){
this.name=name;
}
public String getName(){
return name;
}
public void setText(String text){
this.text=text;
}
public String getText(){
return text;
}
public void setPostTime(String postTime){
this.postTime=postTime;
}
public String getPostTime(){
return postTime;
}
}
解释:该javabean涉及一个类Person。类有四个私有属性,分别是整形的id和三个字符串型属性name、text、postTime;类有一个公开的无参构造函数;类有对应四个私有属性的公开存取方法。
编译:
(2)JDBCUtil:命名为JDBCUtil.java,以utf-8保存到“d:/test/WEB-INF/src/gzmtu/xt/dzsw/utils”目录下,需要先在“d:/test/WEB-INF/src/gzmtu/xt/dzsw/”目录下建“utils”文件夹。
package gzmtu.xt.dzsw.utils;
import java.sql.*;
public class JDBCUtil{
public static final String url="jdbc:mysql://localhost:3306/guestbook?characterEncoding=utf-8";
public static final String user="root";
public static final String password="85578649";
public static Connection getConnection(){
try{
return DriverManager.getConnection(url,user,password);
}catch(SQLException e){
e.printStackTrace();
return null;
}
}
public static void close(PreparedStatement pstmt,Connection conn){
if(pstmt!=null){
try{
pstmt.close();
}catch(SQLException e){
e.printStackTrace();
}
}
if(conn!=null){
try{
conn.close();
}catch(SQLException e){
e.printStackTrace();
}
}
}
public static void close(ResultSet rs,PreparedStatement pstmt,Connection conn){
if(rs!=null){
try{
rs.close();
}catch(SQLException e){
e.printStackTrace();
}
close(pstmt,conn);
}
}
}
解释:该文件主要用于连接数据库和关闭数据库连接。
编译:
(3)接口:命名为IPersonDAO.java,以“utf-8”编码保存到“d:/test/WEB-INF/src/gzmtu/xt/dzsw/dao”目录下,需要实现在“d:/test/WEB-INF/src/gzmtu/xt/dzsw/”目录下建“dao”文件夹。
package gzmtu.xt.dzsw.dao;
import java.util.List;
import gzmtu.xt.dzsw.entity.Person;
public interface IPersonDAO{
int insertRecord(Person person);
List<Person> listGuestBooks();
}
解释:该文件主要定义了对数据库进行操作的函数。
编译:
(4)接口实现类:命名为PersonDAOImpl.java,以utf-8编码保存在“d:/test/WEB-INF/src/gzmtu/xt/dzsw/dao/impl”目录下,需要先在“d:/test/WEB-INF/src/gzmtu/xt/dzsw/dao/”目录下建“impl”文件夹。
package gzmtu.xt.dzsw.dao.impl;
import java.util.List;
import java.util.ArrayList;
import java.sql.*;
import java.text.SimpleDateFormat;
import java.text.ParseException;
import gzmtu.xt.dzsw.entity.Person;
import gzmtu.xt.dzsw.utils.JDBCUtil;
import gzmtu.xt.dzsw.dao.IPersonDAO;
public class PersonDAOImpl implements IPersonDAO{
public int insertRecord(Person person){
Connection conn=null;
PreparedStatement pstmt=null;
int count=0;
String sql="insert into message values(null,?,?,?)";
try{
conn=JDBCUtil.getConnection();
pstmt=conn.prepareStatement(sql);
pstmt.setString(1,person.getName());
pstmt.setString(2,person.getText());
SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
try{
Timestamp time=new Timestamp(sdf.parse(person.getPostTime()).getTime());
pstmt.setTimestamp(3,time);
}catch(ParseException e){
e.printStackTrace();
}
count=pstmt.executeUpdate();
}catch(SQLException e){
e.printStackTrace();
}finally{
JDBCUtil.close(pstmt,conn);
}
return count;
}
public List<Person> listGuestBooks(){
List<Person> list=new ArrayList<Person>();
Connection conn=null;
PreparedStatement pstmt=null;
ResultSet rs=null;
String sql="select * from message";
try{
conn=JDBCUtil.getConnection();
pstmt=conn.prepareStatement(sql);
rs=pstmt.executeQuery();
while(rs.next()){
Person person=new Person();
person.setID(rs.getInt(1));
person.setName(rs.getString(2));
person.setText(rs.getString(3));
person.setPostTime(rs.getString(4));
list.add(person);
}
}catch(SQLException e){
list=null;
e.printStackTrace();
}finally{
JDBCUtil.close(rs,pstmt,conn);
}
return list;
}
}
解释:该文件主要实现接口定义的方法。
编译:
(5)servlet文件:本来有两个servlet文件的,但是后来我一个使用了jsp来编写,所以只有一个servlet。
package gzmtu.xt.dzsw.servlet;
import java.util.*;
import java.text.SimpleDateFormat;
import java.text.ParseException;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import gzmtu.xt.dzsw.entity.Person;
import gzmtu.xt.dzsw.dao.IPersonDAO;
import gzmtu.xt.dzsw.dao.impl.PersonDAOImpl;
@WebServlet(urlPatterns="/viewInsertRecord.do")
public class ViewInsertRecord extends HttpServlet{
public void service(HttpServletRequest request,HttpServletResponse response)throws ServletException,java.io.IOException{
request.setCharacterEncoding("utf-8");
String name=request.getParameter("name");
String text=request.getParameter("text");
String postTime=request.getParameter("postTime");
Person person=new Person();
person.setName(name);
person.setText(text);
person.setPostTime(postTime);
IPersonDAO dao=new PersonDAOImpl();
int count=dao.insertRecord(person);
String result;
if(count==0){
result="留言失败!";
}else{
result="留言成功!";
}
}
}
解释:该文件主要是实现通过调用接口实现类定义的方法来进行插入数据的操作。
编译:
(6)jsp文件:
- jsp文件本身:命名为“list.jsp”,并以utf-8编码保存在“d:/test/”目录下。
<%@ page contentType="text/html;charset=utf-8" %>
<%@ page import="java.util.*" %>
<%@ page import="gzmtu.xt.dzsw.entity.Person" %>
<%@ page import="gzmtu.xt.dzsw.dao.IPersonDAO" %>
<%@ page import="gzmtu.xt.dzsw.dao.impl.PersonDAOImpl" %>
<%
IPersonDAO dao=new PersonDAOImpl();
List<Person> list=dao.listGuestBooks();
int records=list.size();
int curpage=request.getParameter("curpage")==null?1:Integer.parseInt(request.getParameter("curpage"));
%>
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="CSS/message.css">
<script type="text/javascript" src="jQuery/jquery.js"></script>
<script type="text/javascript" src="javaScript/page.js"></script>
</head>
<body>
<%
out.write("<input type='hidden' value='"+records+"' />");
out.write("<input type='hidden' value='"+curpage+"' />");
%>
<dl>
<%
for(int i=(curpage-1)*5;i<curpage*5;i++){
if(i==records) break;
Person person=list.get(i);
%>
<dt>
<span><%= person.getID() %>楼</span>
<span><%= person.getName() %>:</span>
</dt>
<dt></dt>
<dd><%= person.getText() %></dd>
<dd><%= person.getPostTime() %></dd>
<%
}
%>
</dl>
<div class="page">
<span>首页</span>
<span>上一页</span>
<span>下一页</span>
<span>尾页</span>
</div>
</body>
</html>
解释:list.jsp主要是通过接口实现类中定义的方法从数据库获取数据,并将数据进行分页显示。分页显示通过脚本来实现。
- jsp文件的样式:命名为message.css,并以utf-8编码保存在“d:/test/CSS”目录下。
dl {
position:relative;
width:760px;
}
dt {
position:relative;
padding:5px;
}
dt span:nth-child(1) {
display:block;
padding:5px;
background-color:#ddd;
border-radius:5px;
}
dt:nth-child(even) {
display:none;
}
dd:nth-child(odd) {
padding:5px;
text-indent:2em;
}
dd:nth-child(even) {
padding:5px;
position:relative;
right:-580px;
}
.page {
position:relative;
right:-580px;
bottom:5px;
}
.page span {
font:bolder 14px "宋体";
color:black;
line-height:40px;
padding:2px;
border-bottom:1px solid black;
}
.page span:hover {
color:#aaa;
border-bottom:1px solid #aaa;
}
解释:message.css样式文件主要是对list.jsp文件的内容进行修饰,让它变得好看。
- jsp文件使用脚本
$(document).ready(function(){
var records=$('input:hidden').eq(0).attr('value');
var curpage=$('input:hidden').eq(1).attr('value');
var maxpage=Math.floor((records-1)/5)+1;
$('.page>span').eq(0).click(function(){
curpage=1;
$('#message').load('http://localhost:8080/test/list.jsp?curpage='+curpage);
});
$('.page>span').eq(1).click(function(){
curpage=curpage>1?--curpage:1;
$('#message').load('http://localhost:8080/test/list.jsp?curpage='+curpage);
});
$('.page>span').eq(2).click(function(){
curpage=curpage<maxpage?++curpage:maxpage;
$('#message').load('http://localhost:8080/test/list.jsp?curpage='+curpage);
});
$('.page>span').eq(3).click(function(){
curpage=maxpage;
$('#message').load('http://localhost:8080/test/list.jsp?curpage='+curpage);
});
});
解释:分页脚本主要是通过list.jsp文件的两个隐藏表单控件来获取数据库记录数和当前页这两个数据,然后通过用户点击“首页”、“上一页”、“下一页”和“尾页”来进行重新加载“list.jsp”文件,此时请求的链接会带有参数信息,这个参数就可以实现跳转。
四、笔记
- 让留言板整体居中
margin:0px auto;
- css的margin属性无效:给父元素设置宽度。
- h3垂直方向和水平方向都居中
text-align:center; //水平方向
//垂直方向
height:35px;
line-height:35px;
- 用户留言提交后清空文本框的内容:设置type属性值为reset的控件,将该控件设置为不可见。当触发ajaxStop事件时,触发reset按钮的点击事件。
- 使用jQuery ajax的load()函数进行加载list.jsp文件,在进行当前页参数传递时,需要注意,请求的是list.jsp,而不是index.html,所以参数是"list.jsp?参数=参数值"这种形式,而不是“index.html?参数=参数值”。
五、留言板最终文件
百度网盘:
链接:https://pan.baidu.com/s/1fmu_O7G3kBV6L3siQyyvkw
提取码:1234
六、补充说明
- 我目前大三,想要从事前端工程师一职,这篇博客是我的第一个训练项目,代码都是自己想和自己写的,如果有写得不好的地方,还请指教。
- 这篇博客的点子来自陈华老师的“Ajax从入门到精通”一书的第7章,但涉及的技术不一样。老师的后台设计使用了apache服务器+php,但我没用过apache,也没学过php。
- 如果有人转载,请告知我一下,并备注出处。如有转载但未说明或者未备注出处的朋友,一经发现,会追究责任的哦。
- 目前我看过很多csdn的文章(包括各种问题的解决方案),很多都是转载别人的,我希望如果有幸被转载,请备注出处。