前言
上一篇博客讲到了会议的发布功能,之后就是送给自己的上级进行审批了,但是此时有个新的功能插件,就是排座功能,本章就带大家一起来看看,我们如何实现排座功能吧
1、效果图演示
2、功能分析
- 我的会议查询
- 获取到当前用户的id,查询当前用户创建未结束的所有会议
- 会议排座功能
- 当我们点击送审时,需要先进行排座
- 首先,排座中包含个查询的功能,也就是查询当前会议参与的人员,然后展示在我们的排座界面中
- 点击下载,我们就会使用到我们的插件,来将元素进行截取,然后转化成一个对象,再将对象转化成一个数据,再利用工具类进行解码然后将其画成一张图片
- 最后,将其存到数据库,如果成功,关闭子页面,然后,关闭弹窗,至此整个功能完成
3、插件分享--html2canvas
html2canvas
是一个JavaScript库,用于将HTML元素转换为Canvas图像。具体的使用看我的源码如何操作吧,上面有具体的注释
简要介绍如下:
- 功能:
html2canvas
库的主要功能是将指定的HTML元素(包括其子元素)渲染为一个Canvas图像对象。 - 兼容性:
html2canvas
支持大多数现代浏览器,包括Chrome、Firefox、Safari等。 - 使用方法:
a. 引入html2canvas库:在HTML文件中引入html2canvas库的JavaScript文件,可以通过直接下载并引用的方式,或者使用CDN来加载该库。
b. 选择需要转换的HTML元素:通过选择器、ID或类名等方式选择需要转换的HTML元素。
c. 调用html2canvas函数:使用html2canvas(element [, options])方法,传入需要转换的HTML元素作为参数,可选地传入一些配置选项来调整转换的行为。
d. 处理结果:html2canvas方法会返回一个Promise对象,你可以使用.then()方法来处理转换完成后的Canvas对象。
e. 使用Canvas对象:将转换后的Canvas对象用于你的需求,比如将其显示在页面上、保存为图片等。
4、配置选项:html2canvas
提供了一些配置选项,可以通过传入一个包含配置参数的对象来自定义转换的行为,例如设置转换的宽高、忽略某些元素等。
总的来说,html2canvas
是一个方便实用的库,可用于将HTML元素转换为Canvas图像,使得在前端将网页内容截图、生成图片等操作变得更加简单易用。
4、sql分析
- 当我们点击我的会议时,查询当前用户所有的会议,而当前用户,也就是自己发布会议的人,而这个人,在发布会议的时候就已经设定死了,就是登录的人,所以,只需根据会议的主持人字段来进行条件查询
SELECT a.id,a.title,a.content,a.canyuze,a.liexize,a.zhuchiren,b.`name`,a.location ,DATE_FORMAT(a.startTime,'%Y-%m-%d %H:%i:%s') as startTime ,DATE_FORMAT(a.endTime,'%Y-%m-%d %H:%i:%s') as endTime ,a.state ,(case a.state when 0 then '取消会议' when 1 then '新建' when 2 then '待审核' when 3 then '驳回' when 4 then '待开' when 5 then '进行中' when 6 then '开启投票' else '结束会' end ) as meetingState ,a.seatPic,a.remark,a.auditor,c.`name` as auditorName FROM t_oa_meeting_info a inner join t_oa_user b on a.zhuchiren = b.id left JOIN t_oa_user c on a.auditor = c.id where 1=1 and and zhuchiren = ?点击排座功能时,需要将人员数据带到子界面,也就是排座界面进行遍历,所以可以将会议的id,带到子界面,根据id查询出所有的参会人员
String sql="select * from t_oa_user where FIND_IN_SET(id," + "(select concat(canyuze,',',liexize,',',zhuchiren) " + "from t_oa_meeting_info where id=?))";这个SQL语句的意思是从表t_oa_user中选择所有行,其中id在另一个子查询中的结果集中找到。子查询是从表t_oa_meeting_info中选择一行,然后将该行的canyuze、liexize和zhuchiren列值连接成一个字符串。然后使用FIND_IN_SET函数来检查表t_oa_user的id是否在这个字符串中存在。该字符串使用逗号作为分隔符。这条SQL语句的目的是找到在t_oa_meeting_info表中特定id的记录中,指定的id是否存在于t_oa_user表中。
当用户点击确认排座,也就是下载了这个按钮的时候呢,就要修改会议的会议排座图片了
public int updatesetpic(MeetingInfo info) throws Exception { String sql="update t_oa_meeting_info set seatPic=? where id=?"; return super.executeUpdate(sql, info, new String[] {"seatPic","id"}); }
5、 源码展示
5.1、我的会议界面--Mymeeting.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@include file="/common/public.jsp"%> <!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <script type="text/javascript" src="static/js/meeting/myMeeting.js"></script> </head> <style> body{ margin:15px; } .layui-table-cell {height: inherit;} .layui-layer-page .layui-layer-content { overflow: visible !important;} </style> <body> <!-- 搜索栏 --> <div class="layui-form-item" style="margin:15px 0px;"> <div class="layui-inline"> <label class="layui-form-label">会议标题</label> <div class="layui-input-inline"> <input type="hidden" id="zhuchiren" value="${user.id }"/> <input type="text" id="title" autocomplete="off" class="layui-input"> </div> </div> <div class="layui-inline"> <button id="btn_search" type="button" class="layui-btn"><i class="layui-icon layui-icon-search"></i> 查询</button> </div> </div> <!-- 数据表格 --> <table id="tb" lay-filter="tb" class="layui-table" style="margin-top:-15px"></table> <!-- 对话框(送审) --> <div id="audit" style="display:none;"> <form style="margin:20px 15px;" class="layui-form layui-form-pane" lay-filter="audit"> <div class="layui-inline"> <label class="layui-form-label">送审人</label> <div class="layui-input-inline"> <input type="hidden" id="meetingId" value=""/> <select id="auditor" style="poistion:relative;z-index:1000"> <option value="">---请选择---</option> </select> </div> <div class="layui-input-inline"> <button id="btn_auditor" class="layui-btn">送审</button> </div> </div> </form> </div> <!-- 对话框(反馈详情) --> <div id="feedback" style="display:none;padding-left:15px; margin-bottom:60px;"> <fieldset class="layui-elem-field layui-field-title"> <legend>参会人员</legend> </fieldset> <blockquote class="layui-elem-quote" id="meeting_ok"></blockquote> <fieldset class="layui-elem-field layui-field-title"> <legend>缺席人员</legend> </fieldset> <blockquote class="layui-elem-quote" id="meeting_no"></blockquote> <fieldset class="layui-elem-field layui-field-title"> <legend>未读人员</legend> </fieldset> <blockquote class="layui-elem-quote" id="meeting_noread"></blockquote> </div> <script type="text/html" id="tbar"> {{# if(d.state==1 || d.state==3){ }} <a class="layui-btn layui-btn-xs" lay-event="seat">会议排座</a> <a class="layui-btn layui-btn-xs" lay-event="send">送审</a> <a class="layui-btn layui-btn-danger layui-btn-xs" lay-event="del">删除</a> {{# } }} {{# if(d.state!=1 && d.state!=2 && d.state!=3){ }} <a class="layui-btn layui-btn-xs" lay-event="back">反馈详情</a> {{# } }} </script> </body> </html>
5.2、我的会议js页面--Mymeeting.js
let layer,table,$,form; var row; layui.use(['layer','table','jquery','form'],function(){ layer=layui.layer, table=layui.table, form=layui.form, $=layui.jquery; initTable(); query(); //查询事件 $('#btn_search').click(function(){ query(); }); //初始化审批人 initFormSelects(); //送审 $('#btn_auditor').click(function(){ $.post($("#ctx").val()+'/info.action',{ 'methodName':'updateAuditorById', 'id':$('#meetingId').val(), 'auditor':$('#auditor').val() },function(rs){ if(rs.success){ //关闭对话框 layer.closeAll(); //刷新列表 query(); }else{ layer.msg(rs.msg,{icon:5},function(){}); } },'json'); return false; }); }); //1.初始化数据表格 function initTable(){ table.render({ //执行渲染 // url: 'info.action?methodName=myInfos', elem: '#tb', //指定原始表格元素选择器(推荐id选择器) height: 700, //自定义高度 loading: false, //是否显示加载条(默认 true) page:true, cols: [[ //设置表头 {field: 'id', title: '会议编号', width: 90}, {field: 'title', title: '会议标题', width: 120}, {field: 'location', title: '会议地点', width: 140}, {field: 'startTime', title: '开始时间', width: 120}, {field: 'endTime', title: '结束时间', width: 120}, {field: 'meetingState', title: '会议状态', width: 120}, {field: 'seatPic', title: '会议排座', width: 120, templet: function(d){ if(d.seatPic==null || d.seatPic=="") return "尚未排座"; else return "<img width='100px' height='60px' src='"+d.seatPic+"'/>"; } }, {field: 'auditorName', title: '审批人', width: 120}, {field: '', title: '操作', width: 200,toolbar:'#tbar'}, ]] }); } //2.点击查询 function query(){ table.reload('tb', { url: 'info.action', //请求地址 method: 'POST', //请求方式,GET或者POST loading: true, //是否显示加载条(默认 true) page: true, //是否分页 where: { //设定异步数据接口的额外参数,任意设 'methodName':'myInfos', 'zhuchiren':$('#zhuchiren').val(), 'title':$('#title').val(), }, request: { //自定义分页请求参数名 pageName: 'page', //页码的参数名称,默认:page limitName: 'rows' //每页数据量的参数名,默认:limit }, done: function (res, curr, count) { console.log(res); } }); //工具条事件 table.on('tool(tb)', function(obj){ //注:tool 是工具条事件名,test 是 table 原始容器的属性 lay-filter="对应的值" row = obj.data; //获得当前行数据 var layEvent = obj.event; //获得 lay-event 对应的值(也可以是表头的 event 参数对应的值) var tr = obj.tr; //获得当前行 tr 的 DOM 对象(如果有的话) console.log(row); if(layEvent === 'seat'){ //会议排座 open(row.id) } else if(layEvent === 'send'){ //送审 //会议送审功能 // 判断是进行排座了 if(row.seatPic==null || row.seatPic==""){ layer.msg('先请完成会议排座,再进行送审操作!',function(){}); return false; } //在打开送审页面之前,先请完成会议ID的赋值操作 $('#meetingId').val(row.id); openLayerAudit(); // 点击送审时,弹出层中会议送审人员查出来,也就是调用dao方法 // 打开会议送审页面层 } else if(layEvent==="back"){ //反馈详情 openLayerFeedBack(row.id); } else { layer.msg('删除'); } }); } //打开会议排座对话框 function open(id){ layer.open({ type: 2, //layer提供了5种层类型。可传入的值有:0(信息框,默认)1(页面层)2(iframe层)3(加载层)4(tips层) title: '会议排座', //对话框标题 area: ['460px', '340px'], //宽高 skin: 'layui-layer-rim', //样式类名 content: $("#ctx").val()+'/jsp/meeting/seatPic.jsp?id='+id, //弹出内容。可以传入普通的html内容,还可以指定DOM,更可以随着type的不同而不同 }); } //会议送审 function openLayerAudit(){ //每次打开都对送审人进行初始化默认值设置 $('#auditor').val(""); //必须重新渲染 form.render('select'); //弹出对话框 layer.open({ type: 1, //layer提供了5种层类型。可传入的值有:0(信息框,默认)1(页面层)2(iframe层)3(加载层)4(tips层) title:'会议送审', area: ['426px', '140px'], //宽高 skin: 'layui-layer-rim', //样式类名 content: $('#audit'), //弹出内容。可以传入普通的html内容,还可以指定DOM,更可以随着type的不同而不同 }); } //初始化审批人 function initFormSelects(){ $.getJSON($("#ctx").val()+'/addmeeting.action',{ 'methodName':'listtwo' },function(rs){ console.log(rs); let data=rs.data; $.each(data,function(i,e){ $('#auditor').append(new Option(e.name,e.value)); }); //重新渲染 form.render('select'); }); } //打开查看本会议的反馈详情 function openLayerFeedBack(id){ $.getJSON('feedBack.action',{ methodName:'queryMeetingBackByMeetingId', meetingId:id },function(data){ $('#meeting_ok').html(""); $('#meeting_no').html(""); $('#meeting_noread').html(""); if(data.success){ console.log(data.data); $.each(data.data,function(i,e){ if(e.result==1) $('#meeting_ok').html(e.names); else if(e.result==2) $('#meeting_no').html(e.names); else $('#meeting_noread').html(e.names); }); //弹出对话框 layer.open({ type: 1, //layer提供了5种层类型。可传入的值有:0(信息框,默认)1(页面层)2(iframe层)3(加载层)4(tips层) title:'反馈详情', area: ['626px', '320px'], //宽高 skin: 'layui-layer-rim', //样式类名 content: $('#feedback'), //弹出内容。可以传入普通的html内容,还可以指定DOM,更可以随着type的不同而不同 btn:['关闭'], yes:function(index,layero){ layer.closeAll(); } }); } }); }
中间是会议审批、会议反馈的一些功能,后面会讲,我们只需要关乎会议排座的方法就行
5.3、会议排座jsp页面--seatPic.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html > <html> <head> <base href="${pageContext.request.contextPath }/"/> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <link href="static/js/layui/css/layui.css" rel="stylesheet" type="text/css"/> <script type="text/javascript" src="static/js/jquery-3.3.1.min.js"></script> <script type="text/javascript" src="static/js/layui/layui.js"></script> <script type="text/javascript" src="static/js/plugins/html2canvas/html2canvas.js"></script> <title>会议座位安排</title> </head> <style type="text/css"> * { padding: 0; margin: 0; } body{ width: 100%; height: 100%; /* background: red; */ } .tips { /* position: absolute; */ background: pink; display: inline-block; height: 60px; /* width: 60px; */ line-height: 60px; text-align: center; margin: 5px; padding: 0 10px; } .add { position: fixed; right: 10px; top: 10px; display:inline; } #tu { width: 100%; height: 100%; /* background: lightblue; */ /*background: url('u=3318199716,2583790385&fm=26&gp=0.jpg');*/ } .layui-input{ height:30px; } </style> <body id="screen_body"> <div id="tu"></div> <!-- 下面不要使用layui的表单行内模式,会导致canvas的toDataURL()数据为 data:, --> <div class="add"> <div style="display:inline-block;"> <input id="dan_input" type="text" value="" class="layui-input"> </div> <div style="display:inline-block;"> <button onclick="return addDanMu()" class="layui-btn layui-btn-sm">添加座位</button><input id="jie_input" type="button" class="layui-btn layui-btn-sm" value='下载'> </div> </div> </body> <script type="text/javascript"> var $id = function(id) { return document.getElementById(id); } //会议排座拖拽 var dragF = { locked: false, lastObj: undefined, drag: function(obj) { $id(obj).onmousedown = function(e) { var e = e ? e : window.event; if (!window.event) { e.preventDefault(); } /* 阻止标注<a href='/site/js-5791-1.html' target='_blank'><u>浏览器</u></a>下拖动a,img的默认事件 */ dragF.locked = true; $id(obj).style.position = "absolute"; $id(obj).style.zIndex = "100"; if (dragF.lastObj && dragF.lastObj != $id(obj)) { /* 多元素拖动需要恢复上次元素状态 */ dragF.lastObj.style.zIndex = "1"; } dragF.lastObj = $id(obj); var tempX = $id(obj).offsetLeft; var tempY = $id(obj).offsetTop; dragF.x = e.clientX; dragF.y = e.clientY; document.onmousemove = function(e) { var e = e ? e : window.event; if (dragF.locked == false) return false; $id(obj).style.left = tempX + e.clientX - dragF.x + "px"; $id(obj).style.top = tempY + e.clientY - dragF.y + "px"; if (window.event) { e.returnValue = false; } /* 阻止ie下a,img的默认事件 */ } document.onmouseup = function() { dragF.locked = false; } } } } </script> <script type="text/javascript"> var layer; layui.use(['layer'],function(){ layer=layui.layer; //初始化会议排座:根据会议ID获取参会的所有人员的名字(主持人+参会人+列席人) initMeetingUsers(); //绘制会议排座图片 $("#jie_input").on("click", function(event) { //隐藏了会议排座div $('.add').hide(); event.preventDefault(); //使用html2canvas库将id为"screen_body"的元素及其内容转换为Canvas对象。 html2canvas(document.getElementById("screen_body")).then(function(canvas) { //将Canvas对象转换为数据URL,以便在上传或显示之前进行操作。 var dataUrl = canvas.toDataURL(); console.log(dataUrl); //创建一个名为param的空对象,用于存储上传的参数 var param = {}; // 将数据URL赋值给param对象的属性"seatPic",这是要上传的会议排座图片 param['seatPic'] = dataUrl; //动态获取传过来的参数id param['id'] = '${param.id}'; // 将"updatesetpic"赋值给param对象的属性"methodName",表示要调用的方法名。应根据具体情况进行修改。 param['methodName']='updatesetpic'; //输出param对象的内容到控制台,以便调试和查看参数。 console.log(param); //此处需要完成会议排座图片上传操作 $.post('${pageContext.request.contextPath }/info.action',param,function(rs){ if(rs.success){ //先得到当前iframe层的索引 var index = parent.layer.getFrameIndex(window.name); //再执行关闭 parent.layer.close(index); //调用父页面的刷新方法 parent.query(); }else{ //如果上传失败,则使用layer.msg()方法弹出错误消息,并显示错误图标。 layer.msg(rs.msg,{icon:5},function(){}); } },'json'); }); }); }); function initMeetingUsers(){ //http://localhost:8080/xxx/seatPic.jsp?id=12 -> ${param.id} //发送请求到useraction,然后查询当前会议的人员 $.getJSON('${pageContext.request.contextPath }/User.action',{ 'methodName':'meetingpeople', 'meetingId':'${param.id}' },function(rs){ console.log(rs); let data=rs.data; $.each(data,function(i,e){ //有几个人,就增加几个模块 $('#dan_input').val(e.name); addDanMu(); }); }); } //添加会议排座 function addDanMu() { var dan = document.getElementById("dan_input").value; if (dan == "") { alert("请输入弹幕~"); return false; } else { document.getElementById("dan_input").value = ""; //清空 弹幕输入框 // var br = document.createElement("BR"); // <br /> var node = document.createElement("DIV"); // <div> var tipsArr = document.getElementsByClassName('tips'); var i; // console.log(parseInt(tipsArr[tipsArr.length-1].id.substr(4))+1); if (tipsArr.length == 0) { i = 1 } else { i = parseInt(tipsArr[tipsArr.length - 1].id.substr(4)) + 1; } // var aNode = document.createElement("P"); // <p> node.setAttribute("class", "tips"); node.setAttribute("id", "tips" + i); node.setAttribute("onmouseover", "dragF.drag('tips" + i + "');"); var textnode = document.createTextNode(dan); // 创建个 文本节点, 将用户输入的弹幕,存入 创建的 元素节点 <p> 中 // aNode.appendChild(textnode); node.appendChild(textnode); // document.body.appendChild(br); // document.body.appendChild(node); document.getElementById("tu").appendChild(node); return true; } } </script> </html>
会议排座界面的js也在这个页面中了,其中大家可能不懂的就是插件的使用了,我在这里也暂时解释不清楚,但是跟着我的代码走就行了,上面有我的注释,基本还是看的懂的,插件的底层我也还没有研究明白,
还有一个就是
param['id'] = '${param.id}';
关于这一行也有小伙伴不懂,这个$ { param.id }其实就是动态获取父页面传过来的参数id啦,回头去看我的会议js
在这一行其实就把会议的id带过来了
5.4、 UserServlet--后端,查询会议对应的人员
package Servlet; import java.util.List; import java.util.Map; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import com.zking.framework.ActionSupport; import com.zking.framework.ModelDriver; import com.zking.util.PageBean; import com.zking.util.R; import com.zking.util.ResponseUtil; import Dao.Userdao; import Entity.User; public class UserServlet extends ActionSupport implements ModelDriver<User>{ private User user= new User(); private PageBean page= new PageBean(); private Userdao ud= new Userdao(); //查询对应会议的人员 public String meetingpeople(HttpServletRequest req, HttpServletResponse resp) { try { String m= req.getParameter("meetingId"); PageBean pageBean = new PageBean(); List<User> users = ud.queryUserByMeetingId(Integer.valueOf(m)); // layui 的code 返回一定要是 0,不能是200,否者返回不了数据 ResponseUtil.writeJson(resp, R.ok(0, "会议用户数据初始化成功", users)); } catch (Exception e) { e.printStackTrace(); try { ResponseUtil.writeJson(resp, R.error(0, "查询失败")); } catch (Exception e1) { e1.printStackTrace(); } } return null; } @Override public User getModel() { // TODO Auto-generated method stub return user; } }
5.5、查询相关会议人员--Dao层
// 根据会议ID查询相关用户 public List<User> queryUserByMeetingId(Integer integer) throws Exception{ String sql="select * from t_oa_user where FIND_IN_SET(id," + "(select concat(canyuze,',',liexize,',',zhuchiren) " + "from t_oa_meeting_info where id="+integer+"))"; return super.executeQuery(sql, User.class, null); }
5.6、 我的会议后端--查询会议、排座图片下载
package Servlet; import java.util.List; import java.util.Map; import java.util.UUID; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import com.zking.framework.ActionSupport; import com.zking.framework.ModelDriver; import com.zking.util.Base64ImageUtils; import com.zking.util.PageBean; import com.zking.util.PropertiesUtil; import com.zking.util.R; import com.zking.util.ResponseUtil; import Dao.Meetingdao; import Entity.MeetingInfo; public class MeetinginfoServlet extends ActionSupport implements ModelDriver<MeetingInfo>{ private MeetingInfo m= new MeetingInfo(); Meetingdao md= new Meetingdao(); // 我的会议 public String myInfos(HttpServletRequest req, HttpServletResponse resp) { try { PageBean pageBean = new PageBean(); pageBean.setRequest(req); List<Map<String, Object>> infos = md.myInfos(m, pageBean); ResponseUtil.writeJson(resp, R.ok(0, "我的会议查询成功!!!", pageBean.getTotal(), infos)); } catch (Exception e) { e.printStackTrace(); try { ResponseUtil.writeJson(resp, R.error(0, "我的会议查询失败!!!")); } catch (Exception e1) { e1.printStackTrace(); } } return null; } // 下载图片的方法 public String updatesetpic(HttpServletRequest req, HttpServletResponse resp) throws Exception { // 获取到图片的存放地址 String dirPath = PropertiesUtil.getValue("dirPath"); // 获取浏览器请求路径,为了后续保存到数据库 String serverPath = PropertiesUtil.getValue("serverPath"); // 随机生成一个图片的名 String filename = UUID.randomUUID().toString().replaceAll("-", "")+ ".png"; // 将base64编码生成一张图片,保存在目录中 Base64ImageUtils.GenerateImage(m.getSeatPic().replaceAll("data:image/png;base64,", ""), dirPath+filename); // 将seatpic中的内容改变成请求地址 m.setSeatPic(serverPath+filename); // 修改会议排座中数据库图片对应的数据库列段 int r = md.updatesetpic(m); if(r>0) { ResponseUtil.writeJson(resp, R.ok(200, "会议排座成功")); }else { ResponseUtil.writeJson(resp, R.ok(0, "会议排座失败")); } return null; } // 我的审批 public String myAudit(HttpServletRequest req, HttpServletResponse resp) { try { PageBean pageBean = new PageBean(); pageBean.setRequest(req); List<Map<String, Object>> infos = md.myAudit(m, pageBean); ResponseUtil.writeJson(resp, R.ok(0, "我的审批查询成功!!!", pageBean.getTotal(), infos)); } catch (Exception e) { e.printStackTrace(); try { ResponseUtil.writeJson(resp, R.error(0, "我的审批查询失败")); } catch (Exception e1) { e1.printStackTrace(); } } return null; } // 会议送审 public String updateAuditorById(HttpServletRequest req, HttpServletResponse resp) throws Exception { int r = md.updateAuditorById(m); if(r>0) { ResponseUtil.writeJson(resp, R.ok(200, "会议送审成功")); }else { ResponseUtil.writeJson(resp, R.ok(0, "会议送审失败")); } return null; } // 待开会议、历史会议 public String queryMeetingInfoByState(HttpServletRequest req, HttpServletResponse resp) { try { PageBean pageBean = new PageBean(); pageBean.setRequest(req); List<Map<String, Object>> infos = md.queryMeetingInfoByState(m, pageBean); ResponseUtil.writeJson(resp, R.ok(0, "会议查询成功!!!", pageBean.getTotal(), infos)); } catch (Exception e) { e.printStackTrace(); try { ResponseUtil.writeJson(resp, R.error(0, "会议查询失败")); } catch (Exception e1) { e1.printStackTrace(); } } return null; } // 所有会议 public String allInfos(HttpServletRequest req, HttpServletResponse resp) { try { PageBean pageBean = new PageBean(); pageBean.setRequest(req); List<Map<String, Object>> infos = md.allInfos(m, pageBean); ResponseUtil.writeJson(resp, R.ok(0, "会议查询成功!!!", pageBean.getTotal(), infos)); } catch (Exception e) { e.printStackTrace(); try { ResponseUtil.writeJson(resp, R.error(0, "会议查询失败")); } catch (Exception e1) { e1.printStackTrace(); } } return null; } @Override public MeetingInfo getModel() { // TODO Auto-generated method stub return m; } }
关于图片下载相信大家会有一些疑问,这里就来解释一下,关于PropertiesUtil
package com.zking.util; import java.io.IOException; import java.io.InputStream; import java.util.Properties; public class PropertiesUtil { public static String getValue(String key) throws IOException { Properties p = new Properties(); InputStream in = PropertiesUtil.class.getResourceAsStream("/resource.properties"); p.load(in); return p.getProperty(key); } }配置文件
dirPath=D:/pic/picc/ serverPath=/upload/paizuo/ dirPathSign=D:/pic/picc/sign/ serverPathSign=/upload/sign/大家可以看到,这个类通过流的方式获取到了配置文件,并且通过Properties类中的getProperty方法通过配置文件中的键获取值,从而拿到了配置文件中定义的本地路径已经服务器的访问路径,然后,通过Base64ImageUtils类来生成图片并且输出到了指定的本地目录,其中为什么getseatpic有值呢,因为前端传过来的时候,直接赋值给了会议对象(mvc框架中配置的)
package com.zking.util; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import sun.misc.BASE64Decoder; import sun.misc.BASE64Encoder; public class Base64ImageUtils { public static String GetImageStr(String imgFilePath) {// 将图片文件转化为字节数组字符串,并对其进行Base64编码处理 byte[] data = null; // 读取图片字节数组 try { InputStream in = new FileInputStream(imgFilePath); data = new byte[in.available()]; in.read(data); in.close(); } catch (IOException e) { e.printStackTrace(); } // 对字节数组Base64编码 BASE64Encoder encoder = new BASE64Encoder(); return encoder.encode(data);// 返回Base64编码过的字节数组字符串 } public static boolean GenerateImage(String imgStr, String imgFilePath) {// 对字节数组字符串进行Base64解码并生成图片 if (imgStr == null) // 图像数据为空 return false; BASE64Decoder decoder = new BASE64Decoder(); try { // Base64解码 byte[] bytes = decoder.decodeBuffer(imgStr); for (int i = 0; i < bytes.length; ++i) { if (bytes[i] < 0) {// 调整异常数据 bytes[i] += 256; } } // 生成jpeg图片 OutputStream out = new FileOutputStream(imgFilePath); out.write(bytes); out.flush(); out.close(); return true; } catch (Exception e) { return false; } } }后端拿到了这个图像数据,去掉了data:image/png;base64,这个字符串,然后解码放入指定的目录,也就是我们从配置文件获取过来的本地目录dirpath,然后拼接上通过UUID.randomUUID().toString().replaceAll("-", "")生成一个随机的图片文件名,确保文件名的唯一性,并将其后缀设置为.png。从而将图片成功下载
但是从常规企业级项目来说,我们服务区访问路径是不会带项目名字的,所以我们还需要配置服务器的访问数据库的图片路径,也就是从配置文件获取到的服务器访问路径拼接图片名称,最后我们将其设置到我们的对象中,将对象利用sql语句也就是dao方法进行修改,
5.7、配置tomcat服务器磁盘访问路径
上一个段落讲到了我们的图片实际路径和数据库的路径是不一样的,所以如果只做到这一步,服务器是找不到我们的图片的,所以我们就需要去配置我们服务器访问的映射路径了
1、
双击tomccat服务器
点击最底下的moudles
点击右侧第二个按钮
第一行输入的是我们配置文件中的磁盘目录
第二行就是我们服务器的访问目录,点击ok即可
这样我们的服务器,就可以通过我们配置的磁盘映射路径找到对应的图片了
5.8、我的会议dao层--会议排座图片修改,我的会议遍历
package Dao; import java.util.List; import java.util.Map; import com.zking.util.BaseDao; import com.zking.util.PageBean; import com.zking.util.StringUtils; import Entity.MeetingInfo; import Entity.User; public class Meetingdao extends BaseDao<MeetingInfo> { // 通用的会议查询SQL语句,包含会议信息表数据,主持人姓名、审批人姓名、会议状态 private String getSQL() { return "SELECT a.id,a.title,a.content,a.canyuze,a.liexize,a.zhuchiren,b.`name`,a.location\r\n" + ",DATE_FORMAT(a.startTime,'%Y-%m-%d %H:%i:%s') as startTime\r\n" + ",DATE_FORMAT(a.endTime,'%Y-%m-%d %H:%i:%s') as endTime\r\n" + ",a.state\r\n" + ",(case a.state\r\n" + "when 0 then '取消会议'\r\n" + "when 1 then '新建'\r\n" + "when 2 then '待审核'\r\n" + "when 3 then '驳回'\r\n" + "when 4 then '待开'\r\n" + "when 5 then '进行中'\r\n" + "when 6 then '开启投票'\r\n" + "else '结束会' end\r\n" + ") as meetingState\r\n" + ",a.seatPic,a.remark,a.auditor,c.`name` as auditorName\r\n" + "FROM t_oa_meeting_info a\r\n" + "inner join t_oa_user b on a.zhuchiren = b.id\r\n" + "left JOIN t_oa_user c on a.auditor = c.id where 1=1 "; } // 我的会议 public List<Map<String, Object>> myInfos(MeetingInfo info, PageBean pageBean) throws Exception { String sql = getSQL(); String title = info.getTitle(); if(StringUtils.isNotBlank(title)) { sql += " and title like '%"+title+"%'"; } //根据当前登陆用户ID作为主持人字段的条件 sql+=" and zhuchiren="+info.getZhuchiren(); //按照会议ID降序排序 sql+=" order by a.id desc"; System.out.println(sql); return super.executeQuery(sql, pageBean); } // 根据会议ID更新会议的排座图片 public int updatesetpic(MeetingInfo info) throws Exception { String sql="update t_oa_meeting_info set seatPic=? where id=?"; return super.executeUpdate(sql, info, new String[] {"seatPic","id"}); } }
至此,我的会议排座功能就完毕了
6、总结
总的来说,会议排座这个功能还是比较吸引人的,对于一些政府的项目会经常用到,所以在此记录一下,再把流程分析一遍吧,
- 点击排座---
- 弹出子页面--遍历参会人员--点击下载,获取图片数据-----
- 传入后端--后端获取服务器访问路径,获取磁盘路径,创建随机图片名,通过工具类将图片数据解码然后传入指定的磁盘路径拼接图片名,设置数据库路径为服务器路径拼接图片名--
- 判断如果修改成功,抓取到父页面的iframe索引,然后通过索引关闭父页面的弹窗,调取父页面的刷新方法