# layui-admin **Repository Path**: GIT-YJ/layui-admin ## Basic Information - **Project Name**: layui-admin - **Description**: No description available - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 4 - **Created**: 2021-12-29 - **Last Updated**: 2021-12-29 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README ## 一、数据表 ```mysql -- 创建数据库 create database productdb ; -- 进入数据库 use productdb ; -- 创建数据表 create table product ( product_id varchar(50) primary key not null , --产品ID product_name varchar(50) not null , --产品名称 product_price float not null , --产品库存 product_count int not null default 1 , --产品类型 product_image varchar(100) null default 'default.jpg' , --产品图片 product_desc varchar(200) default '暂无描述' --描述 ) -- 添加测试数据 -- insert into 表名(字段1,字段2,...) values (值1,值2,...) -- 查询数据 select * from product ; -- 删除数据 -- delete from 表名 where 条件 -- 修改数据 --update 表名 set 字段1=值,字段2=值,... where 条件 -- 查询数据 --select 字段1,字段2,.. from 表名 where 条件 -- 4、初始化数据 insert into product values ('101','米家智能插座WIFI版',39.5,1,'101.png','101..') ; insert into product values ('102','德尔玛多功能蒸汽清洁机',59.5,2,'102.png','102..') ; insert into product values ('103','90分框体旅行箱',200,3,'103.png','103..') ; insert into product values ('104','米家两六冰箱160L',1999.5,3,'104.png','104..') ; insert into product values ('105','流浪地球CN171运兵车',199.5,1,'105.png','105..') ; create table users ( id int identity(1,1) primary key , username varchar(50) unique not null , password char(32) not null , name varchar(50) , level varchar(10) default '会员' , image varchar(50) default 'default.jpg', sex char(2) default '男' , phone varchar(20) , address varchar(100) , zipcode varchar(10) , email varchar(50) , register_time datetime default getdate(), status int default 1 ) insert into users(username,name,password,phone,level) values ('zing','张三','123456','13417747371','会员') insert into users(username,name,password,phone,level) values ('lisi001','李四','123456','13417747371','会员') insert into users(username,name,password,phone,level) values ('xiaosi002','王五','123456','13417747371','会员') insert into users(username,name,password,phone,level) values ('yan003','赵六','123456','13417747371','会员') insert into users(username,name,password,phone,level) values ('wang004','田七','123456','13417747371','管理员') insert into users(username,name,password,phone,level) values ('liang005','王八','123456','13417747371','管理员') insert into users(username,name,password,phone,level) values ('admin','孙九','123456','13417747371','超级管理员') ``` ## 二、登录 ### 1、登录 ```js layui.use(['layer', 'jquery'], function () { var layer = layui.layer; var $ = layui.jquery; // 绑定登录按钮 $('#loginBtn').on('click', function () { login(); }); // 绑定密码框 $('#LAY-user-login-password').on('keydown', function (e) { if (e.keyCode == 13) { login(); } }) // 登陆 function login() { if ($("#LAY-user-login-username").val() == '' || $("#LAY-user-login-password").val() == '') { layer.msg("请输入用户名或密码"); return false; } var params = $("#loginForm").serialize(); $.ajax({ url: 'http://localhost:8080/layui/api/login', type: 'post', data: params, success: function (res) { if (res.code == 200) { // 把 token 令牌字符串,保存在本地浏览器中 // 便于下次请求时,读取token,并发送回给服务器 localStorage.setItem("token", res.data); // 跳转页面 location.href = "admin.html" return; } layer.msg(res.msg); } }); } }); ``` ### 2、后台主页 #### 1)页面结构 ```html
``` #### 2)选项卡功能实现 ```js layui.use(['element', 'jquery', 'layer'], function () { var element = layui.element; var layer = layui.layer; var $ = layui.jquery; // 菜单按钮 $('.subMenu').on('click', function () { var tabTitle = $(this).text(); var tabId = $(this).prop("id"); var pageUrl = $(this).children('a').data('url'); if ($('.layui-tab-title li[lay-id="' + tabId + '"]').length == 0) { element.tabAdd('demo', { title: tabTitle, content: '', id: tabId }); } element.tabChange('demo', tabId); }); }); ``` ## 三、用户管理 ### 1、查询表单 #### 1)页面结构 ```html
``` #### 2)JS实现——下拉列表框加载用户角色 ```js layui.use(['jquery', 'table', 'layer', 'form'], function () { var $ = layui.jquery; var table = layui.table; var layer = layui.layer; var form = layui.form; //加载用户角色 $.ajax({ url: 'http://localhost:8080/layui/api/level', type: 'get', beforeSend: function (xhr) { let token = localStorage.getItem("token"); // 使用请求头的方式传递 xhr.setRequestHeader("Authorization", "Bearer " + token) }, success: function (res) { if (res.code == 401) { location.href = "index.html"; return; } if (res.code == 200) { $.each(res.data, function (index, level) { $('#level').append(''); }); form.render('select'); } } }); }); ``` ### 2、数据查询 #### 1)页面结构 ```html
``` #### 2)数据查询——开启分页 ```js // 动态加载表格数据 table.render({ elem: '#table' , id: 'tableReload' , headers: { Authorization: "Bearer " + localStorage.getItem("token") } , url: 'http://localhost:8080/layui/api/user/list' , title: '用户列表' , cols: [[ { field: 'username', title: '帐号', width: '20%' } , { field: 'name', title: '姓名', width: '15%' } , { field: 'sex', title: '性别', width: '10%' } , { field: 'level', title: '角色', width: '8%' } , { field: 'registerTime', title: '注册时间', width: '10%', templet: function (res) { return layui.util.toDateString(res.registerTime, 'yyyy-MM-dd'); } } , { field: 'status', title: '状态', width: '5%', templet: function (res) { if (res.status == 1) { return '正常'; } else { return '禁用'; } } } , { fixed: 'right', title: '操作', toolbar: '#optionBar' } ]] , page: true , limit: 2 , parseData: function (res) { console.log("page>>>", res); return { "code": res.code == 200 ? 0 : res.code, //解析接口状态 "msg": res.msg, //解析提示文本 "count": res.count, //解析数据长度 "data": res.data //解析数据列表 }; } }); ``` ### 3、多条件查询 ```js // 查询并重新加载表格 var active = { reload: function () { //执行重载 table.reload('tableReload', { page: { curr: 1 // 重新从第 1 页开始 } , where: { username: $('#username').val(), level: $('#level').val() } }); } }; // 绑定查询按钮 $('#selectBtn').on('click', function () { var type = $(this).data('type'); active[type] ? active[type].call(this) : ''; }); ``` ### 4、删除数据 ```js //监听table行工具事件 table.on('tool(table)', function (obj) { let currentRow = obj.data; let layuiEvent = obj.event; if (layuiEvent === 'del') { layer.confirm('是否确认删除此条目?', function (index) { $.ajax({ url: 'http://localhost:8080/layui/api/user/del', type: 'POST', beforeSend: function (xhr) { xhr.setRequestHeader("Authorization", "Bearer " + localStorage.getItem("token")) }, data: { 'id': currentRow.id }, success: function (data) { if (data.code == 200) { // 删除当前行 // obj.del(); // 重载表格数据 table.reload('tableReload', { page: { curr: 1 } }); // 关闭确认框 layer.close(index); } else { layer.msg(data.msg); } } }); }); } }); ``` ### 5、修改数据 #### 1)页面结构 ```html ``` #### 2)JS实现 ```js // 接上 if (layuiEvent === 'show') { // 1.显示原数据 $('#update-username').val(currentRow.username); if (currentRow.sex == '男') { $("#update-sex1").prop("checked", true); } else { $("#update-sex2").prop("checked", true); } $('#update-phone').val(currentRow.phone); $('#update-address').val(currentRow.address); $('#update-zipcode').val(currentRow.zipCode); $('#update-level').find("option").each(function () { // 判断需要对那个选项进行回显 if (this.value == currentRow.level) { // 进行回显 $(this).prop("selected", "selected"); // 重新渲染 form.render('select'); } }); // 2.重新渲染表单 form.render(); // 3.打开修改表单 layer.open({ type: 1, title: '修改', area: ['50%', '30em'], resize: false, content: $("#updateContainer"), btn: ['确认', '取消'], btn1: function (index) { // 4.验证数据的合法性 if ($('#update-phone').val() == '') { layer.msg("手机不能为空"); $('#update-phone').focus(); return false; } // ... // 5.修改数据 $.ajax({ url: 'http://localhost:8080/layui/api/user/update', type: 'post', data: $('#updateForm').serialize(), dataType: 'json', beforeSend: function (xhr) { // 使用请求头的方式传递 xhr.setRequestHeader("Authorization", "Bearer " + localStorage.getItem("token")) }, success: function (data) { if (data.code == 200) { // 重载表格 table.reload('tableReload', { page: { curr: 1 } }); // 关闭层 layer.close(index); } else { layer.msg(data.msg); } } }); } ``` ## 四、实体对象 ```java public class Product { private String id ; private String name ; private double price ; private int count ; private String image ; private String desc ; ... } ``` ```java public class User { private int id ; private String username ; private String password ; private String name ; private String level ; private String image ; private String sex ; private String phone ; private String address ; private String zipCode ; private String email ; private Date registerTime ; private int status ; ... } ``` ## 五、工具类 ### 1、DBUtil ```java package org.zing.utils; /** * @author zqx * @date 2021-12-12 */ import java.sql.*; public class DBUtil { /** * 帐号 */ private static final String USERNAME = "root"; /** * 密码 */ private static final String PASSWORD = "root"; /** * 连接地址(服务器+数据库) */ private static final String URL = "jdbc:mysql://localhost:3306/productdb?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai"; /** * 驱动程序 : JDBC接口的实现 ,由数据库厂商提供 */ private static final String DRIVER = "com.mysql.cj.jdbc.Driver"; // 静态代码块 : 加载驱动程序 static { // 第二:加载驱动程序 try { Class.forName(DRIVER); } catch (ClassNotFoundException e) { System.out.println("加载驱动程序失败"); e.printStackTrace(); } } /** * 返回连接对象 - 建立连接数据库的桥梁 */ public static Connection getConnection() { // 第三:连接数据库,并返回连接对象 Connection conn = null; try { conn = DriverManager.getConnection(URL, USERNAME, PASSWORD); } catch (SQLException e) { System.out.println("获取连接对象失败"); e.printStackTrace(); } return conn; } /** * 关闭数据库对象,释放资源 * * @param rs * @param stmt * @param conn */ public static void close(Connection conn,Statement stmt, ResultSet rs) { if (rs != null) { try { rs.close(); } catch (SQLException e) { System.out.println("关闭结果集对象失败"); e.printStackTrace(); } } if (stmt != null) { try { stmt.close(); } catch (SQLException e) { System.out.println("关闭语句对象失败"); e.printStackTrace(); } } if (conn != null) { try { conn.close(); } catch (SQLException e) { System.out.println("关闭连接失败"); e.printStackTrace(); } } } /** * 定义主方法,测试是否连接成功 * @param args */ public static void main(String[] args) { System.out.println(DBUtil.getConnection()); } } ``` ### 2、GsonUtil ```java package org.zing.utils; /** * @author zqx * @date 2021-12-12 */ import com.google.gson.Gson; import com.google.gson.reflect.TypeToken; import java.util.Map; public class GsonUtil { /** * 把Json字符串,转换为Map集合 * * @param jsonData JSON字符串 * @return */ public static Map parseMap(String jsonData){ // 创建Gson对象 Gson gson = new Gson(); // 转换 Map map = gson.fromJson(jsonData, new TypeToken>() {}.getType()); // 返回结果 return map; } } ``` ### 3、JwtUtil ```java package org.zing.utils; /** * @author zqx * @date 2021-12-12 */ import cn.hutool.core.date.DateUtil; import io.jsonwebtoken.*; import java.util.Date; import java.util.Map; import java.util.UUID; public class JwtUtil { /** * 密钥,用于signature(签名)部分解密 -- 暗语(口令) -- 盐值 */ private static final String KEY = "112233abc" ; /** * 签发者 */ private static final String ISS = "edu.nf"; /** * 创建 Jwt 令牌字符串 * @param claims 签署内容 * @param ttl 设置过期时间,单位毫秒 - 1 * 24 * 60 * 60 * 1000 * @return */ public static String createToken(Map claims, long ttl) { JwtBuilder builder = Jwts.builder() // 获取签名秘钥,并采用HS256算法对JWT进行的签名 .signWith(SignatureAlgorithm.HS256,KEY) //jwt唯一标识 .setId(UUID.randomUUID().toString()) //设置需要签署的内容 .setClaims(claims) //设置发证人 .setIssuer(ISS) //主题 .setSubject("JWT_AUTH") //签名时间 .setIssuedAt(new Date()); // 过期时间 if(ttl >= 0){ builder.setExpiration(getExpDate(ttl)); } return builder.compact(); } /** * 创建token过期时间 * @param ttl * @return */ private static Date getExpDate(long ttl){ Date expDate = DateUtil.date(System.currentTimeMillis() + ttl); return expDate; } /** * 验证Token是否正确 */ public static void verify(String token){ try { Jwts.parser().setSigningKey(KEY).parseClaimsJws(token); } catch (SecurityException e) { throw new RuntimeException("Invalid JWT signature."); } catch (MalformedJwtException e) { throw new RuntimeException("Invalid JWT token."); } catch (ExpiredJwtException e) { throw new RuntimeException("JWT令牌字符串过期."); } catch (UnsupportedJwtException e) { throw new RuntimeException("Unsupported JWT."); } catch (IllegalArgumentException e) { throw new RuntimeException("JWT token compact of handler are invalid."); } } /** * 获取payload的内容 * @param token * @param name * @param type * @param * @return */ public static T getPayload(String token,String name,Class type) { return Jwts.parser() .setSigningKey(KEY) .parseClaimsJws(token) .getBody() .get(name, type); } } ``` ## 六、过滤器 ### 1、CrosFilter ```java package org.zing.filters; /** * @author zqx * @date 2021-12-12 */ import javax.servlet.*; import javax.servlet.annotation.WebFilter; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; @WebFilter("/api/*") public class CrosFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { } @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { // 1.向下转型 HttpServletRequest request = (HttpServletRequest) servletRequest; HttpServletResponse response = (HttpServletResponse) servletResponse; // 2.跨域处理 // 允许所有的域名 response.setHeader("Access-Control-Allow-Origin", "*"); // 允许请求所有的方法 response.setHeader("Access-Control-Allow-Methods", "*"); // 允许请求头信息字段(Authorization) response.setHeader("Access-Control-Allow-Headers", "Authorization,Origin,X-Requested-With,Content-Type,Accept," + "content-Type,origin,x-requested-with,content-type,accept,authorization,token,id,X-Custom-Header,X-Cookie,Connection,User-Agent,Cookie,*"); // 3.放行 filterChain.doFilter(request,response); } @Override public void destroy() { } } ``` ### 2、EncodingFilter ```java package org.zing.filters; /** * @author zqx * @date 2021-12-12 */ import javax.servlet.*; import javax.servlet.annotation.WebFilter; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; @WebFilter("/*") public class EncodingFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { } @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { // 1.向下转型 HttpServletRequest request = (HttpServletRequest) servletRequest; HttpServletResponse response = (HttpServletResponse) servletResponse; // 2.设置编码 request.setCharacterEncoding("UTF-8"); response.setCharacterEncoding("UTF-8"); // 3.放行 filterChain.doFilter(request,response); } @Override public void destroy() { } } ``` ### 3、ZAuthFilter ```java package org.zing.filters; /** * @author zqx * @date 2021-12-12 */ import com.alibaba.fastjson.JSON; import org.zing.utils.JwtUtil; import org.zing.vo.ResultVO; import javax.servlet.*; import javax.servlet.annotation.WebFilter; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.PrintWriter; @WebFilter("/api/*") public class ZAuthFilter implements Filter { private static final String LOGIN = "login" ; private static final String OPTIONS = "options" ; private static final String TOKEN_HEADER = "Authorization" ; private static final String TOKEN_PREFIX = "Bearer "; @Override public void init(FilterConfig filterConfig) throws ServletException { } @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { // 1.向下转型 HttpServletRequest request = (HttpServletRequest) servletRequest; HttpServletResponse response = (HttpServletResponse) servletResponse; // 2.获取请求方式 -- 对预检请求进行放行 String method = request.getMethod(); if(OPTIONS.equalsIgnoreCase(method)) { filterChain.doFilter(request, response); return ; } // 3.获取客户端请求的URI地址,对于实现登录的Servlet 进行放行 String requestURI = request.getRequestURI(); if(requestURI.endsWith(LOGIN)) { filterChain.doFilter(request, response); return; } // 4.获取客户端发送过来 JWT 令牌字符串 String token = request.getHeader(TOKEN_HEADER); String jwt = token.replace(TOKEN_PREFIX, ""); if(jwt==null || "".equals(jwt)) { responseView(response,"请先登录") ; return ; } // 5.验证token try { JwtUtil.verify(jwt); String username = JwtUtil.getPayload(jwt, "username", String.class); if(username==null || "".equals(username)) { responseView(response,"解析token失败") ; return; } // 验证成功,放行 filterChain.doFilter(request, response); } catch (Exception e) { responseView(response,"会话已失效") ; } } private void responseView(HttpServletResponse response, String message) throws IOException { ResultVO vo = new ResultVO(); vo.setCode(401); vo.setMsg(message); response.setContentType("application/json;charset=utf-8"); String json = JSON.toJSONString(vo); PrintWriter out = response.getWriter(); out.print(json) ; out.flush(); out.close(); } @Override public void destroy() { } } ``` ## 七、DAO ### 1、用户 ```java package org.zing.dao.impl; import org.zing.dao.UserDao; import org.zing.entity.User; import org.zing.utils.DBUtil; import org.zing.vo.PageVO; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; import java.util.Date; import java.util.List; /** * @author zqx * @date 2021-12-12 */ public class UserDaoImpl implements UserDao { @Override public User selectUser(String username) { User user = null ; //第一:定义操作数据库的SQL语句 String sql = "SELECT ID,USERNAME,PASSWORD,NAME,LEVEL,IMAGE,SEX,PHONE,ADDRESS,ZIPCODE,EMAIL,REGISTER_TIME,STATUS FROM USERS WHERE USERNAME=?" ; //第二:获取连接对象 Connection conn = DBUtil.getConnection(); //第三:使用连接对象,获取语句对象(PreparedStatement),并预编译SQL语句 PreparedStatement pstmt = null ; ResultSet rst = null ; try { pstmt= conn.prepareStatement(sql); //第四:设置数据 //语法:语句对象.setXxx(问号占位符索引,数据) ; pstmt.setString(1,username); //第五:执行SQL语句,并接收返回结果 //语句对象.executeUpdate() -> 增,删,改 -> 返回的是受影响的记录数 //语句对象.executeQuery() -> 查 -> 返回的是结果集(ResultSet) rst = pstmt.executeQuery() ; //第六:对结果进行处理 //遍历结构集各行各列的数据,封装到相关的实体对象或集合 //判断有没有数据:结果集对象.next() //获取结果集的数据: //结果集对象.getXxx(查询数据的索引) ; //结果集对象.getXxx(查询数据的字段名称) ; while(rst.next()) { // 1.读取数据 int id = rst.getInt(1) ; String password = rst.getString(3) ; String name = rst.getString(4) ; String level = rst.getString(5) ; String image = rst.getString(6) ; String sex = rst.getString(7) ; String phone = rst.getString(8) ; String address = rst.getString(9) ; String zipCode = rst.getString(10) ; String email = rst.getString(11) ; Date regTime = rst.getTimestamp(12) ; int status = rst.getInt(13) ; // 2.创建实体对象 user = new User() ; //3.封装数据 user.setId(id); user.setUsername(username); user.setPassword(password); user.setName(name); user.setLevel(level); user.setImage(image); user.setSex(sex); user.setPhone(phone); user.setAddress(address); user.setZipCode(zipCode); user.setEmail(email); user.setRegisterTime(regTime); user.setStatus(status); } } catch (SQLException e) { e.printStackTrace(); } finally { // 第七:关闭对象 DBUtil.close(conn,pstmt,null); } return user; } @Override public List queryUser(User user) { List list = new ArrayList() ; // 第一:定义操作数据库的SQL语句,动态添加条件 StringBuilder sql =new StringBuilder("SELECT ID,USERNAME,PASSWORD,NAME,LEVEL,IMAGE,SEX,PHONE,ADDRESS,ZIPCODE,EMAIL,REGISTER_TIME,STATUS FROM USERS WHERE 1=1"); // 1.定义集合,用于存储SQL语句的参数值 List params = new ArrayList(); // 2.拼接SQL语句 if(user!=null && user.getUsername()!=null) { sql.append(" AND USERNAME LIKE ?") ; params.add("%" + user.getUsername() + "%") ; } if(user!=null && user.getLevel()!=null) { sql.append(" AND `LEVEL` = ?") ; params.add(user.getLevel()) ; } if(user!=null && user.getPhone()!=null) { sql.append(" AND PHONE = ?") ; params.add(user.getPhone()) ; } //第二:获取连接对象 Connection conn = DBUtil.getConnection(); //第三:使用连接对象,获取语句对象(PreparedStatement),并预编译SQL语句 PreparedStatement pstmt = null ; ResultSet rst = null ; try { pstmt= conn.prepareStatement(sql.toString()); //第四:设置数据 //语法:语句对象.setXxx(问号占位符索引,数据) ; //遍历集合,填充参数值 for(int i=0;i 增,删,改 -> 返回的是受影响的记录数 //语句对象.executeQuery() -> 查 -> 返回的是结果集(ResultSet) rst = pstmt.executeQuery() ; //第六:对结果进行处理 //遍历结构集各行各列的数据,封装到相关的实体对象或集合 //判断有没有数据:结果集对象.next() //获取结果集的数据: //结果集对象.getXxx(查询数据的索引) ; //结果集对象.getXxx(查询数据的字段名称) ; while(rst.next()) { // 1.读取数据 int id = rst.getInt(1) ; String username = rst.getString(2) ; String password = rst.getString(3) ; String name = rst.getString(4) ; String level = rst.getString(5) ; String image = rst.getString(6) ; String sex = rst.getString(7) ; String phone = rst.getString(8) ; String address = rst.getString(9) ; String zipCode = rst.getString(10) ; String email = rst.getString(11) ; Date regTime = rst.getTimestamp(12) ; int status = rst.getInt(13) ; // 2.创建实体对象 User u = new User() ; //3.封装数据 u.setId(id); u.setUsername(username); u.setPassword(password); u.setName(name); u.setLevel(level); u.setImage(image); u.setSex(sex); u.setPhone(phone); u.setAddress(address); u.setZipCode(zipCode); u.setEmail(email); u.setRegisterTime(regTime); u.setStatus(status); list.add(u) ; } } catch (SQLException e) { e.printStackTrace(); } finally { // 第七:关闭对象 DBUtil.close(conn,pstmt,rst); } return list; } @Override public Long queryUserByPageCount(User user,Integer page,Integer limit) { Long count = 0L; // 第一:定义操作数据库的SQL语句,动态添加条件 StringBuilder sql =new StringBuilder("select count(id) from users where 1=1"); // 1.定义集合,用于存储SQL语句的参数值 List params = new ArrayList(); // 2.拼接SQL语句 if(user!=null && user.getUsername()!=null && !"".equals(user.getUsername().trim())) { sql.append(" and username like ?") ; params.add("%" + user.getUsername() + "%") ; } if(user!=null && user.getLevel()!=null && !"".equals(user.getLevel().trim())) { sql.append(" and `level` = ?") ; params.add(user.getLevel()) ; } // if(user!=null && user.getPhone()!=null) { // sql.append(" and phone = ?") ; // params.add(user.getPhone()) ; // } System.out.println(sql.toString()); //第二:获取连接对象 Connection conn = DBUtil.getConnection(); //第三:使用连接对象,获取语句对象(PreparedStatement),并预编译SQL语句 PreparedStatement pstmt = null ; ResultSet rst = null ; try { pstmt= conn.prepareStatement(sql.toString()); //第四:设置数据 //语法:语句对象.setXxx(问号占位符索引,数据) ; //遍历集合,填充参数值 for(int i=0;i 增,删,改 -> 返回的是受影响的记录数 //语句对象.executeQuery() -> 查 -> 返回的是结果集(ResultSet) rst = pstmt.executeQuery() ; //第六:对结果进行处理 //遍历结构集各行各列的数据,封装到相关的实体对象或集合 //判断有没有数据:结果集对象.next() //获取结果集的数据: //结果集对象.getXxx(查询数据的索引) ; //结果集对象.getXxx(查询数据的字段名称) ; if(rst.next()) { count = rst.getLong(1) ; } } catch (SQLException e) { e.printStackTrace(); } finally { // 第七:关闭对象 DBUtil.close(conn,pstmt,rst); } return count; } @Override public List queryUserByPage(User user,Integer page,Integer limit) { List list = new ArrayList() ; // 第一:定义操作数据库的SQL语句,动态添加条件 StringBuilder sql =new StringBuilder("select id,username,password,name,level,image,sex,phone,address,zipcode,email,register_time,status from users where 1=1"); // 1.定义集合,用于存储SQL语句的参数值 List params = new ArrayList(); // 2.拼接SQL语句 if(user!=null && user.getUsername()!=null && !"".equals(user.getUsername())) { sql.append(" and username like ?") ; params.add("%" + user.getUsername() + "%") ; } if(user!=null && user.getLevel()!=null && !"".equals(user.getLevel())) { sql.append(" and `level` = ?") ; params.add(user.getLevel()) ; } // if(user!=null && user.getPhone()!=null) { // sql.append(" and phone = ?") ; // params.add(user.getPhone()) ; // } sql.append(" limit ?,?") ; params.add((page-1)*limit) ; params.add(limit) ; //第二:获取连接对象 Connection conn = DBUtil.getConnection(); //第三:使用连接对象,获取语句对象(PreparedStatement),并预编译SQL语句 PreparedStatement pstmt = null ; ResultSet rst = null ; try { pstmt= conn.prepareStatement(sql.toString()); //第四:设置数据 //语法:语句对象.setXxx(问号占位符索引,数据) ; //遍历集合,填充参数值 for(int i=0;i 增,删,改 -> 返回的是受影响的记录数 //语句对象.executeQuery() -> 查 -> 返回的是结果集(ResultSet) rst = pstmt.executeQuery() ; //第六:对结果进行处理 //遍历结构集各行各列的数据,封装到相关的实体对象或集合 //判断有没有数据:结果集对象.next() //获取结果集的数据: //结果集对象.getXxx(查询数据的索引) ; //结果集对象.getXxx(查询数据的字段名称) ; while(rst.next()) { // 1.读取数据 int id = rst.getInt(1) ; String username = rst.getString(2) ; String password = rst.getString(3) ; String name = rst.getString(4) ; String level = rst.getString(5) ; String image = rst.getString(6) ; String sex = rst.getString(7) ; String phone = rst.getString(8) ; String address = rst.getString(9) ; String zipCode = rst.getString(10) ; String email = rst.getString(11) ; Date regTime = rst.getTimestamp(12) ; int status = rst.getInt(13) ; // 2.创建实体对象 User u = new User() ; //3.封装数据 u.setId(id); u.setUsername(username); u.setPassword(password); u.setName(name); u.setLevel(level); u.setImage(image); u.setSex(sex); u.setPhone(phone); u.setAddress(address); u.setZipCode(zipCode); u.setEmail(email); u.setRegisterTime(regTime); u.setStatus(status); list.add(u) ; } } catch (SQLException e) { e.printStackTrace(); } finally { // 第七:关闭对象 DBUtil.close(conn,pstmt,rst); } return list; } @Override public int delete(int id) { int r = 0 ; //第一:定义操作数据库的SQL语句 String sql = "delete from users where id=?" ; //第二:获取连接对象 Connection conn = DBUtil.getConnection(); //第三:使用连接对象,获取语句对象(PreparedStatement),并预编译SQL语句 PreparedStatement pstmt = null ; try { pstmt= conn.prepareStatement(sql); //第四:设置数据 //语法:语句对象.setXxx(问号占位符索引,数据) ; pstmt.setInt(1,id); //第五:执行SQL语句,并接收返回结果 //语句对象.executeUpdate() -> 增,删,改 -> 返回的是受影响的记录数 //语句对象.executeQuery() -> 查 -> 返回的是结果集(ResultSet) r = pstmt.executeUpdate() ; //第六:对结果进行处理 //遍历结构集各行各列的数据,封装到相关的实体对象或集合 //判断有没有数据:结果集对象.next() //获取结果集的数据: //结果集对象.getXxx(查询数据的索引) ; //结果集对象.getXxx(查询数据的字段名称) ; } catch (SQLException e) { e.printStackTrace(); } finally { // 第七:关闭对象 DBUtil.close(conn,pstmt,null); } // 返回受影响行数 return r; } @Override public int update(User user) { int r = 0 ; //第一:定义操作数据库的SQL语句 String sql = "update users set name=?,image=?,sex=?,phone=?,address=?,zipCode=? where id=?" ; //第二:获取连接对象 Connection conn = DBUtil.getConnection(); //第三:使用连接对象,获取语句对象(PreparedStatement),并预编译SQL语句 PreparedStatement pstmt = null ; try { pstmt= conn.prepareStatement(sql); //第四:设置数据 //语法:语句对象.setXxx(问号占位符索引,数据) ; pstmt.setString(1,user.getName()); pstmt.setString(2,user.getImage()); pstmt.setString(3,user.getSex()); pstmt.setString(4,user.getPhone()); pstmt.setString(5,user.getAddress()); pstmt.setString(6,user.getZipCode()); pstmt.setInt(7,user.getId()); //第五:执行SQL语句,并接收返回结果 //语句对象.executeUpdate() -> 增,删,改 -> 返回的是受影响的记录数 //语句对象.executeQuery() -> 查 -> 返回的是结果集(ResultSet) r = pstmt.executeUpdate() ; //第六:对结果进行处理 //遍历结构集各行各列的数据,封装到相关的实体对象或集合 //判断有没有数据:结果集对象.next() //获取结果集的数据: //结果集对象.getXxx(查询数据的索引) ; //结果集对象.getXxx(查询数据的字段名称) ; } catch (SQLException e) { e.printStackTrace(); } finally { // 第七:关闭对象 DBUtil.close(conn,pstmt,null); } // 返回受影响行数 return r; } } ``` ```mssql select * from ( select *, row_number() over(order by a.id asc) as rownumber from 表名 as a where 条件 ) as b where rownumber between (当前页数-1)*每页记录数+1 and 当前页数 * 每页记录数 select id,username,password,name,level,image,sex,phone,address,zipcode,email,register_time,status,rownumber from ( select id,username,password,name,level,image,sex,phone,address,zipcode,email,register_time,status, row_number() over(order by u.id asc) as rownumber from users as u ) as t where 1=1 and rownumber between 1 and 2 ``` ### 2、产品 ```java package org.zing.dao.impl; import org.zing.dao.ProductDao; import org.zing.entity.Product; import org.zing.utils.DBUtil; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; import java.util.List; /** * @author zqx * @date 2021-12-12 */ public class ProductDaoImpl implements ProductDao { @Override public List selectAll() { List list = new ArrayList<>() ; //第一:定义操作数据库的SQL语句 ctrl + shift + u String sql = "select product_id,product_name,product_price,product_count,product_image,product_desc from product" ; //第二:获取连接对象 Connection conn = DBUtil.getConnection(); //第三:使用连接对象,获取语句对象(PreparedStatement),并预编译SQL语句 PreparedStatement pstmt = null ; ResultSet rst = null ; try { pstmt= conn.prepareStatement(sql); //第四:设置数据 //语法:语句对象.setXxx(问号占位符索引,数据) ; //第五:执行SQL语句,并接收返回结果 //语句对象.executeUpdate() -> 增,删,改 -> 返回的是受影响的记录数 //语句对象.executeQuery() -> 查 -> 返回的是结果集(ResultSet) rst = pstmt.executeQuery() ; //第六:对结果进行处理 //遍历结构集各行各列的数据,封装到相关的实体对象或集合 //判断有没有数据:结果集对象.next() //获取结果集的数据: //结果集对象.getXxx(查询数据的索引) ; //结果集对象.getXxx(查询数据的字段名称) ; while(rst.next()) { // 1.读取数据 String productId = rst.getString(1) ; String productName = rst.getString(2) ; double productPrice = rst.getDouble(3) ; int productCount = rst.getInt(4) ; String productImage = rst.getString(5) ; String productDesc = rst.getString(6) ; // 2.创建实体对象 Product product = new Product() ; //3.封装数据 product.setId(productId); product.setName(productName); product.setPrice(productPrice); product.setCount(productCount); product.setImage(productImage); product.setDesc(productDesc); //4.把实体对象添加到List集合中 list.add(product) ; } } catch (SQLException e) { e.printStackTrace(); } finally { // 第七:关闭对象 DBUtil.close(conn,pstmt,rst); } // 返回集合对象 return list; } @Override public int insert(Product product) { int r = 0 ; // 第一:定义操作数据库的SQL语句 // ctrl + shift + u String sql = "insert into product(product_id,product_name,product_price,product_count,product_image,product_desc) values (?,?,?,?,?,?)" ; //第二:获取连接对象 Connection conn = DBUtil.getConnection(); //第三:使用连接对象,获取语句对象(PreparedStatement),并预编译SQL语句 PreparedStatement pstmt = null ; try { pstmt= conn.prepareStatement(sql); //第四:设置数据 //语法:语句对象.setXxx(问号占位符索引,数据) ; pstmt.setString(1,product.getId()); pstmt.setString(2,product.getName()); pstmt.setDouble(3,product.getPrice()); pstmt.setInt(4,product.getCount()); pstmt.setString(5,product.getImage()); pstmt.setString(6,product.getDesc()); //第五:执行SQL语句,并接收返回结果 //语句对象.executeUpdate() -> 增,删,改 -> 返回的是受影响的记录数 //语句对象.executeQuery() -> 查 -> 返回的是结果集(ResultSet) r = pstmt.executeUpdate() ; //第六:对结果进行处理 //遍历结构集各行各列的数据,封装到相关的实体对象或集合 //判断有没有数据:结果集对象.next() //获取结果集的数据: //结果集对象.getXxx(查询数据的索引) ; //结果集对象.getXxx(查询数据的字段名称) ; } catch (SQLException e) { e.printStackTrace(); } finally { // 第七:关闭对象 DBUtil.close(conn,pstmt,null); } // 返回受影响行数 return r; } @Override public int delete(String id) { int r = 0 ; //第一:定义操作数据库的SQL语句 String sql = "delete from product where product_id=?" ; //第二:获取连接对象 Connection conn = DBUtil.getConnection(); //第三:使用连接对象,获取语句对象(PreparedStatement),并预编译SQL语句 PreparedStatement pstmt = null ; try { pstmt= conn.prepareStatement(sql); //第四:设置数据 //语法:语句对象.setXxx(问号占位符索引,数据) ; pstmt.setString(1,id); //第五:执行SQL语句,并接收返回结果 //语句对象.executeUpdate() -> 增,删,改 -> 返回的是受影响的记录数 //语句对象.executeQuery() -> 查 -> 返回的是结果集(ResultSet) r = pstmt.executeUpdate() ; //第六:对结果进行处理 //遍历结构集各行各列的数据,封装到相关的实体对象或集合 //判断有没有数据:结果集对象.next() //获取结果集的数据: //结果集对象.getXxx(查询数据的索引) ; //结果集对象.getXxx(查询数据的字段名称) ; } catch (SQLException e) { e.printStackTrace(); } finally { // 第七:关闭对象 DBUtil.close(conn,pstmt,null); } // 返回受影响行数 return r; } @Override public int update(Product product) { int r = 0 ; //第一:定义操作数据库的SQL语句 String sql = "update product set product_name=?,product_price=?,product_count=?,product_desc=?,product_image=? where product_id=?" ; //第二:获取连接对象 Connection conn = DBUtil.getConnection(); //第三:使用连接对象,获取语句对象(PreparedStatement),并预编译SQL语句 PreparedStatement pstmt = null ; try { pstmt= conn.prepareStatement(sql); //第四:设置数据 //语法:语句对象.setXxx(问号占位符索引,数据) ; pstmt.setString(1,product.getName()); pstmt.setDouble(2,product.getPrice()); pstmt.setInt(3,product.getCount()); pstmt.setString(4,product.getDesc()); pstmt.setString(5,product.getImage()); pstmt.setString(6,product.getId()); //第五:执行SQL语句,并接收返回结果 //语句对象.executeUpdate() -> 增,删,改 -> 返回的是受影响的记录数 //语句对象.executeQuery() -> 查 -> 返回的是结果集(ResultSet) r = pstmt.executeUpdate() ; //第六:对结果进行处理 //遍历结构集各行各列的数据,封装到相关的实体对象或集合 //判断有没有数据:结果集对象.next() //获取结果集的数据: //结果集对象.getXxx(查询数据的索引) ; //结果集对象.getXxx(查询数据的字段名称) ; } catch (SQLException e) { e.printStackTrace(); } finally { // 第七:关闭对象 DBUtil.close(conn,pstmt,null); } // 返回受影响行数 return r; } @Override public int updateCount(String id, int count) { int r = 0 ; //第一:定义操作数据库的SQL语句 String sql = "update product set product_count=? where product_id=?" ; //第二:获取连接对象 Connection conn = DBUtil.getConnection(); //第三:使用连接对象,获取语句对象(PreparedStatement),并预编译SQL语句 PreparedStatement pstmt = null ; try { pstmt= conn.prepareStatement(sql); //第四:设置数据 //语法:语句对象.setXxx(问号占位符索引,数据) ; pstmt.setInt(1,count); pstmt.setString(2,id); //第五:执行SQL语句,并接收返回结果 //语句对象.executeUpdate() -> 增,删,改 -> 返回的是受影响的记录数 //语句对象.executeQuery() -> 查 -> 返回的是结果集(ResultSet) r = pstmt.executeUpdate() ; //第六:对结果进行处理 //遍历结构集各行各列的数据,封装到相关的实体对象或集合 //判断有没有数据:结果集对象.next() //获取结果集的数据: //结果集对象.getXxx(查询数据的索引) ; //结果集对象.getXxx(查询数据的字段名称) ; } catch (SQLException e) { e.printStackTrace(); } finally { // 第七:关闭对象 DBUtil.close(conn,pstmt,null); } // 返回受影响行数 return r; } } ``` ## 八、VO对象 ### 1、ResultVO ```java package org.zing.vo; /** * @author zqx * @date 2021-12-12 */ public class ResultVO { /** * 消息代码,默认200 */ private int code = 200; /** * 客户端消息 */ private String msg = ""; /** * 返回客户端具体的数据结果 */ private Object data; public int getCode() { return code; } public void setCode(int code) { this.code = code; } public String getMsg() { return msg; } public void setMsg(String msg) { this.msg = msg; } public Object getData() { return data; } public void setData(Object data) { this.data = data; } } ``` ### 2、PageVO ```java package org.zing.vo; /** * @author zqx * @date 2021-12-14 */ public class PageVO extends ResultVO{ /** * 总记录数 */ private Long count ; public Long getCount() { return count; } public void setCount(Long count) { this.count = count; } } ``` ## 九、Servlet ### 1、BaseServlet ```java package org.zing.servlet; import com.alibaba.fastjson.JSON; import org.zing.vo.PageVO; import org.zing.vo.ResultVO; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.PrintWriter; /** * @author zqx * @date 2021-12-12 */ public class BaseServlet extends HttpServlet { public ResultVO successJson(Object value) { ResultVO resultVo = new ResultVO(); resultVo.setData(value); return resultVo; } public ResultVO successJson(Object value, int code, String msg) { ResultVO resultVo = new ResultVO(); resultVo.setData(value); resultVo.setCode(code); resultVo.setMsg(msg); return resultVo; } public ResultVO successJson() { return new ResultVO(); } public ResultVO errorJson() { ResultVO resultVo = new ResultVO(); resultVo.setCode(500); resultVo.setMsg("系统繁忙,请稍后再试!"); return resultVo; } public ResultVO errorJson(int code, String msg) { ResultVO resultVo = new ResultVO(); resultVo.setCode(code); resultVo.setMsg(msg); return resultVo; } public ResultVO errorJson(String msg) { ResultVO resultVo = new ResultVO(); resultVo.setCode(500); resultVo.setMsg(msg); return resultVo; } /** * 响应 JSON 字符串 到客户端浏览中 * @param resp * @param resultVO 响应数据 * @throws IOException */ public void print(HttpServletResponse resp, ResultVO resultVO) throws IOException { resp.setCharacterEncoding("UTF-8"); resp.setContentType("application/json;charset=UTF-8"); PrintWriter out = resp.getWriter(); out.print(JSON.toJSONString(resultVO)); out.flush(); out.close(); } protected PageVO pageVO(Long count, Object data){ PageVO vo = new PageVO(); vo.setCode(0); vo.setCount(count); vo.setData(data); return vo; } } ``` ### 2、登录 ```java package org.zing.servlet; import org.zing.dao.UserDao; import org.zing.dao.impl.UserDaoImpl; import org.zing.entity.User; import org.zing.utils.JwtUtil; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.HashMap; import java.util.Map; /** * @author zqx * @date 2021-12-12 */ @WebServlet("/api/login") public class LoginServlet extends BaseServlet { @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // 第一:获取页面发送过来的商品信息(合法性验证[略]) String username = req.getParameter("username") ; String password = req.getParameter("password") ; // 第二:创建实体对象,并封装数据 // 第三:创建DAO对象,实现数据添加操作 UserDao userDao = new UserDaoImpl() ; User user = userDao.selectUser(username); // 第四:响应结果 if(user!=null && password!=null && password.equals(user.getPassword())) { // 定义一个Map对象,用于封装相关的令牌 Map map = new HashMap<>(); // 封装数据 -- 客户端浏览器只保留帐号名(用户ID)即可 map.put("username", username); // 创建Token,设置有效时间为5分钟 String token = JwtUtil.createToken(map, 5 * 60 * 1000); // 响应客户端 print(resp, successJson(token)); return; } print(resp, errorJson(401, "错误的帐号或密码")); } } ``` ### 2、获取角色 ```java package org.zing.servlet; import org.zing.dao.UserDao; import org.zing.dao.impl.UserDaoImpl; import org.zing.entity.User; import org.zing.utils.JwtUtil; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.ArrayList; import java.util.List; /** * @author zqx * @date 2021-12-12 */ @WebServlet("/api/level") public class LevelServlet extends BaseServlet { private static final String TOKEN_HEADER = "Authorization"; private static final String TOKEN_PREFIX = "Bearer "; @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { List list = new ArrayList<>() ; list.add("会员"); list.add("管理员"); list.add("超级管理员"); // 响应客户端 print(resp, successJson(list)); } } ``` ### 3、获取登录用户信息 ```java package org.zing.servlet; import org.zing.dao.UserDao; import org.zing.dao.impl.UserDaoImpl; import org.zing.entity.User; import org.zing.utils.JwtUtil; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.HashMap; import java.util.Map; /** * @author zqx * @date 2021-12-12 */ @WebServlet("/api/admin") public class AdminServlet extends BaseServlet { private static final String TOKEN_HEADER = "Authorization"; private static final String TOKEN_PREFIX = "Bearer "; @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // 获取客户端发送过来的token String token = req.getHeader(TOKEN_HEADER); String jwt = token.replace(TOKEN_PREFIX, ""); // 获取payload信息 String username = JwtUtil.getPayload(jwt, "username", String.class); // 查询当前登录用户信息 UserDao userDao = new UserDaoImpl(); User user = userDao.selectUser(username); // 响应客户端 print(resp, successJson(user)); } } ``` ### 4、用户列表 ```java package org.zing.servlet; import org.zing.dao.UserDao; import org.zing.dao.impl.UserDaoImpl; import org.zing.entity.User; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.List; /** * @author zqx * @date 2021-12-12 */ @WebServlet("/api/user/list") public class UserListServlet extends BaseServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { Integer page = Integer.parseInt(req.getParameter("page")); Integer limit = Integer.parseInt(req.getParameter("limit")); String username = req.getParameter("username"); String level = req.getParameter("level"); User user = new User() ; user.setUsername(username); user.setLevel(level); UserDao userDao = new UserDaoImpl(); List list = userDao.queryUserByPage(user,page,limit); Long count = userDao.queryUserByPageCount(user, page, limit); print(resp, pageVO(count,list)); } } ``` ## 十一、基本资料 >修改头像和密码 ### 1、页面结构 ```html Insert title here

修改头像

个人信息

``` ### 2、DAO接口 ```java /** * 修改头像 * @param username * @param head * @return */ int updateHead(String username,String head); /** * 修改密码 * @param username * @param pass * @return */ int updatePwd(String username,String pass); ``` ### 3、DAO接口实现 ```java @Override public int updateHead(String username, String head) { int r = 0 ; //第一:定义操作数据库的SQL语句 String sql = "update users set image=? where username=?" ; //第二:获取连接对象 Connection conn = DBUtil.getConnection(); //第三:使用连接对象,获取语句对象(PreparedStatement),并预编译SQL语句 PreparedStatement pstmt = null ; try { pstmt= conn.prepareStatement(sql); //第四:设置数据 //语法:语句对象.setXxx(问号占位符索引,数据) ; pstmt.setString(1,head); pstmt.setString(2,username); //第五:执行SQL语句,并接收返回结果 //语句对象.executeUpdate() -> 增,删,改 -> 返回的是受影响的记录数 //语句对象.executeQuery() -> 查 -> 返回的是结果集(ResultSet) r = pstmt.executeUpdate() ; //第六:对结果进行处理 //遍历结构集各行各列的数据,封装到相关的实体对象或集合 //判断有没有数据:结果集对象.next() //获取结果集的数据: //结果集对象.getXxx(查询数据的索引) ; //结果集对象.getXxx(查询数据的字段名称) ; } catch (SQLException e) { e.printStackTrace(); } finally { // 第七:关闭对象 DBUtil.close(conn,pstmt,null); } // 返回受影响行数 return r; } @Override public int updatePwd(String username, String pass) { int r = 0 ; //第一:定义操作数据库的SQL语句 String sql = "update users set password=? where username=?" ; //第二:获取连接对象 Connection conn = DBUtil.getConnection(); //第三:使用连接对象,获取语句对象(PreparedStatement),并预编译SQL语句 PreparedStatement pstmt = null ; try { pstmt= conn.prepareStatement(sql); //第四:设置数据 //语法:语句对象.setXxx(问号占位符索引,数据) ; pstmt.setString(1,pass); pstmt.setString(2,username); //第五:执行SQL语句,并接收返回结果 //语句对象.executeUpdate() -> 增,删,改 -> 返回的是受影响的记录数 //语句对象.executeQuery() -> 查 -> 返回的是结果集(ResultSet) r = pstmt.executeUpdate() ; //第六:对结果进行处理 //遍历结构集各行各列的数据,封装到相关的实体对象或集合 //判断有没有数据:结果集对象.next() //获取结果集的数据: //结果集对象.getXxx(查询数据的索引) ; //结果集对象.getXxx(查询数据的字段名称) ; } catch (SQLException e) { e.printStackTrace(); } finally { // 第七:关闭对象 DBUtil.close(conn,pstmt,null); } // 返回受影响行数 return r; } ``` ### 4、上传工具类 ```java package org.zing.utils; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.UUID; import javax.servlet.http.Part; /** * 文件上传 * @author zqx * @date 2021-12-12 */ public class FileUploadUtil { /** * 1.设置文件上传大小 */ private long maxSize ; /** * 2.设置允许上传文件的类型 */ private List allowFileExt = new ArrayList(); /** * 设置默认容量和默认允许上传的文件 */ public FileUploadUtil() { // 默认允许上传1MB的文件 this.maxSize = 1 * 1024 * 1024; this.setAllowFileExt("doc,jpg,png"); } public long getMaxSize() { return maxSize; } public void setMaxSize(long maxSize) { this.maxSize = maxSize; } public List getAllowFileExt() { return allowFileExt; } public void setAllowFileExt(String ext) { if(ext!=null && !"".equals(ext)) { this.allowFileExt.clear(); } String[] str = ext.split(","); for (String s : str) { this.allowFileExt.add(s); } } /** * 1.获取上传文件名 * @param header * @return */ private String getFilename(String header) { String filename = ""; int beginIndex = header.indexOf("filename=\"") + 10; int endIndex = header.lastIndexOf("\""); filename = header.substring(beginIndex, endIndex); return filename; } /** * 2.获取上传文件扩展名 * @param filename * @return */ private String getFileExt(String filename) { String value = ""; if (filename == null || filename.lastIndexOf('.') != -1 && filename.lastIndexOf('.') == filename.length() - 1) { return ""; } int beginIndex = filename.lastIndexOf('.') + 1; int endIndex = filename.length(); value = filename.substring(beginIndex, endIndex); return value; } /** * 3.上传文件,文件名为UUID * @param part * @param path * @return */ public String upload(Part part, String path) { String header = part.getHeader("Content-Disposition"); String filename = this.getFilename(header); String ext = this.getFileExt(filename); String newFilename = UUIDUtil.createUUID() + "." + ext ; return upload(part,path,newFilename); } /** * 上传文件 * @param part * @param path * @param flag flag为true,则文件为名UUID,flag为false,则文件名为原文件名 * @return */ public String upload(Part part,String path, boolean flag) { String header = part.getHeader("Content-Disposition"); String filename = this.getFilename(header); String ext = this.getFileExt(filename); String newFilename = UUIDUtil.createUUID() + "." + ext ; if(!flag) { return upload(part,path,newFilename); } return upload(part,path,filename) ; } /** * 上传文件,文件名由用户指定 * @param part * @param path * @param file * @return */ public String upload(Part part, String path,String file) { // 判断是否允许上传文件容量大小 if (part.getSize() > this.getMaxSize()) { throw new RuntimeException("上传文件容量溢出,只能上传"+this.getMaxSize()/1024*1024+"字节文件") ; } // 获取文件后缀 String ext = this.getFileExt(file); // 判断是否允许上传文件类型 if (!this.getAllowFileExt().contains(ext)) { throw new RuntimeException("上传失败,只能上传文件的类型有:"+this.getAllowFileExt().toString()) ; } // 获取上传文件路径 String uploadFile = path + File.separator + file; try { part.write(uploadFile); } catch (IOException e) { e.printStackTrace(); return "上传失败" ; } return file ; } } ``` ### 5、UUID生成工具类 ```java package org.zing.utils; import java.util.UUID; /** * @author zqx * @date 2021-12-12 */ public class UUIDUtil { public static String createUUID() { return UUID.randomUUID().toString().replace("-", "") ; } public static void main(String[] args) { //System.out.println(UUID.randomUUID().toString()); System.out.println(UUIDUtil.createUUID()); } } ``` ### 6、Servlet #### 1)上传头像 ```java package org.zing.servlet; import org.zing.dao.UserDao; import org.zing.dao.impl.UserDaoImpl; import org.zing.utils.FileUploadUtil; import org.zing.utils.JwtUtil; import javax.servlet.ServletException; import javax.servlet.annotation.MultipartConfig; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.Part; import java.io.File; import java.io.IOException; /** * @author zqx * @date 2021-12-12 */ @WebServlet("/api/user/upload") @MultipartConfig public class UserUploadHeadServlet extends BaseServlet { @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { Part part = req.getPart("attach") ; // 获取客户端发送过来的token String token = req.getHeader(JwtUtil.TOKEN_HEADER); String jwt = token.replace(JwtUtil.TOKEN_PREFIX, ""); // 获取payload信息 String username = JwtUtil.getPayload(jwt, "username", String.class); // 使用上传工具类来实现文件的上传 FileUploadUtil fuu = new FileUploadUtil() ; String path = req.getServletContext().getRealPath("/file/") ; // 设置允许上传文件的大小和类型 fuu.setMaxSize(10*1024*1024); fuu.setAllowFileExt("jpg,png,jpeg"); // 文件上传,并返回上传的文件名 String filename = fuu.upload(part, path) ; // 查找之前的头像 UserDao userDao = new UserDaoImpl(); String image = userDao.selectUser(username).getImage(); // 删除图片 File f = new File(path+ File.separator+image) ; if(f.exists()) { f.delete(); } // 修改图片 userDao.updateHead(username,filename); print(resp, successJson(filename,200,"修改头像成功")); } } ``` #### 2)上传图片 ```java package org.zing.servlet; import org.zing.utils.FileUploadUtil; import javax.servlet.ServletException; import javax.servlet.annotation.MultipartConfig; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.Part; import java.io.IOException; /** * @author zqx * @date 2021-12-12 */ @WebServlet("/api/file/upload") @MultipartConfig public class FileUploadServlet extends BaseServlet { @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { Part part = req.getPart("attach") ; // 使用上传工具类来实现文件的上传 FileUploadUtil fuu = new FileUploadUtil() ; String path = req.getServletContext().getRealPath("/file/") ; // 设置允许上传文件的大小和类型 fuu.setMaxSize(10*1024*1024); fuu.setAllowFileExt("jpg,png,jpeg"); // 文件上传,并返回上传的文件名 String filename = fuu.upload(part, path,true) ; print(resp, successJson(filename,200,"上传文件成功")); } } ``` 3)修改密码 ```java package org.zing.servlet; import org.zing.dao.UserDao; import org.zing.dao.impl.UserDaoImpl; import org.zing.entity.User; import org.zing.utils.JwtUtil; import javax.servlet.ServletException; import javax.servlet.annotation.MultipartConfig; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; /** * 修改密码 * @author zqx * @date 2021-12-12 */ @WebServlet("/api/user/changePwd") @MultipartConfig public class UserChangePassServlet extends BaseServlet { @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String password = req.getParameter("password"); String newPass = req.getParameter("newPass"); String confirmPass = req.getParameter("confirmPass"); if(newPass==null || !newPass.equals(confirmPass)) { throw new RuntimeException("两次密码不一致") ; } // 获取客户端发送过来的token String jwt = getJwt(req); // 获取payload信息 String username = JwtUtil.getPayload(jwt, "username", String.class); UserDao userDao = new UserDaoImpl(); User user = userDao.selectUser(username); if(user!=null && !user.getPassword().equals(password)) { throw new RuntimeException("密码错误") ; } userDao.updatePwd(username,newPass); print(resp, successJson(null,200,"修改密码成功")); } } ``` ## 十二、在线编辑器 TinyMCE是一款易用、且功能强大的所见即所得的富文本编辑器。同类程序有:UEditor、Kindeditor、Simditor、CKEditor、wangEditor、Suneditor、froala等等。 TinyMCE的优势: - 开源可商用,基于LGPL2.1 - 插件丰富,自带插件基本涵盖日常所需功能(示例看下面的Demo-2) - 接口丰富,可扩展性强,有能力可以无限拓展功能 - 界面好看,符合现代审美 - 提供经典、内联、沉浸无干扰三种模式(详见“介绍与入门”) - 对标准支持优秀(自v5开始) - 多语言支持,官网可下载几十种语言。 官网及文档:[www.tiny.cloud](https://gitee.com/link?target=https%3A%2F%2Fwww.tiny.cloud%2F) 中文文档:[http://tinymce.ax-z.cn/](https://gitee.com/link?target=http%3A%2F%2Ftinymce.ax-z.cn%2F) 官网下载:[www.tiny.cloud/get-tiny/self-hosted/](https://gitee.com/link?target=https%3A%2F%2Fwww.tiny.cloud%2Fget-tiny%2Fself-hosted%2F) Github:[github.com/tinymce](https://gitee.com/link?target=https%3A%2F%2Fgithub.com%2Ftinymce%2F) [【推荐】为vue开发者整合的tinymce组件](https://packy-tang.gitee.io/vue-tinymce/) **注:此中文文档自TinyMCE v5开始编写,对v4不做介绍。本站所用版本为v5** ### 1、快速开始 #### 1、引入TinyMCE脚本 ``` ``` #### 2、结构化页面并初始化TinyMCE ```

TinyMCE快速开始示例

``` #### 3、提交表单 当form提交时,TinyMCE会将内容塞进textarea,你可以通过正常的post方法获取到编辑器中的内容,行为与普通textarea完全一致。 注意:传统点击submit提交按钮会自动同步内容,但ajax之类的用事件提交会导致内容没有同步,暂时的解决办法是在初始化参数中setup参数里加入事件监听,让他自动同步。 ``` setup: function(editor){ editor.on('change',function(){ editor.save(); }); }, // 或 // $('#mytextarea').html(tinymce.get('mytextarea').getContent()); // $('#mytextarea').html(tinyMCE.editors['mytextarea'].getContent()); ``` ### 2、基本使用 #### 1、选择器配置 selector是TinyMCE的重要配置选项,使用CSS选择器语法来确定页面上哪个元素被TinyMCE替换成编辑器。 ``` tinymce.init({ selector: '#textarea' }); ``` #### 2、插件配置 TinyMCE 的功能是通过使用插件扩展的,这些插件是使用`plugins`选项启用的。 启用插件非常简单,只需将插件名作为参数,多个插件用空格分隔的字符串,也支持使用数组的方式。 以下示例启用列表 ( `lists`)、高级列表 ( `advlist`)、链接 ( `link`) 和图像 ( `image`) 插件。 ``` tinymce.init({ selector: 'textarea', // 字符串方式 plugins: 'advlist link image lists' // 数组方式 // plugins: ['advlist','link','image','lists'] }); ``` 有关插件及其选项的完整列表,请参阅:[向 TinyMCE 添加插件](https://gitee.com/link?target=https%3A%2F%2Fwww.tiny.cloud%2Fdocs%2Fplugins%2F)。 **案例:代码插件** ``` tinymce.init({ selector: 'textarea', // change this value according to your HTML plugins: 'codesample', toolbar: 'codesample' }); ``` codesample_global_prismjs选项 > **注意**:此功能仅适用于 TinyMCE 5.2 及更高版本。 此配置选项允许在突出显示代码示例块时使用全局 Prism.js 版本,而不是使用`codesample`插件内捆绑的 Prism.js 版本。这允许使用 Prism.js 的自定义版本,包括其他语言。 使用此选项时,请确保在站点上加载了 Prism.js 和任何语言附加组件,以及 TinyMCE 脚本: > 语法支持:[https://prismjs.com/](https://gitee.com/link?target=https%3A%2F%2Fprismjs.com%2F) ``` tinymce.init({ selector: 'textarea', plugins: 'codesample', toolbar: 'codesample', codesample_global_prismjs: true }); ``` 设置代码列表 ``` tinymce.init({ selector: 'textarea', plugins: 'codesample', codesample_languages: [ { text: 'HTML/XML', value: 'markup' }, { text: 'JavaScript', value: 'javascript' }, { text: 'CSS', value: 'css' }, { text: 'PHP', value: 'php' }, { text: 'Ruby', value: 'ruby' }, { text: 'Python', value: 'python' }, { text: 'Java', value: 'java' }, { text: 'C', value: 'c' }, { text: 'C#', value: 'csharp' }, { text: 'C++', value: 'cpp' } ], toolbar: 'codesample' }); ``` #### 3、工具栏配置 TinyMCE 提供了一组默认的工具栏控件,可以使用该`toolbar`选项覆盖这些控件。 ``` tinymce.init({ selector: 'textarea', // change this value according to the HTML toolbar: 'undo redo | styleselect | bold italic | alignleft aligncenter alignright alignjustify | outdent indent | bullist numlist' }); ``` 该`toolbar`选项定义工具栏按钮的存在、顺序和分组。 使用空格分隔的列表来指定 TinyMCE 的工具栏按钮。通过`|`在按钮名称之间使用“ ”管道字符创建工具栏组 除了通过`toolbar`定义常用工具栏外,还有以下两个工具栏的配置,分别为: - quickbars_insert_toolbar:[插入]快捷工具栏 - quickbars_selection_toolbar:[选择]快捷工具栏 在编辑器内容区,光标插入(回车)或选择时,在光标位置出现的快捷工具栏。 ``` tinymce.init({ selector: '#textarea1', inline: true, plugins: 'quickbars', quickbars_insert_toolbar: 'quickimage quicktable', quickbars_selection_toolbar: 'bold italic | quicklink h2 h3 blockquote', }); ``` #### 4、菜单栏配置 有两个菜单选项:`menubar`和`menu`。`menubar`用于定义*菜单*的存在和顺序,例如**File**、**Edit**和**View**。`menu`用于定义*菜单项*的存在和顺序,例如**新建文档**、全**选**和**源代码**。 ##### 1)定义菜单顺序 ``` // 定义菜单顺序(默认子菜单) let menubars = 'file edit view insert format table' tinymce.init({ selector: '#mytextarea', language: 'zh_CN', menubar: menubars, }); ``` ##### 2)自定义菜单和子菜单 ``` tinymce.init({ selector: '#mytextarea', language: 'zh_CN', // 第一:自定义菜单 menu: { // 2.1)定义菜单 edit: { // 2.2)定义菜单标题 title: '编辑', // 2.3)定义子菜单 items: 'undo redo selectall' }, file: { title: '文件', items: 'undo redo' } }, // 第二:引用自定义菜单(根据需要组合顺序) menubar: 'edit file', }); ``` ### 3、TinyMCE的三种模式 TinyMCE 具有三种主要的集成模式: - **经典的**完整编辑器模式 - **内联**编辑模式 - 无干扰模式 #### 1、内联模式 ##### 1)概述 此页英文文档:[https://www.tiny.cloud/docs/general-configuration-guide/use-tinymce-inline/](https://gitee.com/link?target=https%3A%2F%2Fwww.tiny.cloud%2Fdocs%2Fgeneral-configuration-guide%2Fuse-tinymce-inline%2F) > **注意**:移动设备不支持此选项。 内联编辑模式用于合并页面的编辑和阅读视图,以获得无缝的编辑体验和真正的所见即所得行为。 内联编辑模式不会用 iframe 替换所选元素,而是就地编辑元素的内容。内联编辑器设计为: - “隐藏”,直到选择可编辑的内容 - 使用了初始化的页面的 CSS 样式表 - 使内容区域周围的 UI 最小化,以最小化编辑器占用的区域。 **经典模式VS内联模式:** - 经典模式:基于表单,使用表单某字段填充内容,编辑器始终作为表单的一部分。 - 内联模式:将编辑视图与阅读视图合二为一,当其被点击后,元素才会被编辑器替换,而不是编辑器始终可见。 ##### 2)开启内联模式 1)内联模式仅适用于块元素内的内容(例如:`div`或`h1`) 2)要启用内联模式,请将`inline`选项设置为`true` ``` 内联模式

内联模式

你好,世界!点击这里看看!
``` #### 2、无干扰模式 ##### 1)概述 此页英文文档:[https://www.tiny.cloud/docs/general-configuration-guide/use-tinymce-distraction-free/](https://gitee.com/link?target=https%3A%2F%2Fwww.tiny.cloud%2Fdocs%2Fgeneral-configuration-guide%2Fuse-tinymce-distraction-free%2F) 该模式呈现的效果是没有菜单栏或工具栏的内联模式。大多数编辑器功能都可以使用:键盘快捷键、上下文菜单、上下文工具栏和`quickbars`插件提供的工具栏功能访问 ##### 2)开启无干扰模式 ```html 无干扰模式

无干扰模式

你好,世界!点击这里看看!
```