第二次作业——包含登录界面的简易计算器
一.设计思路:
1.简要说明:
本次作业是基于SpringBoot框架开发的, 使用SpringBoot框架有很多好处,比如SpringBoot简化了原始Spring框架的使用,它本身内嵌了tomcat容器。
对于计算器部分,新做了纯前端(html,js)实现的一个能实现单括号混合运算的计算器,没有用原来用javagui设计的计算器,因为是简单的用html和js实现的计算器逻辑,所以可能会有一些bug
对于登录逻辑部分,前端(html,js)实现。
对于连接数据库部分,使用H2数据库的内存模式,数据不会存储到本地,随着重启项目数据会失去(不重启项目数据会一直存在)。
2.项目使用说明(README.md):
1.项目启动步骤 浏览器访问:http://localhost:10018/lbh/login 输入账号密码:admin/admin123 登录成功使用计算器 PS: 登录、登出、计算机操作都会计入数据库 2.数据库访问路径 数据库类型:H2 访问地址:http://localhost:10018/lbh/h2 账号:root 密码:123456 PS: 使用H2数据库的内存模式,数据不会存储到本地,随着重启项目数据会失去(不重启项目数据一直在)。
二.后端代码设计
1.结构
创建一些要用的包,后端逻辑实现主要在controller,mapper,vo。具体的结构如下图:
2.主要功能:
controller:里面放了两个控制器类IndexController和SysIndexController,主要处理用户请求,并返回相应的视图或数据。
mapper:放了一个LogsMapper映射器接口,用来操作数据库执行sql语句
vo:放了一个Logs值对象类,用来封装操作日志的数据,用于显示层和业务层之间的数据传递
3.具体代码实现:
Indexcontroller:
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; @Controller public class IndexController { //登录页 @RequestMapping("/login") public String login(){ // System.out.println("login"); return "cal/login"; } //计算器页 @RequestMapping("/index") public String index(){ // System.out.println("index"); return "cal/calculator"; } }
SysIndexController:
package com.owl.controller; import cn.hutool.core.codec.Base64; import cn.hutool.core.lang.UUID; import com.owl.mapper.LogsMapper; import com.owl.vo.Logs; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController public class SysIndexController { @Autowired private LogsMapper logsMapper; @RequestMapping("/") public String index(){ return "计算器后端服务已启动."; } @GetMapping("/saveLog") public String saveLog(@RequestParam String logs){ String id = UUID.fastUUID().toString(); String opr_type = null; String opr_details = null; if(logs.equals("0")){ System.out.println("保存登录请求"); opr_type = "登入系统"; opr_details = "记录用户admin登入系统"; }else if(logs.equals("2")){ System.out.println("保存退出请求"); opr_type = "登出系统"; opr_details = "记录用户admin登出系统"; } else { System.out.println("保存计算请求"); opr_type = "计算"; String base64 = Base64.decodeStr(logs); opr_details = "记录用户计算"+base64+"数据"; } logsMapper.insert(id,opr_type,opr_details); return "操作成功"; } }
LogsMapper:
package com.owl.mapper; import org.apache.ibatis.annotations.Insert; import org.apache.ibatis.annotations.Mapper; @Mapper public interface LogsMapper{ @Insert("insert into logs(id,opr_type,opr_details) values(#{id},#{opr_type},#{opr_details}) ") int insert(String id,String opr_type,String opr_details); }
Logs:
package com.owl.vo; import lombok.Data; import lombok.experimental.Accessors; @Accessors(chain = true) @Data public class Logs { private String id; private String opr_type; private String opr_details; }
OwlApplication:
package com.owl; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.web.servlet.ServletComponentScan; @ServletComponentScan @SpringBootApplication public class OwlApplication { public static void main(String[] args) { SpringApplication.run(OwlApplication.class, args); System.out.println("计算器后端服务已启动."); } }
三.前端代码设计:
1.结构:
我在resources资源文件夹创建了了两个子文件夹db和templates.cal分别是用来存放数据库相关的文件和两个thymeleaf模版文件来渲染login登录页面和calculator计算器界面。
还有两个yml文件用来设置SpringBoot应用程序的一些属性,比如服务器的端口,我把它改成了10018,默认是8080。具体结构如下图:
2.具体功能:
db.schema.sql:sql脚本文件,用于创建并初始化表,存储操作日志的数据
calulator.html:用纯前端实现了计算器,功能方面能够满足+,-,*,/,[n2](平方),[Vn](开方)。
login.html:实现了简单的登录页面,包括一些简单的校验
3.具体代码实现:
schema.sql:
CREATE TABLE IF NOT EXISTS logs( id varchar(64) NOT NULL, opr_type varchar(64), opr_details varchar(128), PRIMARY KEY(id) );
calculator.html:
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>计算器</title> <style > * { margin: 0; padding: 0; } /*body背景色*/ body { width: 100%; background: -webkit-linear-gradient(left, #762b75, #c38baa); /* 标准的语法(必须放在q最后) */ } /*table部分*/ table { width: 460px; height: 470px; margin: 100px auto; } table tr { width: 100%; height: 20%; } table tr td { box-sizing: border-box; width: 16.66%; font-size: 20px; color: #d7d2cc; text-align: center; cursor: pointer; box-shadow: 0px 0px 4px #F7F7F9 inset; } table tr button { width: 100%; height: 100%; outline: none; background-color: transparent; border: none; font-size: 20px; color: #d7d2cc; cursor: pointer; } table tr button:hover { opacity: 0.6; background: linear-gradient(45deg, #2b5876, #4e4376); } /*屏幕显示部分*/ table td input { width: 100%; height: 100%; background-color: transparent; border: none; outline: none; cursor: pointer; font-size: 20px; color: #d7d2cc; text-align: right; } </style> </head> <body> <div> <button style="width: 5%; height: 100%; outline: none; background-color: lightseagreen; border: none; font-size: 16px; color: aliceblue; cursor: pointer;margin-top: -5%;float: right;margin-right: 2%;" onclick="logout()">退出登录</button> </div> <table> <tr> <td colspan="4"> <input type="text" id="p1" class="content"></input> </td> <td><button onclick="reset()" value="c">C</button></td> </tr> <tr> <td><button onclick="showyou(this.value)" value=1>1</button></td> <td><button onclick="showyou(this.value)" value=2>2</button></td> <td><button onclick="showyou(this.value)" value=3>3</button></td> <td><button onclick="showyou(this.value)" value="+">+</button></td> <td><button onclick="showyou(this.value)" value="[n2]">n<sup>2</sup></button></td> </tr> <tr> <td><button onclick="showyou(this.value)" value=4>4</button></td> <td><button onclick="showyou(this.value)" value=5>5</button></td> <td><button onclick="showyou(this.value)" value=6>6</button></td> <td><button onclick="showyou(this.value)" value="-">-</button></td> <td><button onclick="showyou(this.value)" value="[Vn]">√n</button></td> </tr> <tr> <td><button onclick="showyou(this.value)" value=7>7</button></td> <td><button onclick="showyou(this.value)" value=8>8</button></td> <td><button onclick="showyou(this.value)" value=9>9</button></td> <td><button onclick="showyou(this.value)" value="*">*</button></td> <td><button onclick="showyou(this.value)" value="/">/</button></td> </tr> <tr> <td><button onclick="showyou(this.value)" value=".">.</button></td> <td><button onclick="showyou(this.value)" value="0">0</button></td> <td><button onclick="showyou(this.value)" value="(">(</button></td> <td><button onclick="showyou(this.value)" value=")">)</button></td> <td><button onclick="cal()" value="=">=</button></td> </tr> </table> <div id="audio-box"></div> <script type="text/javascript"> var index = 0; //点击数字展示 function showyou(value){ if(index==1){ reset(); } index = 0; var p1 = document.getElementById("p1").value; if(p1==''){ if(value!='+' && value!='-' && value!='*' && value!='/' && value!='=' && value!='[n2]' && value!='[Vn]'){ document.getElementById("p1").value = value; } }else{ p1 = p1+value; document.getElementById("p1").value = p1; } // console.log(document.getElementById("p1").value); } function cal(){ var tmp0; var tmp1; index = 1; //计算逻辑 var p1 = document.getElementById("p1").value; if(p1==''){ document.getElementById("p1").value = ""; return; } p1 = document.getElementById("p1").value = p1+"="; tmp0 = document.getElementById("p1").value;//记录计算公式 if(p1.includes("(") && p1.includes(")")){ console.log("公式计算"); if(!p1.includes("+") && !p1.includes("-") && !p1.includes("*") && !p1.includes("/")){ document.getElementById("p1").value = "ERROR"; } var lastValue; //先计算括号内 var kh = p1.substring( p1.indexOf("(") + 1, p1.indexOf(")")); var khValue = cal2(kh); // alert(khValue); const pre1 = p1.split("("); var tmp0 = pre1[0]; if(tmp0==''){//前面无计算 lastValue = khValue; }else{ lastValue = cal2(tmp0+khValue); } const pre2 = p1.split(")"); var tmp1 = pre2[1]; console.log("tmp1="+tmp1) if(tmp1!='' && tmp1!='='){//后面有计算 lastValue = cal2(lastValue+tmp1); } // alert(lastValue); p1 = p1+lastValue; document.getElementById("p1").value = p1; }else{ // console.log("p1="+p1); if(p1.includes("+")){ console.log("加法"); const array = p1.split("+"); var start = array[0]; var endStr = array[1]; const endStrArray = endStr.split("="); var end = endStrArray[0]; // console.log(start+"+"+end); //开始运算 var result = parseFloat(start)+parseFloat(end); // result = result.toFixed(2); p1 = p1+result; document.getElementById("p1").value = p1; } if(p1.includes("-")){ console.log("减法"); const array = p1.split("-"); var start = array[0]; var endStr = array[1]; const endStrArray = endStr.split("="); var end = endStrArray[0]; // console.log(start+"+"+end); //开始运算 var result = parseFloat(start)-parseFloat(end); p1 = p1+result; document.getElementById("p1").value = p1; } if(p1.includes("*")){ console.log("乘法"); const array = p1.split("*"); var start = array[0]; var endStr = array[1]; const endStrArray = endStr.split("="); var end = endStrArray[0]; // console.log(start+"+"+end); //开始运算 var result = parseFloat(start)*parseFloat(end); p1 = p1+result; document.getElementById("p1").value = p1; } if(p1.includes("/")){ console.log("除法"); const array = p1.split("/"); var start = array[0]; var endStr = array[1]; const endStrArray = endStr.split("="); var end = endStrArray[0]; // console.log(start+"+"+end); //开始运算 var result = parseFloat(start)/parseFloat(end); p1 = p1+result; document.getElementById("p1").value = p1; } if(p1.includes("[n2]")){ //4x2= console.log("平方"); const array = p1.split("[n2]"); var start = array[0]; // var endStr = array[1]; // const endStrArray = endStr.split("="); // var end = endStrArray[0]; // console.log(start+"+"+end); //开始运算 var result = parseFloat(start)*parseFloat(start); p1 = p1+result; document.getElementById("p1").value = p1; } if(p1.includes("[Vn]")){ //4x2= console.log("开方"); const array = p1.split("[Vn]"); var start = array[0]; // var endStr = array[1]; // const endStrArray = endStr.split("="); // var end = endStrArray[0]; // console.log(start+"+"+end); //开始运算 var result = Math.sqrt(parseFloat(start)); p1 = p1+result; document.getElementById("p1").value = p1; } tmp1 = document.getElementById("p1").value;//记录计算结果 //保存计算记录 //第一步:建立所需的对象 var httpRequest = new XMLHttpRequest(); //第二步:打开连接 将请求参数写在url中 ps:"http://localhost:8080/rest/xxx" var base = new Base64(); var result = base.encode(tmp1); var url = "saveLog?logs="+result; httpRequest.open('GET', url, true); //第三步:发送请求 将请求参数写在URL中 httpRequest.send(); httpRequest.onreadystatechange = function () { if (httpRequest.readyState == 4 && httpRequest.status == 200) { // console.log("登录成功"); var json = httpRequest.responseText; } }; } function cal2(p1){ if(p1.includes("+")){ console.log("加法"); const array = p1.split("+"); var start = array[0]; var endStr = array[1]; const endStrArray = endStr.split("="); var end = endStrArray[0]; // console.log(start+"+"+end); //开始运算 var result = parseFloat(start)+parseFloat(end); return result; } if(p1.includes("-")){ console.log("减法"); const array = p1.split("-"); var start = array[0]; var endStr = array[1]; const endStrArray = endStr.split("="); var end = endStrArray[0]; // console.log(start+"+"+end); //开始运算 var result = parseFloat(start)-parseFloat(end); return result; } if(p1.includes("*")){ console.log("乘法"); const array = p1.split("*"); var start = array[0]; var endStr = array[1]; const endStrArray = endStr.split("="); var end = endStrArray[0]; // console.log(start+"+"+end); //开始运算 var result = parseFloat(start)*parseFloat(end); return result; } if(p1.includes("/")){ console.log("除法"); const array = p1.split("/"); var start = array[0]; var endStr = array[1]; const endStrArray = endStr.split("="); var end = endStrArray[0]; // console.log(start+"+"+end); //开始运算 var result = parseFloat(start)/parseFloat(end); return result; } } } function reset(){ document.getElementById("p1").value = ""; } function logout(){ var r=confirm("确认退出登录?"); if (r==true) { // console.log("登出成功"); //第一步:建立所需的对象 var httpRequest = new XMLHttpRequest(); //第二步:打开连接 将请求参数写在url中 ps:"http://localhost:8080/rest/xxx" var url = "saveLog?logs=2"; httpRequest.open('GET', url, true); //第三步:发送请求 将请求参数写在URL中 httpRequest.send(); httpRequest.onreadystatechange = function () { if (httpRequest.readyState == 4 && httpRequest.status == 200) { // console.log("登录成功"); var json = httpRequest.responseText; window.location.href="login"; } }; }else{ return; } } function Base64() { // private property _keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; // public method for encoding this.encode = function (input) { var output = ""; var chr1, chr2, chr3, enc1, enc2, enc3, enc4; var i = 0; input = _utf8_encode(input); while (i < input.length) { chr1 = input.charCodeAt(i++); chr2 = input.charCodeAt(i++); chr3 = input.charCodeAt(i++); enc1 = chr1 >> 2; enc2 = ((chr1 & 3) << 4) | (chr2 >> 4); enc3 = ((chr2 & 15) << 2) | (chr3 >> 6); enc4 = chr3 & 63; if (isNaN(chr2)) { enc3 = enc4 = 64; } else if (isNaN(chr3)) { enc4 = 64; } output = output + _keyStr.charAt(enc1) + _keyStr.charAt(enc2) + _keyStr.charAt(enc3) + _keyStr.charAt(enc4); } return output; } // public method for decoding this.decode = function (input) { var output = ""; var chr1, chr2, chr3; var enc1, enc2, enc3, enc4; var i = 0; input = input.replace(/[^A-Za-z0-9\+\/\=]/g, ""); while (i < input.length) { enc1 = _keyStr.indexOf(input.charAt(i++)); enc2 = _keyStr.indexOf(input.charAt(i++)); enc3 = _keyStr.indexOf(input.charAt(i++)); enc4 = _keyStr.indexOf(input.charAt(i++)); chr1 = (enc1 << 2) | (enc2 >> 4); chr2 = ((enc2 & 15) << 4) | (enc3 >> 2); chr3 = ((enc3 & 3) << 6) | enc4; output = output + String.fromCharCode(chr1); if (enc3 != 64) { output = output + String.fromCharCode(chr2); } if (enc4 != 64) { output = output + String.fromCharCode(chr3); } } output = _utf8_decode(output); return output; } // private method for UTF-8 encoding _utf8_encode = function (string) { string = string.replace(/\r\n/g,"\n"); var utftext = ""; for (var n = 0; n < string.length; n++) { var c = string.charCodeAt(n); if (c < 128) { utftext += String.fromCharCode(c); } else if((c > 127) && (c < 2048)) { utftext += String.fromCharCode((c >> 6) | 192); utftext += String.fromCharCode((c & 63) | 128); } else { utftext += String.fromCharCode((c >> 12) | 224); utftext += String.fromCharCode(((c >> 6) & 63) | 128); utftext += String.fromCharCode((c & 63) | 128); } } return utftext; } // private method for UTF-8 decoding _utf8_decode = function (utftext) { var string = ""; var i = 0; var c = c1 = c2 = 0; while ( i < utftext.length ) { c = utftext.charCodeAt(i); if (c < 128) { string += String.fromCharCode(c); i++; } else if((c > 191) && (c < 224)) { c2 = utftext.charCodeAt(i+1); string += String.fromCharCode(((c & 31) << 6) | (c2 & 63)); i += 2; } else { c2 = utftext.charCodeAt(i+1); c3 = utftext.charCodeAt(i+2); string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63)); i += 3; } } return string; } } </script> </body> </html>
login.html:
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>登录</title> <style > * { margin: 0; padding: 0; } /*body背景色*/ body { width: 100%; background: -webkit-linear-gradient(left, #762b75, #c38baa); /* 标准的语法(必须放在q最后) */ } /*table部分*/ table { width: 460px; height: 470px; margin: 100px auto; } table tr { width: 100%; height: 20%; } table tr td { box-sizing: border-box; width: 16.66%; font-size: 20px; color: #d7d2cc; text-align: center; cursor: pointer; box-shadow: 0px 0px 4px #F7F7F9 inset; } table tr button { width: 100%; height: 100%; outline: none; background-color: transparent; border: none; font-size: 20px; color: #d7d2cc; cursor: pointer; } table tr button:hover { opacity: 0.6; background: linear-gradient(45deg, #2b5876, #4e4376); } /*屏幕显示部分*/ table td input { width: 100%; height: 100%; background-color: transparent; border: none; outline: none; cursor: pointer; font-size: 20px; color: #d7d2cc; text-align: right; } </style> </head> <body> <div style="width: 460px;height: 470px;margin: 100px auto;text-align: center;margin-top:20%;color: aliceblue; "> <div> <h2>用户登录</h2> </div> <br/> <div > <span>账户:</span> <span><input type="text" id="username" class="content" value=""></input></span> </div> <div> <span>密码:</span> <span><input type="password" id="pwd" class="content" value=""></input></span> </div> <br/> <div> <button style="width: 43%; height: 100%; outline: none; background-color: lightseagreen; border: none; font-size: 16px; color: aliceblue; cursor: pointer;" onclick="login()">登 录</button> </div> </div> <script type="text/javascript"> //登录 function login(){ //获取用户名输入框内输入的内容 var username = document.getElementById("username").value; //获取密码框输入的内容 var pwd = document.getElementById("pwd").value; //判断用户名是否为空,如果为空,弹出提示信息并返回 if(username==''){ alert("请输入账号");return; } //判断密码是否为空 if(pwd==''){ alert("请输入密码");return; } //判断用户名的密码是否为...如果是弹出confirm确认框 if(username=='admin' && pwd=='admin123' ){ var r=confirm("确认登录?"); //如果用户确认,发送一个GET请求到savelog接口,传递logs参数为0 if (r==true) { //第一步:建立所需的对象 var httpRequest = new XMLHttpRequest(); //第二步:打开连接 将请求参数写在url中 ps:"http://localhost:8080/rest/xxx" var url = "saveLog?logs=0"; httpRequest.open('GET', url, true); //第三步:发送请求 将请求参数写在URL中 httpRequest.send(); //第四步:接收服务器响应,根据返回的结果,跳转到index界面 httpRequest.onreadystatechange = function () { if (httpRequest.readyState == 4 && httpRequest.status == 200) { // console.log("登录成功"); var json = httpRequest.responseText; window.location.href="index"; } }; //如果用户取消,返回 }else{ return; } //如果用户名和密码不匹配,弹出错误信息 }else{ alert("账号或密码错误"); } } </script> </body> </html>
application-dev.yml:
(初始化的路径,服务器端口,数据库url,root,pwd等等,自己都可以改)
server: port: 10018 servlet: context-path: /lbh spring: datasource: driver-class-name: org.h2.Driver url: jdbc:h2:mem:test username: root password: 123456 h2: console: path: /h2 enabled: true settings: web-allow-others: true sql: init: schema-locations: classpath:db/schema.sql #mybatis配置 mybatis: mapper-locations: classpath:mapper/**.xml configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
四.运行效果实例展示及登录逻辑的程序流程图:
1.我们首先运行main方法启动项目:
2.接下来打开文档README.md,根据文本进行登录的操作:
(1).浏览器访问登录界面:
(2).根据readme.md提示输入账号密码确认登录后进入计算器界面:
确认登录:
取消登录:
(3).退出登录
确认退出:
取消退出:
3.登录逻辑的流程图:
五.计算器逻辑校验以及数据库历史记录:
1.校验数据库连接:
测试连接:
连接:
2.校验登录以及退出的数据库历史记录:
我重启了一次项目(之前的演示数据库计入了历史记录)并用select语句在logs表中只验证登录一次退出一次的数据库记录,如下图:
3.校验用户计算的数据库历史记录:
我在第2点的基础上再次登录进计算器并输入了一条2 + 3的加法运算,接下来select一下看看数据库的历史记录
所以很明显我们可以看出数据库的LOGS表中有登录,退出,运算的历史记录。
4.计算器基本运算的测试用例:
加法:
减法:
乘法:
除法:
平方:
开方:
单括号混合运算: