要求位址: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 | |
估計這個任務需要多少時間 | 8h | 10h |
開發 | 4h | 5h |
需求分析 (包括學習新技術) | ||
生成設計文檔 | ||
設計複審 | 0h | |
代碼規範 | ||
具體設計 | ||
具體編碼 | ||
代碼複審 | ||
測試(自我測試,修改代碼,送出修改) | ||
報告 | ||
測試報告 | ||
計算工作量 | 2h | |
并提出過程改進計劃 | 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;width: 600px;height: 450px;border: 1px dashed gray;background-color: white">
12 <div style="width: 100px;border-right: 1px solid #bbbbbb;height: 100%;display:flex;align-content: center;justify-content: center">
13 <div style="font-size: 30px;width: 30px;margin-top: 20px">志成出題</div>
14 </div>
15 <div style="width: 100%;height: 100%;display: flex;flex-direction: column">
16 <div style="width: 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; width: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;width: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;width: 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;width: 150px;margin-top: 5px">
39 <span>答題時間:</span><span>120秒</span>
40 </div>
41 </div>
42
43 </div>
44 <div id="test"
45 style="width: 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="width: 90px">{{ari.question}}</div>
49 <span> = </span>
50 <input v-bind:id="index" style="border:none;border-bottom: 1px solid #bbbbbb;width: 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="width: 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;width: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;width: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。
當選好題數并單擊按鈕時,選題數部分消失,時間開始計時,題目出現,并且使用者可以輸入題目的答案并送出。
其次網頁還具有換膚功能,當點選換膚時,會改變文字和背景色。
當點選送出時,答題情況部分出現,時間靜止,考試結束。
當超過時間時,彈出提醒,并且輸入框此時無法輸入,進行與送出一樣的操作。
總結:
由于此前已有計算類的實作,是以該作業的難度在于頁面的互動以及邏輯的處理,與隊友一路做下來,發現所花的時間甚多,特别是調試和測試上,一改再改,與計劃的時間相差甚遠,至此有感,技術決定效率,效率決定未來。