一.排座的介绍,作用
1.排座介绍
A.前端实现
- 使用HTML、CSS和JavaScript创建座位布局界面,可以是矩阵形式或其他形式。
- 使用JavaScript为每个座位创建一个点击事件处理函数。
- 当用户点击一个座位时,JavaScript代码可以改变该座位的状态,例如设置为已选或已占用。根据状态的不同,可以应用不同的样式来标识座位的状态。
- 可以通过Ajax或类似技术与后端通信,将座位的状态信息发送给服务器,以便在后端进行处理。
B.数据库实现
- 如果需要长期保留座位状态信息,可以使用数据库来存储和管理数据。
- 可以创建一个座位表,其中每条记录表示一个座位,包括唯一标识符、行号、列号、状态等字段。
- 当用户选定或预订座位时,可以更新座位表中相应座位的状态字段。
C.后端实现
- 后端可以使用任何你熟悉的服务器端语言和框架,如Node.js、Python(Django或Flask框架)、Java(Spring框架)等。
- 后端需要提供API来处理前端发送的请求,包括获取座位状态、更新座位状态等。
- 当用户选定或预订座位时,后端可以将相关信息存储到数据库或其他持久化存储中。
通过这种前后端配合的方式,实现排座功能可以让用户在Web网页上方便地选择、预订或查看座位的状态,同时也方便了座位的管理和数据处理。具体实现细节会根据具体需求和技术栈而有所差异。
2.排座作用
A.座位预订
对于某些场合,如电影院、演唱会、剧院等,用户可能希望在网上事先选择并预订座位。通过在Web网页上实现排座功能,用户可以方便地浏览座位的可用性,并选择他们喜欢的座位进行预订。
B.座位安排
在一些会议、培训或其他活动中,组织者可能希望在提前安排好座位,以便参与者能够有序地就座。通过在Web网页上实现排座功能,组织者可以轻松地创建座位布局并将参与者分配到具体座位上。
C. 实时座位状态显示
在一些场合,如展览、展示区或交通工具中,为了提供实时信息给用户,可以在Web网页上显示座位的实时状态。这样用户可以了解座位的可用性,并据此进行选择和决策。
通过在Web网页中实现排座功能,可以提供更便捷的座位管理和使用体验,为用户和组织者带来便利。同时,这也是将传统座位管理引入到在线环境中的一种创新方式。
二.利用Layui实现排座
1.基础版(通过html+css+js实现)
A.基础版源码(html):
<html> <head> <title>会议座位安排</title> <style type="text/css"> * { padding: 0; margin: 0; } .tips { /* position: absolute; */ background: pink; display: inline-block; height: 70px; width: 70px; line-height: 70px; text-align: center; margin: 15px; } .add { position: fixed; right: 0; top: 0 } #tu { width: 100%; height: 100%; background: lightblue /*background: url('u=3318199716,2583790385&fm=26&gp=0.jpg');*/ } </style> <script src="https://ajax.aspnetcdn.com/ajax/jquery/jquery-3.5.1.min.js"></script> <script type="text/javascript" src="http://html2canvas.hertzen.com/dist/html2canvas.js"></script> </head> <body> <div id="tu"></div> <!-- <div class="tips" id="tips1" onmouseover="dragF.drag('tips1');"> <img src="/images/skinslogo.gif"><br>图片可以拖动</div> <div class="tips" id="tips2" onmouseover="dragF.drag('tips2');">座位1 </div> <div class="tips" id="tips3" onmouseover="dragF.drag('tips3');">座位2 </div> --> <div class="add"> <input id="dan_input" type="text" value=""> <button onclick="return addDanMu()">添加座位</button> <input id="jie_input" type="button" value='下载'> </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> function addDanMu() { var dan = document.getElementById("dan_input").value; if (dan == "") { alert("请输入弹幕~"); return false; } else { //1.从数据库中查询出所有的房间和病区 created //2.循环绑定房间和病区 //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"); //此处放置房间和病区对应的ID属性(唯一) 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; } } function initdata(){ //1.从数据库中查询出所有的房间和病区 created //2.循环绑定房间和病区 //foreach(){ var node = document.createElement("DIV"); var tipsArr = document.getElementsByClassName('tips'); node.setAttribute("class", "tips"); //此处放置房间和病区对应的ID属性(唯一) node.setAttribute("id", "tips" + i); //设置点击事件 //此处放置房间和病区的名称信息 var textnode = document.createTextNode(); // 创建个 文本节点, 将用户输入的弹幕,存入 创建的 元素节点 <p> 中 node.appendChild(textnode); document.getElementById("tu").appendChild(node); //} } </script> <script type="text/javascript"> $("#jie_input").on("click", function(event) { event.preventDefault(); html2canvas(document.getElementById("tu")).then(function(canvas) { var dataUrl = canvas.toDataURL(); var newImg = document.createElement("img"); newImg.src = dataUrl; // document.body.appendChild(newImg); // console.log(dataUrl) this.downloadFile('测试.png', dataUrl); }); }); //下载 function downloadFile(fileName, content) { let aLink = document.createElement('a'); let blob = this.base64ToBlob(content); //new Blob([content]); let evt = document.createEvent("HTMLEvents"); evt.initEvent("click", true, true); //initEvent 不加后两个参数在FF下会报错 事件类型,是否冒泡,是否阻止浏览器的默认行为 aLink.download = fileName; aLink.href = URL.createObjectURL(blob); // aLink.dispatchEvent(evt); //aLink.click() aLink.dispatchEvent(new MouseEvent('click', { bubbles: true, cancelable: true, view: window })); //兼容火狐 } //base64转blob function base64ToBlob(code) { let parts = code.split(';base64,'); let contentType = parts[0].split(':')[1]; let raw = window.atob(parts[1]); let rawLength = raw.length; let uInt8Array = new Uint8Array(rawLength); for (let i = 0; i < rawLength; ++i) { uInt8Array[i] = raw.charCodeAt(i); } return new Blob([uInt8Array], { type: contentType }); } </script> </html>
2.进阶版
A.实现思路
首先在我们的我的会议中的会议排座中添加一个点击事件,
点击后打开一个内嵌的的一个界面
我们可以将这些名字的块状元素进行拖到,按照需求进行排座,当然我们在日常会议中可能会有一些计划之外的情况出现,某些领导也要参与会议,添加座位也就变的合理起来了!
我们的这些名字的块状元素是通过我们发布会议时,通过回显将我们的名字放进会议排座里面。最重要的就是我们的图片保存,和回显。图片保存我们需要在我们的服务器对于图片进行一个保存路径进行配置,然后在我们的web路径下同样需要安装几个包,最后在我的盘符的同样构建包,用于我们项目图片的一个保存,最后将我们的下载图片进行一个数据回显
B. 具体实现
1.编写根据会议ID更新会议的排座图片sql,配置action
public int updateSeatPicById(MeetingInfo info) throws Exception { String sql=" update t_oa_meeting_info set seatPic=? where id=? "; return super.executeUpdate(sql, info, new String [] {"seatPic","id" }); }
public String updateSeatPicById(HttpServletRequest req, HttpServletResponse resp) throws Exception { //获取图片地址 String dirPath = PropertiesUtil.getValue("dirPath"); String serverPath = PropertiesUtil.getValue("serverPath"); //随机生成图片名字 String fileName = UUID.randomUUID().toString().replaceAll("-", "") +".png"; //D:/temp/images/a.png Base64ImageUtils.GenerateImage(info.getSeatPic(). replaceAll("data:image/png;base64,", ""), dirPath+ "/"+fileName); //讲setpic 的内容变成请求地址 info.setSeatPic(serverPath + "/"+ fileName); //修改数据库会议排座图片所对应的字段名 try { int add = meetingInfoDao. updateSeatPicById(info); ResponseUtil.writeJson(resp, R.ok(0, "排座图片修改成功")); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } return null; }
2.服务器配置,resource.properties保存图片(用于映射)
dirPath=D:/temp/images/T287/ serverPath=/upload/paizuo/ dirPathSign=D:/temp/images/T287/sign/ serverPathSign=/upload/sign/
3.构建我的会议(jsp),和相对应的js
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@include file="/common/header.jsp"%> <!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <script type="text/javascript" src="${pageContext.request.contextPath }/static/js/meeting/myMeeting.js"></script> <title>用户管理</title> </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:15px;"> <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>
let layer,table,$,form; let row; layui.use(['layer','table','jquery','form'],function(){ layer=layui.layer, table=layui.table, form=layui.form, $=layui.jquery; initTable(); //查询事件 $('#btn_search').click(function(){ query(); }); }); //1.初始化数据表格 function initTable(){ table.render({ //执行渲染 elem: '#tb', //指定原始表格元素选择器(推荐id选择器) height: 400, //自定义高度 loading: false, //是否显示加载条(默认 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='120px' src='"+d.seatPic+"'/>"; } }, {field: 'auditName', 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(); } else if(layEvent==="back"){ //反馈详情 openLayerFeedBack(row.id); } else { } }); } //打开会议排座对话框 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:'jsp/meeting/seatPic.jsp?id='+id, //弹出内容。可以传入普通的html内容,还可以指定DOM,更可以随着type的不同而不同 }); }
JS解释
query()函数是用于数据的查询,其作用给予加载数据
4.构建排座jsp,导入排座需要的资源(添加jQuery、html2canvas相关插件 )
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@include file="/common/header.jsp"%> <!DOCTYPE html > <html> <head> <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="${pageContext.request.contextPath }/static/js/jquery-3.3.1.min.js"></script> <script type="text/javascript" src="${pageContext.request.contextPath }/static/js/layui/layui.js"></script> <script type="text/javascript" src="${pageContext.request.contextPath }/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: skyblue; 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) { $('.add').hide(); event.preventDefault(); html2canvas(document.getElementById("screen_body")).then(function(canvas) { var dataUrl = canvas.toDataURL(); console.log(dataUrl); var param = {}; param['seatPic'] = dataUrl; param['id'] = '${param.id}'; param['methodName']='updateSeatPicById'; console.log(param); //此处需要完成会议排座图片上传操作 $.post('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(rs.msg,{icon:5},function(){}); } },'json'); }); }); }); function initMeetingUsers(){ //http://localhost:8080/xxx/seatPic.jsp?id=12 -> ${param.id} $.getJSON('user.action',{ 'methodName':'queryUserByMeetingId', '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>
5.最终效果图