要求地址:https://edu.cnblogs.com/campus/gzcc/GZCC-16SE1/homework/2213
github地址:https://github.com/GVictory/MakeOutQuestionsWithInterface
前言:
据课上要求,需要实现一个带页面的四则运算,并实现一定的扩展功能,本程序或许不能称之为一个程序,因为其使用java web技术,是一个运行于浏览器的网页,但又可称之为单机,因为只要有浏览器和电脑,均能运行此网页。
题目要求:
- 自动生成题目,单个题目最多不能超过4个运算符,操作数小于100。
- 用户可以输入答案。
- 若用户输入答案正确,则提示正确;若答案错误,则提示错误,并要提示正确答案是多少。
实现扩展要求:
- 用户答题结束以后,程序可以显示用户答题所用的时间。
- 程序可以设置答题时间,时间设置为整数,单位为秒,最大不能超过120秒,若超过了答题时间未答题,则提示:时间已到,不能答题。
- 程序可以设置皮肤功能,可以改变界面的颜色即可。
- 用户可以选择出题的个数(最多不能超过5个题目),答题结束可以显示用户答错的题目个数和答对的题目个数。
所用版本:
操作系统:windows10
开发环境:intellij IDEA 2016.4
开发语言:java,html,css,javascript,spring
结对组队:
- 姓名:李志成 学号:201606110064 博客园地址:https://www.cnblogs.com/97lzc/
- 姓名:郭木凯 学号:201606110066 博客园地址:https://www.cnblogs.com/GMUK/
- 李志成: 题目和答案生成,前台题目和答案的渲染。
- 郭木凯: 前台设计,换肤,计时。
实现思路:
由于本次作业使用的是java web技术,所以实现思路可分为如下:
- 前端:使用flex布局搭起整体的布局,并ajax异步请求后端获得随机生成的JSON格式的题目和答案,通过vue.js框架将数据绑定到组件中。组件的事件则通过jquery微框架编写。
- 后端:使用springboot搭起后端,controller调用计算类根据前端传来的题目数生成相应的题目和答案并返回给前端。
个人软件过程耗时估计与统计表:
Personal Software Process Stages | Time Senior Student | Time |
计划 | 0.5h | 0.5h |
估计这个任务需要多少时间 | 8h | 10h |
开发 | 4h | 5h |
需求分析 (包括学习新技术) | 0.5h | 0.5h |
生成设计文档 | 0.5h | 0.5h |
设计复审 | 0.5h | 0h |
代码规范 | 0h | 0.5h |
具体设计 | 0.5h | 0.5h |
具体编码 | 4h | 5h |
代码复审 | 0.5h | 0.5h |
测试(自我测试,修改代码,提交修改) | 0.5h | 0.5h |
报告 | 0.5h | 0.5h |
测试报告 | 0.5h | 0.5h |
计算工作量 | 2h | 2h |
并提出过程改进计划 | 1h | 1h |
实现代码:
首先是前端页面的实现,通过html,css,JavaScript,jQuery,vue.js编写
1 <!DOCTYPE html> 2 <html lang="en" xmlns:v-bind="http://www.w3.org/1999/xhtml"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>志成出题</title> 6 <script src="https://cdn.staticfile.org/vue/2.4.2/vue.min.js"></script> 7 <script src="https://apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js"></script> 8 </head> 9 <body style="height: 100%;background-color: #F5f5f5;"> 10 <div style="display: flex;justify-content: center;align-content: center"> 11 <div id="boss" style="display:flex; 600px;height: 450px;border: 1px dashed gray;background-color: white"> 12 <div style=" 100px;border-right: 1px solid #bbbbbb;height: 100%;display:flex;align-content: center;justify-content: center"> 13 <div style="font-size: 30px; 30px;margin-top: 20px">志成出题</div> 14 </div> 15 <div style=" 100%;height: 100%;display: flex;flex-direction: column"> 16 <div style=" 100%;height: 90px;display: flex;align-items: flex-end;flex-direction: row;align-items:center;justify-content: space-between"> 17 <div> 18 <div id="selectCount" 19 style="display: flex;flex-direction: column;align-items: center;margin-left: 20px"> 20 <select id="select" 21 style="background-color:#fafdfe;height:28px; 90px; line-height:28px; border:1px solid #9bc0dd; -moz-border-radius:2px; -webkit-border-radius:2px; border-radius:2px; "> 22 <option value="1">1</option> 23 <option value="2">2</option> 24 <option value="3">3</option> 25 <option value="4">4</option> 26 <option value="5" selected>5</option> 27 </select> 28 <button id="countButton" onclick="getArithmetic()" 29 style="margin-top:10px;border:none;height: 30px;60px;background-color: rgba(37,155,36,100);color: white;border-radius: 15%;font-size: 9px"> 30 确定 31 </button> 32 </div> 33 </div> 34 <div> 35 <div style="display: flex;align-items: flex-start; 150px;margin-bottom: 5px"> 36 <span>所用时间:</span><span id="useTime">0</span><span>秒</span> 37 </div> 38 <div style="display: flex;align-items: flex-start; 150px;margin-top: 5px"> 39 <span>答题时间:</span><span>120秒</span> 40 </div> 41 </div> 42 43 </div> 44 <div id="test" 45 style=" 100%;height: 270px;border-top: 1px solid #bbbbbb;border-bottom:1px solid #bbbbbb;display: flex;flex-direction: column;padding: 30px;box-sizing:border-box"> 46 <div id="body"> 47 <div style="margin-bottom: 10px;display: flex;" v-for="(ari,index) in arithmetic"> 48 <div style=" 90px">{{ari.question}}</div> 49 <span> = </span> 50 <input v-bind:id="index" style="border:none;border-bottom: 1px solid #bbbbbb; 70px"/> 51 <i name="answers" style="margin-right: 25px;margin-left: 25px" v-bind:id="index+10">X</i> 52 <div name="answers"> 53 <span>正确答案:</span> 54 <span>{{ari.answer}}</span> 55 </div> 56 </div> 57 </div> 58 </div> 59 <div id="result" 60 style=" 100%;height:85px;display: flex;flex-direction: row;justify-content: space-between;align-items: center;padding: 20px;box-sizing: border-box"> 61 <div> 62 <div id="text"> 63 <span>您答对了<span id="right">3</span>道题,答错了<span id="error">2</span>道题</span><br/> 64 <span>答题正确率为<span id="rate">60%</span></span> 65 </div> 66 </div> 67 <div style="display: flex;align-items: flex-end" id="commit"> 68 <button onclick="commitAnswer()" 69 style="border:none;height: 45px;100px;background-color: rgba(37,155,36,100);color: white;border-radius: 12px;font-size: 17px;margin-right: 5px"> 70 提交 71 </button> 72 <button id="skin" onclick="changeSkin()" 73 style="border:none;height: 30px;40px;background-color: rgba(37,155,36,100);color: white;border-radius: 15%;font-size: 9px"> 74 换肤 75 </button> 76 </div> 77 </div> 78 </div> 79 </div> 80 </div> 81 </body> 82 <script> 83 var questions = new Array(); 84 var vue; 85 var t; 86 87 function getArithmetic() { 88 var option = $("#select option:selected").text(); 89 $.ajax({ 90 type: 'GET', 91 url: "/hello?count=" + option, 92 success: function (data) { 93 var jsonData = JSON.stringify(data); 94 var obj = JSON.parse(jsonData); 95 array = obj; 96 for (var p in obj) {//遍历json对象的每个key/value对,p为key 97 var ob = {"question": p.toString(), "answer": obj[p]} 98 questions.push(ob); 99 } 100 vue = new Vue({ 101 el: '#test', 102 data: { 103 arithmetic: questions 104 } 105 }) 106 $("#body").show() 107 $("#selectCount").hide(); 108 $("#commit").show(); 109 $("#text").hide(); 110 $("[name='answers']").hide(); 111 var num = 0; 112 t = setInterval(function () { 113 num++; 114 $("#useTime").text(num) 115 if (num == 120) { 116 clearInterval(t); 117 alert("时间已到,答题结束") 118 commitAnswer(); 119 } 120 }, 1000); 121 122 } 123 }); 124 } 125 126 $(function () { 127 $("#body").hide(); 128 $("#commit").hide(); 129 $("#text").hide() 130 }) 131 132 function changeSkin() { 133 $("#boss").css("background-color", "#2b2b2b"); 134 $("#boss").addClass("changeSkin"); 135 } 136 137 function commitAnswer() { 138 var rightCount = 0; 139 var errorCount = 0; 140 for (var i in questions) { 141 $("#" + i).attr("disabled", "disabled"); 142 if (questions[i].answer == $("#" + i).val()) { 143 var num = parseInt(i) + 10; 144 $("#" + num.toString()).text("√"); 145 rightCount++; 146 } else { 147 errorCount++; 148 } 149 } 150 clearInterval(t); 151 $("[name='answers']").show(); 152 $("#commit").hide(); 153 $("#text").show(); 154 $("#right").text(rightCount); 155 $("#error").text(errorCount); 156 $("#rate").text((rightCount / (rightCount + errorCount) * 100) + "%") 157 } 158 </script> 159 <style> 160 .changeSkin { 161 color: #f5a528; 162 } 163 </style> 164 </html>
其次是后端控制器代码,如下:
1 public class testContraller { 2 @GetMapping("/getArithmetic")
3 public HashMap<String, Integer> getArithmetic(@RequestParam Integer count) throws JSONException {
4 return new Arithmetic().getArithmetic(count);
5 }
6 }
最后是解题函数(此处展示重要部分):
1 private static String getQuestion(Integer operatorNumber,Integer numberRange){
2 char[] operator = new char[]{'+', '-', '*', '/'}; 3 Random random = new Random(); 4 StringBuilder stringBuilder = new StringBuilder(); 5 for (int operatorIndex = 0; operatorIndex < operatorNumber; operatorIndex++) { 6 stringBuilder.append(random.nextInt(numberRange+1)); 7 stringBuilder.append(operator[random.nextInt(4)]); 8 } 9 stringBuilder.append(random.nextInt(numberRange+1)); 10 return stringBuilder.toString(); 11 } 12 13 private static Float getAnswer(String question){ 14 Stack<Character> operatorStack=new Stack<Character>(); 15 Stack<Float> numberStack=new Stack<Float>(); 16 char operatorTemp; 17 StringBuilder numberTemp=new StringBuilder(); 18 for (int questionIndex=0;questionIndex<question.length();questionIndex++){ 19 char singleChar=question.charAt(questionIndex); 20 if (Character.isDigit(singleChar)){ 21 numberTemp.append(singleChar); 22 }else { 23 if (!operatorStack.isEmpty()&&operatorStack.getTop()=='*'){ 24 operatorStack.pop(); 25 numberStack.push(numberStack.pop()*Float.valueOf(numberTemp.toString())); 26 }else if (!operatorStack.isEmpty()&&operatorStack.getTop()=='/'){ 27 operatorStack.pop(); 28 numberStack.push(numberStack.pop()/Float.valueOf(numberTemp.toString())); 29 }else if(!operatorStack.isEmpty()&&operatorStack.getTop()=='-'){ 30 numberStack.push(-Float.valueOf(numberTemp.toString())); 31 }else { 32 numberStack.push(Float.valueOf(numberTemp.toString())); 33 } 34 operatorStack.push(singleChar); 35 numberTemp.delete(0,numberTemp.length()); 36 } 37 } 38 if (!operatorStack.isEmpty()&&operatorStack.getTop()=='*'){ 39 numberStack.push(numberStack.pop()*Float.valueOf(numberTemp.toString())); 40 operatorStack.pop(); 41 }else if (!operatorStack.isEmpty()&&operatorStack.getTop()=='/'){ 42 numberStack.push(numberStack.pop()/Float.valueOf(numberTemp.toString())); 43 operatorStack.pop(); 44 }else if(!operatorStack.isEmpty()&&operatorStack.getTop()=='-'){ 45 numberStack.push(-Float.valueOf(numberTemp.toString())); 46 }else { 47 numberStack.push(Float.valueOf(numberTemp.toString())); 48 } 49 while (!operatorStack.isEmpty()){ 50 operatorStack.pop(); 51 numberStack.push(numberStack.pop()+numberStack.pop()); 52 } 53 return numberStack.pop(); 54 }
运行结果:
一进来网页时,网页的模样如下图,主要由五大部分组成,分别是选题数,时间计时,题目,换肤,答题情况,一开始进来时由于题目数未选择,所以时间计时是静止的,题目部分不存在。
PS:题数只能选择1-5。
当选好题数并单击按钮时,选题数部分消失,时间开始计时,题目出现,并且用户可以输入题目的答案并提交。
其次网页还具有换肤功能,当点击换肤时,会改变文字和背景色。
当点击提交时,答题情况部分出现,时间静止,考试结束。
当超过时间时,弹出提醒,并且输入框此时无法输入,进行与提交一样的操作。
总结:
由于此前已有计算类的实现,所以该作业的难度在于页面的交互以及逻辑的处理,与队友一路做下来,发现所花的时间甚多,特别是调试和测试上,一改再改,与计划的时间相差甚远,至此有感,技术决定效率,效率决定未来。