You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Library/Reader_注释

4244 lines
213 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

// 声明该类所在的包名为 javabean
package javabean;
// 导入// 声明该类所在的包名为 javabean
package javabean;
// 导入 java.sql 包中的 Connection 类,用于建立与数据库的连接
import java.sql.Connection;
// 导入 java.sql 包中的 PreparedStatement 类,用于执行预编译的 SQL 语句
import java.sql.PreparedStatement;
// 导入 java.sql 包中的 ResultSet 类,用于存储数据库查询结果
import java.sql.ResultSet;
// 导入 java.sql 包中的 SQLException 类,用于处理 SQL 操作中可能出现的异常
import java.sql.SQLException;
// 定义一个名为 Reader 的公共类,用于处理读者登录相关操作
public class Reader {
// 抑制“可能存在空指针引用”的警告
@SuppressWarnings("null")
/**
* 该方法用于处理读者的登录逻辑
* @param user 读者的账号
* @param psw 读者的密码
* @return 返回登录结果的提示信息
* @throws ClassNotFoundException 如果在加载数据库驱动类时出现问题,抛出该异常
* @throws SQLException 如果在执行 SQL 操作时出现问题,抛出该异常
*/
public String login(String user, String psw) throws ClassNotFoundException, SQLException {
// 检查账号是否为空或仅包含空白字符
if (user == null || user.trim().equals("")) {
// 如果账号为空,返回提示信息
return "账号不能为空";
}
// 检查密码是否为空或仅包含空白字符
else if (psw == null || psw.trim().equals("")) {
// 如果密码为空,返回提示信息
return "密码不能为空";
}
// 声明一个 Connection 对象,用于存储与数据库的连接,初始值为 null
Connection connection = null;
// 声明一个 PreparedStatement 对象,用于执行预编译的 SQL 语句,初始值为 null
PreparedStatement pstmt = null;
// 声明一个 ResultSet 对象,用于存储数据库查询结果,初始值为 null
ResultSet resultSet = null;
// 定义一个 SQL 查询语句,使用占位符 ? 来防止 SQL 注入
String sql = "select * from borrow_card where ID=? and PASSWORD=?";
// 调用 Base 类的 getConnection 方法获取数据库连接,并将其赋值给 connection 对象
connection = Base.getConnection();
// 使用 connection 对象创建一个 PreparedStatement 对象,用于执行预编译的 SQL 语句
pstmt = (PreparedStatement) connection.prepareStatement(sql);
// 为 SQL 语句中的第一个占位符(即 ID设置值值为传入的 user 参数
pstmt.setString(1, user);
// 为 SQL 语句中的第二个占位符(即 PASSWORD设置值值为传入的 psw 参数
pstmt.setString(2, psw);
// 执行 SQL 查询语句,并将查询结果存储在 resultSet 对象中
resultSet = pstmt.executeQuery();
// 检查结果集中是否有下一行记录
if (resultSet.next()) {
// 如果有记录,说明账号和密码匹配,返回 "1" 表示登录成功
return "1";
}
// 如果结果集中没有记录,说明账号或密码错误,返回提示信息
return "账号或密码错误";
}
}
// 声明该 Servlet 类所在的包名为 servlet.reader
package servlet.reader;
import java.io.IOException;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javabean.Base;
import net.sf.json.JSONArray;
import net.sf.json.JSONObject;
/**
* Servlet implementation class Book
* 该 Servlet 用于处理读者相关的书籍查询请求,返回符合条件的书籍信息的 JSON 数据
*/
@WebServlet("/reader/book")
public class Book extends HttpServlet {
// 重写 doGet 方法,处理 HTTP GET 请求
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 设置响应的内容类型为 JSON 格式,并指定字符编码为 UTF-8
resp.setContentType("application/json; charset=utf8");
// 接收客户端传递的参数
// limit 表示每页显示的记录数
String limit = req.getParameter("limit");
// page 表示当前页码
String page = req.getParameter("page");
// condition 表示查询的条件字段
String condition = (String) req.getParameter("condition");
// conditionValue 表示查询条件字段的值
String conditionValue = (String) req.getParameter("conditionValue");
// 初始化 SQL 查询的 WHERE 子句为空,表示无限制条件
String where = "";
// 如果 page 参数为空,默认设置为第 1 页
if (page == null) {
page = "1";
}
// 如果 limit 参数为空,默认设置每页显示 10 条记录
if (limit == null) {
limit = "10";
}
// 准备数据库操作所需的对象
// 数据库连接对象
Connection connection = null;
// 用于执行查询书籍信息的预编译 SQL 语句对象
PreparedStatement pstmt = null;
// 用于执行查询书籍总数的预编译 SQL 语句对象
PreparedStatement countPstmt = null;
// 存储查询书籍信息的结果集
ResultSet resultSet = null;
// 存储查询书籍总数的结果集
ResultSet countSet = null;
// 存储查询书籍信息的 SQL 语句
String sql = "";
// 存储查询书籍总数的 SQL 语句
String countSql = "";
// 准备返回给客户端的参数
// 状态码1 表示无数据0 表示查询成功
int code = 1;
// 提示信息,默认提示无数据
String msg = "无数据";
// 符合条件的书籍总数,初始化为 0
int count = 0;
// 用于存储单条书籍信息的 JSON 对象
JSONObject jsonData = new JSONObject();
// 用于存储所有书籍信息的 JSON 数组
JSONArray jsonArray = new JSONArray();
// 用于存储最终返回给客户端的 JSON 数据,包含状态码、总数、提示信息和书籍信息数组
JSONObject jsonResult = new JSONObject();
// 进行数据库查询操作
try {
// 通过 Base 类的 getConnection 方法获取数据库连接
connection = (Connection) Base.getConnection();
// 初始化查询书籍信息的 SQL 语句,从 books 表中查询所有字段
sql = "select * from books ";
// 如果查询条件字段和条件值都不为空,则添加 WHERE 子句到 SQL 语句中
if (condition != null && conditionValue != null && !condition.equals("") && !conditionValue.equals("")) {
where = " where " + condition + " like '%" + conditionValue + "%' ";
sql += where;
}
// 添加分页查询的 LIMIT 子句到 SQL 语句中
sql += " limit ?,?";
// 预编译 SQL 语句
pstmt = connection.prepareStatement(sql);
// 设置 LIMIT 子句的第一个参数,计算当前页的起始记录索引
pstmt.setInt(1, (Integer.parseInt(page) - 1) * Integer.parseInt(limit));
// 设置 LIMIT 子句的第二个参数,即每页显示的记录数
pstmt.setInt(2, Integer.parseInt(limit));
// 执行查询操作,获取结果集
resultSet = pstmt.executeQuery();
// 遍历结果集
while (resultSet.next()) {
// 获取书籍所在图书馆的 ID
String library = resultSet.getString("library_id");
// 构建查询图书馆名称的 SQL 语句
String sql1 = "select * from library where ID =" + library;
// 预编译该 SQL 语句
PreparedStatement pstmt1 = connection.prepareStatement(sql1);
// 执行查询操作,获取结果集
ResultSet rs1 = pstmt1.executeQuery();
// 存储图书馆名称,初始为空
String lib = "";
// 遍历结果集,获取图书馆名称
while (rs1.next()) {
lib = rs1.getString("name");
}
// 获取书籍的分类 ID
String sortid = resultSet.getString("sort_id");
// 构建查询书籍分类名称的 SQL 语句
String sql2 = "select * from book_sort where ID =" + sortid;
// 预编译该 SQL 语句
PreparedStatement pstmt2 = connection.prepareStatement(sql2);
// 执行查询操作,获取结果集
ResultSet rs2 = pstmt2.executeQuery();
// 存储书籍分类名称,初始为空
String sort = "";
// 遍历结果集,获取书籍分类名称
while (rs2.next()) {
sort = rs2.getString("name");
}
// 将书籍信息添加到 JSON 对象中
jsonData.put("id", resultSet.getString("id"));
jsonData.put("name", resultSet.getString("name"));
jsonData.put("author", resultSet.getString("author"));
jsonData.put("library_id", lib);
jsonData.put("sort_id", sort);
jsonData.put("position", resultSet.getString("position"));
jsonData.put("status", resultSet.getString("status"));
jsonData.put("description", resultSet.getString("description"));
// 将单条书籍信息的 JSON 对象添加到 JSON 数组中
jsonArray.add(jsonData);
// 清空 JSON 对象,以便存储下一条书籍信息
jsonData = new JSONObject();
}
// 构建查询书籍总数的 SQL 语句
countSql = "select count(*) as count from books ";
// 添加 WHERE 子句到总数查询的 SQL 语句中
countSql += where;
// 预编译该 SQL 语句
countPstmt = connection.prepareStatement(countSql);
// 执行查询操作,获取结果集
countSet = countPstmt.executeQuery();
// 从结果集中获取书籍总数
if (countSet.next()) {
count = countSet.getInt("count");
}
// 如果 JSON 数组不为空,说明查询到了数据,将状态码设置为 0提示信息设置为查询成功
if (!jsonArray.isEmpty()) {
code = 0;
msg = "查询成功";
}
} catch (ClassNotFoundException e) {
// 如果在加载数据库驱动类时出现异常,将提示信息设置为 class 没找到
msg = "class没找到";
} catch (SQLException e) {
// 如果在执行 SQL 语句时出现异常,将提示信息设置为 sql 错误
msg = "sql错误";
} finally {
try {
// 关闭数据库资源,包括 PreparedStatement 和 ResultSet
Base.closeResource(null, pstmt, resultSet);
Base.closeResource(connection, countPstmt, countSet);
} catch (SQLException e) {
// 如果在关闭资源时出现异常,将提示信息设置为关闭资源失败
msg = "关闭资源失败";
}
}
// 将状态码、总数、提示信息和书籍信息数组添加到最终的 JSON 对象中
jsonResult.put("code", code);
jsonResult.put("count", count);
jsonResult.put("msg", msg);
jsonResult.put("data", jsonArray.toArray());
// 获取响应的输出流
PrintWriter out = resp.getWriter();
// 将最终的 JSON 对象转换为字符串并输出到客户端
out.print(jsonResult.toString());
}
}
// 包声明表明该类属于servlet.reader包
package servlet.reader;
// 导入处理输入输出异常的类
import java.io.IOException;
// 导入用于向客户端发送字符文本的类
import java.io.PrintWriter;
// 导入表示数据库连接的类
import java.sql.Connection;
// 导入用于执行预编译SQL语句的类
import java.sql.PreparedStatement;
// 导入用于存储数据库查询结果的类
import java.sql.ResultSet;
// 导入处理SQL操作异常的类
import java.sql.SQLException;
// 导入处理Servlet异常的类
import javax.servlet.ServletException;
// 导入用于注解Servlet映射路径的类
import javax.servlet.annotation.WebServlet;
// 导入HttpServlet类用于创建Servlet
import javax.servlet.http.HttpServlet;
// 导入表示HTTP请求的类
import javax.servlet.http.HttpServletRequest;
// 导入表示HTTP响应的类
import javax.servlet.http.HttpServletResponse;
// 导入用于管理用户会话的类
import javax.servlet.http.HttpSession;
// 导入自定义的数据库连接管理类
import javabean.Base;
// 导入用于处理JSON数组的类
import net.sf.json.JSONArray;
// 导入用于处理JSON对象的类
import net.sf.json.JSONObject;
/**
* Servlet实现类Borrow
* 该Servlet用于处理读者借阅记录的查询请求返回符合条件的借阅记录的JSON数据
*/
@WebServlet("/reader/borrow")
public class Borrow extends HttpServlet {
// 重写doGet方法用于处理HTTP GET请求
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 设置响应的内容类型为JSON格式并指定字符编码为UTF-8
resp.setContentType("application/json; charset=utf8");
// 接收客户端传递的参数
// limit表示每页显示的记录数
String limit = req.getParameter("limit");
// page表示当前页码
String page = req.getParameter("page");
// condition表示查询的条件字段
String condition = (String) req.getParameter("condition");
// conditionValue表示查询条件字段的值
String conditionValue = (String) req.getParameter("conditionValue");
// 初始化SQL查询的WHERE子句为空表示无限制条件
String where = "";
// 如果page参数为空默认设置为第1页
if (page == null) {
page = "1";
}
// 如果limit参数为空默认设置每页显示10条记录
if (limit == null) {
limit = "10";
}
// 准备数据库操作所需的对象
// 数据库连接对象
Connection connection = null;
// 用于执行查询借阅记录的预编译SQL语句对象
PreparedStatement pstmt = null;
// 用于执行查询借阅记录总数的预编译SQL语句对象
PreparedStatement countPstmt = null;
// 存储查询借阅记录的结果集
ResultSet resultSet = null;
// 存储查询借阅记录总数的结果集
ResultSet countSet = null;
// 存储查询借阅记录的SQL语句
String sql = "";
// 存储查询借阅记录总数的SQL语句
String countSql = "";
// 准备返回给客户端的参数
// 状态码1表示无数据0表示查询成功
int code = 1;
// 提示信息,默认提示无数据
String msg = "无数据";
// 符合条件的借阅记录总数初始化为0
int count = 0;
// 获取当前请求的会话对象
HttpSession session = req.getSession();
// 用于存储单条借阅记录信息的JSON对象
JSONObject jsonData = new JSONObject();
// 用于存储所有借阅记录信息的JSON数组
JSONArray jsonArray = new JSONArray();
// 用于存储最终返回给客户端的JSON数据包含状态码、总数、提示信息和借阅记录信息数组
JSONObject jsonResult = new JSONObject();
// 进行数据库查询操作
try {
// 通过Base类的getConnection方法获取数据库连接
connection = (Connection) Base.getConnection();
// 初始化查询借阅记录的SQL语句从borrow_books表中查询所有字段并且只查询当前读者的记录
sql = "select * from borrow_books where card_id = " + session.getAttribute("reader");
// 如果查询条件字段和条件值都不为空则添加额外的WHERE子句到SQL语句中
if (condition != null && conditionValue != null && !condition.equals("") && !conditionValue.equals("")) {
where = " and " + condition + " like '%" + conditionValue + "%' ";
sql += where;
}
// 添加分页查询的LIMIT子句到SQL语句中
sql += " limit ?,?";
// 打印生成的SQL语句方便调试
System.out.println("???" + sql);
// 预编译SQL语句
pstmt = connection.prepareStatement(sql);
// 设置LIMIT子句的第一个参数计算当前页的起始记录索引
pstmt.setInt(1, (Integer.parseInt(page) - 1) * Integer.parseInt(limit));
// 设置LIMIT子句的第二个参数即每页显示的记录数
pstmt.setInt(2, Integer.parseInt(limit));
// 执行查询操作,获取结果集
resultSet = pstmt.executeQuery();
// 遍历结果集
while (resultSet.next()) {
// 将借阅记录的各个字段信息添加到JSON对象中
jsonData.put("id", resultSet.getString("id"));
jsonData.put("card_id", resultSet.getString("card_id"));
jsonData.put("book_id", resultSet.getString("book_id"));
jsonData.put("borrow_date", resultSet.getString("borrow_date"));
jsonData.put("end_date", resultSet.getString("end_date"));
jsonData.put("return_date", resultSet.getString("return_date"));
// 将单条借阅记录信息的JSON对象添加到JSON数组中
jsonArray.add(jsonData);
// 清空JSON对象以便存储下一条借阅记录信息
jsonData = new JSONObject();
}
// 构建查询借阅记录总数的SQL语句同样只查询当前读者的记录
countSql = "select count(*) as count from borrow_books where card_id = "
+ req.getSession().getAttribute("reader");
// 添加额外的WHERE子句到总数查询的SQL语句中
countSql += where;
// 预编译该SQL语句
countPstmt = connection.prepareStatement(countSql);
// 执行查询操作,获取结果集
countSet = countPstmt.executeQuery();
// 从结果集中获取借阅记录总数
if (countSet.next()) {
count = countSet.getInt("count");
}
// 如果JSON数组不为空说明查询到了数据将状态码设置为0提示信息设置为查询成功
if (!jsonArray.isEmpty()) {
code = 0;
msg = "查询成功";
}
} catch (ClassNotFoundException e) {
// 如果在加载数据库驱动类时出现异常将提示信息设置为class没找到
msg = "class没找到";
} catch (SQLException e) {
// 如果在执行SQL语句时出现异常将提示信息设置为sql错误
msg = "sql错误";
} finally {
try {
// 关闭数据库资源包括PreparedStatement和ResultSet
Base.closeResource(null, pstmt, resultSet);
Base.closeResource(connection, countPstmt, countSet);
} catch (SQLException e) {
// 如果在关闭资源时出现异常,将提示信息设置为关闭资源失败
msg = "关闭资源失败";
}
}
// 将状态码、总数、提示信息和借阅记录信息数组添加到最终的JSON对象中
jsonResult.put("code", code);
jsonResult.put("count", count);
jsonResult.put("msg", msg);
jsonResult.put("data", jsonArray.toArray());
// 获取响应的输出流
PrintWriter out = resp.getWriter();
// 将最终的JSON对象转换为字符串并输出到客户端
out.print(jsonResult.toString());
}
}
// 包声明该Servlet属于servlet.reader包读者相关Servlet
package servlet.reader;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
/**
* Servlet实现类退出登录处理
* 功能:处理读者退出登录请求,销毁会话信息并跳转回登录页面
*/
@WebServlet("/reader/exit") // 声明Servlet映射路径/reader/exit
public class Exit extends HttpServlet {
// 序列化版本号IDE自动生成用于保证反序列化兼容性
private static final long serialVersionUID = 1L;
// 处理HTTP GET请求退出操作一般通过GET发起
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
// 获取当前用户会话
HttpSession session = req.getSession();
// 检查会话中是否存在读者登录信息("reader"为登录时存储的会话属性)
if (session.getAttribute("reader") != null) {
// 移除会话中的读者登录信息,实现退出
session.removeAttribute("reader");
}
// 重定向回读者登录页面(使用动态上下文路径保证部署灵活性)
// req.getContextPath() 获取当前应用的上下文路径(如:/LibrarySystem
// 跳转目标:/reader/04readerFrame.jsp读者登录页面
resp.sendRedirect(req.getContextPath() + "/reader/04readerFrame.jsp");
}
}
// 声明该Servlet类所在的包用于处理读者相关的Servlet操作
package servlet.reader;
// 导入处理输入输出异常的类
import java.io.IOException;
// 导入用于向客户端发送字符文本的类
import java.io.PrintWriter;
// 导入表示数据库连接的类
import java.sql.Connection;
// 导入用于执行预编译SQL语句的类
import java.sql.PreparedStatement;
// 导入用于存储数据库查询结果的类
import java.sql.ResultSet;
// 导入处理SQL操作异常的类
import java.sql.SQLException;
// 导入处理Servlet异常的类
import javax.servlet.ServletException;
// 导入用于注解Servlet映射路径的类
import javax.servlet.annotation.WebServlet;
// 导入HttpServlet类用于创建Servlet
import javax.servlet.http.HttpServlet;
// 导入表示HTTP请求的类
import javax.servlet.http.HttpServletRequest;
// 导入表示HTTP响应的类
import javax.servlet.http.HttpServletResponse;
// 导入用于管理用户会话的类
import javax.servlet.http.HttpSession;
// 导入自定义的数据库连接管理类
import javabean.Base;
// 导入用于处理JSON数组的类
import net.sf.json.JSONArray;
// 导入用于处理JSON对象的类
import net.sf.json.JSONObject;
/**
* Servlet实现类Illegal
* 该Servlet用于处理读者违规借阅记录的查询请求返回符合条件的违规借阅记录的JSON数据
*/
@WebServlet("/reader/illegal")
public class Illegal extends HttpServlet {
// 重写doGet方法用于处理HTTP GET请求
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 设置响应的内容类型为JSON格式并指定字符编码为UTF-8
resp.setContentType("application/json; charset=utf8");
// 接收客户端传递的参数
// limit表示每页显示的记录数
String limit = req.getParameter("limit");
// page表示当前页码
String page = req.getParameter("page");
// condition表示查询的条件字段
String condition = (String) req.getParameter("condition");
// conditionValue表示查询条件字段的值
String conditionValue = (String) req.getParameter("conditionValue");
// 初始化SQL查询的WHERE子句为空表示无限制条件
String where = "";
// 如果page参数为空默认设置为第1页
if (page == null) {
page = "1";
}
// 如果limit参数为空默认设置每页显示10条记录
if (limit == null) {
limit = "10";
}
// 准备数据库操作所需的对象
// 数据库连接对象
Connection connection = null;
// 用于执行查询违规借阅记录的预编译SQL语句对象
PreparedStatement pstmt = null;
// 用于执行查询违规借阅记录总数的预编译SQL语句对象
PreparedStatement countPstmt = null;
// 存储查询违规借阅记录的结果集
ResultSet resultSet = null;
// 存储查询违规借阅记录总数的结果集
ResultSet countSet = null;
// 存储查询违规借阅记录的SQL语句
String sql = "";
// 存储查询违规借阅记录总数的SQL语句
String countSql = "";
// 准备返回给客户端的参数
// 状态码1表示无数据0表示查询成功
int code = 1;
// 提示信息,默认提示无数据
String msg = "无数据";
// 符合条件的违规借阅记录总数初始化为0
int count = 0;
// 获取当前请求的会话对象
HttpSession session = req.getSession();
// 用于存储单条违规借阅记录信息的JSON对象
JSONObject jsonData = new JSONObject();
// 用于存储所有违规借阅记录信息的JSON数组
JSONArray jsonArray = new JSONArray();
// 用于存储最终返回给客户端的JSON数据包含状态码、总数、提示信息和违规借阅记录信息数组
JSONObject jsonResult = new JSONObject();
// 进行数据库查询操作
try {
// 通过Base类的getConnection方法获取数据库连接
connection = (Connection) Base.getConnection();
// 初始化查询违规借阅记录的SQL语句从borrow_books表中查询所有字段
// 筛选出违规信息不为空且长度大于0的记录并且只查询当前读者的记录
sql = "select * from borrow_books where ILLEGAL is not null and length(trim(illegal))>0 AND CARD_ID = "
+ session.getAttribute("reader");
// 如果查询条件字段和条件值都不为空则添加额外的WHERE子句到SQL语句中
if (condition != null && conditionValue != null && !condition.equals("") && !conditionValue.equals("")) {
where = " and " + condition + " like '%" + conditionValue + "%' ";
sql += where;
}
// 添加分页查询的LIMIT子句到SQL语句中
sql += " limit ?,?";
// 打印生成的SQL语句方便调试
System.out.println("???" + sql);
// 预编译SQL语句
pstmt = connection.prepareStatement(sql);
// 设置LIMIT子句的第一个参数计算当前页的起始记录索引
pstmt.setInt(1, (Integer.parseInt(page) - 1) * Integer.parseInt(limit));
// 设置LIMIT子句的第二个参数即每页显示的记录数
pstmt.setInt(2, Integer.parseInt(limit));
// 执行查询操作,获取结果集
resultSet = pstmt.executeQuery();
// 遍历结果集
while (resultSet.next()) {
// 将违规借阅记录的各个字段信息添加到JSON对象中
jsonData.put("id", resultSet.getString("id"));
jsonData.put("card_id", resultSet.getString("card_id"));
jsonData.put("book_id", resultSet.getString("book_id"));
jsonData.put("borrow_date", resultSet.getString("borrow_date"));
jsonData.put("end_date", resultSet.getString("end_date"));
jsonData.put("return_date", resultSet.getString("return_date"));
jsonData.put("illegal", resultSet.getString("illegal"));
jsonData.put("manager_id", resultSet.getString("manager_id"));
// 将单条违规借阅记录信息的JSON对象添加到JSON数组中
jsonArray.add(jsonData);
// 清空JSON对象以便存储下一条违规借阅记录信息
jsonData = new JSONObject();
}
// 构建查询违规借阅记录总数的SQL语句同样筛选出违规信息不为空且长度大于0的记录
// 并且只查询当前读者的记录
countSql = "select count(*) as count from borrow_books where ILLEGAL is not null and length(trim(illegal))>0 AND CARD_ID = "
+ session.getAttribute("reader");
// 添加额外的WHERE子句到总数查询的SQL语句中
countSql += where;
// 预编译该SQL语句
countPstmt = connection.prepareStatement(countSql);
// 执行查询操作,获取结果集
countSet = countPstmt.executeQuery();
// 从结果集中获取违规借阅记录总数
if (countSet.next()) {
count = countSet.getInt("count");
}
// 如果JSON数组不为空说明查询到了数据将状态码设置为0提示信息设置为查询成功
if (!jsonArray.isEmpty()) {
code = 0;
msg = "查询成功";
}
} catch (ClassNotFoundException e) {
// 如果在加载数据库驱动类时出现异常将提示信息设置为class没找到
msg = "class没找到";
} catch (SQLException e) {
// 如果在执行SQL语句时出现异常将提示信息设置为sql错误
msg = "sql错误";
} finally {
try {
// 关闭数据库资源包括PreparedStatement和ResultSet
Base.closeResource(null, pstmt, resultSet);
Base.closeResource(connection, countPstmt, countSet);
} catch (SQLException e) {
// 如果在关闭资源时出现异常,将提示信息设置为关闭资源失败
msg = "关闭资源失败";
}
}
// 将状态码、总数、提示信息和违规借阅记录信息数组添加到最终的JSON对象中
jsonResult.put("code", code);
jsonResult.put("count", count);
jsonResult.put("msg", msg);
jsonResult.put("data", jsonArray.toArray());
// 获取响应的输出流
PrintWriter out = resp.getWriter();
// 将最终的JSON对象转换为字符串并输出到客户端
out.print(jsonResult.toString());
}
}
// 声明该Servlet类所在的包用于处理读者相关的Servlet操作
package servlet.reader;
// 导入处理输入输出异常的类
import java.io.IOException;
// 导入用于向客户端发送字符文本的类
import java.io.PrintWriter;
// 导入处理SQL操作异常的类
import java.sql.SQLException;
// 导入HashMap类用于存储键值对方便组织响应数据
import java.util.HashMap;
// 导入处理Servlet异常的类
import javax.servlet.ServletException;
// 导入用于注解Servlet映射路径的类
import javax.servlet.annotation.WebServlet;
// 导入HttpServlet类用于创建Servlet
import javax.servlet.http.HttpServlet;
// 导入表示HTTP请求的类
import javax.servlet.http.HttpServletRequest;
// 导入表示HTTP响应的类
import javax.servlet.http.HttpServletResponse;
// 导入用于管理用户会话的类
import javax.servlet.http.HttpSession;
// 导入自定义的Reader类用于处理读者登录逻辑
import javabean.Reader;
// 导入用于处理JSON对象的类
import net.sf.json.JSONObject;
/**
* 该Servlet用于处理读者的登录请求。
* 当客户端发送POST请求到 /readerLogin 路径时,会验证读者的账号和密码,
* 并根据验证结果返回相应的JSON数据给客户端。
*/
@WebServlet("/readerLogin")
public class ReaderLogin extends HttpServlet {
/**
* 处理HTTP GET请求。
* 此方法在该Servlet中只是简单地将当前应用的上下文路径返回给客户端
* 一般登录操作不使用GET请求这里只是一个默认的处理。
*
* @param request 客户端的HTTP请求对象
* @param response 服务器的HTTP响应对象
* @throws ServletException 如果在处理请求过程中出现Servlet相关的错误
* @throws IOException 如果在输入输出过程中出现错误
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.getWriter().append("Served at: ").append(request.getContextPath());
}
/**
* 处理HTTP POST请求实现读者登录功能。
*
* @param request 客户端的HTTP请求对象包含读者输入的账号和密码
* @param response 服务器的HTTP响应对象用于返回登录结果的JSON数据
* @throws ServletException 如果在处理请求过程中出现Servlet相关的错误
* @throws IOException 如果在输入输出过程中出现错误
*/
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 设置响应的内容类型为JSON格式并指定字符编码为UTF-8
response.setContentType("application/json; charset=utf8");
// 获取用于向客户端输出响应内容的PrintWriter对象
PrintWriter out = response.getWriter();
// 从HTTP请求中获取读者输入的账号和密码
String user = request.getParameter("user");
String psw = request.getParameter("psw");
// 创建一个HashMap对象用于存储要返回给客户端的响应信息
// 键为字符串类型,值为对象类型
HashMap<String, Object> hashMap = new HashMap<String, Object>();
// 创建Reader类的实例用于调用登录验证方法
Reader reader = new Reader();
// 用于存储登录验证结果的变量
String result = null;
try {
// 调用Reader类的login方法进行登录验证传入账号和密码
result = reader.login(user, psw);
} catch (ClassNotFoundException | SQLException e) {
// 捕获可能出现的类未找到异常或SQL异常并打印异常堆栈信息
e.printStackTrace();
}
// 判断登录验证结果是否为 "1""1" 表示登录成功
if (result != null && result.equals("1")) {
// 获取当前请求的会话对象,如果不存在则创建一个新的会话
HttpSession session = request.getSession();
// 将登录的读者账号存储到会话中,方便后续使用
session.setAttribute("reader", user);
// 设置一个标志,表示读者是首次登录(这里假设 "1" 表示登录状态)
session.setAttribute("reader_first", "1");
// 向HashMap中添加登录成功的状态码
hashMap.put("code", 0);
// 向HashMap中添加登录成功的提示信息
hashMap.put("msg", "登录成功");
// 向HashMap中添加登录成功后要跳转的页面URL
hashMap.put("url", request.getContextPath() + "/reader/01main.jsp");
} else {
// 登录失败向HashMap中添加登录失败的状态码
hashMap.put("code", 1);
// 向HashMap中添加登录失败的提示信息即登录验证结果中的错误信息
hashMap.put("msg", result);
}
// 将HashMap对象转换为JSON对象
JSONObject json = JSONObject.fromObject(hashMap);
// 将JSON对象转换为字符串并通过PrintWriter输出到客户端
out.write(json.toString());
}
}
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>图书馆欢迎页</title>
<!-- 引入Bootstrap样式 -->
<link href="../css/bootstrap.min.css" rel="stylesheet">
<!-- 引入jQueryBootstrap依赖 -->
<script src="../js/jquery.min.js"></script>
<!-- 引入Bootstrap核心JS -->
<script src="../js/bootstrap.min.js"></script>
<!-- 自定义样式 -->
<style>
/* 轮播图容器样式 */
.showCarousel .carousel-inner > .item > img {
display: block;
width: 100%; /* 图片宽度占满容器 */
height: 620px; /* 固定高度620px */
}
.showCarousel {
margin-top: 2%; /* 轮播图顶部留白2% */
}
/* 字幕文字样式 */
font {
font-size: 40px;
color: black;
font-family: YouYuan; /* 幼圆字体 */
font-weight: 900; /* 加粗 */
}
/* 轮播图字幕位置调整 */
.carousel-caption {
margin-bottom: 10%; /* 底部留白10% */
}
</style>
</head>
<body>
<%-- 会话校验与自动跳转逻辑 --%>
<%
// 检查是否已登录且为首次访问通过reader_first标记判断
if (session.getAttribute("reader") != null
&& session.getAttribute("reader_first") != null
&& session.getAttribute("reader_first").equals("1")) {
// 更新标记为已访问(防止重复跳转)
session.setAttribute("reader_first", "2");
// 首次登录时通过JS跳转至框架页使用parent.location实现嵌套页面跳转
%>
<script>window.parent.location.href = "./04readerFrame.jsp";</script>
<%
}
%>
<!-- 轮播图主体 -->
<div id="carousel-example-generic" class="carousel slide showCarousel"
data-ride="carousel" <!-- 启用Bootstrap轮播功能 -->
data-interval="2000" <!-- 自动切换间隔2秒 -->
style="width:96%; margin-left:2%;"> <!-- 容器宽度96%左侧留白2% -->
<!-- 轮播图指示点(下方小圆点) -->
<ol class="carousel-indicators">
<li data-target="#carousel-example-generic" data-slide-to="0" class="active"></li> <!-- 第1个指示点默认激活 -->
<li data-target="#carousel-example-generic" data-slide-to="1"></li> <!-- 第2个指示点 -->
<li data-target="#carousel-example-generic" data-slide-to="2"></li> <!-- 第3个指示点 -->
</ol>
<!-- 轮播图图片容器 -->
<div class="carousel-inner" role="listbox">
<!-- 第1张图片默认激活 -->
<div class="item active">
<img src="../public/image/1.jpg" alt="图书馆全景">
<div class="carousel-caption">
<font style="color:white;">唯有知识永不负你!</font> <!-- 白色字幕 -->
</div>
</div>
<!-- 第2张图片 -->
<div class="item">
<img src="../public/image/2.jpg" alt="阅读场景">
<div class="carousel-caption">
<font style="color:white;">学海无涯,书韵悠扬</font>
</div>
</div>
<!-- 第3张图片 -->
<div class="item">
<img src="../public/image/3.jpg" alt="书架特写">
<div class="carousel-caption">
<font style="color:white;">图一份神闲气静,书几笔悦目赏心</font>
</div>
</div>
</div>
<!-- 轮播图左右控制箭头 -->
<a class="left carousel-control" href="#carousel-example-generic" role="button" data-slide="prev">
<span class="glyphicon glyphicon-chevron-left" aria-hidden="true"></span> <!-- 左箭头图标 -->
<span class="sr-only">Previous</span> <!-- 屏幕阅读器文本 -->
</a>
<a class="right carousel-control" href="#carousel-example-generic" role="button" data-slide="next">
<span class="glyphicon glyphicon-chevron-right" aria-hidden="true"></span> <!-- 右箭头图标 -->
<span class="sr-only">Next</span>
</a>
</div>
</body>
</html>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>读者导航栏</title>
<!-- 引入Bootstrap样式 -->
<link href="../css/bootstrap.min.css" rel="stylesheet">
<!-- 引入jQueryBootstrap依赖 -->
<script src="../js/jquery.min.js"></script>
<!-- 引入Bootstrap核心JS支持导航栏交互 -->
<script src="../js/bootstrap.min.js"></script>
<style>
/* 可扩展:此处可添加导航栏自定义样式 */
</style>
</head>
<body>
<!-- 导航栏容器Bootstrap导航组件 -->
<nav class="navbar navbar-inverse" role="navigation"> <!-- navbar-inverse深色主题 -->
<div class="container-fluid"> <!-- 流体容器,适应全宽屏幕 -->
<!-- 导航栏头部(用于移动端折叠菜单) -->
<div class="navbar-header">
<!-- 折叠菜单按钮(仅在移动端显示) -->
<button type="button" class="navbar-toggle collapsed"
data-toggle="collapse" data-target="#bs-example-navbar-collapse-1"
aria-expanded="false">
<span class="sr-only">Toggle navigation</span> <!-- 屏幕阅读器文本 -->
<span class="icon-bar"></span> <!-- 汉堡菜单图标线 -->
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<!-- 品牌名称(左侧固定显示) -->
<a class="navbar-brand" href="#">欢迎登陆图书管理系统</a>
</div>
<!-- 折叠式导航内容(默认折叠,点击按钮展开) -->
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
<!-- 左侧导航项 -->
<ul class="nav navbar-nav">
<!-- 固定显示:借阅者页面标识 -->
<li>
<p style="margin-top:25%;color:grey;">
借阅者页面<span class="sr-only">(current)</span>
</p>
</li>
</ul>
<!-- 右侧导航项(用户状态相关) -->
<ul class="nav navbar-nav navbar-right">
<!-- 用户名显示(登录后可见) -->
<li>
<a href="javascript:;">
<%if(session.getAttribute("reader")==null){ %>
<!-- 未登录时显示空 -->
<%} else{%>
<!-- 已登录时显示用户名 -->
<%=session.getAttribute("reader") %>
<%} %>
</a>
</li>
<!-- 退出/登录按钮(根据会话动态显示) -->
<%if(session.getAttribute("reader") !=null) {%>
<!-- 已登录:显示退出按钮 -->
<li><a href="./exit" target="_parent">退出</a></li>
<%}else{%>
<!-- 未登录:显示登录链接 -->
<li><a href="04readerFrame.jsp" target="_parent">登录</a></li>
<%}%>
</ul>
</div>
</div>
</nav>
</body>
</html>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<!-- 设置字符编码为UTF-8 -->
<meta charset="utf-8">
<!-- 设置页面在IE浏览器中的渲染模式 -->
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<!-- 设置页面在移动设备上的视图宽度为设备宽度初始缩放比例为1 -->
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- 页面标题,此处为空 -->
<title></title>
<!-- 引入Bootstrap的CSS文件用于页面样式布局 -->
<link href="../css/bootstrap.min.css" rel="stylesheet">
<!-- 引入jQuery库因为Bootstrap的所有JavaScript插件都依赖jQuery所以必须放在前边 -->
<script src="../js/jquery.min.js"></script>
<!-- 引入Bootstrap的JavaScript文件用于实现交互效果 -->
<script src="../js/bootstrap.min.js"></script>
<style>
/* 当鼠标悬停在侧边栏的链接上时,设置背景颜色为#337ab7文字颜色为白色 */
.sidebar li a:hover{
background:#337ab7;
color:white;
}
/* 当侧边栏的链接获得焦点时,设置背景颜色为天蓝色,文字颜色为白色 */
.faq-tabbable li a:focus{
background:skyblue;
color:white;
}
</style>
</head>
<body>
<!-- 创建一个居中的div宽度和高度都为100%字体大小为20px此处原代码冒号使用了中文冒号应改为英文冒号 -->
<div style="text-align:center;width:100%;height:100%;font-size:20px;">
<!-- 创建一个堆叠式的导航菜单使用Bootstrap的nav类进行样式设置 -->
<ul class="nav nav-pills nav-stacked nav-inverse sidebar faq-tabbable">
<%
// 检查会话中是否存在"reader"属性,即判断用户是否已登录
if(session.getAttribute("reader")!=null){
%>
<!-- 以下是用户已登录时显示的导航菜单项 -->
<!-- 首页菜单项点击后在名为view_frame的框架中打开01main.jsp页面 -->
<li role="presentation"><a href="01main.jsp" target="view_frame"><span class="glyphicon glyphicon-picture" aria-hidden="true">&nbsp;首页</span></a></li>
<!-- 图书查询菜单项点击后在名为view_frame的框架中打开05book.jsp页面 -->
<li role="presentation"><a href="05book.jsp" target="view_frame"><span class="glyphicon glyphicon-search" aria-hidden="true">&nbsp;图书查询</span></a></li>
<!-- 读者规则菜单项点击后在名为view_frame的框架中打开12rules.jsp页面 -->
<li role="presentation"><a href="12rules.jsp" target="view_frame"><span class="glyphicon glyphicon-bell" aria-hidden="true">&nbsp;读者规则</span></a></li>
<!-- 查看公告菜单项点击后在名为view_frame的框架中打开07announcement.jsp页面 -->
<li role="presentation"><a href="07announcement.jsp" target="view_frame"><span class="glyphicon glyphicon-bullhorn" aria-hidden="true">&nbsp;查看公告</span></a></li>
<!-- 个人信息菜单项点击后在名为view_frame的框架中打开index.jsp页面 -->
<li role="presentation"><a href="index.jsp" target="view_frame"><span class="glyphicon glyphicon-user" aria-hidden="true">&nbsp;个人信息</span></a></li>
<!-- 借阅信息菜单项点击后在名为view_frame的框架中打开06borrow.jsp页面 -->
<li role="presentation"><a href="06borrow.jsp" target="view_frame"><span class="glyphicon glyphicon-book" aria-hidden="true">&nbsp;借阅信息</span></a></li>
<!-- 违章信息菜单项点击后在名为view_frame的框架中打开08illegalInfo.jsp页面 -->
<li role="presentation"><a href="08illegalInfo.jsp" target="view_frame"><span class="glyphicon glyphicon-remove" aria-hidden="true">&nbsp;违章信息</span></a></li>
<!-- 读者留言菜单项点击后在名为view_frame的框架中打开13message.jsp页面 -->
<li role="presentation"><a href="13message.jsp" target="view_frame"><span class="glyphicon glyphicon-pencil" aria-hidden="true">&nbsp;读者留言</span></a></li>
<%
} else {
%>
<!-- 以下是用户未登录时显示的导航菜单项 -->
<!-- 首页菜单项点击后在名为view_frame的框架中打开01main.jsp页面 -->
<li role="presentation"><a href="01main.jsp" target="view_frame"><span class="glyphicon glyphicon-picture" aria-hidden="true">&nbsp;首页</span></a></li>
<!-- 图书查询菜单项点击后在名为view_frame的框架中打开05book.jsp页面 -->
<li role="presentation"><a href="05book.jsp" target="view_frame"><span class="glyphicon glyphicon-search" aria-hidden="true">&nbsp;图书查询</span></a></li>
<!-- 读者规则菜单项点击后在名为view_frame的框架中打开12rules.jsp页面 -->
<li role="presentation"><a href="12rules.jsp" target="view_frame"><span class="glyphicon glyphicon-bell" aria-hidden="true">&nbsp;读者规则</span></a></li>
<!-- 查看公告菜单项点击后在名为view_frame的框架中打开07announcement.jsp页面 -->
<li role="presentation"><a href="07announcement.jsp" target="view_frame"><span class="glyphicon glyphicon-bullhorn" aria-hidden="true">&nbsp;查看公告</span></a></li>
<!-- 查看留言菜单项点击后在名为view_frame的框架中打开15checkMessage.jsp页面 -->
<li role="presentation"><a href="15checkMessage.jsp" target="view_frame"><span class="glyphicon glyphicon-envelope" aria-hidden="true">&nbsp;查看留言</span></a></li>
<%
}
%>
</ul>
</div>
</body>
</html>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<!-- 设置字符编码为UTF-8 -->
<meta charset="utf-8">
<!-- 设置页面在IE浏览器中的渲染模式 -->
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<!-- 设置页面在移动设备上的视图宽度为设备宽度初始缩放比例为1 -->
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- 页面标题,显示为借阅者页面 -->
<title>借阅者页面</title>
<!-- 引入Bootstrap的CSS文件用于页面样式布局 -->
<link href="../css/bootstrap.min.css" rel="stylesheet">
<!-- 引入jQuery库因为Bootstrap的所有JavaScript插件都依赖jQuery所以必须放在前边 -->
<script src="../js/jquery.min.js"></script>
<!-- 引入Bootstrap的JavaScript文件用于实现交互效果 -->
<script src="../js/bootstrap.min.js"></script>
</head>
<!-- 使用frameset标签将页面划分为多个框架rows属性指定框架按行划分这里将页面分为上下两部分上部分占7%,下部分占剩余空间 -->
<frameset rows="7%,*" frameborder="no" border="0">
<!-- 上半部分框架src属性指定要加载的页面为02readerNav.jspscrolling="no"表示不显示滚动条 -->
<frame src="02readerNav.jsp" scrolling="no">
<!-- 下半部分再使用frameset标签将其划分为左右两部分 -->
<frameset cols="14%,*" frameborder="no" border="0">
<!-- 左半部分框架src属性指定要加载的页面为03readerNavLeft.jsp -->
<frame src="03readerNavLeft.jsp">
<%
// 检查会话中是否存在"reader"属性,即判断用户是否已登录
if(session.getAttribute("reader") == null){
%>
<!-- 如果用户未登录将右半部分框架加载06borrow.jsp页面 -->
<frame src="06borrow.jsp" name="view_frame">
<%
} else {
%>
<!-- 如果用户已登录将右半部分框架加载01main.jsp页面 -->
<frame src="01main.jsp" name="view_frame">
<%
}
%>
</frameset>
</frameset>
</html>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<!-- 强制指定页面编码为UTF-8 -->
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<!-- 页面标题(未设置,可根据功能补充) -->
<title>图书查询管理</title>
<!-- 响应式布局元标签 -->
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- 引入前端框架资源 -->
<!-- Bootstrap基础样式 -->
<link href="../css/bootstrap.min.css" rel="stylesheet">
<!-- Bootstrap表格扩展样式 -->
<link href="../css/bootstrap-table.css" rel="stylesheet">
<!-- jQuery库前端交互基础 -->
<script src="../js/jquery.min.js"></script>
<!-- Bootstrap表格功能 -->
<script src="../js/bootstrap-table.js"></script>
<!-- 表格中文语言包 -->
<script src="../bootstrap-table-zh-CN.js"></script>
<!-- LayUI框架国产UI组件库用于弹窗、表格等 -->
<link rel="stylesheet" href="../public/layui/css/layui.css" media="all">
<style>
/* 自定义LayUI表格样式 */
.layui-table, .layui-table-view {
margin: 0 0px; /* 移除表格默认外边距 */
}
/* 下拉框选中项颜色(匹配系统主题) */
.layui-form-select dl dd.layui-this { background-color: #01AAED; }
</style>
</head>
<body>
<!-- 声明JavaBean用于数据库操作作用域会话级 -->
<jsp:useBean id="msg" scope="session" class="javabean.JDBCBean"></jsp:useBean>
<!-- 引入LayUI核心脚本 -->
<script src="../public/layui/layui.js" charset="utf-8"></script>
<!-- 表格容器LayUI隐藏容器渲染后显示 -->
<table class="layui-hide" id="cardTable" lay-filter="formFilter"></table>
<!-- 头部工具栏模板使用LayUI的script模板语法 -->
<script type="text/html" id="headBar">
条件搜索:
<div class="layui-inline">
<!-- 搜索条件下拉框 -->
<select id="condition" name="condition" lay-verify="required">
<option value=""></option>
<option value="id">图书编号</option>
<option value="name">图书名称</option>
<option value="author">作者</option>
<option value="position">位置</option>
<option value="description">描述</option>
</select>
</div>
<div class="layui-inline">
<!-- 搜索关键词输入框 -->
<input class="layui-input" id="conditionValue" name="conditionValue" autocomplete="off">
</div>
<!-- 搜索按钮绑定LayUI事件 -->
<button class="layui-btn layui-bg-blue" data-type="reload" lay-event="search">搜索</button>
</script>
<script>
layui.use(['table', 'jquery'], function () {
var $ = layui.jquery,
table = layui.table;
// 1. 表格初始化渲染
var tableIns = table.render({
elem: '#cardTable', // 绑定表格容器
url: './book', // 数据接口对应Book Servlet
toolbar: '#headBar', // 绑定头部工具栏模板
cols: [[ // 表格列配置
{field: 'id', width: 120, title: '图书编号', sort: true},
{field: 'name', width: 130, title: '图书名称', sort: true},
{field: 'author', width: 100, title: '作者', sort: true},
{field: 'library_id', title: '图书馆', width: 100, sort: true},
{field: 'sort_id', width: 100, title: '分类', sort: true},
{field: 'position', width: 100, title: '位置', sort: true},
{field: 'status', width: 100, title: '状态', sort: true,
// 状态字段模板:根据值显示不同颜色
templet: function (item) {
return item.status === '0' ?
'<span style="color:orange">已借出</span>' :
'<span style="color:green">未借出</span>';
}
},
{field: 'description', minWidth: 80, title: '描述'}
]],
page: { // 分页配置
theme: '#2F88FF', // 分页条颜色
layout: ['count', 'prev', 'page', 'next', 'limit']
}
});
// 2. 头部工具栏事件处理
table.on('toolbar(formFilter)', function (obj) {
var checkStatus = table.checkStatus(obj.config.id);
switch (obj.event) {
case 'search':
// 获取搜索条件
var condition = $('#condition').val(),
conditionValue = $('#conditionValue').val();
// 重载表格触发Book Servlet重新查询
tableIns.reload({
where: {
condition: condition,
conditionValue: conditionValue
},
page: { curr: 1 } // 重置为第一页
});
break;
case 'add':
// 弹出添加图书弹窗layer框架
layer.open({
type: 2,
title: '添加图书',
area: ['800px', '500px'],
content: 'cardadd.jsp' // 跳转到添加页面
});
break;
}
});
// 3. 行工具事件处理(编辑等)
table.on('tool(formFilter)', function (obj) {
var data = obj.data,
layEvent = obj.event;
switch (layEvent) {
case 'edit':
// 弹出编辑弹窗需补充具体URL
layer.open({
type: 2,
title: '编辑图书',
area: ['800px', '600px'],
content: '' // 待补充编辑页面路径
});
break;
}
});
});
</script>
</body>
</html>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<!-- 设置页面内容类型和字符编码为UTF-8 -->
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<!-- 页面标题,可根据实际情况修改 -->
<title>Insert title here</title>
<!-- 引入Bootstrap的CSS文件用于页面样式布局 -->
<link href="../css/bootstrap.min.css" rel="stylesheet">
<!-- 引入Bootstrap表格的CSS文件用于表格样式 -->
<link href="../css/bootstrap-table.css" rel="stylesheet">
<!-- 引入jQuery库因为Bootstrap的所有JavaScript插件都依赖jQuery所以必须放在前边 -->
<script src="../js/jquery.min.js"></script>
<!-- 引入Bootstrap表格的JavaScript文件用于实现表格功能 -->
<script src="../js/bootstrap-table.js"></script>
<!-- 引入Bootstrap表格的中文语言包 -->
<script src="../bootstrap-table-zh-CN.js"></script>
<!-- 引入Layui框架的CSS文件用于页面样式 -->
<link rel="stylesheet" href="../public/layui/css/layui.css" media="all">
<style>
/* 设置Layui表格和表格视图的外边距为0 */
.layui-table,.layui-table-view{
margin: 0 0px;
}
</style>
</head>
<body>
<!-- 引入Layui框架的JavaScript文件 -->
<script src="../public/layui/layui.js" charset="utf-8"></script>
<!-- 表单 -->
<%
// 检查会话中是否存在"reader"属性,若不存在则表示用户未登录
if(session.getAttribute("reader")==null){%>
<script>
// 若用户未登录,将页面重定向到登录页面
location.href = "../loginReader.html";
</script>
<%
}
%>
<!-- 创建一个隐藏的表格用于后续Layui表格渲染 -->
<table class="layui-hide" id="cardTable" lay-filter="formFilter"></table>
<!-- 头部工具栏模板 -->
<script type="text/html" id="headBar">
条件搜索:
<div class="layui-inline">
<!-- 条件选择下拉框 -->
<select id="condition" name="condition" lay-verify="required">
<option value=""></option>
<option value="book_id">图书编号</option>
<option value="borrow_date">借阅日期</option>
<option value="end_date">截止日期</option>
<option value="return_date">归还日期</option>
</select>
</div>
<div class="layui-inline">
<!-- 条件值输入框 -->
<input class="layui-input" id="conditionValue" name="conditionValue" id="demoReload" autocomplete="off">
</div>
<!-- 搜索按钮,点击触发搜索事件 -->
<button class="layui-btn" name="condition" data-type="reload" lay-event="search">搜索</button>
</script>
<script>
// 使用Layui的use方法加载table和jquery模块
layui.use(['table','jquery'], function(){
// 获取jQuery对象
$ = layui.jquery;
// 获取Layui的table模块
var table = layui.table;
// 进行表格渲染
var tableIns = table.render({
// 绑定表格渲染的目标元素
elem: '#cardTable',
// 表格数据的请求URL对应后端处理借阅记录查询的Servlet
url:'./borrow',
// 绑定头部工具栏模板
toolbar: '#headBar',
// 表格列配置
cols: [[
// 借阅证号列
{field:'card_id', width:180, title: '借阅证号', sort: true},
// 图书编号列
{field:'book_id', width:130, title: '图书编号', sort: true},
// 借阅日期列
{field:'borrow_date', width:250, title: '借阅日期', sort: true},
// 截止日期列
{field:'end_date', title: '截止日期', width: 250, sort: true},
// 归还时间列
{field:'return_date', width:250, title: '归还时间', sort: true}
]],
// 开启分页功能
page: true
});
// 监听头部工具栏事件
table.on('toolbar(formFilter)', function(obj){
// 获取表格的选中状态
var checkStatus = table.checkStatus(obj.config.id);
switch(obj.event){
// 条件查找借阅记录
case 'search':
// 获取条件值输入框的元素
var conditionValue = $('#conditionValue');
// 获取条件选择下拉框的元素
var condition = $('#condition');
// 进行搜索,重新渲染表格
tableIns.reload({
where: {
// 设定异步数据接口的额外参数,传递搜索条件和条件值
"condition": condition.val(),
"conditionValue": conditionValue.val()
},
page: {
// 重新从第1页开始
curr: 1
}
});
break;
case 'add':
// 弹出添加借书证的弹窗
var addCardLayer = layer.open({
type: 2,
title: '添加借书证',
area: ['800px', '500px'],
maxmin: true,
shadeClose: true,
// 弹窗内容的页面路径
content: 'cardadd.jsp',
});
// 可将弹窗设置为全屏显示,此处注释掉了
// layer.full(addCardLayer);
break;
}
});
// 监听侧边工具栏事件
table.on('tool(formFilter)', function(obj){
// 获取当前行的数据
var data = obj.data;
// 获取当前触发的事件名
var layEvent = obj.event;
// 获取当前行的DOM元素
var tr = obj.tr;
switch(obj.event){
case 'edit':
// 弹出更改信息的弹窗
layer.open({
type: 2,
title: '更改信息',
area: ['800px', '600px'],
maxmin: true,
shadeClose: true,
// 弹窗内容的页面路径,此处为空,需要补充
content: '',
});
break;
}
});
});
</script>
</body>
</html>
<%@ page import="java.sql.*" %>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<!-- 设置字符编码为UTF-8 -->
<meta charset="utf-8">
<!-- 设置页面在IE浏览器中的渲染模式 -->
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<!-- 设置页面在移动设备上的视图宽度为设备宽度初始缩放比例为1 -->
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- 页面标题,此处为空 -->
<title></title>
<!-- 引入Bootstrap的CSS文件用于页面样式布局 -->
<link href="../css/bootstrap.min.css" rel="stylesheet">
<!-- 引入jQuery库因为Bootstrap的所有JavaScript插件都依赖jQuery所以必须放在前边 -->
<script src="../js/jquery.min.js"></script>
<!-- 引入Bootstrap的JavaScript文件用于实现交互效果 -->
<script src="../js/bootstrap.min.js"></script>
<style>
/* 设置页面主体的字体为幼圆 */
body{
font-family:YouYuan;
}
</style>
</head>
<body>
<!-- 创建一个背景颜色为钢蓝色高度为30px文字颜色为白色圆角半径为8px宽度为页面宽度90%且居中显示的div -->
<div style="background: steelblue;height: 30px;;color:#fff;border-radius: 8px;width: 90%;margin:auto auto;">
<!-- 创建一个滚动字幕文字大小为20px字体为幼圆向左滚动水平和垂直间距为0.1%无限循环滚动速度为20滚动延迟为100 -->
<!-- 鼠标悬停时滚动停止,鼠标移开时继续滚动 -->
<marquee style="font-size:20px;font-family:YouYuan;" behavior="scroll" direction="left" hspace="0.1%" vspace="0.1%" loop="-1" scrollamount="20" scrolldelay="100" onMouseOut="this.start()" onMouseOver="this.stop()">
❤图书馆公告栏,记得查收公告呀!❤
</marquee>
</div>
<!-- 创建一个居中对齐的div -->
<div class="a" align="center">
<!-- 显示标题“近期公告” -->
<h2>近期公告</h2>
<!-- 使用JSP的useBean标签创建一个名为check的JavaBean实例作用域为会话级别类名为javabean.JDBCBean -->
<jsp:useBean id="check" scope="session" class="javabean.JDBCBean"></jsp:useBean>
<%
// 定义SQL查询语句从announcement表中查询所有记录
String sql="select*from announcement";
// 调用JavaBean的executeQuery方法执行SQL查询并将结果存储在ResultSet对象中
ResultSet rs = check.executeQuery(sql);
// 遍历ResultSet对象处理每一条查询结果
while (rs.next()) {
%>
<!-- 创建一个Bootstrap的面板组件样式为信息面板左边距为5%宽度为80% -->
<div class="panel panel-info" style="margin-left:5%;width:80%;">
<!-- 面板头部,居中对齐 -->
<div class="panel-heading" align="center">
<!-- 显示公告的标题 -->
<span><%=rs.getString("TITLE") %></span>
<!-- 显示公告的发布日期右边距为1% -->
<span style="margin-right:1%;"><%=rs.getString("PUBLISH_DATE") %></span>
</div>
<!-- 面板主体 -->
<div class="panel-body" >
<!-- 显示公告的详细内容,设置文字换行 -->
<p style="word-wrap:break-word;"><%=rs.getString("DETAIL") %></p>
</div>
</div>
<%
}
%>
</div>
</body>
</html>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!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>
<!-- 引入Bootstrap的基础CSS文件用于页面布局和样式 -->
<link href="../css/bootstrap.min.css" rel="stylesheet">
<!-- 引入Bootstrap表格的CSS文件用于美化表格样式 -->
<link href="../css/bootstrap-table.css" rel="stylesheet">
<!-- 引入jQuery库因为Bootstrap的JavaScript插件依赖于它 -->
<script src="../js/jquery.min.js"></script>
<!-- 引入Bootstrap表格的JavaScript文件用于实现表格的交互功能 -->
<script src="../js/bootstrap-table.js"></script>
<!-- 引入Bootstrap表格的中文语言包使表格提示信息显示为中文 -->
<script src="../bootstrap-table-zh-CN.js"></script>
<!-- 引入Layui框架的CSS文件用于页面的样式和组件 -->
<link rel="stylesheet" href="../public/layui/css/layui.css" media="all">
<!-- 重复引入Layui框架的CSS文件可删除重复部分 -->
<link rel="stylesheet" href="../public/layui/css/layui.css" media="all">
<style>
/* 设置Layui表格和表格视图的外边距为0 */
.layui-table,.layui-table-view{
margin: 0 0px;
}
/* 设置Layui表单下拉框中选中项的背景颜色为橙色 */
.layui-form-select dl dd.layui-this { background-color: #FF5722; }
</style>
</head>
<body>
<%
// 检查会话中是否存在 "reader" 属性,如果不存在则表示用户未登录
if(session.getAttribute("reader") == null){
%>
<script>
// 如果用户未登录,将页面重定向到登录页面
location.href="../loginReader.html";
</script>
<%
}
%>
<!-- 引入Layui框架的JavaScript文件 -->
<script src="../public/layui/layui.js" charset="utf-8"></script>
<!-- 创建一个隐藏的表格用于后续Layui表格的渲染 -->
<table class="layui-hide" id="cardTable" lay-filter="formFilter"></table>
<!-- 定义头部工具栏的模板 -->
<script type="text/html" id="headBar">
条件搜索:
<div class="layui-inline">
<!-- 创建一个下拉框,用于选择搜索条件 -->
<select id="condition" name="condition" lay-verify="required">
<option value=""></option>
<option value="book_id">图书编号</option>
<option value="borrow_date">借阅日期</option>
<option value="end_date">截止日期</option>
<option value="return_date">归还日期</option>
<option value="illegal">违章信息</option>
<option value="manager_id">处理人</option>
</select>
</div>
<div class="layui-inline">
<!-- 创建一个输入框,用于输入搜索条件的值 -->
<input class="layui-input" id="conditionValue" name="conditionValue" id="demoReload" autocomplete="off">
</div>
<!-- 创建一个红色按钮,点击触发搜索事件 -->
<button class="layui-btn layui-btn-danger" name="condition" data-type="reload" lay-event="search">搜索</button>
</script>
<script>
// 使用Layui的use方法加载 table 和 jquery 模块
layui.use(['table','jquery'], function(){
// 获取 jQuery 对象
$ = layui.jquery;
// 获取 Layui 的 table 模块
var table = layui.table;
// 渲染表格
var tableIns = table.render({
// 绑定表格渲染的目标元素
elem: '#cardTable',
// 表格数据的请求 URL对应后端处理违章信息查询的 Servlet
url:'./illegal',
// 绑定头部工具栏的模板
toolbar: '#headBar',
// 定义表格的列
cols: [[
// 借阅证号列,宽度为 180px可排序
{field:'card_id', width:180, title: '借阅证号', sort: true},
// 图书编号列,最小宽度为 80px可排序
{field:'book_id', minwidth:80, title: '图书编号', sort: true},
// 借阅日期列,宽度为 250px可排序
{field:'borrow_date', width:250, title: '借阅日期', sort: true},
// 截止日期列,宽度为 250px可排序
{field:'end_date', title: '截止日期', width: 250, sort: true},
// 归还时间列,宽度为 250px可排序
{field:'return_date', width:250, title: '归还时间', sort: true},
// 违章信息列,宽度为 180px可排序文字颜色为红色
{field:'illegal', width:180, title: '违章信息', sort: true,style:'color: red;'},
// 处理人列,宽度为 90px可排序
{field:'manager_id', width:90, title: '处理人', sort: true}
]],
// 开启分页功能
page: true,
// 设置分页条的主题颜色为橙色
page: {theme: '#FF5722'},
});
// 监听头部工具栏的事件
table.on('toolbar(formFilter)', function(obj){
// 获取表格的选中状态
var checkStatus = table.checkStatus(obj.config.id);
switch(obj.event){
// 处理搜索事件
case 'search':
// 获取输入框中的搜索条件值
var conditionValue = $('#conditionValue');
// 获取下拉框中选择的搜索条件
var condition = $('#condition');
// 重新加载表格数据,传递搜索条件和条件值
tableIns.reload({
where: {
// 设定异步数据接口的额外参数
"condition": condition.val(),
"conditionValue": conditionValue.val()
},
page: {
// 重新从第 1 页开始
curr: 1
}
});
break;
// 处理添加借书证事件
case 'add':
// 弹出一个层,用于添加借书证
var addCardLayer = layer.open({
type: 2,
title: '添加借书证',
area: ['800px', '500px'],
maxmin: true,
shadeClose: true,
// 层的内容为 cardadd.jsp 页面
content: 'cardadd.jsp',
});
// 可将层设置为全屏显示,此处注释掉了
// layer.full(addCardLayer);
break;
}
});
// 监听表格行工具的事件
table.on(('tool(formFilter)'), function(obj){
// 获取当前行的数据
var data = obj.data;
// 获取当前触发的事件名
var layEvent = obj.event;
// 获取当前行的 DOM 元素
var tr = obj.tr;
switch(obj.event){
// 处理编辑事件
case 'edit':
// 弹出一个层,用于更改信息
layer.open({
type: 2,
title: '更改信息',
area: ['800px', '600px'],
maxmin: true,
shadeClose: true,
// 层的内容为空,需要补充具体的页面路径
content: '',
});
break;
}
});
});
</script>
</body>
</html>
<%@ page import="java.sql.*"%>
<%@ page import="java.util.*"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!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>修改密码</title>
<!-- 引入toastr的JavaScript文件toastr是一个用于显示通知消息的插件 -->
<script src="${ctx}/toastr/toastr.min.js"></script>
<!-- 引入toastr的CSS文件用于设置通知消息的样式 -->
<link rel="stylesheet" href="${ctx}/toastr/toastr.min.css">
</head>
<body>
<!-- 使用JSP的useBean标签创建一个名为check的JavaBean实例作用域为会话级别类名为javabean.JDBCBean -->
<jsp:useBean id="check" scope="session" class="javabean.JDBCBean"></jsp:useBean>
<%
// 从请求中获取用户输入的新密码(第一次输入)
String psw1 = request.getParameter("psw1");
// 从请求中获取用户输入的新密码(第二次输入)
String psw2 = request.getParameter("psw2");
// 输出两次输入的密码,用于调试,此处注释掉了
//out.println(psw1 + " " + psw2);
// 从会话中获取当前登录读者的ID并将其转换为字符串类型
String id = session.getAttribute("reader").toString();
// 检查两次输入的密码是否相同,且不为空或仅包含空格
if (psw1.equals(psw2) && psw1 != null && psw2 != null && !psw1.trim().equals("")
&& !psw2.trim().equals("")) {
// 构建SQL更新语句将borrow_card表中对应ID的用户密码更新为新密码
String sql = "update borrow_card set PASSWORD ='" + psw1 + "' where ID=" + id;
try {
// 调用JavaBean的executeUpdate方法执行SQL更新语句并获取受影响的行数
int i = check.executeUpdate(sql);
// 判断受影响的行数是否为1即是否成功更新了一条记录
if (i == 1) {
%>
<!-- 如果修改成功弹出提示框告知用户并将页面重定向到index.jsp -->
<script>
alert('修改成功!');
window.location.href = "index.jsp";
</script>
<%
} else {
%>
<!-- 如果修改未成功弹出提示框告知用户并将页面重定向到index.jsp -->
<script>
alert('修改未成功!');
window.location.href = "index.jsp";
</script>
<%
}
} catch (Exception e) {
%>
<!-- 如果执行SQL语句时出现异常弹出提示框告知用户并将页面重定向到index.jsp -->
<script>
alert('修改未成功!');
window.location.href = "index.jsp";
</script>
<%
}
} else {
%>
<!-- 如果两次输入的密码不相同或为空弹出提示框告知用户并将页面重定向到index.jsp -->
<script>
alert('修改未成功!');
window.location.href = "index.jsp";
</script>
<%
}
%>
</body>
</html>
<%@ page import="java.sql.*" %>
<%@ page import="java.util.*" %>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<!-- 设置字符编码为UTF-8 -->
<meta charset="utf-8">
<!-- 设置页面在IE浏览器中的渲染模式 -->
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<!-- 设置页面在移动设备上的视图宽度为设备宽度初始缩放比例为1 -->
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- 页面标题,此处为空 -->
<title></title>
<!-- 引入Bootstrap的CSS文件用于页面样式布局 -->
<link href="../css/bootstrap.min.css" rel="stylesheet">
<!-- 引入jQuery库因为Bootstrap的所有JavaScript插件都依赖jQuery所以必须放在前边 -->
<script src="../js/jquery.min.js"></script>
<!-- 引入Bootstrap的JavaScript文件用于实现交互效果 -->
<script src="../js/bootstrap.min.js"></script>
<!-- 引入自定义的消息样式文件 -->
<link rel="stylesheet" id="templatecss" type="text/css" href="../public/css/message.css">
<style>
/* 设置页面主体的背景颜色为白色,文字颜色为黑色 */
body{
background-color:#fff;
color:black;
}
</style>
</head>
<body>
<script>
// 文档加载完成后初始化所有带有data-toggle="popover"属性的元素为弹出框
$(function () {
$("[data-toggle='popover']").popover();
});
</script>
<!-- 使用JSP的useBean标签创建一个名为msg的JavaBean实例作用域为会话级别类名为javabean.JDBCBean -->
<jsp:useBean id="msg" scope="session" class="javabean.JDBCBean"></jsp:useBean>
<!-- 创建一个居中对齐的div显示标题 -->
<div align="center"><h1 style="color:steelblue;font-family:YouYuan;font-weight:900;">读者规则信息查看</h1></div>
<!-- 创建一个居中对齐的div设置上边距为5% -->
<div style="margin-top:5%;" align="center">
<%
// 定义SQL查询语句从rules表中查询所有记录
String sql = "select * from rules";
// 调用JavaBean的executeQuery方法执行SQL查询并将结果存储在ResultSet对象中
ResultSet rs = msg.executeQuery(sql);
// 遍历ResultSet对象处理每一条查询结果
while (rs.next()) {
// 判断借阅证规则编号是否为奇数
if(Integer.parseInt(rs.getString("ID")) % 2== 1){
%>
<!-- 创建一个蓝色按钮,当鼠标悬停或点击时弹出提示框 -->
<button type="button" class="btn btn-lg btn-info"
title="可借阅数量:<%=rs.getString("BORROW_NUM") %>"
data-html="true"
data-container="body"
data-trigger="focus"
data-toggle="popover"
data-placement="left"
data-content="可借阅天数:<%=rs.getString("LIMIT_DAY") %><br>可借阅图书馆:<%=rs.getString("BORROW_LIBRARY") %><br>过期扣费/天:<%=rs.getString("OVERTIME_FEE") %>"
style="width:40%;height:100%;font-size:17px;margin-top:4%;">
<!-- 显示借阅证规则编号 -->
借阅证规则编号:<%=rs.getString("ID")%>
</button><br><br>
<%
} else {
%>
<!-- 创建一个蓝色按钮,当鼠标悬停或点击时弹出提示框,提示框显示在右侧 -->
<button type="button" class="btn btn-lg btn-primary"
title="可借阅数量:<%=rs.getString("BORROW_NUM") %>"
data-html="true"
data-container="body"
data-trigger="focus"
data-toggle="popover"
data-placement="right"
data-content="可借阅天数:<%=rs.getString("LIMIT_DAY") %><br>可借阅图书馆:<%=rs.getString("BORROW_LIBRARY") %><br>过期扣费/天:<%=rs.getString("OVERTIME_FEE") %>"
style="width:40%;height:100%;font-size:17px;margin-top:4%;">
<!-- 显示借阅证规则编号 -->
借阅证规则编号:<%=rs.getString("ID")%>
</button><br><br>
<%
}
}
%>
</div>
</body>
</html>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<!-- 设置字符编码为 UTF-8 -->
<meta charset="utf-8">
<!-- 设置页面在 IE 浏览器中的渲染模式 -->
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<!-- 设置页面在移动设备上的视图,宽度为设备宽度,初始缩放比例为 1 -->
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- 页面标题,此处为空 -->
<title></title>
<!-- 引入 Bootstrap 的 CSS 文件,用于页面样式布局 -->
<link href="../css/bootstrap.min.css" rel="stylesheet">
<!-- 引入 jQuery 库,因为 Bootstrap 的所有 JavaScript 插件都依赖 jQuery所以必须放在前边 -->
<script src="../js/jquery.min.js"></script>
<!-- 引入 Bootstrap 的 JavaScript 文件,用于实现交互效果 -->
<script src="../js/bootstrap.min.js"></script>
<!-- 引入自定义的消息样式文件 -->
<link rel="stylesheet" id="templatecss" type="text/css" href="../public/css/message.css">
</head>
<body id="home">
<script>
// 此部分代码被注释掉了,原本功能是获取当前日期时间并显示在弹窗中
// 同时尝试将日期时间赋值给一个 id 为 date_info 的输入框
//$(document).ready(function () {
// var date = new Date(+new Date()+8*3600*1000).toISOString().replace(/T/g,' ').replace(/\.[\d]{3}Z/,'');
// alert(date);//2017-01-22 11:08:46
// $('#date_info').val(today);
//})
</script>
<!-- 外层 div可能用于自定义样式根据 message.css 文件) -->
<div class="rain">
<!-- 内层 div可能用于定义边框样式等 -->
<div class="border start">
<!-- 创建一个表单,表单数据将以 POST 方式提交到 14messageSub.jsp -->
<form action="14messageSub.jsp" method="post">
<!-- 留言板标签,设置左边距 -->
<label for="mes" style="margin-left:36%;">留言板</label>
<!-- 文本区域,用于用户输入留言内容 -->
<textarea id="mes" name="msg" placeholder="请输入您的留言" style="height:50%;width:90%"></textarea>
<!-- 以下三行是被注释掉的代码,原本用于添加日期输入框 -->
<!-- <label for="date">日期</label>-->
<!-- <input id="date" type="text" name="time" placeholder="请输入时间" style="width:90%"/><br> -->
<!-- <input type="datetime_local" id="date_info" placeholder="请输入时间"/> -->
<br><br>
<!-- 按钮区域,居中对齐 -->
<div align="center">
<!-- 提交按钮,点击后将表单数据提交到指定的 action 页面 -->
<button type="submit" class="btn btn-warning" style="margin-left:0">提交</button>&emsp;&emsp;&emsp;
<!-- 重置按钮,点击后清空表单中的所有输入内容 -->
<button type="reset" class="btn btn-danger" style="margin-right:0">重置</button>&emsp;&emsp;&emsp;
<!-- 查看留言按钮,点击后跳转到 15checkMessage.jsp 页面 -->
<button type="reset" class="btn btn-info" style="margin-right:0" onClick="location.href='15checkMessage.jsp'">查看留言</button>
</div>
</form>
</div>
</div>
</body>
</html>
<%@ page import="java.sql.*" %>
<%@ page import="java.util.*" %>
<%@ page import="javabean.DateTime"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<!-- 设置页面字符编码 -->
<meta charset="utf-8">
<!-- 兼容 IE 浏览器 -->
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<!-- 响应式布局设置 -->
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- 页面标题,此处为空 -->
<title></title>
<!-- 引入 Bootstrap 样式 -->
<link href="../css/bootstrap.min.css" rel="stylesheet">
<!-- 引入 jQuery 库,因为 Bootstrap 的 JavaScript 插件依赖于它 -->
<script src="../js/jquery.min.js"></script>
<!-- 引入 Bootstrap JavaScript 文件 -->
<script src="../js/bootstrap.min.js"></script>
<!-- 引入自定义的消息样式文件 -->
<link rel="stylesheet" id="templatecss" type="text/css"
href="../public/css/message.css">
</head>
<body>
<!-- 使用 JSP 的 useBean 标签创建一个名为 msg 的 JavaBean 实例,作用域为会话级别,类名为 javabean.JDBCBean -->
<jsp:useBean id="msg" scope="session" class="javabean.JDBCBean"></jsp:useBean>
<%
// 创建 DateTime 类的实例,用于获取日期时间
DateTime date = new DateTime();
// 获取当前日期时间
String time = date.show();
// 从请求中获取用户输入的留言内容
String mes = request.getParameter("msg");
try {
// 从会话中获取读者的借阅证编号,并转换为字符串
String card = session.getAttribute("reader").toString();
// 构建 SQL 插入语句,将读者的借阅证编号、留言内容和留言时间插入到 message 表中
String sql = "insert into message(card_id,detail,public_date)values('" + card + "','" + mes + "','" + time + "');";
// 检查借阅证编号和留言内容是否不为空,且留言内容不是仅包含空白字符
if (card != null && mes != null && !mes.trim().equals("")) {
// 调用 JDBCBean 实例的 executeUpdate 方法执行 SQL 插入语句,并获取受影响的行数
int i = msg.executeUpdate(sql);
// 如果受影响的行数为 1说明插入成功
if (i == 1) {
%>
<script>
// 弹出提示框,告知用户留言成功
alert('留言成功!');
// 页面跳转到查看留言的页面
window.location.href = "15checkMessage.jsp";
</script>
<%
} else {
%>
<script>
// 弹出提示框,告知用户留言未成功
alert('留言未成功!');
// 页面跳转到留言输入页面
window.location.href = "13message.jsp";
</script>
<%
}
} else {
%>
<script>
// 弹出提示框,告知用户留言未成功且留言不能为空
alert('留言未成功!留言不能为空!');
// 页面跳转到留言输入页面
window.location.href = "13message.jsp";
</script>
<%
}
%>
<%
// 捕获异常,当出现异常时执行以下操作
} catch (Exception e) {
%>
<script>
// 弹出提示框,告知用户留言未成功并提示先登录
alert('留言未成功!请先登录!');
// 页面跳转到借阅图书页面
window.location.href = "06borrowBooks.jsp";
</script>
<%
}
%>
</body>
</html>
<%@ page import="java.sql.*" %>
<%@ page import="java.util.*" %>
<%@ page import="javabean.DateTime"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<!-- 设置页面字符编码为 UTF-8 -->
<meta charset="utf-8">
<!-- 设置页面在 IE 浏览器中的渲染模式 -->
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<!-- 设置页面在移动设备上的视图,宽度为设备宽度,初始缩放比例为 1 -->
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- 页面标题,此处为空 -->
<title></title>
<!-- 引入 Bootstrap 的 CSS 文件,用于页面样式布局 -->
<link href="../css/bootstrap.min.css" rel="stylesheet">
<!-- 引入 jQuery 库,因为 Bootstrap 的所有 JavaScript 插件都依赖 jQuery所以必须放在前边 -->
<script src="../js/jquery.min.js"></script>
<!-- 引入 Bootstrap 的 JavaScript 文件,用于实现交互效果 -->
<script src="../js/bootstrap.min.js"></script>
<!-- 引入自定义的消息样式文件 -->
<link rel="stylesheet" id="templatecss" type="text/css" href="../public/css/message.css">
<style type="text/css">
/* 设置具有 text-dark 类的元素的文字颜色为黑色,字体为幼圆 */
.text-dark{
color:black;
font-family:YouYuan;
}
/* 设置页面主体的背景颜色为白色,字体大小为 16px */
body{
background-color:#fff;
font-size:16px;
}
</style>
</head>
<body>
<!-- 使用 JSP 的 useBean 标签创建一个名为 msg 的 JavaBean 实例,作用域为会话级别,类名为 javabean.JDBCBean -->
<jsp:useBean id="msg" scope="session" class="javabean.JDBCBean"></jsp:useBean>
<!-- 创建一个居中对齐的 div显示留言板标题 -->
<div align="center"><h3 style="color:#F08080;">☆★留言板★☆</h3></div>
<%
// 定义 SQL 查询语句,从 message 表中查询 CARD_ID、DETAIL 和 PUBLIC_DATE 字段,并按 PUBLIC_DATE 降序排列
String sql = "select CARD_ID,DETAIL,PUBLIC_DATE from message order by PUBLIC_DATE desc";
// 调用 JavaBean 的 executeQuery 方法执行 SQL 查询,并将结果存储在 ResultSet 对象中
ResultSet rs = msg.executeQuery(sql);
// 遍历 ResultSet 对象,处理每一条查询结果
while (rs.next()) {
%>
<!-- 创建一个 Bootstrap 的面板组件,宽度为页面宽度的 60%,左边距为 20% -->
<div class="panel panel-default" style="width:60%;height:80%; margin-left:20%;">
<!-- 面板主体内容,居中对齐 -->
<div class="panel-body" align="center">
<!-- 显示留言者的借阅证编号,使用 bg-info 类设置背景颜色text-dark 类设置文字样式 -->
<p class="bg-info text-dark"><%=rs.getString("CARD_ID")%></p>
<!-- 创建一个 div设置文字换行 -->
<div style="word-wrap:break-word;">
<!-- 显示留言内容,使用 bg-warning 类设置背景颜色text-dark 类设置文字样式 -->
<p class="bg-warning text-dark"><%=rs.getString("DETAIL")%></p>
</div>
<!-- 显示留言的发布日期,使用 bg-danger 类设置背景颜色text-dark 类设置文字样式 -->
<p class="bg-danger text-dark"><%=rs.getString("PUBLIC_DATE")%></p>
</div>
</div>
<!-- 插入一条水平线,用于分隔不同的留言 -->
<hr>
<%
}
%>
</body>
</html>
<%@ page import="java.sql.*"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<!-- 设置字符编码为 UTF-8 -->
<meta charset="UTF-8" />
<!-- 让页面在 IE 浏览器中以最新的渲染模式显示,支持 Chrome Frame -->
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<!-- 设置页面在移动设备上的视图,宽度为设备宽度,初始缩放比例为 1 -->
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- 页面标题 -->
<title>Nifty Modal Window Effects</title>
<!-- 页面描述 -->
<meta name="description"
content="Nifty Modal Window Effects with CSS Transitions and Animations" />
<!-- 页面关键词 -->
<meta name="keywords"
content="modal, window, overlay, modern, box, css transition, css animation, effect, 3d, perspective" />
<!-- 页面作者 -->
<meta name="author" content="Codrops" />
<!-- 引入 Bootstrap 的 CSS 文件,用于页面样式布局 -->
<link href="../css/bootstrap.min.css" rel="stylesheet">
<!-- 引入 jQuery 库,因为 Bootstrap 的所有 JavaScript 插件都依赖 jQuery所以必须放在前边 -->
<script src="../js/jquery.min.js"></script>
<!-- 引入 Bootstrap 的 JavaScript 文件,用于实现交互效果 -->
<script src="../js/bootstrap.min.js"></script>
<!-- 引入自定义的默认样式文件 -->
<link rel="stylesheet" type="text/css" href="../public/css/default.css" />
<!-- 引入自定义的组件样式文件 -->
<link rel="stylesheet" type="text/css"
href="../public/css/component.css" />
<!-- 引入现代特性检测库,用于检测浏览器是否支持某些 CSS3 特性 -->
<script src="../public/js/modernizr.custom.js"></script>
</head>
<body>
<%
// 检查会话中是否存在 "reader" 属性,如果不存在则表示用户未登录
if (session.getAttribute("reader") == null) {
%>
<script>
// 如果用户未登录,将页面重定向到登录页面
location.href = "../loginReader.html";
</script>
<%
}
%>
<!-- 使用 JSP 的 useBean 标签创建一个名为 check 的 JavaBean 实例,作用域为会话级别,类名为 javabean.JDBCBean -->
<jsp:useBean id="check" scope="session" class="javabean.JDBCBean"></jsp:useBean>
<!-- 模态框,用于修改密码 -->
<div class="md-modal md-effect-13" id="modal-13">
<div class="md-content">
<!-- 模态框标题 -->
<h3>修改密码</h3>
<!-- 表单,用于输入新密码并提交到 11updatePswSus.jsp 页面 -->
<form action="11updatePswSus.jsp" method="post"
class="form-horizontal">
<div class="form-group" align="center">
<br>
<!-- 新密码标签 -->
<label for="psw1" class="col-sm-2 control-label">新密码</label>
<div class="col-sm-10" align="center">
<!-- 新密码输入框 -->
<input type="password" class="form-control" name="psw1" id="password1" placeholder="请输入新密码" style="width:70%;"/>
</div>
</div>
<br>
<div class="form-group" align="center">
<!-- 再次输入新密码标签 -->
<label for="psw2" class="col-sm-2 control-label">新密码</label>
<div class="col-sm-10">
<!-- 再次输入新密码输入框 -->
<input type="password" class="form-control" name="psw2" id="password2" placeholder="请再次输入密码进行确认" style="width:70%;"/>
</div>
</div>
<div align="center">
<!-- 提交按钮 -->
<input type="submit" class="btn btn-primary" value="确认">
</div>
<br>
</form>
<!-- 取消按钮,用于关闭模态框 -->
<button class="md-close btn-primary">取消</button>
<br>
</div>
</div>
<div class="container">
<!-- 页面头部 -->
<header style="backgorund-color:lightblue;height:20%;font-align:center;width:100%;font-family:YouYuan;">
<!-- 页面标题 -->
<h1>个人信息
<!-- 图标和提示信息 -->
<span class="glyphicon glyphicon-bookmark" aria-hidden="true" style="font-size:17px;">&nbsp;Success is waking up in the morning,so excited about what you have to do.</span>
</h1>
</header>
<div class="main clearfix" style="font-size:10px;margin-top:5%;font-family:YouYuan;">
<div class="column">
<%
try {
// 从会话中获取当前登录读者的借阅证编号
String cardId = session.getAttribute("reader").toString();
// 构建 SQL 查询语句,从 borrow_card 表中查询当前读者的信息
String sql = "select*from borrow_card where ID = '" + cardId + "';";
// 调用 JavaBean 的 executeQuery 方法执行 SQL 查询,并将结果存储在 ResultSet 对象中
ResultSet rs = check.executeQuery(sql);
// 遍历查询结果
while (rs.next()) {
// 获取借阅证编号
int id = rs.getInt(1);
%>
<!-- 显示借阅证编号 -->
<p><span class="glyphicon glyphicon-tags">&nbsp;借阅证编号:<%=rs.getString("ID")%></span></p><br>
<!-- 显示借阅证姓名 -->
<p><span class="glyphicon glyphicon-user">&nbsp;借阅证姓名:<%=rs.getString("READER")%></span></p><br>
<!-- 显示规则编号 -->
<p><span class="glyphicon glyphicon-tag">&nbsp;规则编号:<%=rs.getString("RULE_ID")%></span></p><br>
<!-- 显示借阅证状态 -->
<p><span class="glyphicon glyphicon-star-empty">&nbsp;状态:
<%
// 根据 STATUS 字段的值判断借阅证状态
if (rs.getString("STATUS").equals("1")) {
out.println("可用");
} else {
out.println("挂失");
}
%>
</span></p>
<%
}
} catch (Exception e) {
// 异常处理,可根据实际需求添加日志记录等操作
}
%>
</div>
<div class="column" align="center" style="font-size:15px">
<!-- 修改密码按钮,点击后弹出模态框 -->
<button class="md-trigger" data-modal="modal-13" style="margin-top:20%;">修改密码</button>
</div>
</div>
</div>
<!-- 模态框遮罩层 -->
<div class="md-overlay"></div>
<!-- 引入 classie.js 库,用于操作 DOM 元素的类名 -->
<script src="../public/js/classie.js"></script>
<!-- 引入模态框效果的 JavaScript 文件 -->
<script src="../public/js/modalEffects.js"></script>
<!-- 为了实现模糊效果引入的脚本 -->
<script>
// 这对于 IE 浏览器很重要
var polyfilter_scriptpath = '/js/';
</script>
<script src="../public/js/cssParser.js"></script>
<script src="../public/js/css-filters-polyfill.js"></script>
</body>
</html>
package javabean;
// 导入 java.sql 包中的相关类,用于与数据库进行交互
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
/**
* 该类提供了与数据库交互的基础方法,包括获取数据库连接、执行查询、执行更新以及释放资源等操作。
*/
public class Base {
// 从 DBConstants 类中获取数据库驱动信息,并将其设置为常量,后续不可修改
private static final String driver = DBConstants.driver;
// 从 DBConstants 类中获取数据库连接的 URL 信息,并将其设置为常量,后续不可修改
private static final String url = DBConstants.url;
// 从 DBConstants 类中获取数据库用户名信息,并将其设置为常量,后续不可修改
private static final String username = DBConstants.username;
// 从 DBConstants 类中获取数据库密码信息,并将其设置为常量,后续不可修改
private static final String password = DBConstants.password;
/**
* 获取数据库连接
*
* @return 返回一个数据库连接对象,如果连接失败则返回 null
* @throws ClassNotFoundException 如果找不到数据库驱动类,抛出该异常
*/
public static Connection getConnection() throws ClassNotFoundException {
// 初始化数据库连接对象为 null
Connection connection = null;
try {
// 加载数据库驱动类
Class.forName(driver);
// 通过 DriverManager 获取数据库连接
connection = (Connection) DriverManager.getConnection(url, username, password);
} catch (SQLException e) {
// 若发生 SQL 异常,打印异常堆栈信息
e.printStackTrace();
}
// 返回数据库连接对象
return connection;
}
/**
* 公共查询方法,用于执行 SQL 查询语句
*
* @param connection 数据库连接对象,用于与数据库建立连接
* @param preparedStatement 预编译的 SQL 语句对象,用于执行带参数的 SQL 语句
* @param resultSet 结果集对象,用于存储查询结果
* @param sql 要执行的 SQL 查询语句
* @param params SQL 语句中的参数数组
* @return 返回包含查询结果的结果集对象
* @throws SQLException 如果执行 SQL 语句时发生错误,抛出该异常
*/
public static ResultSet executequery(Connection connection, PreparedStatement preparedStatement,
ResultSet resultSet, String sql, Object[] params) throws SQLException {
// 如果预编译的 SQL 语句对象为空,则创建一个新的预编译对象
if (preparedStatement == null) {
preparedStatement = (PreparedStatement) connection.prepareStatement(sql);
}
// 遍历参数数组,将参数设置到预编译的 SQL 语句中
for (int i = 0; params != null && i < params.length; i++) {
preparedStatement.setObject(i + 1, params[i]);
}
// 执行查询语句,并将结果存储在结果集对象中
resultSet = preparedStatement.executeQuery();
// 返回结果集对象
return resultSet;
}
/**
* 公共修改方法,用于执行 SQL 更新、插入、删除等语句
*
* @param connection 数据库连接对象,用于与数据库建立连接
* @param preparedStatement 预编译的 SQL 语句对象,用于执行带参数的 SQL 语句
* @param sql 要执行的 SQL 修改语句
* @param params SQL 语句中的参数数组
* @return 返回受影响的行数
* @throws SQLException 如果执行 SQL 语句时发生错误,抛出该异常
*/
public static int executeUpdate(Connection connection, PreparedStatement preparedStatement, String sql,
Object[] params) throws SQLException {
// 如果预编译的 SQL 语句对象为空,则创建一个新的预编译对象
if (preparedStatement == null) {
preparedStatement = (PreparedStatement) connection.prepareStatement(sql);
}
// 遍历参数数组,将参数设置到预编译的 SQL 语句中
for (int i = 0; params != null && i < params.length; i++) {
preparedStatement.setObject(i + 1, params[i]);
}
// 执行更新语句,并返回受影响的行数
int updateRows = preparedStatement.executeUpdate();
// 返回受影响的行数
return updateRows;
}
/**
* 释放数据库资源,包括结果集、预编译语句和数据库连接
*
* @param connection 数据库连接对象
* @param preparedStatement 预编译的 SQL 语句对象
* @param resultSet 结果集对象
* @return 如果所有资源都成功释放,返回 true否则返回 false
* @throws SQLException 如果关闭资源时发生错误,抛出该异常
*/
public static boolean closeResource(Connection connection, PreparedStatement preparedStatement, ResultSet resultSet)
throws SQLException {
// 初始化标志位为 true表示资源释放成功
boolean flag = true;
// 如果结果集对象不为空,则尝试关闭结果集
if (resultSet != null) {
try {
resultSet.close();
// 关闭后将结果集对象置为 null便于垃圾回收
resultSet = null;
} catch (SQLException e) {
// 若关闭结果集时发生异常,打印异常堆栈信息,并将标志位设为 false
e.printStackTrace();
flag = false;
}
}
// 如果预编译语句对象不为空,则尝试关闭预编译语句
if (preparedStatement != null) {
try {
preparedStatement.close();
// 关闭后将预编译语句对象置为 null便于垃圾回收
preparedStatement = null;
} catch (SQLException e) {
// 若关闭预编译语句时发生异常,打印异常堆栈信息,并将标志位设为 false
e.printStackTrace();
flag = false;
}
}
// 如果数据库连接对象不为空,则尝试关闭数据库连接
if (connection != null) {
try {
connection.close();
// 关闭后将数据库连接对象置为 null便于垃圾回收
connection = null;
} catch (SQLException e) {
// 若关闭数据库连接时发生异常,打印异常堆栈信息,并将标志位设为 false
e.printStackTrace();
flag = false;
}
}
// 返回标志位
return flag;
}
}
package javabean;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.TreeMap;
/**
* Common 类提供了一些通用的数据库操作方法,例如获取指定表的行数和获取图书馆信息的映射。
*/
public class Common {
/**
* 获取指定表的行总数。
*
* @param table 要查询的表名
* @return 表中的行数,如果表名为空或者查询过程中出现异常则返回 0
* @throws SQLException 如果在执行 SQL 语句时发生数据库访问错误
* @throws ClassNotFoundException 如果无法找到数据库驱动类
*/
public static int getCount(String table) throws SQLException, ClassNotFoundException {
// 检查传入的表名是否为空或 null如果是则直接返回 0
if (table == null || table.equals("")) {
return 0;
}
// 声明数据库连接对象
Connection connection = null;
// 声明预编译 SQL 语句对象
PreparedStatement pstmt = null;
// 声明结果集对象
ResultSet resultSet = null;
// 通过 Base 类的 getConnection 方法获取数据库连接
connection = Base.getConnection();
try {
// 准备 SQL 查询语句,用于统计指定表的行数
pstmt = connection.prepareStatement("select count(*) as count from " + table);
// 执行查询操作,将结果存储在结果集对象中
resultSet = pstmt.executeQuery();
// 将结果集指针移动到第一行
resultSet.next();
// 从结果集中获取名为 "count" 的列的值,并返回该值
return resultSet.getInt("count");
} catch (Exception e) {
// 如果查询过程中出现异常,返回 0
return 0;
} finally {
// 无论查询是否成功,都调用 Base 类的 closeResource 方法关闭数据库连接、预编译语句和结果集
Base.closeResource(connection, pstmt, resultSet);
}
}
/**
* 获取图书馆信息的映射,键为图书馆的 ID值为图书馆的名称。
*
* @return 包含图书馆信息的 TreeMap如果出现异常则返回 null
* @throws SQLException 如果在执行 SQL 语句时发生数据库访问错误
*/
public static TreeMap<String, String> getLibraryMap() throws SQLException {
// 声明数据库连接对象
Connection connection = null;
// 声明用于查询图书馆信息的预编译 SQL 语句对象
PreparedStatement libraryPstmt = null;
// 声明用于存储图书馆信息查询结果的结果集对象
ResultSet librarySet = null;
// 定义查询图书馆信息的 SQL 语句
String librarySql = "select id,name from library";
// 创建一个 TreeMap 用于存储图书馆信息TreeMap 会根据键的自然顺序进行排序
TreeMap<String, String> map = new TreeMap<String, String>();
// 获取图书馆信息
try {
// 通过 Base 类的 getConnection 方法获取数据库连接
connection = (Connection) Base.getConnection();
// 准备查询图书馆信息的 SQL 语句
libraryPstmt = connection.prepareStatement(librarySql);
// 执行查询操作,将结果存储在结果集对象中
librarySet = libraryPstmt.executeQuery();
// 遍历结果集
while (librarySet.next()) {
// 将图书馆的 ID 作为键,图书馆的名称作为值,存入 TreeMap 中
map.put(librarySet.getString("id"), librarySet.getString("name"));
}
} catch (ClassNotFoundException e) {
// 如果找不到数据库驱动类,返回 null
return null;
} catch (SQLException e) {
// 如果执行 SQL 语句时发生异常,返回 null
return null;
} finally {
// 无论查询是否成功,都调用 Base 类的 closeResource 方法关闭数据库连接、预编译语句和结果集
Base.closeResource(connection, libraryPstmt, librarySet);
}
// 返回存储图书馆信息的 TreeMap
return map;
}
/**
* 主方法,用于测试 getLibraryMap 方法。
*
* @param args 命令行参数
* @throws SQLException 如果在执行 SQL 语句时发生数据库访问错误
*/
public static void main(String[] args) throws SQLException {
// 调用 getLibraryMap 方法并打印结果
System.out.println(Common.getLibraryMap());
}
}
package javabean;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* CompareDate 类用于比较两个日期字符串所表示的日期之间的天数差。
*/
public class CompareDate {
/**
* 该方法用于计算两个日期字符串所表示的日期之间相差的天数。
*
* @param Str1 第一个日期字符串,格式必须为 "yyyy-MM-dd HH:mm:ss"
* @param Str2 第二个日期字符串,格式必须为 "yyyy-MM-dd HH:mm:ss"
* @return 两个日期之间相差的天数,如果解析日期字符串时发生异常则返回 0
*/
public static long show(String Str1, String Str2) {
// 用于存储两个日期之间的毫秒差值
long between = 0;
// 创建一个 SimpleDateFormat 对象,指定日期字符串的格式为 "yyyy-MM-dd HH:mm:ss"
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
try {
// 将第一个日期字符串解析为 Date 对象
Date date1 = format.parse(Str1);
// 将第二个日期字符串解析为 Date 对象
Date date2 = format.parse(Str2);
// 计算两个日期对象之间的毫秒差值
between = (date2.getTime() - date1.getTime());
// 打印第一个日期对象,方便调试查看
System.out.println(date1);
// 打印第二个日期对象,方便调试查看
System.out.println(date2);
} catch (ParseException e) {
// 如果在解析日期字符串时发生异常,打印异常堆栈信息
e.printStackTrace();
}
// 将毫秒差值转换为天数,一天有 24 小时,每小时 60 分钟,每分钟 60 秒,每秒 1000 毫秒
long days = between / (24 * 60 * 60 * 1000);
// 返回计算得到的天数差值
return days;
}
}
package javabean;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
/**
* DateTime 类提供了一系列用于日期和时间处理的静态方法,
* 可以获取当前日期时间、计算指定天数偏移后的日期时间等。
*/
public class DateTime {
/**
* 获取当前日期和时间,格式为 "yyyy-MM-dd HH:mm:ss"。
*
* @return 格式化后的当前日期和时间字符串
*/
public static String show() {
// 创建一个 SimpleDateFormat 对象,指定日期时间的格式为 "yyyy-MM-dd HH:mm:ss"
SimpleDateFormat myFmt2 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
// 获取当前日期和时间
Date now = new Date();
// 使用指定格式将当前日期和时间转换为字符串并返回
return myFmt2.format(now);
}
/**
* 获取指定天数偏移后的日期和时间,格式为 "yyyy-MM-dd HH:mm:ss"。
*
* @param n 要偏移的天数,正数表示未来的日期,负数表示过去的日期
* @return 格式化后的偏移日期和时间字符串
*/
public static String show(int n) {
// 获取当前日期和时间
Date d = new Date();
// 创建一个 SimpleDateFormat 对象,指定日期时间的格式为 "yyyy-MM-dd HH:mm:ss"
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
// 将当前日期和时间按照指定格式转换为字符串
String currdate = format.format(d);
// 获取一个 Calendar 实例,用于日期计算
Calendar ca = Calendar.getInstance();
// 在当前日期的基础上增加或减少指定的天数
ca.add(Calendar.DATE, n);
// 获取计算后的日期
d = ca.getTime();
// 将计算后的日期按照指定格式转换为字符串
String enddate = format.format(d);
// 返回计算后的日期和时间字符串
return enddate;
}
/**
* 获取指定天数偏移后的日期,只包含年、月、日,格式为 "yyyy-MM-dd"。
*
* @param n 要偏移的天数,正数表示未来的日期,负数表示过去的日期
* @return 格式化后的偏移日期字符串
*/
public static String showDate(int n) {
// 获取当前日期和时间
Date d = new Date();
// 创建一个 SimpleDateFormat 对象,指定日期的格式为 "yyyy-MM-dd"
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
// 将当前日期按照指定格式转换为字符串
String currdate = format.format(d);
// 获取一个 Calendar 实例,用于日期计算
Calendar ca = Calendar.getInstance();
// 在当前日期的基础上增加或减少指定的天数
ca.add(Calendar.DATE, n);
// 获取计算后的日期
d = ca.getTime();
// 将计算后的日期按照指定格式转换为字符串
String enddate = format.format(d);
// 返回计算后的日期字符串
return enddate;
}
/**
* 获取指定天数偏移后的日期,只包含月和日,格式为 "MM-dd"。
*
* @param n 要偏移的天数,正数表示未来的日期,负数表示过去的日期
* @return 格式化后的偏移日期字符串
*/
public static String showMD(int n) {
// 获取当前日期和时间
Date d = new Date();
// 创建一个 SimpleDateFormat 对象,指定日期的格式为 "MM-dd"
SimpleDateFormat format = new SimpleDateFormat("MM-dd");
// 将当前日期按照指定格式转换为字符串
String currdate = format.format(d);
// 获取一个 Calendar 实例,用于日期计算
Calendar ca = Calendar.getInstance();
// 在当前日期的基础上增加或减少指定的天数
ca.add(Calendar.DATE, n);
// 获取计算后的日期
d = ca.getTime();
// 将计算后的日期按照指定格式转换为字符串
String enddate = format.format(d);
// 返回计算后的日期字符串
return enddate;
}
}
package javabean;
/**
* DBConstants 类用于存储数据库连接所需的常量信息,
* 这些常量包含了数据库驱动、连接 URL、用户名和密码
* 方便在项目中统一管理和使用数据库连接相关的配置。
*/
public class DBConstants {
/**
* 数据库驱动类的全限定名。这里使用的是 MySQL 8.x 及以上版本的 JDBC 驱动,
* 如果使用的是 MySQL 5.x 版本,驱动类名应为 "com.mysql.jdbc.Driver"。
*/
public static final String driver = "com.mysql.cj.jdbc.Driver";
/**
* 数据库连接的 URL。该 URL 指定了要连接的数据库的地址、端口、数据库名以及一些连接参数。
* - "jdbc:mysql://":表示使用 JDBC 连接 MySQL 数据库的协议。
* - "localhost:3306"表示数据库服务器的地址为本地主机localhost端口号为 3306。
* - "library":表示要连接的数据库名。
* - "&useSSL=false":表示不使用 SSL 加密连接。
* - "serverTimezone=UTC"指定数据库服务器的时区为协调世界时UTC避免时间处理上的问题。
* - "useUnicode=true&characterEncoding=UTF-8":表示使用 Unicode 字符集,并将字符编码设置为 UTF-8
* 确保能够正确处理各种语言的字符。
*/
public static final String url = "jdbc:mysql://localhost:3306/library?&useSSL=false&serverTimezone=UTC&useUnicode=true&characterEncoding=UTF-8";
/**
* 连接数据库时使用的用户名。这里使用的是 MySQL 的默认超级用户 "root"
* 在实际项目中,建议使用具有适当权限的普通用户来连接数据库,以提高安全性。
*/
public static final String username = "root";
/**
* 连接数据库时使用的密码。这里的密码为 "123456"
* 在实际项目中,应使用更复杂、安全的密码,并妥善保管,避免泄露。
*/
public static final String password = "123456";
}
package javabean;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
/**
* EndTime 类提供了一个静态方法,用于计算从当前日期开始经过指定天数后的日期和时间。
*/
public class EndTime {
/**
* 计算从当前日期开始经过指定天数后的日期和时间。
*
* @param n 要增加的天数,可以是正数(表示未来的日期)或负数(表示过去的日期)。
* @return 经过指定天数后的日期和时间,格式为 "yyyy-MM-dd HH:mm:ss"。
*/
public static String show(int n) {
// 获取当前日期和时间
Date d = new Date();
// 创建一个 SimpleDateFormat 对象,用于格式化日期和时间,格式为 "yyyy-MM-dd HH:mm:ss"
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
// 将当前日期和时间格式化为字符串
String currdate = format.format(d);
// 打印当前日期和时间
System.out.println("现在的日期是:" + currdate);
// 获取一个 Calendar 实例,用于进行日期计算
Calendar ca = Calendar.getInstance();
// 在当前日期的基础上增加指定的天数
ca.add(Calendar.DATE, n);
// 获取增加天数后的日期
d = ca.getTime();
// 将增加天数后的日期格式化为字符串
String enddate = format.format(d);
// 返回增加天数后的日期和时间字符串
return enddate;
}
}
package javabean;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
/**
* JDBCBean 类封装了与数据库交互的基本操作,包括建立连接、执行更新、执行查询以及关闭连接等功能。
*/
public class JDBCBean {
// 从 DBConstants 类中获取数据库驱动信息,使用 static final 修饰确保其不可变
private static final String driver = DBConstants.driver;
// 从 DBConstants 类中获取数据库连接的 URL 信息,使用 static final 修饰确保其不可变
private static final String url = DBConstants.url;
// 从 DBConstants 类中获取数据库用户名信息,使用 static final 修饰确保其不可变
private static final String username = DBConstants.username;
// 从 DBConstants 类中获取数据库密码信息,使用 static final 修饰确保其不可变
private static final String password = DBConstants.password;
// 数据库连接对象,用于与数据库建立连接
private Connection conn = null;
// 用于执行 SQL 语句的 Statement 对象
private Statement stmt = null;
/**
* 构造函数,在创建 JDBCBean 对象时自动尝试与数据库建立连接。
*/
public JDBCBean() {
try {
// 加载数据库驱动类
Class.forName(driver);
// 通过 DriverManager 获取数据库连接
conn = DriverManager.getConnection(url, username, password);
// 创建一个 Statement 对象,用于执行 SQL 语句
stmt = conn.createStatement();
// 若连接成功,打印提示信息
System.out.println("同数据库建立连接!");
} catch (Exception ex) {
// 若连接过程中出现异常,打印错误提示信息
System.out.println("无法同数据库建立连接!");
}
}
/**
* 执行 SQL 更新语句(如 INSERT、UPDATE、DELETE
*
* @param s 要执行的 SQL 更新语句
* @return 执行更新操作后受影响的行数,若执行出错则返回 0
*/
public int executeUpdate(String s) {
// 用于存储执行更新操作后受影响的行数
int result = 0;
try {
// 打印要执行的 SQL 语句和 Statement 对象信息,方便调试
System.out.println(s + "------" + stmt + "-----");
// 执行 SQL 更新语句,并将受影响的行数赋值给 result
result = stmt.executeUpdate(s);
} catch (Exception e) {
// 若执行更新操作时出现异常,打印错误提示信息
System.out.println("执行更新错误!");
}
// 返回受影响的行数
return result;
}
/**
* 执行 SQL 查询语句(如 SELECT
*
* @param s 要执行的 SQL 查询语句
* @return 包含查询结果的 ResultSet 对象,若执行出错则返回 null
*/
public ResultSet executeQuery(String s) {
// 用于存储查询结果的 ResultSet 对象
ResultSet rs = null;
try {
// 执行 SQL 查询语句,并将结果存储在 rs 中
rs = stmt.executeQuery(s);
} catch (Exception e) {
// 若执行查询操作时出现异常,打印错误提示信息和异常信息
System.out.println("执行查询错误! " + e.getMessage());
}
// 返回包含查询结果的 ResultSet 对象
return rs;
}
/**
* 关闭数据库连接和 Statement 对象,释放资源。
*/
public void close() {
try {
// 关闭 Statement 对象
stmt.close();
// 关闭数据库连接
conn.close();
} catch (Exception e) {
// 若关闭过程中出现异常,不做处理
}
}
}
package javabean;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.SimpleDateFormat;
import java.util.Date;
import net.sf.json.JSONObject;
/**
* Util 类提供了一系列实用工具方法涵盖字符串处理、日期格式化、JSON 数据生成和 MD5 加密等功能。
*/
public class Util {
/**
* 计算字符串中指定子字符串的出现次数。
* 此方法可用于计算 JSON 字符串中特定对象标识的数量。
*
* @param str 要进行检查的原始字符串
* @param contain 要查找的子字符串
* @return 子字符串在原始字符串中出现的次数
*/
public static int getCountString(String str, String contain) {
// 通过替换子字符串后计算长度差,再除以子字符串长度得到出现次数
int count = (str.length() - str.replace(contain, "").length()) / contain.length();
return count;
}
/**
* 格式化从数据库取出的日期时间字符串,去除可能存在的 ".0" 后缀。
*
* @param dateTime 从数据库取出的日期时间字符串
* @return 去除 ".0" 后缀后的日期时间字符串,如果原字符串无 ".0" 或为空则返回原字符串或 null
*/
public static String getFormatDateTime(String dateTime) {
// 若日期时间字符串不为空且包含 ".0"
if (dateTime != null && dateTime.indexOf(".0") != -1) {
// 截取去除 ".0" 后的部分并返回
return dateTime.substring(0, dateTime.length() - 2);
} else if (dateTime != null) {
// 若不包含 ".0" 且不为空,直接返回原字符串
return dateTime;
}
// 若为空则返回 null
return null;
}
/**
* 获取当前时间的字符串表示,格式为 "yyyy-MM-dd hh:mm:ss"。
*
* @return 当前时间的格式化字符串
*/
public static String getCurrentTimeString() {
// 获取当前日期对象
Date date = new Date();
// 定义日期时间格式化模式
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
// 将日期对象按指定格式转换为字符串并返回
return dateFormat.format(date);
}
/**
* 生成包含状态码、消息和数据的 JSON 格式字符串响应。
*
* @param code 状态码
* @param msg 消息内容
* @param data 附带的数据,如果为 null 则不包含在 JSON 中
* @return 生成的 JSON 格式字符串
*/
public static String jsonResponse(int code, String msg, String data) {
// 创建一个 JSONObject 对象
JSONObject jsonObject = new JSONObject();
// 向 JSON 对象中添加状态码
jsonObject.put("code", code);
// 向 JSON 对象中添加消息内容
jsonObject.put("msg", msg);
// 若数据不为空,则添加到 JSON 对象中
if (data != null) {
jsonObject.put("data", data);
}
// 将 JSON 对象转换为字符串并返回
return jsonObject.toString();
}
/**
* 对输入的字符串进行 MD5 加密。
*
* @param plainText 待加密的明文字符串
* @return 加密后的 32 位 MD5 字符串
*/
public static String stringToMD5(String plainText) {
byte[] secretBytes = null;
try {
// 获取 MD5 算法的 MessageDigest 实例
MessageDigest md = MessageDigest.getInstance("md5");
// 对输入字符串的字节数组进行加密处理
secretBytes = md.digest(plainText.getBytes());
} catch (NoSuchAlgorithmException e) {
// 若指定的 MD5 算法不存在,抛出运行时异常
throw new RuntimeException("没有这个md5算法");
}
// 将加密后的字节数组转换为 16 进制字符串
String md5code = new BigInteger(1, secretBytes).toString(16);
// 补齐到 32 位
for (int i = 0; i < 32 - md5code.length(); i++) {
md5code = "0" + md5code;
}
return md5code;
}
/**
* 对密码进行加盐后的 MD5 加密。
*
* @param password 原始密码
* @return 加盐加密后的 MD5 字符串
*/
public static String passMd5(String password) {
// 定义盐值
String salt = "ew!.E";
// 将密码和盐值拼接后进行 MD5 加密并返回结果
return Util.stringToMD5(password + salt);
}
/**
* 主方法,用于测试 passMd5 方法。
*
* @param args 命令行参数
*/
public static void main(String[] args) {
// 打印对 "admin" 密码加盐加密后的 MD5 结果
System.out.println(Util.passMd5("admin"));
// 以下代码为注释掉的测试获取当前时间功能的代码
// Date date = new Date();
// SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
// System.out.println(dateFormat.format(date));
}
}
/*!
* classie - class helper functions
* from bonzo https://github.com/ded/bonzo
*
* classie.has( elem, 'my-class' ) -> true/false
* classie.add( elem, 'my-new-class' )
* classie.remove( elem, 'my-unwanted-class' )
* classie.toggle( elem, 'my-class' )
*/
/*jshint browser: true, strict: true, undef: true */
/*global define: false */
// 立即执行函数表达式 (IIFE),将 window 对象作为参数传入,避免全局作用域污染
( function( window ) {
// 使用严格模式,开启更严格的语法检查
'use strict';
// 从 bonzo 库借鉴的类操作辅助函数
// 该函数用于生成一个正则表达式,用于匹配元素类名中的指定类名
function classReg( className ) {
// 正则表达式解释:
// (^|\\s+) 匹配字符串的开头或者一个或多个空白字符
// className 是要匹配的类名
// (\\s+|$) 匹配一个或多个空白字符或者字符串的结尾
return new RegExp("(^|\\s+)" + className + "(\\s+|$)");
}
// 用于管理元素类名的函数声明
// 这些函数将根据浏览器是否支持 classList 属性来实现不同的逻辑
var hasClass, addClass, removeClass;
// 检查浏览器是否支持 classList 属性
if ( 'classList' in document.documentElement ) {
// 如果支持 classList 属性
// hasClass 函数用于检查元素是否包含指定的类名
hasClass = function( elem, c ) {
// 使用 classList 的 contains 方法检查元素是否包含指定类名
return elem.classList.contains( c );
};
// addClass 函数用于给元素添加指定的类名
addClass = function( elem, c ) {
// 使用 classList 的 add 方法添加指定类名
elem.classList.add( c );
};
// removeClass 函数用于从元素中移除指定的类名
removeClass = function( elem, c ) {
// 使用 classList 的 remove 方法移除指定类名
elem.classList.remove( c );
};
}
else {
// 如果浏览器不支持 classList 属性
// hasClass 函数使用正则表达式来检查元素是否包含指定的类名
hasClass = function( elem, c ) {
// 使用 classReg 函数生成的正则表达式来测试元素的 className 属性
return classReg( c ).test( elem.className );
};
// addClass 函数用于给元素添加指定的类名
addClass = function( elem, c ) {
// 先检查元素是否已经包含指定的类名
if ( !hasClass( elem, c ) ) {
// 如果不包含,则在元素的 className 属性后面添加该类名
elem.className = elem.className + ' ' + c;
}
};
// removeClass 函数用于从元素中移除指定的类名
removeClass = function( elem, c ) {
// 使用 classReg 函数生成的正则表达式来替换元素 className 属性中的指定类名
elem.className = elem.className.replace( classReg( c ), ' ' );
};
}
// toggleClass 函数用于切换元素的指定类名
function toggleClass( elem, c ) {
// 根据元素是否包含指定类名来选择调用 removeClass 或 addClass 函数
var fn = hasClass( elem, c ) ? removeClass : addClass;
// 调用选择的函数来切换类名
fn( elem, c );
}
// 创建一个 classie 对象,包含所有的类操作函数
var classie = {
// 完整的函数名
hasClass: hasClass,
addClass: addClass,
removeClass: removeClass,
toggleClass: toggleClass,
// 简短的函数名,方便使用
has: hasClass,
add: addClass,
remove: removeClass,
toggle: toggleClass
};
// 模块加载机制的处理
if ( typeof define === 'function' && define.amd ) {
// 如果使用 AMD 模块加载器(如 RequireJS则使用 define 函数定义模块
define( classie );
} else {
// 如果没有使用 AMD 模块加载器,则将 classie 对象添加到 window 对象上,作为全局变量使用
window.classie = classie;
}
})( window );
/*!
* css-filters-polyfill.js
*
* Author: Christian Schepp Schaefer
* Summary: A polyfill for CSS filter effects
* License: MIT
* Version: 0.22
*
* URL:
* https://github.com/Schepp/
*
*/
// 立即执行函数表达式,将 window 对象作为参数传入,避免全局变量污染
;(function(window){
// 创建一个名为 polyfilter 的对象,用于封装所有与 CSS 滤镜填充相关的功能和属性
var polyfilter = {
// Detect if we are dealing with IE <= 9
// http://james.padolsey.com/javascript/detect-_ie-in-js-using-conditional-comments/
// 检测当前浏览器是否为 IE 9 及以下版本
_ie: (function(){
var undef;
// 初始化版本号为 3
var v = 3;
// 创建一个 div 元素
var div = document.createElement('div');
// 获取 div 元素内的所有 i 元素
var all = div.getElementsByTagName('i');
// 通过条件注释来检测 IE 版本
// 不断增加版本号 v直到条件注释不满足即当前 IE 版本不大于 v
while(
// 设置 div 的 innerHTML 为条件注释
div.innerHTML = '<!--[if gt IE ' + (++v) + ']><i></i><![endif]-->',
// 如果条件注释生效,会创建一个 i 元素,此时 all[0] 存在
all[0]
);
// 如果 v 大于 4说明检测到了 IE 版本,返回该版本号;否则返回 undefined
return v > 4 ? v : undef;
})(),
// 用于缓存创建的 SVG 元素,避免重复创建
_svg_cache: {},
// 创建 SVG 元素的辅助函数
_create_svg_element: function(tagname,attributes){
// SVG 的命名空间
var xmlns = 'http://www.w3.org/2000/svg';
// 使用 createElementNS 方法创建 SVG 元素
var elem = document.createElementNS(xmlns,tagname);
// 遍历传入的属性对象
for(var key in attributes){
// 为 SVG 元素设置属性
elem.setAttributeNS(null,key,attributes[key]);
}
return elem;
},
// 创建完整 SVG 滤镜定义的函数
_create_svg: function(id,filterelements){
// SVG 的命名空间
var xmlns = 'http://www.w3.org/2000/svg';
// 创建一个 SVG 元素
var svg = document.createElementNS(xmlns,'svg');
// 设置 SVG 元素的宽度为 0
svg.setAttributeNS(null,'width','0');
// 设置 SVG 元素的高度为 0
svg.setAttributeNS(null,'height','0');
// 设置 SVG 元素的样式为绝对定位,避免影响页面布局
svg.setAttributeNS(null,'style','position:absolute');
// 创建一个 filter 元素
var svg_filter = document.createElementNS(xmlns,'filter');
// 为 filter 元素设置 id
svg_filter.setAttributeNS(null,'id',id);
// 将 filter 元素添加到 SVG 元素中
svg.appendChild(svg_filter);
// 遍历传入的滤镜元素数组
for(var i = 0; i < filterelements.length; i++){
// 将每个滤镜元素添加到 filter 元素中
svg_filter.appendChild(filterelements[i]);
}
return svg;
},
// 记录待处理的外部样式表数量
_pending_stylesheets: 0,
// 存储所有样式表(内联和外部)的信息
_stylesheets: [],
// 判断是否处于开发模式
_development_mode: (function(){
// 检查当前页面的主机名是否为 localhost 或者以 .local 结尾,或者是 IP 地址
if(location.hostname === 'localhost' || location.hostname.search(/.local$/) !== -1 || location.hostname.search(/\d+\.\d+\.\d+\.\d+/) !== -1){
// 如果处于开发模式,在控制台输出提示信息
if(window.console) console.log('Detected localhost or IP address. Assuming you are a developer. Caching of stylesheets is disabled.');
return true;
}
// 如果不是开发模式,在控制台输出提示信息
if(window.console) console.log('Caching of stylesheets is enabled. You need to refresh twice to see any changes.');
return false;
})(),
// 处理页面上所有样式表的函数
process_stylesheets: function(){
var xmlHttp = [];
// 延迟 2 秒后检查库文件路径是否正确,避免干扰初始处理
window.setTimeout(function(){
var xmlHttpCheck;
// 根据浏览器支持情况创建 XMLHttpRequest 对象
if (window.XMLHttpRequest) {
xmlHttpCheck = new XMLHttpRequest();
} else if (window.ActiveXObject) {
xmlHttpCheck = new ActiveXObject("Microsoft.XMLHTTP");
}
// 打开一个 GET 请求,检查 sepia.htc 文件是否存在
xmlHttpCheck.open('GET', window.polyfilter_scriptpath + 'htc/sepia.htc', true);
// 监听请求状态变化
xmlHttpCheck.onreadystatechange = function(){
// 当请求完成且状态码不是 200 时,说明路径可能有误
if(xmlHttpCheck.readyState == 4 && xmlHttpCheck.status != 200){
// 弹出提示框,提示用户配置正确的绝对路径
alert('The configured path \r\rvar polyfilter_scriptpath = "' + window.polyfilter_scriptpath + '"\r\rseems wrong!\r\rConfigure the polyfill\'s correct absolute(!) script path before referencing the css-filters-polyfill.js, like so:\r\rvar polyfilter_scriptpath = "/js/css-filters-polyfill/";\r\rLeaving IE dead in the water is no option. You damn Mac user... ;)');
}
};
try{
// 发送请求
xmlHttpCheck.send(null);
} catch(e){}
},2000);
// 获取页面上所有的样式表元素
var stylesheets = document.querySelectorAll ? document.querySelectorAll('style,link[rel="stylesheet"]') : document.getElementsByTagName('*');
// 遍历所有样式表元素
for(var i = 0; i < stylesheets.length; i++){
// 使用立即执行函数避免闭包问题
(function(i){
// 根据元素的节点名进行不同处理
switch(stylesheets[i].nodeName){
default:
// 如果节点名不是 STYLE 或 LINK不做处理直接跳出
break;
case 'STYLE':
// 当节点名是 STYLE 时,说明是内联样式表
polyfilter._stylesheets.push({
// 获取样式表的媒体查询属性,如果没有则默认为 'all'
media: stylesheets[i].media || 'all',
// 获取样式表的内容
content: stylesheets[i].innerHTML
});
break;
case 'LINK':
// 当节点名是 LINK 时
if(stylesheets[i].rel === 'stylesheet'){
// 且该 LINK 元素的 rel 属性为 'stylesheet',说明是外部样式表
var index = polyfilter._stylesheets.length;
// 将该外部样式表的信息添加到 _stylesheets 数组中
polyfilter._stylesheets.push({
// 获取样式表的媒体查询属性,如果没有则默认为 'all'
media: stylesheets[i].media || 'all'
});
// 待处理的外部样式表数量加 1
polyfilter._pending_stylesheets++;
// 获取外部样式表的链接地址
var href = stylesheets[i].href;
// 如果不是开发模式,且浏览器支持 localStorage并且 localStorage 中已经缓存了该样式表
if(!polyfilter._development_mode && window.localStorage && window.localStorage.getItem('polyfilter_' + href)){
// 待处理的外部样式表数量减 1
polyfilter._pending_stylesheets--;
// 从 localStorage 中获取缓存的样式表内容,并赋值给 _stylesheets 数组中对应的元素
polyfilter._stylesheets[index].content = localStorage.getItem('polyfilter_' + href);
// 如果所有待处理的外部样式表都已处理完毕
if(polyfilter._pending_stylesheets === 0){
// 调用 process 方法进行后续处理
polyfilter.process();
}
}
// 无论是否有缓存,都要发起请求获取最新的样式表内容
try{
// 根据浏览器支持情况创建 XMLHttpRequest 对象
if(window.XMLHttpRequest) {
var xmlHttp = new XMLHttpRequest();
} else if(window.ActiveXObject) {
var xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
}
// 打开一个 GET 请求,请求外部样式表
xmlHttp.open('GET', href, true);
// 监听请求状态变化
xmlHttp.onreadystatechange = function(){
// 当请求完成时
if(xmlHttp.readyState === 4){
// 如果状态码为 0可能是跨域问题导致请求失败
if(xmlHttp.status === 0){
if(window.console) console.log('Could not fetch external CSS via HTTP-Request ' + href + '. Probably because of cross origin.');
// 如果 _stylesheets 数组中对应的元素还没有内容
if(!polyfilter._stylesheets[index].content){
// 待处理的外部样式表数量减 1
polyfilter._pending_stylesheets--;
// 将响应内容赋值给 _stylesheets 数组中对应的元素
polyfilter._stylesheets[index].content = xmlHttp.responseText;
// 如果所有待处理的外部样式表都已处理完毕
if(polyfilter._pending_stylesheets === 0){
// 调用 process 方法进行后续处理
polyfilter.process();
}
}
} else {
// 如果状态码正常
if(!polyfilter._stylesheets[index].content){
// 待处理的外部样式表数量减 1
polyfilter._pending_stylesheets--;
// 将响应内容赋值给 _stylesheets 数组中对应的元素
polyfilter._stylesheets[index].content = xmlHttp.responseText;
// 如果所有待处理的外部样式表都已处理完毕
if(polyfilter._pending_stylesheets === 0){
// 调用 process 方法进行后续处理
polyfilter.process();
}
}
// 如果不是开发模式,且浏览器支持 localStorage
if(!polyfilter._development_mode && window.localStorage){
try{
// 将样式表内容缓存到 localStorage 中
window.localStorage.setItem('polyfilter_' + href, polyfilter._stylesheets[index].content);
}
catch(e){
// 如果 localStorage 空间不足,输出提示信息
if(window.console) console.log('Local storage quota have been exceeded. Caching of stylesheet ' + href + ' is not possible');
}
}
}
}
};
try{
// 发送请求
xmlHttp.send(null);
} catch(e){
// 如果请求发送失败,可能是使用 file:// 协议进行测试导致的
if(window.console) console.log('Could not fetch external CSS via HTTP-Request ' + href + '. Are you maybe testing using the file://-protocol?');
if(!polyfilter._stylesheets[index].content){
// 待处理的外部样式表数量减 1
polyfilter._pending_stylesheets--;
if(polyfilter._pending_stylesheets === 0){
// 调用 process 方法进行后续处理
polyfilter.process();
}
}
}
} catch(e){}
}
break;
}
})(i);
}
// 如果所有待处理的外部样式表都已处理完毕
if(this._pending_stylesheets === 0){
// 调用 process 方法进行后续处理
this.process();
}
},
// 处理 CSS 规则声明的函数
_processDeclarations: function(rule){
var newstyles = '';
// 遍历规则中的所有声明
for(var k in rule.declarations){
var declaration = rule.declarations[k];
// 如果声明的属性是 'filter'
if(declaration.property === 'filter'){
if(document.querySelectorAll){
// 检查浏览器是否支持 document.querySelectorAll 方法
// 使用选择器 rule.mSelectorText 获取匹配的元素集合
var elems = document.querySelectorAll(rule.mSelectorText);
for(var k = 0; k < elems.length; k++){
// 遍历匹配的元素集合,将当前 filter 声明的值存储到元素的 style.polyfilterStore 属性中
// 方便后续可能的使用
elems[k].style.polyfilterStore = declaration.valueText;
}
}
// 获取 filter 属性声明的原始值
var gluedvalues = declaration.valueText;
// 将原始值按 ")" 后面跟着一个或多个空白字符进行分割,得到每个单独的滤镜值
var values = gluedvalues.split(/\)\s+/),
// 初始化一个对象,用于存储不同浏览器支持的滤镜属性值
properties = {
filtersW3C: [], // 存储 W3C 标准的滤镜属性值
filtersWebKit: [], // 存储 WebKit 内核浏览器(如 Safari、旧版 Chrome的滤镜属性值
filtersSVG: [], // 存储 SVG 滤镜属性值
filtersIE: [], // 存储 Internet Explorer 浏览器的滤镜属性值
behaviorsIE: [] // 存储 Internet Explorer 浏览器的行为属性值(用于模拟滤镜效果)
};
// 遍历分割后的每个滤镜值
for(var idx in values){
var value = values[idx] + ')'; // 恢复完整的滤镜值
// 调用 polyfilter 对象的 convert 方法,将单个滤镜值转换为不同浏览器支持的属性值
var currentproperties = polyfilter.convert(value);
// 遍历转换后的属性值对象
for(var key in currentproperties){
if(typeof properties[key] !== 'undefined'){
// 如果当前属性在 properties 对象中存在,则将转换后的属性值追加到对应数组中
properties[key] = properties[key].concat(currentproperties[key]);
}
}
}
// 开始构建新的样式规则字符串,添加选择器和左花括号
newstyles += rule.mSelectorText + '{';
if(properties['filtersW3C'].length > 0){
// 如果存在 W3C 标准的滤镜属性值
var filter =
webkitFilter =
mozFilter =
oFilter =
msFilter =
properties['filtersW3C'].join(' '); // 将 W3C 标准的滤镜属性值用空格连接成一个字符串
if(properties['filtersWebKit'] && properties['filtersWebKit'].length > 0){
// 如果存在 WebKit 内核浏览器的滤镜属性值,则使用这些值替换 webkitFilter
webkitFilter = properties['filtersWebKit'].join(' ');
}
if(typeof this._ie === 'undefined'){
// 如果不是 Internet Explorer 浏览器,则添加 -ms-filter 属性
newstyles += '-ms-filter:' + msFilter + ';';
}
// 添加不同浏览器前缀的 filter 属性
newstyles += '-webkit-filter:' + webkitFilter + ';';
newstyles += '-moz-filter:' + mozFilter + ';';
newstyles += '-o-filter:' + oFilter + ';';
}
if(properties['filtersSVG'].length > 0){
if(properties['filtersSVG'][0] != 'none'){
// 如果 SVG 滤镜属性值不为 'none'
// 生成一个唯一的 ID用于标识 SVG 滤镜
var id = gluedvalues.replace(/[^a-z0-9]/g,'');
if(typeof this._svg_cache[id] === 'undefined'){
// 如果 SVG 滤镜缓存中不存在该 ID 对应的 SVG 元素
// 创建一个新的 SVG 元素,并将其存储到缓存中
this._svg_cache[id] = this._create_svg(id,properties['filtersSVG']);
if(typeof XMLSerializer === 'undefined'){
// 如果浏览器不支持 XMLSerializer则直接将 SVG 元素添加到 body 中
document.body.appendChild(this._svg_cache[id]);
}
else {
// 如果浏览器支持 XMLSerializer
var s = new XMLSerializer();
// 将 SVG 元素序列化为字符串
var svgString = s.serializeToString(this._svg_cache[id]);
if(svgString.search('SourceGraphic') != -1){
// 如果 SVG 字符串中包含 'SourceGraphic',则将 SVG 元素添加到 body 中
document.body.appendChild(this._svg_cache[id]);
}
}
}
if(typeof XMLSerializer === 'undefined'){
// 如果浏览器不支持 XMLSerializer则使用 url(#id) 形式引用 SVG 滤镜
newstyles += 'filter: url(#' + id + ')';
}
else {
var s = new XMLSerializer();
// 将 SVG 元素序列化为字符串
var svgString = s.serializeToString(this._svg_cache[id]);
if(svgString.search('SourceGraphic') != -1){
// 如果 SVG 字符串中包含 'SourceGraphic',则使用 url(#id) 形式引用 SVG 滤镜
newstyles += 'filter: url(#' + id + ')';
}
else {
// 否则,使用 data URI 形式引用 SVG 滤镜
newstyles += 'filter: url(\'data:image/svg+xml;utf8,' + svgString + '#' + id + '\')';
}
}
}
else {
// 如果 SVG 滤镜属性值为 'none',则添加 filter: none;
newstyles += 'filter: none;';
}
}
if(typeof this._ie !== 'undefined'){
// 如果是 Internet Explorer 浏览器
if(properties['filtersIE'].length > 0){
// 如果存在 Internet Explorer 浏览器的滤镜属性值
var filtersIE = properties['filtersIE'].join(' ');
// 添加 filter 属性
newstyles += 'filter:' + filtersIE + ';';
}
if(properties['behaviorsIE'].length > 0){
// 如果存在 Internet Explorer 浏览器的行为属性值
var behaviorsIE = properties['behaviorsIE'].join(' ');
// 添加 behavior 属性
newstyles += 'behavior:' + behaviorsIE + ';';
}
}
// 完成当前选择器样式规则的构建,添加右花括号和换行符
newstyles += '}\r\n';
}
}
// 返回新生成的样式规则字符串
return newstyles;
},
// 存储 .htc 文件的绝对路径
scriptpath: window.polyfilter_scriptpath ? window.polyfilter_scriptpath : (function(){
// 如果没有在 window 对象中配置 polyfilter_scriptpath
// 弹出提示框,提醒用户在引用 css - filters - polyfill.js 之前配置正确的绝对路径
alert('Please configure the polyfill\'s absolute(!) script path before referencing the css-filters-polyfill.js, like so:\r\nvar polyfilter_scriptpath = "/js/css-filters-polyfill/";');
// 默认返回当前目录
return './'
})(),
// 处理样式表的主函数
process: function(){
// 创建一个 CSS 解析器实例
var parser = new CSSParser();
// 遍历存储的所有样式表
for(var i = 0; i < this._stylesheets.length; i++){
// 初始化新样式字符串
var newstyles = '';
// 使用解析器解析当前样式表的内容
var sheet = parser.parse(this._stylesheets[i].content, false, true);
// 如果解析结果不为空
if(sheet !== null) for(var j in sheet.cssRules){
var rule = sheet.cssRules[j];
// 根据规则类型进行不同处理
switch(rule.type){
default:
// 默认情况不做处理
break;
case 1:
// 类型 1 通常表示普通的样式规则
// 调用 _processDeclarations 方法处理该规则,并将结果添加到新样式字符串中
newstyles += this._processDeclarations(rule);
break;
case 4:
// 类型 4 通常表示 @media 媒体查询规则
// 构建媒体查询的开头部分
newstyles += '@media ' + rule.media.join(',') + '{';
// 遍历媒体查询内的子规则
for(var k in rule.cssRules){
var mediarule = rule.cssRules[k];
// 调用 _processDeclarations 方法处理子规则,并将结果添加到新样式字符串中
newstyles += this._processDeclarations(mediarule);
}
// 构建媒体查询的结尾部分
newstyles += '}';
break;
}
}
// 创建一个新的 <style> 元素
var newstylesheet = document.createElement('style');
// 设置该 <style> 元素的媒体属性
newstylesheet.setAttribute('media',this._stylesheets[i].media);
if(typeof polyfilter._ie === 'undefined'){
// 如果不是 IE 浏览器
// 将新生成的样式字符串设置为 <style> 元素的 innerHTML
newstylesheet.innerHTML = newstyles;
// 将 <style> 元素添加到 <head> 标签中
document.getElementsByTagName('head')[0].appendChild(newstylesheet);
}
else {
// 如果是 IE 浏览器
// 将 <style> 元素添加到 <head> 标签中
document.getElementsByTagName('head')[0].appendChild(newstylesheet);
// 使用 styleSheet.cssText 属性设置样式内容
newstylesheet.styleSheet.cssText = newstyles;
}
}
},
// 初始化函数,用于给 CSSStyleDeclaration 原型添加自定义属性
init: function(){
if(Object.defineProperty){
// 如果浏览器支持 Object.defineProperty 方法
// 给 CSSStyleDeclaration 原型添加一个名为 'polyfilter' 的属性
Object.defineProperty(CSSStyleDeclaration.prototype, 'polyfilter', {
// 定义获取属性值的方法
get: function(){
// 返回之前存储在 polyfilterStore 中的值
return this.polyfilterStore;
},
// 定义设置属性值的方法
set: function(gluedvalues){
// 将传入的滤镜值按 ")" 后面跟着一个或多个空白字符进行分割
values = gluedvalues.split(/\)\s+/);
var properties = {
filtersW3C: [],
filtersWebKit: [],
filtersSVG: [],
filtersIE: [],
behaviorsIE: []
}
// 遍历分割后的每个滤镜值
for(var idx in values){
var value = values[idx] + ')';
// 调用 convert 方法将单个滤镜值转换为不同浏览器支持的属性值
var currentproperties = polyfilter.convert(value);
// 遍历转换后的属性值对象
for(var key in currentproperties){
if(typeof properties[key] !== 'undefined'){
// 将转换后的属性值追加到对应数组中
properties[key] = properties[key].concat(currentproperties[key]);
}
}
}
if(properties['filtersW3C'].length > 0){
if(typeof polyfilter._ie === 'undefined'){
// 如果不是 IE 浏览器,设置 -ms-filter 属性
this.msFilter = properties['filtersW3C'].join(' ');
}
// 设置不同浏览器前缀的 filter 属性
this.webkitFilter =
this.mozFilter =
this.oFilter = properties['filtersW3C'].join(' ');
}
if(properties['filtersWebKit'].length > 0){
// 如果有 WebKit 特定的滤镜值,优先使用
this.webkitFilter = properties['filtersWebKit'].join(' ');
}
if(properties['filtersSVG'].length > 0){
if(properties['filtersSVG'][0] != 'none'){
// 生成一个唯一的 ID去除滤镜值字符串中的非字母数字字符
var id = gluedvalues.replace(/[^a-z0-9]/g,'');
// 检查 SVG 滤镜缓存中是否已经存在该 ID 对应的 SVG 滤镜
if(typeof polyfilter._svg_cache[id] === 'undefined'){
// 若不存在,创建一个新的 SVG 滤镜并缓存起来
polyfilter._svg_cache[id] = polyfilter._create_svg(id,properties['filtersSVG']);
// 检查浏览器是否支持 XMLSerializer 对象
if(typeof XMLSerializer === 'undefined'){
// 若不支持,直接将 SVG 滤镜添加到页面的 body 元素中
document.body.appendChild(polyfilter._svg_cache[id]);
}
else {
// 若支持,将 SVG 滤镜序列化为字符串
var s = new XMLSerializer();
var svgString = s.serializeToString(polyfilter._svg_cache[id]);
// 检查序列化后的 SVG 字符串中是否包含 'SourceGraphic'
if(svgString.search('SourceGraphic') != -1){
// 若包含,将 SVG 滤镜添加到页面的 body 元素中
document.body.appendChild(polyfilter._svg_cache[id]);
}
}
}
// 再次检查浏览器是否支持 XMLSerializer 对象
if(typeof XMLSerializer === 'undefined'){
// 若不支持,使用 url(#id) 的形式引用 SVG 滤镜
this.filter = 'url(#' + id + ')';
}
else {
// 若支持,将 SVG 滤镜再次序列化为字符串
var s = new XMLSerializer();
var svgString = s.serializeToString(polyfilter._svg_cache[id]);
// 检查序列化后的 SVG 字符串中是否包含 'SourceGraphic'
if(svgString.search('SourceGraphic') != -1){
// 若包含,使用 url(#id) 的形式引用 SVG 滤镜
this.filter = 'url(#' + id + ')';
}
else {
// 若不包含,使用 data URI 的形式引用 SVG 滤镜
this.filter = 'url(\'data:image/svg+xml;utf8,' + svgString + '#' + id + '\')';
}
}
}
else {
// 如果 SVG 滤镜属性值为 'none',则设置 filter 属性为 'none'
this.filter = 'none';
}
// 检查当前浏览器是否为 IE
if(typeof polyfilter._ie !== 'undefined'){
if(properties['filtersIE'].length > 0){
// 如果存在 IE 支持的滤镜属性,将其连接成字符串并赋值给 filter 属性
this.filter = properties['filtersIE'].join(' ');
}
else {
// 若不存在,清空 filter 属性
this.filter = '';
}
if(properties['behaviorsIE'].length > 0){
// 如果存在 IE 支持的行为属性,将其连接成字符串并赋值给 behavior 属性
this.behavior = properties['behaviorsIE'].join(' ');
}
else {
// 若不存在,清空 behavior 属性
this.behavior = '';
}
}
// 将传入的滤镜值存储到 polyfilterStore 属性中,方便后续获取
this.polyfilterStore = gluedvalues;
}
}),
get: function(){
// 定义获取 polyfilter 属性值的方法,返回存储的滤镜值
return this.polyfilterStore;
},
set: function(gluedvalues){
// 定义设置 polyfilter 属性值的方法
values = gluedvalues.split(/\)\s+/);
var properties = {
filtersW3C: [],
filtersWebKit: [],
filtersSVG: [],
filtersIE: [],
behaviorsIE: []
}
// 遍历分割后的每个滤镜值
for(idx in values){
var value = values[idx] + ')';
// 调用 convert 方法将单个滤镜值转换为不同浏览器支持的属性值
currentproperties = polyfilter.convert(value);
// 将转换后的属性值追加到对应数组中
for(key in currentproperties){
if(typeof properties[key] !== 'undefined'){
properties[key] = properties[key].concat(currentproperties[key]);
}
}
}
if(properties['filtersW3C'].length > 0){
if(typeof polyfilter._ie === 'undefined'){
// 如果不是 IE 浏览器,设置 -ms-filter 属性
this.msFilter = properties['filtersW3C'].join(' ');
}
// 设置不同浏览器前缀的 filter 属性
this.webkitFilter =
this.mozFilter =
this.oFilter = properties['filtersW3C'].join(' ');
}
if(properties['filtersWebKit'].length > 0){
// 如果有 WebKit 特定的滤镜值,优先使用
this.webkitFilter = properties['filtersWebKit'].join(' ');
}
if(properties['filtersSVG'].length > 0){
if(properties['filtersSVG'][0] != 'none'){
// 生成一个唯一的 ID用于标识 SVG 滤镜
var id = gluedvalues.replace(/[^a-z0-9]/g,'');
if(typeof polyfilter._svg_cache[id] === 'undefined'){
// 如果 SVG 滤镜缓存中不存在该 ID 对应的 SVG 元素
polyfilter._svg_cache[id] = polyfilter._create_svg(id,properties['filtersSVG']);
if(typeof XMLSerializer === 'undefined'){
// 如果浏览器不支持 XMLSerializer将 SVG 元素添加到 body 中
document.body.appendChild(polyfilter._svg_cache[id]);
}
else {
var s = new XMLSerializer();
var svgString = s.serializeToString(polyfilter._svg_cache[id]);
if(svgString.search('SourceGraphic') != -1){
// 如果 SVG 字符串中包含 'SourceGraphic',将 SVG 元素添加到 body 中
document.body.appendChild(polyfilter._svg_cache[id]);
}
}
}
if(typeof XMLSerializer === 'undefined'){
// 如果浏览器不支持 XMLSerializer使用 url(#id) 形式引用 SVG 滤镜
this.filter = 'url(#' + id + ')';
}
else {
var s = new XMLSerializer();
var svgString = s.serializeToString(polyfilter._svg_cache[id]);
if(svgString.search('SourceGraphic') != -1){
// 如果 SVG 字符串中包含 'SourceGraphic',使用 url(#id) 形式引用 SVG 滤镜
this.filter = 'url(#' + id + ')';
}
else {
// 否则,使用 data URI 形式引用 SVG 滤镜
this.filter = 'url(\'data:image/svg+xml;utf8,' + svgString + '#' + id + '\')';
}
}
}
else {
// 如果 SVG 滤镜属性值为 'none',则设置 filter 属性为 'none'
this.filter = 'none';
}
}
if(typeof polyfilter._ie !== 'undefined'){
if(properties['filtersIE'].length > 0){
// 如果存在 IE 支持的滤镜属性,设置 filter 属性
this.filter = properties['filtersIE'].join(' ');
}
if(properties['behaviorsIE'].length > 0){
// 如果存在 IE 支持的行为属性,设置 behavior 属性
this.behavior = properties['behaviorsIE'].join(' ');
}
}
// 存储原始的滤镜值,方便后续获取
this.polyfilterStore = gluedvalues;
}
});
}
},
// 将传入的滤镜值转换为不同浏览器支持的属性值
convert: function(value){
// 检查是否为 'none' 滤镜
var fmatch = value.match(/none/i);
if(fmatch !== null){
var properties = this.filters.none();
}
// 检查是否为 'grayscale' 滤镜
var fmatch = value.match(/(grayscale)\(([0-9\.]+)\)/i);
if(fmatch !== null){
var amount = parseFloat(fmatch[2],10);
var properties = this.filters.grayscale(amount);
}
// 检查是否为 'sepia' 滤镜
var fmatch = value.match(/(sepia)\(([0-9\.]+)\)/i);
if(fmatch !== null){
var amount = parseFloat(fmatch[2],10);
var properties = this.filters.sepia(amount);
}
// 检查是否为 'blur' 滤镜
var fmatch = value.match(/(blur)\(([0-9]+)[px]*\)/i);
if(fmatch !== null){
var amount = parseInt(fmatch[2],10);
var properties = this.filters.blur(amount);
}
// 检查是否为 'brightness' 滤镜
var fmatch = value.match(/(brightness)\(([0-9\.]+)%\)/i);
if(fmatch !== null){
var amount = parseFloat(fmatch[2],10);
var properties = this.filters.brightness(amount);
}
// 检查是否为 'drop-shadow' 滤镜
var fmatch = value.match(/(drop\-shadow)\(([0-9]+)[px]*\s+([0-9]+)[px]*\s+([0-9]+)[px]*\s+([\#0-9]+)\)/i);
if(fmatch !== null){
var offsetX = parseInt(fmatch[2],10);
var offsetY = parseInt(fmatch[3],10);
var radius = parseInt(fmatch[4],10);
var color = fmatch[5];
var properties = this.filters.dropShadow(offsetX,offsetY,radius,color);
}
return properties;
},
// 定义各种滤镜效果的处理函数
filters: {
// 'none' 滤镜效果
none: function(){
var properties = {};
if(typeof polyfilter._ie === 'undefined'){
// 非 IE 浏览器,设置 W3C 标准和 SVG 滤镜属性为 'none'
properties['filtersW3C'] = ['none'];
properties['filtersSVG'] = ['none'];
}
else {
// IE 浏览器,设置 IE 支持的滤镜属性为 'none'
properties['filtersIE'] = ['none'];
}
return properties;
},
// 'grayscale' 灰度滤镜效果
grayscale: function(amount){
amount = amount || 0;
var properties = {};
if(typeof polyfilter._ie === 'undefined'){
// 非 IE 浏览器,设置 W3C 标准的灰度滤镜属性
properties['filtersW3C'] = ['grayscale(' + amount + ')'];
// 创建 SVG 滤镜元素
var svg_fe1 = polyfilter._create_svg_element('feColorMatrix',{
type: 'matrix',
values: (0.2126 + 0.7874 * (1 - amount)) + ' '
+ (0.7152 - 0.7152 * (1 - amount)) + ' '
+ (0.0722 - 0.0722 * (1 - amount)) + ' 0 0 '
+ (0.2126 - 0.2126 * (1 - amount)) + ' '
+ (0.7152 + 0.2848 * (1 - amount)) + ' '
+ (0.0722 - 0.0722 * (1 - amount)) + ' 0 0 '
+ (0.2126 - 0.2126 * (1 - amount)) + ' '
+ (0.7152 - 0.7152 * (1 - amount)) + ' '
+ (0.0722 + 0.9278 * (1 - amount)) + ' 0 0 0 0 0 1 0'
});
properties['filtersSVG'] = [svg_fe1];
}
else {
// IE 浏览器,根据灰度值决定是否应用 'gray' 滤镜
properties['filtersIE'] = amount >= 0.5 ? ['gray'] : [];
}
return properties;
},
// 其他滤镜效果sepia、blur、invert、brightness、dropShadow的处理逻辑类似分别根据不同浏览器和参数设置相应的属性值
// ...
}
// 定义滤镜处理对象,包含各种滤镜效果的处理方法
filters: {
// 无滤镜效果处理函数
none: function() {
// 初始化一个空对象,用于存储不同浏览器的滤镜属性
var properties = {};
// 检查是否不是 IE 浏览器
if (typeof polyfilter._ie === 'undefined') {
// 按照 W3C 标准提案,设置无滤镜效果
properties['filtersW3C'] = ['none'];
// 针对 Firefox 浏览器,使用 SVG 实现无滤镜效果
properties['filtersSVG'] = ['none'];
} else {
// 如果是 IE 浏览器,设置 IE 特定的无滤镜效果
properties['filtersIE'] = ['none'];
}
// 返回包含不同浏览器滤镜属性的对象
return properties;
},
// 灰度滤镜效果处理函数
grayscale: function(amount) {
// 如果未传入灰度值,默认为 0
amount = amount || 0;
// 初始化一个空对象,用于存储不同浏览器的滤镜属性
var properties = {};
// 检查是否不是 IE 浏览器
if (typeof polyfilter._ie === 'undefined') {
// 按照 W3C 标准提案,设置灰度滤镜效果及对应强度
properties['filtersW3C'] = ['grayscale(' + amount + ')'];
// 针对 Firefox 浏览器,使用 SVG 实现灰度滤镜效果
// 参考文档https://dvcs.w3.org/hg/FXTF/raw-file/tip/filters/index.html
var svg_fe1 = polyfilter._create_svg_element('feColorMatrix', {
type: 'matrix',
// 根据灰度值计算颜色矩阵的值
values: (0.2126 + 0.7874 * (1 - amount)) + ' '
+ (0.7152 - 0.7152 * (1 - amount)) + ' '
+ (0.0722 - 0.0722 * (1 - amount)) + ' 0 0 '
+ (0.2126 - 0.2126 * (1 - amount)) + ' '
+ (0.7152 + 0.2848 * (1 - amount)) + ' '
+ (0.0722 - 0.0722 * (1 - amount)) + ' 0 0 '
+ (0.2126 - 0.2126 * (1 - amount)) + ' '
+ (0.7152 - 0.7152 * (1 - amount)) + ' '
+ (0.0722 + 0.9278 * (1 - amount)) + ' 0 0 0 0 0 1 0'
});
properties['filtersSVG'] = [svg_fe1];
} else {
// 如果是 IE 浏览器,当灰度值大于等于 0.5 时,应用灰度滤镜
properties['filtersIE'] = amount >= 0.5 ? ['gray'] : [];
}
// 返回包含不同浏览器滤镜属性的对象
return properties;
},
// 棕褐色滤镜效果处理函数
sepia: function(amount) {
// 如果未传入棕褐色强度值,默认为 0
amount = amount || 0;
// 初始化一个空对象,用于存储不同浏览器的滤镜属性
var properties = {};
// 检查是否不是 IE 浏览器
if (typeof polyfilter._ie === 'undefined') {
// 按照 W3C 标准提案,设置棕褐色滤镜效果及对应强度
properties['filtersW3C'] = ['sepia(' + amount + ')'];
// 针对 Firefox 浏览器,使用 SVG 实现棕褐色滤镜效果
// 参考文档https://dvcs.w3.org/hg/FXTF/raw-file/tip/filters/index.html
var svg_fe1 = polyfilter._create_svg_element('feColorMatrix', {
type: 'matrix',
// 根据棕褐色强度值计算颜色矩阵的值
values: (0.393 + 0.607 * (1 - amount)) + ' '
+ (0.769 - 0.769 * (1 - amount)) + ' '
+ (0.189 - 0.189 * (1 - amount)) + ' 0 0 '
+ (0.349 - 0.349 * (1 - amount)) + ' '
+ (0.686 + 0.314 * (1 - amount)) + ' '
+ (0.168 - 0.168 * (1 - amount)) + ' 0 0 '
+ (0.272 - 0.272 * (1 - amount)) + ' '
+ (0.534 - 0.534 * (1 - amount)) + ' '
+ (0.131 + 0.869 * (1 - amount)) + ' 0 0 0 0 0 1 0'
});
properties['filtersSVG'] = [svg_fe1];
} else {
// 如果是 IE 浏览器,当棕褐色强度值大于等于 0.5 时,应用灰度滤镜和光照效果
properties['filtersIE'] = amount >= 0.5 ? ['gray', 'progid:DXImageTransform.Microsoft.Light()'] : [];
// 当棕褐色强度值大于等于 0.5 时,引入棕褐色效果的 HTC 文件
properties['behaviorsIE'] = amount >= 0.5 ? ['url("' + polyfilter.scriptpath + 'htc/sepia.htc")'] : [];
}
// 返回包含不同浏览器滤镜属性的对象
return properties;
},
// 模糊滤镜效果处理函数
blur: function(amount) {
// 对模糊半径进行四舍五入取整,如果未传入则默认为 0
amount = Math.round(amount) || 0;
// 初始化一个空对象,用于存储不同浏览器的滤镜属性
var properties = {};
// 检查是否不是 IE 浏览器
if (typeof polyfilter._ie === 'undefined') {
// 按照 W3C 标准提案,设置模糊滤镜效果及对应半径
properties['filtersW3C'] = ['blur(' + amount + 'px)'];
// 针对 Firefox 浏览器,使用 SVG 实现模糊滤镜效果
// 参考文档https://dvcs.w3.org/hg/FXTF/raw-file/tip/filters/index.html
var svg_fe1 = polyfilter._create_svg_element('feGaussianBlur', {
'in': 'SourceGraphic',
stdDeviation: amount
});
properties['filtersSVG'] = [svg_fe1];
} else {
// 如果是 IE 浏览器,设置 IE 特定的模糊滤镜效果
properties['filtersIE'] = ['progid:DXImageTransform.Microsoft.Blur(pixelradius=' + amount + ')'];
}
// 返回包含不同浏览器滤镜属性的对象
return properties;
},
// 反相滤镜效果处理函数
invert: function(amount) {
// 如果未传入反相强度值,默认为 0
amount = amount || 0;
// 初始化一个空对象,用于存储不同浏览器的滤镜属性
var properties = {};
// 检查是否不是 IE 浏览器
if (typeof polyfilter._ie === 'undefined') {
// 按照 W3C 标准提案,设置反相滤镜效果及对应强度
properties['filtersW3C'] = ['invert(' + amount + ')'];
// 针对 Firefox 浏览器,使用 SVG 实现反相滤镜效果
// 参考文档https://dvcs.w3.org/hg/FXTF/raw-file/tip/filters/index.html
var svg_fe1 = polyfilter._create_svg_element('feComponentTransfer', {});
var svg_fe1sub = polyfilter._create_svg_element('feFuncR', {
type: 'table',
tableValues: amount + ' ' + (1 - amount)
});
svg_fe1.appendChild(svg_fe1sub);
var svg_fe1sub = polyfilter._create_svg_element('feFuncG', {
type: 'table',
tableValues: amount + ' ' + (1 - amount)
});
svg_fe1.appendChild(svg_fe1sub);
var svg_fe1sub = polyfilter._create_svg_element('feFuncB', {
type: 'table',
tableValues: amount + ' ' + (1 - amount)
});
svg_fe1.appendChild(svg_fe1sub);
properties['filtersSVG'] = [svg_fe1];
} else {
// 如果是 IE 浏览器,当反相强度值大于等于 0.5 时,应用反相滤镜
properties['filtersIE'] = amount >= 0.5 ? ['invert'] : [];
}
// 返回包含不同浏览器滤镜属性的对象
return properties;
},
// 亮度滤镜效果处理函数
brightness: function(amount) {
// 如果未传入亮度值,默认为 0
amount = amount || 0;
// 初始化一个空对象,用于存储不同浏览器的滤镜属性
var properties = {};
// 检查是否不是 IE 浏览器
if (typeof polyfilter._ie === 'undefined') {
// 按照 W3C 标准提案,设置亮度滤镜效果及对应亮度值
properties['filtersW3C'] = ['brightness(' + amount + '%)'];
// WebKit 内核浏览器的特殊处理
properties['filtersWebKit'] = ['brightness(' + (amount - 100) + '%)'];
// 针对 Firefox 浏览器,使用 SVG 实现亮度滤镜效果
// 参考文档https://dvcs.w3.org/hg/FXTF/raw-file/tip/filters/index.html
var svg_fe1 = polyfilter._create_svg_element('feComponentTransfer', {});
var svg_fe1sub = polyfilter._create_svg_element('feFuncR', {
type: 'linear',
slope: amount / 100
});
svg_fe1.appendChild(svg_fe1sub);
var svg_fe1sub = polyfilter._create_svg_element('feFuncG', {
type: 'linear',
slope: amount / 100
});
svg_fe1.appendChild(svg_fe1sub);
var svg_fe1sub = polyfilter._create_svg_element('feFuncB', {
type: 'linear',
slope: amount / 100
});
svg_fe1.appendChild(svg_fe1sub);
properties['filtersSVG'] = [svg_fe1];
} else {
// 如果是 IE 浏览器,设置 IE 特定的光照效果滤镜
properties['filtersIE'] = ['progid:DXImageTransform.Microsoft.Light()'];
// 引入亮度效果的 HTC 文件
properties['behaviorsIE'] = ['url("' + polyfilter.scriptpath + 'htc/brightness.htc")'];
}
// 返回包含不同浏览器滤镜属性的对象
return properties;
},
// 投影滤镜效果处理函数
dropShadow: function(offsetX, offsetY, radius, color) {
// 对投影的 X 轴偏移量进行四舍五入取整,如果未传入则默认为 0
offsetX = Math.round(offsetX) || 0;
// 对投影的 Y 轴偏移量进行四舍五入取整,如果未传入则默认为 0
offsetY = Math.round(offsetY) || 0;
// 对投影的模糊半径进行四舍五入取整,如果未传入则默认为 0
radius = Math.round(radius) || 0;
// 如果未传入投影颜色,默认为黑色
color = color || '#000000';
// 初始化一个空对象,用于存储不同浏览器的滤镜属性
var properties = {};
// 检查是否不是 IE 浏览器
if (typeof polyfilter._ie === 'undefined') {
// 按照 W3C 标准提案,设置投影滤镜效果及对应参数
properties['filtersW3C'] = ['drop-shadow(' + offsetX + 'px ' + offsetY + 'px ' + radius + 'px ' + color + ')'];
// 针对 Firefox 浏览器,使用 SVG 实现投影滤镜效果
// 参考文档https://dvcs.w3.org/hg/FXTF/raw-file/tip/filters/index.html
var svg_fe1 = polyfilter._create_svg_element('feGaussianBlur', {
'in': 'SourceAlpha',
stdDeviation: radius
});
var svg_fe2 = polyfilter._create_svg_element('feOffset', {
dx: offsetX + 1,
dy: offsetY + 1,
result: 'offsetblur'
});
var svg_fe3 = polyfilter._create_svg_element('feFlood', {
'flood-color': color
});
var svg_fe4 = polyfilter._create_svg_element('feComposite', {
in2: 'offsetblur',
operator: 'in'
});
var svg_fe5 = polyfilter._create_svg_element('feMerge', {});
var svg_fe5sub = polyfilter._create_svg_element('feMergeNode', {});
svg_fe5.appendChild(svg_fe5sub);
var svg_fe5sub = polyfilter._create_svg_element('feMergeNode', {
'in': 'SourceGraphic'
});
svg_fe5.appendChild(svg_fe5sub);
properties['filtersSVG'] = [svg_fe1, svg_fe2, svg_fe3, svg_fe4, svg_fe5];
} else {
// 如果是 IE 浏览器,设置 IE 特定的发光和阴影效果滤镜
properties['filtersIE'] = ['progid:DXImageTransform.Microsoft.Glow(color=' + color + ',strength=0)', 'progid:DXImageTransform.Microsoft.Shadow(color=' + color + ',strength=0)'];
// 引入投影效果的 HTC 文件
properties['behaviorsIE'] = ['url("' + polyfilter.scriptpath + 'htc/drop-shadow.htc")'];
}
// 返回包含不同浏览器滤镜属性的对象
return properties;
}
}
// 初始化部分,根据不同的情况调用处理样式表的方法
// 如果页面中引入了 jQuery 库
if (window.jQuery) {
// 当文档准备好时,调用 polyfilter 对象的 process_stylesheets 方法处理样式表
window.jQuery(document).ready(function (e) {
polyfilter.process_stylesheets();
});
}
// 如果页面中存在 contentLoaded 函数
else if (window.contentLoaded) {
// 当页面内容加载完成后,调用 polyfilter 对象的 process_stylesheets 方法处理样式表
contentLoaded(window, function () {
polyfilter.process_stylesheets();
});
}
// 其他情况
else {
// 如果浏览器支持 addEventListener 方法W3C 标准)
if (window.addEventListener) {
// 当 DOM 内容加载完成后,调用 polyfilter 对象的 process_stylesheets 方法处理样式表
document.addEventListener('DOMContentLoaded', function () {
polyfilter.process_stylesheets();
}, false);
}
// 如果浏览器支持 attachEvent 方法Microsoft 标准)
else if (window.attachEvent) {
// 当页面加载完成后,调用 polyfilter 对象的 process_stylesheets 方法处理样式表
window.attachEvent('onload', function () {
polyfilter.process_stylesheets();
});
}
}
// 调用 polyfilter 对象的 init 方法,安装样式的设置器和获取器
polyfilter.init();
ntentLoaded(window, function () {
polyfilter.process_stylesheets();
});
}
// 其他情况
else {
// 如果浏览器支持 addEventListener 方法W3C 标准)
if (window.addEventListener) {
// 当 DOM 内容加载完成后,调用 polyfilter 对象的 process_stylesheets 方法处理样式表
document.addEventListener('DOMContentLoaded', function () {
polyfilter.process_stylesheets();
}, false);
}
// 如果浏览器支持 attachEvent 方法Microsoft 标准)
else if (window.attachEvent) {
// 当页面加载完成后,调用 polyfilter 对象的 process_stylesheets 方法处理样式表
window.attachEvent('onload', function () {
polyfilter.process_stylesheets();
});
}
}
// 调用 polyfilter 对象的 init 方法,安装样式的设置器和获取器
polyfilter.init();