# CET6 单词本
前端 html:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 <html > <head > <meta http-equiv ="content-type" content ="text/html; charset=UTF-8" /> <meta http-equiv ="content-type" content ="text/html; charset=UTF-8" /> <meta name ="robots" content ="noindex, nofollow" /> <meta name ="googlebot" content ="noindex, nofollow" /> <meta name ="viewport" content ="width=device-width, initial-scale=1" /> <link rel ="stylesheet" type ="text/css" href ="./css/index.css" /> <title > CET6单词本</title > </head > <body > <section id ="todoapp" > <header class ="header" > <h1 > CET6单词本</h1 > <input v-model ="inputValue" placeholder ="请输入单词" @keyup.enter ="add" autofocus ="autofocus" autocomplete ="off" placeholder ="请输入任务" class ="new-todo" /> </header > <section class ="main" > <ul class ="todo-list" > <li class ="todo" v-for ="(item,index) in list" > <div class ="view" > <span class ="index" > {{ index+1 }}.</span > <label > {{ item }}</label > <button class ="destroy" @click ="remove(index)" > </button > </div > </li > </ul > </section > <footer class ="footer" v-show ="list.length!=0" > <span class ="todo-count" v-if ="list.length!=0" > <strong > {{ list.length }}</strong > items left </span > <button v-show ="list.length!=0" class ="clear-completed" @click ="clear" > Clear </button > </footer > </section > <script src ="https://cdn.jsdelivr.net/npm/vue/dist/vue.js" > </script > <script > var app = new Vue ({ el : "#todoapp" , data : { list : ["springboot" ], inputValue : "" }, methods : { add : function ( ) { this .list .push (this .inputValue ); }, remove : function (index ) { console .log ("删除" ); console .log (index); this .list .splice (index, 1 ); }, clear : function ( ) { this .list = []; } }, }) </script > </body > </html >
添加后端数据库加载:
1 2 3 table:termBook id; word;
# 添加单词
# 持久层
规划需要执行的 SQL 语句:
1 insert into t_terms (id,word) value (编号,单词)
重复单词:
1 select * from t_terms where word= ?
设计接口和抽象方法:
mapper:
1 2 3 4 5 6 7 8 9 10 package com.jun.termbook.mapper;import com.jun.termbook.entity.Word;public interface WordMapper { Integer insert (Word word) ; Word findByWord (String word) ; }
xml:
1 2 3 4 5 6 7 8 9 10 11 12 <insert id ="insert" useGeneratedKeys ="true" keyProperty ="id" > insert into t_terms( id,word ) values ( #{id}, #{word} ) </insert > <select id ="findByWord" resultType ="com.jun.termbook.entity.Word" > select * from t_terms where word = #{word} </select >
test:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @Test public void insertTest () { Word word = new Word (); word.setId(2 ); word.setWord("wookplace" ); Integer insert = wordMapper.insert(word); System.out.println(insert); } @Test public void findByWordTest () { Word result = wordMapper.findByWord("springboot" ); System.out.println(result); }
# 业务层
# 规划异常:
单词已存在的异常
WordAlreadyExistsException
InsertException
# 设计接口和抽象方法:
IWordService:
1 2 3 public interface IWordService { void add (Word word) ; }
wordServiceImpl:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 @Service public class WordServiceImpl implements IWordService { @Autowired private WordMapper wordMapper; @Override public void add (Word word) { String word1 = word.getWord(); Integer id = word.getId(); Word result = wordMapper.findByWord(word1); if (result!=null ){ throw new WordAlreadyExistsException ("该单词已经存在" ); } Word wordAdd = new Word (); wordAdd.setWord(word1); wordAdd.setId(id); Integer rows = wordMapper.insert(wordAdd); if (rows!=1 ){ throw new InsertException ("数据插入异常" ); } } }
测试:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 @SpringBootTest @RunWith(SpringRunner.class) public class WordServiceTests { @Autowired private IWordService wordService; @Test public void insertTest () { try { Word word = new Word (); word.setId(3 ); word.setWord("niuma" ); wordService.add(word); System.out.println("OK" ); }catch (SecurityException e){ System.out.println(e.getClass().getSimpleName()); System.out.println(e.getMessage()); } } }
# 控制层
# 创建响应
状态码、状态描述信息、数据。这部分功能封装到一个类中,将这类作为方法放回值,返回给前端浏览器。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 package com.cy.store.util;import java.io.Serializable;public class JsonResult <E> implements Serializable { private Integer state; private String message; private E date; public JsonResult () { } public JsonResult (Throwable e) { this .message = e.getMessage(); } public JsonResult (Integer state, E date) { this .state = state; this .date = date; } public Integer getState () { return state; } public void setState (Integer state) { this .state = state; } public String getMessage () { return message; } public void setMessage (String message) { this .message = message; } public E getDate () { return date; } public void setDate (E date) { this .date = date; } }
# 设计请求
请求路径:/termbook
请求方式:POST
请求数据:Word word
响应结果: JsonResult
# 处理请求
WordController
1 2 3 4 5 6 7 8 9 10 11 12 13 @RestController @RequestMapping("termbook") public class WordController extends BaseController { @Autowired private IWordService wordService; @RequestMapping("add") public JsonResult<Void> insertWord (Word word) { wordService.add(word); return new JsonResult <>(OK); } }
# 前端页面
1 2 3 4 5 6 7 8 <header class ="header" > <h1 > CET6单词本</h1 > <form id ="add-word" > <input placeholder ="请输入单词" autofocus ="autofocus" autocomplete ="off" name ="word" type ="text" class ="new-todo" /> </form > </header >
1 2 3 4 5 6 7 8 9 10 11 12 13 $(document ).keydown (function (event ){ var key = event.which ; if (key==13 ){ $.ajax ({ url : "/termbook/add" , type : "POST" , data : $("#add-word" ).serialize (), dataType : "JSON" , }); } });
# 查询所有的单词并展示到前端
# 持久层
规划 sql 语句
xml 编写:
1 2 3 <select id ="showAll" resultMap ="WordMap" > select * from t_terms order by id DESC </select >
# 业务层
1 2 3 4 5 6 7 8 9 10 11 12 package com.jun.termbook.service;import com.jun.termbook.entity.Word;import java.util.List;public interface IWordService { void add (Word word) ; List<Word> findAll () ; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 package com.jun.termbook.service.impl;import com.jun.termbook.entity.Word;import com.jun.termbook.mapper.WordMapper;import com.jun.termbook.service.IWordService;import com.jun.termbook.service.ex.InsertException;import com.jun.termbook.service.ex.WordAlreadyExistsException;import com.jun.termbook.service.ex.WordNotFoundException;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Service;import java.util.List;@Service public class WordServiceImpl implements IWordService { @Autowired private WordMapper wordMapper; @Override public void add (Word word) { String word1 = word.getWord(); Integer id = word.getId(); Word result = wordMapper.findByWord(word1); if (result!=null ){ throw new WordAlreadyExistsException ("该单词已经存在" ); } Word wordAdd = new Word (); wordAdd.setWord(word1); wordAdd.setId(id); Integer rows = wordMapper.insert(wordAdd); if (rows!=1 ){ throw new InsertException ("数据插入异常" ); } } @Override public List<Word> findAll () { List<Word> words = wordMapper.showAll(); if (words==null ){ throw new WordNotFoundException ("没有单词记录" ); } return words; } }
# 控制层
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 package com.jun.termbook.controller;import com.jun.termbook.entity.Word;import com.jun.termbook.service.IWordService;import com.jun.termbook.util.JsonResult;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;import java.util.List;@RestController @RequestMapping("termbook") public class WordController extends BaseController { @Autowired private IWordService wordService; @RequestMapping("add") public JsonResult<Void> insertWord (Word word) { wordService.add(word); return new JsonResult <>(OK); } @RequestMapping("showAll") public JsonResult<List<Word>> showAll () { List<Word> data = wordService.findAll(); return new JsonResult <List<Word>>(OK,data); } }
在 /termbook/showAll 下有 json 数据
# 前端页面
1 2 3 4 5 6 7 8 9 10 11 12 13 14 <div id ="all-list" > <section class ="main" id ="showAll" > <ul class ="todo-list" > <li class ="todo" v-for ="(item,index) in list" > <div class ="view" > <span class ="index" > {{ index+1 }}.</span > <label > {{ item }}</label > <button class ="destroy" @click ="remove(index)" > </button > </div > </li > </ul > </section > </div >
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 $(document ).ready (function ( ) { showAllList (); }); function showAllList ( ) { $("#all-list" ).empty (); $.ajax ({ url : "/termbook/showAll" , type : "GET" , dataType : "JSON" , success : function (json ) { var list = json.data ; for (var i = 0 ; i < list.length ; i++) { console .log (list[i].title ); var html = '<section class="main" id="showAll">' +'<ul class="todo-list">' +'<li class="todo" v-for="(item,index) in list">' +'<div class="view">' +'<span class="index"> #{id}.</span>' +'<label> #{word} </label>' +'<button class="destroy" @click="remove(index)"></button>' +'</div>' +'</li>' +'</ul>' +'</section>' ; html = html.replace (/#{id}/g , list[i].id ); html = html.replace (/#{word}/g , list[i].word ); $("#all-list" ).append (html); } } });
# 根据 id 删除单个单词
# 持久层
sql 语句规划
1 delete from t_terms where id= ?
mapper 接口:
1 Integer deleteById (Integer id) ;
xml:
1 2 3 <delete id ="deleteById" > delete from t_terms where id=#{id} </delete >
单元测试:
1 2 3 4 5 @Test public void deleteByIdTest () { wordMapper.deleteById(3 ); System.out.println("ok" ); }
# 业务层
规划异常:
无异常可规划
直接代码:
1 void deleteById (Integer id) ;
1 2 3 4 @Override public void deleteById (Integer id) { wordMapper.deleteById(id); }
单元测试:
1 2 3 4 @Test public void deleteById () { wordService.deleteById(2 ); }
# 控制层
# 前端页面
一直报错:
1 2 3 4 5 6 7 8 9 10 11 12 13 {timestamp : "2022-11-20T04:43:37.736+00:00" , status : 400 , error : "Bad Request" ,…} error : "Bad Request" path : "/termbook/undefined/delete" status : 400 timestamp : "2022-11-20T04:43:37.736+00:00"
解决:
用 console.log 排查到是 url: “/termbook/”+id+"/delete", 中没有 id 输入,添加下列代码进行字符串拼接,
1 html = html.replace (/#{index}/ ,list[i].id );
例如:
将 “W3School” 替换字符串中的 “Microsoft”:
1 2 3 4 5 6 <script type="text/javascript" > var str="Visit Microsoft!" document .write (str.replace (/Microsoft/ , "W3School" ))</script>
最终 js:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 function remove (id ){ console .log (id) $.ajax ({ url : "/termbook/" +id+"/delete" , type : "POST" , dataType : "JSON" , success : function (json ) { if (json.state == 200 ) { showAllList (); alert ("删除成功" ); } else { alert ("删除失败" ) } }, }); }
# 删除所有单词:
# 持久层
SQL 规划:
mapper 接口:
xml:
1 2 3 <delete id ="deleteAll" > delete from t_terms </delete >
# 业务层
规划异常:
删除异常就先不写了,空时的异常也不写了,日后补充。
1 2 3 4 @Override public void deleteAll () { wordMapper.deleteAll(); }
测试:
1 2 3 4 @Test public void deleteAllTest () { wordService.deleteAll(); }
# 控制层
1 2 3 4 5 @RequestMapping("deleteAll") public JsonResult<Void> deleteAll () { wordService.deleteAll(); return new JsonResult <>(OK); }
# 前端代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 function clearAll () { console.log("点击了clear" ); $.ajax({ url:"/termbook/deleteAll" , type:"POST" , dataType:"JSON" , success: function (json) { if (json.state == 200 ) { showAllList(); alert("删除成功" ); } else { alert("删除失败" ); } }, }); }
改进 sql 语句,将删除所有 word 的 sql 语句改为
这条语句可以让表恢复为从 1 开始索引,如果是 delete 则会出现不为 1 开始,而是以删除前最后一条记录的 id+1 开始。
debug:
问题:
删除单个 word 出现显示记录异常:5 个单词已记录 4 个单词已记录 3 个单词已记录
解决:
把 remove 中刷新页面的函数 showAllList () 改为 location.reload ();
# 最终完整的开发页面(保留了注释代码)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 <html > <head > <meta http-equiv ="content-type" content ="text/html; charset=UTF-8" /> <meta http-equiv ="content-type" content ="text/html; charset=UTF-8" /> <meta name ="robots" content ="noindex, nofollow" /> <meta name ="googlebot" content ="noindex, nofollow" /> <meta name ="viewport" content ="width=device-width, initial-scale=1" /> <link rel ="stylesheet" type ="text/css" href ="../css/index.css" /> <title > CET6单词本</title > </head > <body > <section id ="todoapp" > <header class ="header" > <h1 > CET6单词本</h1 > <form id ="add-word" > <input placeholder ="请输入单词" autofocus ="autofocus" autocomplete ="off" name ="word" type ="text" class ="new-todo" /> </form > </header > <div id ="all-list" > </div > <div > <footer class ="footer" > <span class ="todo-count" > <div id ="word-length" > </div > </span > <button class ="clear-completed" onclick ="clearAll()" > 清除所有单词记录 </button > </footer > </div > </section > <script src ="https://cdn.staticfile.org/vue/2.6.14/vue.min.js" > </script > <script src ="https://cdn.bootcdn.net/ajax/libs/axios/0.21.1/axios.min.js" > </script > <script src ="https://cdn.bootcss.com/qs/6.7.0/qs.min.js" > </script > <script src ="../bootstrap3/jquery-1.9.1.min.js" > </script > <script type ="text/javascript" > $(document ).ready (function ( ) { showAllList (); }); $(document ).keydown (function (event ){ var key = event.which ; if (key==13 ){ $.ajax ({ url : "/termbook/add" , type : "POST" , data : $("#add-word" ).serialize (), dataType : "JSON" , }); } }); function showAllList ( ) { $("#all-list" ).empty (); $.ajax ({ url : "/termbook/showAll" , type : "GET" , dataType : "JSON" , success : function (json ) { var list = json.data ; for (var i = 0 ; i < list.length ; i++) { var html = '<section class="main" id="showAll">\n' +'<ul class="todo-list">\n' +'<li class="todo" ">\n' +'<div class="view">\n' +'<span class="index"> #{id}.</span>\n' +'<label> #{word} </label>\n' +'<button class="destroy" onclick="remove(#{id})"></button>\n' +'</div>\n' +'</li>\n' +'</ul>\n' +'</section>' ; html = html.replace (/#{id}/g , list[i].id ); html = html.replace (/#{word}/g , list[i].word ); html = html.replace ("#{id}" ,list[i].id ); $("#all-list" ).append (html); } var html2 = '<strong>{{ list.length }}</strong> 个单词已记录' ; html2 = html2.replace ("{{ list.length }}" ,list.length ); $("#word-length" ).append (html2); } }); } function remove (id ){ console .log (id); $.ajax ({ url : "/termbook/" +id+"/delete" , type : "POST" , dataType : "JSON" , success : function (json ) { if (json.state == 200 ) { location.reload (); alert ("删除成功" ); } else { alert ("删除失败" ) } }, }); } function clearAll ( ) { console .log ("点击了clear" ); $.ajax ({ url :"/termbook/deleteAll" , type :"POST" , dataType :"JSON" , success : function (json ) { if (json.state == 200 ) { showAllList (); alert ("删除成功" ); } else { alert ("删除失败" ); } }, }); } </script > </body > </html >
# 简化后代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 <html > <head > <meta http-equiv ="content-type" content ="text/html; charset=UTF-8" /> <meta http-equiv ="content-type" content ="text/html; charset=UTF-8" /> <meta name ="robots" content ="noindex, nofollow" /> <meta name ="googlebot" content ="noindex, nofollow" /> <meta name ="viewport" content ="width=device-width, initial-scale=1" /> <link rel ="stylesheet" type ="text/css" href ="../css/index.css" /> <title > CET6单词本</title > </head > <body > <section id ="todoapp" > <header class ="header" > <h1 > CET6单词本</h1 > <form id ="add-word" > <input placeholder ="请输入单词" autofocus ="autofocus" autocomplete ="off" name ="word" type ="text" class ="new-todo" /> </form > </header > <div id ="all-list" > </div > <div > <footer class ="footer" > <span class ="todo-count" > <div id ="word-length" > </div > </span > <button class ="clear-completed" onclick ="clearAll()" > 清除所有单词记录 </button > </footer > </div > </section > <script src ="https://cdn.staticfile.org/vue/2.6.14/vue.min.js" > </script > <script src ="https://cdn.bootcdn.net/ajax/libs/axios/0.21.1/axios.min.js" > </script > <script src ="https://cdn.bootcss.com/qs/6.7.0/qs.min.js" > </script > <script src ="../bootstrap3/jquery-1.9.1.min.js" > </script > <script type ="text/javascript" > $(document ).ready (function ( ) { showAllList (); }); $(document ).keydown (function (event ){ var key = event.which ; if (key==13 ){ $.ajax ({ url : "/termbook/add" , type : "POST" , data : $("#add-word" ).serialize (), dataType : "JSON" , }); } }); function showAllList ( ) { $("#all-list" ).empty (); $.ajax ({ url : "/termbook/showAll" , type : "GET" , dataType : "JSON" , success : function (json ) { var list = json.data ; for (var i = 0 ; i < list.length ; i++) { var html = '<section class="main" id="showAll">\n' +'<ul class="todo-list">\n' +'<li class="todo" ">\n' +'<div class="view">\n' +'<span class="index"> #{id}.</span>\n' +'<label> #{word} </label>\n' +'<button class="destroy" onclick="remove(#{id})"></button>\n' +'</div>\n' +'</li>\n' +'</ul>\n' +'</section>' ; html = html.replace (/#{id}/g , list[i].id ); html = html.replace (/#{word}/g , list[i].word ); html = html.replace ("#{id}" ,list[i].id ); $("#all-list" ).append (html); } var html2 = '<strong>{{ list.length }}</strong> 个单词已记录' ; html2 = html2.replace ("{{ list.length }}" ,list.length ); $("#word-length" ).append (html2); } }); } function remove (id ){ console .log (id); $.ajax ({ url : "/termbook/" +id+"/delete" , type : "POST" , dataType : "JSON" , success : function (json ) { if (json.state == 200 ) { location.reload (); alert ("删除成功" ); } else { alert ("删除失败" ) } }, }); } function clearAll ( ) { console .log ("点击了clear" ); $.ajax ({ url :"/termbook/deleteAll" , type :"POST" , dataType :"JSON" , success : function (json ) { if (json.state == 200 ) { showAllList (); alert ("删除成功" ); } else { alert ("删除失败" ); } }, }); } </script > </body > </html >