精确控制会议氛围:创新排座功能为您打造完美会议

简介: 精确控制会议氛围:创新排座功能为您打造完美会议

前言


上一篇博客讲到了会议的发布功能,之后就是送给自己的上级进行审批了,但是此时有个新的功能插件,就是排座功能,本章就带大家一起来看看,我们如何实现排座功能吧


1、效果图演示


a069309533ff4d82913016120f682b11.gif


2、功能分析


  • 我的会议查询
  • 获取到当前用户的id,查询当前用户创建未结束的所有会议
  • 会议排座功能
  • 当我们点击送审时,需要先进行排座
  • 首先,排座中包含个查询的功能,也就是查询当前会议参与的人员,然后展示在我们的排座界面中
  • 点击下载,我们就会使用到我们的插件,来将元素进行截取,然后转化成一个对象,再将对象转化成一个数据,再利用工具类进行解码然后将其画成一张图片
  • 最后,将其存到数据库,如果成功,关闭子页面,然后,关闭弹窗,至此整个功能完成


3、插件分享--html2canvas


html2canvas是一个JavaScript库,用于将HTML元素转换为Canvas图像。具体的使用看我的源码如何操作吧,上面有具体的注释

简要介绍如下:

  1. 功能:html2canvas库的主要功能是将指定的HTML元素(包括其子元素)渲染为一个Canvas图像对象。
  2. 兼容性:html2canvas支持大多数现代浏览器,包括Chrome、Firefox、Safari等。
  3. 使用方法:

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

025f0407851f4fd6ab7d842448e1958c.png

在这一行其实就把会议的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、

58d9f529a4054d72acd69a08fbb26c1d.png


双击tomccat服务器

a22fe5462688469ca85885ed778a8323.png


点击最底下的moudles

2dbd67c5dc864373b04046a3f3b50ad3.png

点击右侧第二个按钮

9032152fc2f94e228d272841f29aeb16.png

第一行输入的是我们配置文件中的磁盘目录

第二行就是我们服务器的访问目录,点击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索引,然后通过索引关闭父页面的弹窗,调取父页面的刷新方法
目录
相关文章
|
人工智能 自然语言处理 运维
《2022中国云游戏行业认知与观察》——第三章、元境多位专家分享对云游戏行业的 见解与期望(采访 & 演讲实录)——3.2 对话元境王矛,详解元 境蓝图:以全面的技术 重新定义计算范式(下)
《2022中国云游戏行业认知与观察》——第三章、元境多位专家分享对云游戏行业的 见解与期望(采访 & 演讲实录)——3.2 对话元境王矛,详解元 境蓝图:以全面的技术 重新定义计算范式(下)
185 0
|
编解码 分布式计算 Cloud Native
《2022中国云游戏行业认知与观察》——第三章、元境多位专家分享对云游戏行业的 见解与期望(采访 & 演讲实录)——3.2 对话元境王矛,详解元 境蓝图:以全面的技术 重新定义计算范式(中)
《2022中国云游戏行业认知与观察》——第三章、元境多位专家分享对云游戏行业的 见解与期望(采访 & 演讲实录)——3.2 对话元境王矛,详解元 境蓝图:以全面的技术 重新定义计算范式(中)
152 0
|
运维 Cloud Native 调度
《2022中国云游戏行业认知与观察》——第三章、元境多位专家分享对云游戏行业的 见解与期望(采访 & 演讲实录)——3.2 对话元境王矛,详解元 境蓝图:以全面的技术 重新定义计算范式(上)
《2022中国云游戏行业认知与观察》——第三章、元境多位专家分享对云游戏行业的 见解与期望(采访 & 演讲实录)——3.2 对话元境王矛,详解元 境蓝图:以全面的技术 重新定义计算范式(上)
226 0
|
机器学习/深度学习 人工智能 自然语言处理
阳过→阳康,数据里的时代侧影;谷歌慌了!看各公司如何应对ChatGPT;两份优质AI年报;本周技术高光时刻 | ShowMeAI每周通讯 #003-12.24
这是ShowMeAI每周通讯的第3期。本期内容关键词:新冠、ChatGPT、2022 AI 报告、腾讯·绝悟、阿里·AliceMind、小红书·全站智投、OpenAI·Point-E、Google·CALM、Wayve·MILE、AI2·MemPrompt、Stanford x MosaicML·PubMed GPT、腾讯全员大会、特斯拉裁员、图森未来裁员、AI 应用与工具大全。
547 0
阳过→阳康,数据里的时代侧影;谷歌慌了!看各公司如何应对ChatGPT;两份优质AI年报;本周技术高光时刻 | ShowMeAI每周通讯 #003-12.24
|
传感器 人工智能 自然语言处理
Paper之ACMCH&UIST&ICUC&IJHC:2009年~2019年人机交互技术(计算系统人为因素会议&用户界面软件&计算国际会议&国际人类计算机研究&人机交互的交易)历年最佳论文简介及其解读
Paper之ACMCH&UIST&ICUC&IJHC:2009年~2019年人机交互技术(计算系统人为因素会议&用户界面软件&计算国际会议&国际人类计算机研究&人机交互的交易)历年最佳论文简介及其解读
Paper之ACMCH&UIST&ICUC&IJHC:2009年~2019年人机交互技术(计算系统人为因素会议&用户界面软件&计算国际会议&国际人类计算机研究&人机交互的交易)历年最佳论文简介及其解读
|
运维 架构师 前端开发
架构师眼中的文化:组织不扁平,3天后信息衰减到20%(1)
架构师眼中的文化:组织不扁平,3天后信息衰减到20%(1)
141 0
架构师眼中的文化:组织不扁平,3天后信息衰减到20%(1)
|
架构师 程序员 iOS开发
架构师眼中的文化:组织不扁平,3天后信息衰减到20%(2)
架构师眼中的文化:组织不扁平,3天后信息衰减到20%(2)
380 0
架构师眼中的文化:组织不扁平,3天后信息衰减到20%(2)
|
机器学习/深度学习 人工智能 自然语言处理
网易有道词典一个小更新,消除了你和全球一半人类的沟通障碍
完全不懂外语的你也可以拿起手机,和老外直接对话了:甚至不需要选择彼此说着的是什么语言。
268 0
网易有道词典一个小更新,消除了你和全球一半人类的沟通障碍
|
架构师 Cloud Native 程序员
架构师眼中的文化:组织不扁平,3天后信息衰减到20%(3)
架构师眼中的文化:组织不扁平,3天后信息衰减到20%(3)
202 0
|
人工智能 运维 算法
算法博士平均月入4万,数据可视化技能全球吃香 | 2020年《顶级数据团队建设全景报告》重磅发布
算法博士平均月入4万,数据可视化技能全球吃香 | 2020年《顶级数据团队建设全景报告》重磅发布
174 0
下一篇
无影云桌面