实习笔记
这是实习水了好几天来第一次被安排的任务,写一个个人用户中心模块的增删改查,我大概花了三天写完,离谱。。这里做一些笔记吧。
首先公司项目的技术是,springboot,前端用的springboot的内置模板Freemarker,但不是jsp页面,全是html和ftl页面,前后端交互的数据通过ajax请求来交互,前端ui用的layui框架
后端的话使用的是原生的jdbc对应的springtemplate来进行增删改查,分别controller,service,dao三层,并且将接口省去了,确实方便很多。。。。
我的任务就是对一个页面进行增删改查,如下图:
layui的官方网站
- 引入layui:https://www.layuicdn.com/#Layui
- layui开发文档:https://www.layui.site/doc/index.htm
- 在线演示layui组件:https://www.layui.site/demo/index.htm
layui的cdn引入模板
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <!-- 引入 layui.css --> <link rel="stylesheet" href="https://www.layuicdn.com/layui-v2.6.8/css/layui.css"> <!-- 引入 layui.js --> <script src="https://www.layuicdn.com/layui-v2.6.8/layui.js"> </head> <body> </body> </html> |
因为在一个页面中,有子页面,因此前端有两个页面,一个主页面person,一个提交页面personSub
整体页面逻
主页面person的js逻辑
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 |
var fwqxsqdata; var perCenTable; var isFirst = true; // 应用权限表格 var YYQXSQtable; //服务权限申请 layui.use(['table', 'element', 'laypage', 'form'], function () { var table = layui.table, $ = layui.jquery, element = layui.element , page = layui.laypage , form = layui.form; //Tab的切换功能,切换事件监听等,需要依赖element模块 var height = $(window).height(); var heightStr = 'full-300'; if (height > 500 && height < 900) { heightStr = 'full-180'; } var lbParmas = { page: 1, limit: 10, scenceIds: $("#scenceIds").val() }; // 获取数据服务列表 var getList = function () { lbParmas = { pageNo: 1, pageSize: 10, moudleName: $("#MoudleName").val() }; postAjax(requestUrl + "/perCenterApi/selectGrzxXmMKList", lbParmas, function (data) { console.log(data) page.render({ elem: 'layuipage', count: data.count, contentType: "application/json;charset=UTF-8", curr: lbParmas.pageNo, limit: lbParmas.pageSize, first: "首页", last: "尾页", layout: ['count', 'prev', 'page', 'next', 'limit', 'skip'], jump: function (obj, first) { if (!first) { lbParmas.pageNo = obj.curr; lbParmas.pageSize = obj.limit; lbParmas.scenceIds=$("#scenceIds").val(); getAjax(requestUrl + "/perCenterApi/selectGrzxXmMKList", lbParmas,function (data) { perCenTable.reload({ data: data.list }); $('#perCenTable').parent().find('.layui-table-header').find('.layui-form-checkbox').hide(); }) } } }); perCenTable = table.render({ elem: '#perCenTable', id: 'perCenTable' , cols: [ [ //标题栏 {type: 'checkbox'}, {field: 'id', title: 'id',hide:true}, {field: 'moduleName', title: '模块名称'}, {field: 'dispSn', title: '排序'}, {field: 'bz', title: '备注'}, ] ] , even: true , height: heightStr , data: data.list , page: false //是否显示分页 , limit: Number.MAX_VALUE }); $('#perCenTable').parent().find('.layui-table-header').find('.layui-form-checkbox').hide(); }, function (res) { }); }; table.on('checkbox(perCenTable)',function(obj){ $('#perCenTable').parent().find('.layui-table-header').find('.layui-form-checkbox').hide(); if(obj.checked) { fwqxsqdata = obj; } else { fwqxsqdata = null; $(obj.tr).find('td div.laytable-cell-checkbox div.layui-form-checkbox').attr('class','layui-unselect layui-form-checkbox'); } }); getList(''); //查询 $("#Fsearch").click(function () { getList(lbParmas); }); //重置 $("#Freset").click(function () { $("#MoudleName").val(""); getList(); }); function init(){ lbParmas.scenceIds = $('#scenceIds').val(); getList(); } $("#mqSearchBtn").click(function () { init(); }) //新增 $('#insertBtn').click(function () { var width = $(window).width(); var height = $(window).height(); var index = layer.open({ type: 2, title: '新增', // area: [width + 'px', height + 'px'], area: ['75%', '70%'], content: 'personalCenterSubApi.html?flag=add', end: function () { location.reload(); } }); // layer.full(index); }); //编辑 $('#updateBtn').click(function () { if (!fwqxsqdata) { layer.alert('请选择一条数据!'); return; } var title = fwqxsqdata.data.serverName; var width = $(window).width(); var height = $(window).height(); console.log(fwqxsqdata.data.id); var index = layer.open({ type: 2, title: title, // area: [width + 'px', height + 'px'], area: ['85%', '70%'], content: 'personalCenterSubApi.html?flag=update&id=' + fwqxsqdata.data.id, end: function () { location.reload(); } }); // layer.full(index); }); //查看 $('#chaKanBtn').click(function () { if (!fwqxsqdata) { layer.alert('请选择一条数据!'); return; } var title = fwqxsqdata.data.serverName; var width = $(window).width(); var height = $(window).height(); var index = layer.open({ type: 2, title: title, // area: [width + 'px', height + 'px'], area: ['85%', '70%'], content: 'personalCenterSubApi.html?flag=chakan&id=' + fwqxsqdata.data.id }); // layer.full(index); }); //删除 $('#delBtn').click(function () { if (!fwqxsqdata) { layer.alert('请选择一条数据!'); return; } console.log(fwqxsqdata.data.id) var parmas = { id: fwqxsqdata.data.id }; postAjax(requestUrl + "/perCenterApi/deleteGrzxMK?" , parmas, function (data) { if (data.msg == '删除成功') { layer.alert('删除成功!'); fwqxsqdata = null; $("#scenceIds").val(""); getList(); } else { layer.alert('删除失败!'); } }); }); }) |
子页面personSub页面的js逻辑
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 |
layui.use(['table', 'element', 'laypage', 'form'], function () { var table = layui.table, $ = layui.jquery, element = layui.element , upload = layui.upload , page = layui.laypage , form = layui.form; //Tab的切换功能,切换事件监听等,需要依赖element模块 var flag = getUrlParam('flag'); $('#id').val(getUrlParam('id'));//id //跳转页面动态 if (flag == 'update') { var param = { id: getUrlParam ('id') }; //下拉框加载 postAjax(requestUrl + "/perCenterApi/getModuleByParCode", param, function (data) { $.each(data, function(index, value) { $("#moduleCode").append('<option value=' + value.code + '>' + value.name + '</option>'); }); layui.form.render("select"); postAjax(requestUrl + "/perCenterApi/selectGrzxXm", param, function (data) { $('#showPic').show() $('#showPic').unbind() $('#showPic').click(function () { layer.open({ type: 2, area :['70%','70%'], content: requestUrl + '/perCenterApi/getPicUrl?id=' + getUrlParam('id') }); }) $('#moduleCode').find("option:selected").text(data.data.moduleName)//模块名称 form.render('select'); $('#dispSn').val(data.data.dispSn);//排序 $('#remark').val(data.data.bz);//备注 }); $('select').attr('disabled', 'disabled'); }); } else if (flag == 'chakan') { $('#saveBtn').hide(); var param = { id: getUrlParam('id') }; postAjax(requestUrl + "/perCenter/getModuleByParCode", param, function (data) { $.each(data, function (index, value) { $("#moduleCode").append('<option value=' + value.code + '>' + value.name + '</option>'); }); layui.form.render("select"); postAjax(requestUrl + "/perCenterApi/selectGrzxXm", param, function (data) { console.log(data) // $('#moduleCode').val(data.data.moduleName);//模块名称 //下拉框设置值得这么写 $('#moduleCode').find("option:selected").text(data.data.moduleName) $('#dispSn').val(data.data.dispSn);//排序 $('#remark').val(data.data.bz);//备注 form.render('select'); }); $('input,textarea,select').attr('disabled', 'disabled'); }); }else{ postAjax(requestUrl + "/perCenterApi/getModuleByParCode", param, function (data) { $.each(data, function(index, value) { $("#moduleCode").append('<option value=' + value.code + '>' + value.name + '</option>'); }); layui.form.render("select"); }); } // 提交 $('#saveBtn').click(function (data) { var moduleCode = $('#moduleCode').val(); if (moduleCode == '') { layer.alert('模块名称不能为空'); return; } let moduleName = $("#moduleCode").find("option:selected").text(); $('#moduleName').val(moduleName) var dispSn = $('#dispSn').val();//排序 if (dispSn == '') { layer.alert('排序不能为空'); return; } var bz = $('#remark').val();//排序 $('#bz').val(bz); var datas = new FormData(document.getElementById('perCenSub')); if (flag == 'update') { postAjaxForm(requestUrl + "/perCenterApi/updateGrzxMK", datas, function (res) { if (res.msg == '修改成功') { layer.alert('修改成功!',function () { colseWindow(); }); } else { layer.alert('修改失败!'); } }); } else if (flag == 'add') { let moduleName = $("#moduleCode").find("option:selected").text(); //先进行判断 console.log(moduleName) var param = { moduleName: moduleName }; var a = false; postAjax(requestUrl + "/perCenterApi/selectGrzxXmByName", param, function (data) { console.log(moduleName) console.log(data) if(data.data==null){ a=false }else{ if(moduleName == data.data.moduleName) a = true } console.log(a) if(a==false){ postAjaxForm(requestUrl + "/perCenterApi/addGrzxMk", datas, function (res) { console.log("log:"+res.msg) if (res.msg == '新增成功') { layer.alert('新增成功!',function () { colseWindow(); }); } else { layer.alert('新增失败!'); } }); }else{ layer.alert('新增失败!'); } }); } }); // 关闭页面 $('#closeBtn').click(function () { colseWindow(); }); //关闭子页面跳转父页面 function colseWindow() { var index = parent.layer.getFrameIndex(window.name); parent.layer.close(index); } }); |
下面演示一个功能的全部代码
前面已经知道了,逻辑代码中,使用layui.use方面进行页面刷新,用户进入页面后,处于观看状态,这时为用户准备了很多按钮,每一个按钮都绑定一个事件,这里介绍一两个功能。
新增一条数据
主页面绑定的按钮新增事件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
//新增 $('#insertBtn').click(function () { var width = $(window).width(); var height = $(window).height(); var index = layer.open({ type: 2, title: '新增', // area: [width + 'px', height + 'px'], area: ['75%', '70%'], content: 'personalCenterSubApi.html?flag=add', end: function () { location.reload(); } }); // layer.full(index); }); |
通过绑定id为insertBtn的按钮点击事件,然后利用layui框架的open函数打开一个页面内置窗口(为什么要这么做?因为你新增,你得新增数据啊,当然数据就填写在这个新增的窗口中)
新开窗口的页面逻辑
可以看到,这里没有页面加载函数,事实上,也不需要,通过前面首页传来的参数flag,分别调用新增,修改,查看函数。并且,绑定了两个按钮。下面看下这个新增的详细代码
1 2 3 4 5 6 7 8 |
else{ postAjax(requestUrl + "/perCenterApi/getModuleByParCode", param, function (data) { $.each(data, function(index, value) { $("#moduleCode").append('<option value=' + value.code + '>' + value.name + '</option>'); }); layui.form.render("select"); }); } |
你可能会好奇,为什么新增有两端代码(下面一段),前面一段是什么?其实,是加载下拉框。这里我不能再访问那个页面路径了,因此选个效果图看看:
$.each(data, function(index, value)则是一个jq中的遍历函数,将其渲染到下拉框中,通过css中的id选择,选择到下拉框
1 2 3 4 5 |
<select id="moduleCode" name="moduleCode" lay-filter="moduleCode"> <option value="" id="option">请选择</option> <#-- <option value="1">QUICK</option>--> <#-- <option value="2">TEST</option>--> </select> |
此时,用户再次处于查看状态,再写前端逻辑时,一定要注意到用户的状态!下面我把这种状态称为停滞状态,方便记忆。停滞状态的用户需要通过点击等事件来响应操作,这时就能想到了这个页面只有两个绑定事件,一个提交按钮,一个关闭按钮,因此,新增就在提交按钮里!但是提交按钮,不都是让你新增的,还有修改,因此需要一个flag判断,还记得首页我们请求发送到这个页面带了一个参数flag=add吗?
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 |
else if (flag == 'add') { let moduleName = $("#moduleCode").find("option:selected").text(); //先进行判断 console.log(moduleName) var param = { moduleName: moduleName }; var a = false; postAjax(requestUrl + "/perCenterApi/selectGrzxXmByName", param, function (data) { console.log(moduleName) console.log(data) if(data.data==null){ a=false }else{ if(moduleName == data.data.moduleName) a = true } console.log(a) if(a==false){ postAjaxForm(requestUrl + "/perCenterApi/addGrzxMk", datas, function (res) { console.log("log:"+res.msg) if (res.msg == '新增成功') { layer.alert('新增成功!',function () { colseWindow(); }); } else { layer.alert('新增失败!'); } }); }else{ layer.alert('新增失败!'); } }); } |
这里有个业务需求就是不能新增重复的名称,因此再新增之前,进行了一次查询,这个先不看了,可以看到新增直接发送请求来到了/perCenterApi/addGrzxMk这里,接下来看下controller代码之前,看下提交的参数datas,这个参数datas在前面的代码中声明是这样的
1 |
var datas = new FormData(document.getElementById('perCenSub')); |
因为,datas的类型是object,并且是一个类似。而这里用到了FormData函数,需要传入一个表单对象,会将表单中的值全部映射到datas,并且postajax提交时,注明一下postajaxForm,
1 2 3 4 5 6 |
//这里的讲下formdata对象,其实就是一个map键值对集合,可以通过formdata函数将表单中的对象转为集合通过ajax方便传值 //我们可以通过name来访问表单中的字段 formdata.get("username"); //获取username的值 formdata.get("pwd"); //获取pwd的值 //我们也可以添加数据 formdata.append("token","fegahgag54") |
后端controller层代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
@RequestMapping(value = "/addGrzxMk",name = "新增模块信息") @ResponseBody public ResultModel addGrzxMk(String bz, String moduleName, String dispSn, HttpServletResponse response, HttpServletRequest request ){ ResultModel resultModel = new ResultModel(); String msg = ""; boolean success = true; UserInfo userInfo = WebUtil.getCurrUserInfo(request,response); GarzaXmas garzaXmas = new GarzaXmas(); garzaXmas.setBz(bz); garzaXmas.setModuleName(moduleName); garzaXmas.setDispSn(dispSn); garzaXmas.setRyId(userInfo.getUserId()); msg = personalCenterService.addGrzxMk(garzaXmas); resultModel.setSuccess(success); resultModel.setMsg(msg); return resultModel; } |
这里用到了springmvc的知识点:
为什么提交的url来到了controller对应的这个addGrzxMk这里?
这里是可以说是springmvc的原理,其实老大哥tomcat本来也有这个原理,不就是servlet吗?通过注解配置WebServlet(“/hello”)实现,至于这些:
- tomcat中如何实现url输入路径,配置注解@WebServlet就找到对应的servlet方法?
- 那为什么现在改了@requestmapping注解,或者说为什么要用springmvc?springmvc的底层原理路由是怎么样的
- spring在当中是扮演了怎么样的角色,和tomcat的关系,和springmvc的关系?
这里开一个新文章来说说这些问题
那么继续,既然获得了ajax请求过来的数据,那么就好办, 新增嘛,肯定是声明一个新增的这个类对象来封装这些数据。代码中该对象变量是garzaXmas。这里为什么不是用对象来接收?很简单,因为前端传来的值并不是这个对象的所有字段都包含了。另外一点就是,如果是查询,删除,修改这些需求的话,是根据id或者名称来的,那么函数之间传参直接用一个string类型的字符串可以吗?可以,但不建议,因为习惯都是通过对象在逻辑之间传递,并且后续你查询,如果是加where条件呢?你又得改代码,加参数。
下面来看下service层的代码吧
1 2 3 4 5 6 7 8 9 10 11 12 |
public String addGrzxMk(GarzaXmas grzxXmry) { int i = 0 ; try { i = personalCenterDao.addGrzxMk(grzxXmry); if (i >0){ return "新增成功"; } } catch (Exception e) { e.printStackTrace(); } return "新增失败"; } |
没啥好说的
下面是dao层的代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
public int addGrzxMk(GarzaXmas grzxXmry) { String uuid = UUID.randomUUID().toString(); String sql ="Insert into t_grzx_mk(id,ry_id,module_name,disp_sn,bz) values(?,?,?,?,?)"; LobHandler handler = new DefaultLobHandler(); Integer execute = jt.execute(sql, new AbstractLobCreatingPreparedStatementCallback(handler) { @Override protected void setValues(PreparedStatement ps, LobCreator lobCreator) throws SQLException, DataAccessException { int index = 1; ps.setObject(index++, uuid); ps.setObject(index++, grzxXmry.getRyId()); ps.setObject(index++, grzxXmry.getModuleName()); ps.setObject(index++, grzxXmry.getDispSn()); ps.setObject(index++, grzxXmry.getBz()); } } ); this.close(); return execute; } |
这里也没啥好说的,唯一点就是使用了DefaultLobHandler这个类,来实现插入一些BLOB(二进制大型对象)和 CLOB(字符大型对象)数据,参考链接:
https://www.cnblogs.com/dalianpai/p/13725105.html
至此,插入功能就结束了,其他删改查的功能我不会重复写,但这里列出一些需要注意的细节
查删改中的细节
既然新增不能插入重复的数据,那么修改也需要先进行一次判断吗?不用,可以直接将下面列表进行固定,锁住。
input文本框渲染值和下拉框如何渲染值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
postAjax(requestUrl + "/perCenter/getModuleByParCode", param, function (data) { $.each(data, function (index, value) { $("#moduleCode").append('<option value=' + value.code + '>' + value.name + '</option>'); }); layui.form.render("select"); postAjax(requestUrl + "/perCenterApi/selectGrzxXm", param, function (data) { console.log(data) // $('#moduleCode').val(data.data.moduleName);//模块名称 //下拉框设置值得这么写 $('#moduleCode').find("option:selected").text(data.data.moduleName) $('#dispSn').val(data.data.dispSn);//排序 $('#remark').val(data.data.bz);//备注 form.render('select'); }); $('input,textarea,select').attr('disabled', 'disabled'); }); |
这里是查看ajax请求返回的数据并渲染到前端的代码,至于前面的下拉框锁住就是最后一句代码: $(‘input,textarea,select’).attr(‘disabled’, ‘disabled’);不过这里是全部锁住了,因为是查看嘛。
最顶层的查询和重置
这个模块涉及的知识点是;分页和模糊查询
1 2 3 4 5 6 7 8 9 |
//查询 $("#Fsearch").click(function () { getList(lbParmas); }); //重置 $("#Freset").click(function () { $("#MoudleName").val(""); getList(); }); |
实现的代码简单,关键还是之前写了刷新页面列表的函数getList,这个函数的查询可不是返回一个对象或者对象列表,而是返回一个页面对象。对的也就是分页功能。这里用到了layui的分页助手。下面看下这个函数吧
前端代码
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 |
postAjax(requestUrl + "/perCenterApi/selectGrzxXmMKList", lbParmas, function (data) { console.log(data) page.render({ elem: 'layuipage', count: data.count, contentType: "application/json;charset=UTF-8", curr: lbParmas.pageNo, limit: lbParmas.pageSize, first: "首页", last: "尾页", layout: ['count', 'prev', 'page', 'next', 'limit', 'skip'], jump: function (obj, first) { if (!first) { lbParmas.pageNo = obj.curr; lbParmas.pageSize = obj.limit; lbParmas.scenceIds=$("#scenceIds").val(); getAjax(requestUrl + "/perCenterApi/selectGrzxXmMKList", lbParmas,function (data) { perCenTable.reload({ data: data.list }); $('#perCenTable').parent().find('.layui-table-header').find('.layui-form-checkbox').hide(); }) } } }); |
controller层代码
1 2 3 4 5 6 7 8 9 10 11 12 13 |
@RequestMapping(value = "/selectGrzxXmMKList",name = "查询个人拥有的模块信息") @ResponseBody public Map<String,Object> selectGrzxXmMK(HttpServletRequest request, HttpServletResponse response, Integer pageNo, Integer pageSize,String moudleName,String id){ UserInfo userInfo= WebUtil.getCurrUserInfo(request, response); Map map = new HashMap<String,Object>(); List<GarzaXmas> garzaXmasList = personalCenterService.selectGrzxXmMK(id,userInfo,moudleName,pageNo,pageSize); int count = personalCenterService.selectGrzxXmMKCount(userInfo); map.put("list",garzaXmasList); map.put("count",count); return map; } |
从controller可以看到,查询数据库需要的参数是pageSize,pageNo。如果没有模糊查询的话是不需要moudleName这个参数的,id参数多余的别用看。事实上也正是如此,分页查询使用limit正是需要pageSize和pageNo,模块名称则是用来where,看下面sql:
1 2 3 4 5 6 |
sb.append("select id,MODULE_NAME moduleName,BZ bz,DISP_SN dispSn\n" + " from t_grzx_mk where RY_ID = ? "); if (Utils.availableStr(moduleName)){ sb.append(" and MODULE_NAME like '%" + moduleName + "%'"); } sb.append("LIMIT " + (pageNo-1 ) * pageSize + "," + pageSize); |
总结
到此,这个小页面的增删改查笔记就写完了。如果文章有不对的地方,欢迎大佬们评论留言指出!