复盘工作2024-11
复盘工作-2024-11-01
1.问题:(编辑、查看页共用的)jsp页面当是从“查看”按钮点击进入时,禁用页面上“创建分票”按钮的最推荐方法:直接隐藏掉,通过jQuery的.hide()方法;以及这种思路相关我错误用法的分析:
/** * 问题:(编辑、查看页共用的)jsp页面当是从“查看”按钮点击进入时,禁用页面上“创建分票”按钮的最推荐方法: * 1.直接隐藏掉,通过jQuery的.hide()方法 * <a href="#" class="easyui-linkbutton l-btn l-btn-plain" plain="true" icon="icon-add" * onclick="updateRwCreateGzps(' 创建分票','','pmLpSelectPersonList','100%','100%')" id=""> * <span class="l-btn-left"><span class="l-btn-text icon-add l-btn-icon-left">创建分票</span></span> * </a> * jQuery的.hide()方法封装了“设置元素的display属性为none”这一操作,即其实现了.css('display', 'none'); * display是css属性,而不是HTML属性,所以不能用.prop('display', 'none'); */ $(function () { // 这里我一开始写错了:location.href.url.indexOf('load=createFpDetail') != -1 // 应该是location.href。 // location.href属于window.location对象,这个href属性代表了当前窗口(或标签页)的url地址 // location.href值如下: // http://localhost:8092/项目名/pmLpGzpController.do? // selectPersonList&gzpId=4028e4fa9240eb4a01924226fc300010 // &load=createFpDetail&_=1730462929196 // js里字符串的indexOf api:例如:a.indexOf(b),返回字符串a中子串b第一次出现的索引, // 如果存在则其值为0、1、2...(即索引) // 若返回-1,则说明不含子串b console.log('location.href: ' + location.href); if (location.href.indexOf('load=createFpDetail') != -1) { // url中含有load=createFpDetail,说明是点“查看”来打开的该页面 // (查看按钮点击后url里拼接load=createFpDetail的代码这里省略) debugger; // 我一开始写的.prop('disabled', true);无效。 // 因为对<a>标签(即超连接)本身并不支持disabled属性 // disabled主要用于表单元素,例如<button><input><select><textarea>的禁用 // $('.easyui-linkbutton.l-btn.l-btn-plain').prop('disabled', true); // 正确写法 $('.easyui-linkbutton.l-btn.l-btn-plain').hide(); // jQuery的.hide()方法封装了“设置元素的css属性,即设置元素的display属性为none”这一操作,即其实现了.css('display', 'none'); // $('.easyui-linkbutton.l-btn.l-btn-plain').css('display', 'none'); // 如下写法无效。display是css属性,而不是HTML属性,所以不能用.prop('display', 'none'); // $('.easyui-linkbutton.l-btn.l-btn-plain').prop('display', 'none'); // 同理.attr('display', 'none');也不好使,因为display不是HTML属性 // $('.easyui-linkbutton.l-btn.l-btn-plain').attr('display', 'none'); } })
复盘工作-2024-11-03
1.接上个问题:方法2(不好,不如直接隐藏掉按钮)禁用按钮的点击事件
/** * 问题:(编辑、查看页共用的)jsp页面当是从“查看”按钮点击进入时,禁用页面上“创建分票”按钮的最推荐方法: * 方法2(不好,不如直接隐藏掉按钮)禁用按钮的点击事件 * <a href="#" class="easyui-linkbutton l-btn l-btn-plain" plain="true" icon="icon-add" * onclick="updateRwCreateGzps(' 创建分票','','pmLpSelectPersonList','100%','100%')" id=""> * <span class="l-btn-left"><span class="l-btn-text icon-add l-btn-icon-left">创建分票</span></span> * </a> */ $(function () { console.log('location.href: ' + location.href); if (location.href.indexOf('load=createFpDetail') != -1) { // url中含有load=createFpDetail,说明是点“查看”来打开的该页面 // (查看按钮点击后url里拼接load=createFpDetail的代码这里省略) debugger; // 我一开始写的如下错误代码会报错:TypeError: $(...).onclick is not a function // 错误代码:$('.easyui-linkbutton.l-btn.l-btn-plain').onclick(function () { // 错误代码:tip('查看页面不允许创建分票'); // 错误代码:}) // 分析:jQuery中不能使用原生dom属性例如onclick。而应该使用.click(function(){}), // 或.on('click', function(){});.on('click', function(){});这种更合适 // $('.easyui-linkbutton.l-btn.l-btn-plain').click(function (event) { // debugger; // event.preventDefault(); // tip('test:查看页面不允许创建分票'); // }) // 使用.on('click', function(){}); $('.easyui-linkbutton.l-btn.l-btn-plain').on('click', function (event) { // 阻止默认事件 event.preventDefault(); tip('查看页面不允许创建分票'); }) } })
复盘工作-2024-11-05
1.问题:从旧功能复制代码过来后没有改了input、textarea标签的id字段但没改name字段,那么html提交表单时,后端从实体中获取该字段时为null。
分析:后端根据前端input、textarea标签的name字段(而不是id字段),来给(接收到的)实体赋值。所以input、textarea标签的id和name字段都要修改为正确的。
前端表单元素:
<!-- 从旧功能复制代码过来后没有改了input、textarea标签的id字段但没改name字段,那么html提交表单时,后端从实体中获取该字段时为null。 分析:后端根据前端input、textarea标签的name字段(而不是id字段),来给(接收到的)实体赋值。所以input、textarea标签的id和name字段都要修改为正确的。 --> <textarea id="delayReason" style="width:600px;" class="inputxt" rows="2" name="delayReason" datatype="*">${pmYjRwcPage.delayReason}</textarea>
后端:
@RequestMapping(params = "practiceDoUpdateDelayInfo") @ResponseBody public AjaxJson practiceDoUpdateDelayInfo(PmYjRwcEntity pmYjRwc, HttpServletRequest request) { // 当前端提交的form表单里没有name='delayReason'的输入框时,后端这里pmYjRwc实体中就获取不到delayReason,其值为null // 只有当前端提交的form表单里有name='delayReason'的输入框时,后端这里pmYjRwc实体中才能获取到前端录入的delayReason String delayReason = pmYjRwc.getDelayReason(); System.out.println(delayReason); // 具体业务代码省略 }
2.问题:(1)给表格加文件类型图标:js动态根据文件扩展名来给表格设置不同的背景图片来实现;(2)相关样式梳理;(3)js里switch语句练习
jsp页面:
<t:dgCol title="文件名称" field="fileName" formatterjs="addBackground" align="left" query="true" queryMode="single" width="600"></t:dgCol> <script src="webpage/com/pmis/archive/practicePmArchiveList.js"></script>
practicePmArchiveList.js:
/** * (1)给表格加文件类型图标:js动态根据文件扩展名来给表格设置不同的背景图片来实现;(2)相关样式梳理;(3)js里switch语句练习 * * css是静态的样式表语言,它在页面加载时已经确定,且不支持动态传递参数。 * 无法实现根据参数(这里即文件扩展名)来设置不同的backgroundImage的url路径, * 所以采用js来实现,创建span,然后根据文件扩展名动态设置属性(即不同的backgroundImage的url路径) * @param value * @param row * @param index * @returns {string} */ function addBackground(value, row, index) { // 获取文件类型(扩展名) let fileExtension = row.fileExtension; /** * js连续判断 switch格式,两种用法: * switch(true) { * case (用于判断true还是false的表达式): * // 该情况下要执行的语句; * // break;跳出switch语句,不再执行后续case和default * break; * case (用于判断true还是false的表达式): * // 该情况下要执行的语句; * // break;跳出switch语句,不再执行后续case和default * break; * default: * // 当表达式不等于任何一个value时执行的代码; * } * 或 * switch(变量值) { * case (常量值1): * // 该情况下要执行的语句; * // break;跳出switch语句,不再执行后续case和default * break; * case (常量值2): * // 该情况下要执行的语句; * // break;跳出switch语句,不再执行后续case和default * break; * default: * // 当表达式不等于任何一个value时执行的代码; * } */ // switch(true) { // case ('png' === fileExtension || 'jpg' === fileExtension): // fileExtension = 'jpg'; // break; // case ('doc' === fileExtension || 'docx' === fileExtension): // fileExtension = 'docx'; // break; // case ('pdf' === fileExtension): // fileExtension = 'pdf'; // break; // case ('xls' === fileExtension || 'xlsx' === fileExtension): // fileExtension = 'xlsx'; // break; // case ('zip' === fileExtension || 'rar' === fileExtension): // fileExtension = 'zip'; // break; // default: // fileExtension = 'default'; // } // 等价于如下用法: // switch(fileExtension) { // case ('png'): // fileExtension = 'jpg'; // break; // case ('jpg'): // fileExtension = 'jpg'; // break; // case ('doc'): // fileExtension = 'docx'; // break; // case ('docx'): // fileExtension = 'docx'; // break; // case ('pdf'): // fileExtension = 'pdf'; // break; // case ('xls'): // fileExtension = 'xlsx'; // break; // case ('xlsx'): // fileExtension = 'xlsx'; // break; // case ('zip'): // fileExtension = 'zip'; // break; // case ('rar'): // fileExtension = 'zip'; // break; // default: // debugger; // fileExtension = 'default'; // } /** * 优化上述代码: */ switch(fileExtension) { case ('png'): case ('jpg'): fileExtension = 'jpg'; break; case ('doc'): case ('docx'): fileExtension = 'docx'; break; case ('pdf'): fileExtension = 'pdf'; break; case ('xls'): case ('xlsx'): fileExtension = 'xlsx'; break; case ('zip'): case ('rar'): fileExtension = 'zip'; break; default: fileExtension = 'default'; } /** * 如下写法是错误的 * * 分析:switch语句在js里并不是一个表达式,而是一个语句, * 这意味着switch不能直接返回一个值给变量,它只能根据条件执行不同的代码块 * switch(变量名或表达式) { * case value1: * // 当表达式等于value1时执行的代码 * break; * case value1: * // 当表达式等于value1时执行的代码 * break; * default: * // 当表达式不等于任何一个value时执行的代码 * } */ // fileExtension = switch(fileExtension) { // case ('png'): // return 'jpg'; // break; // case ('rar'): // return 'zip'; // break; // default: // return 'default'; // } /** * 如下写法是错误的: * * 如下写法无法实现预期逻辑, * case ('png' || 'jpg')最终只会取'png' * 分析:'png' || 'jpg',是||运算,最终返回true or false,这时'png'被视为true,所以该表达式最终返回true, * 然而switch是与case后面的常量值进行严格相等===判断, * 即case ('png' || 'jpg')最终返回的是'png', * (即并没有'jpg')所以最终无法实现预期逻辑 * * 即case ('png' || 'jpg')实际等同于case ('png'), */ // 错误代码: // switch(fileExtension) { // case ('png' || 'jpg'): // fileExtension = 'jpg'; // break; // case ('doc' || 'docx'): // fileExtension = 'docx'; // break; // case ('pdf'): // fileExtension = 'pdf'; // break; // case ('xls' || 'xlsx'): // fileExtension = 'xlsx'; // break; // case ('zip' || 'rar'): // fileExtension = 'zip'; // break; // default: // fileExtension = 'default'; // } /** * 创建span元素 * 1.一开始我写的是错的:let span = document.createElement('<span>'); * F12会报错:Uncaught DOMException: Failed to execute 'createElement' on 'Document': * The tag name provided ('<span>') is not a valid name. * 2.分析:document.createElement();接收一个参数,参数是HTML标签的名称,而不是整个标签。 * 即应该是document.createElement('span');且标签名称应使用小写。 * document.createElement()会创建一个新元素,并返回该元素的引用。 */ let span = document.createElement('span'); /** * 设置背景图片 * 1.我一开始写的是错的:span.style.backgroundImage('url(/images/archive/' + fileExtension + '.png'); * F12里报错:TypeError: span.style.backgroundImage is not a function * 2.分析: * 2.1错误1:在js中,span.style.backgroundImage不是一个函数,而是一个属性。 * (后面设置的display、transform等等也同理,都是属性,而不是函数) * 因此不能像调用函数那样给其传值。 * 2.2错误2: */ span.style.backgroundImage = 'url(images/archive/' + fileExtension + '.png)'; /** * span.style.display='inline-block'使元素即可以设置宽高,又可以像内联元素一样排列。 */ span.style.display = 'inline-block'; /** * 向下偏移5px * 分析:英文里transform意为“改变” * transform属性,用于元素的移动、旋转等 * translateY()函数是transform属性的一个值 * translateY(参数值),延Y轴平移,y轴向下为正向,参数值为正数时,即向下平移;参数值为负数时,即向上平移; */ span.style.transform = 'translateY(5px)'; /** * 设置图片大小 * 1.我一开始写的是错误的:span.style.backgroundSize='contains'; * 2.分析:span.style.backgroundSize有两个属性值可以设置为与“包含”有关的行为,分别是“contain”和“cover”。 * contain:保持图像的宽高比,缩放图像使其完全适应背景定位区域,同时确保图像的至少一个维度(宽或高)等于背景定位区域的相应维度。 * cover:保持图像的宽高比,缩放图像使其完全覆盖背景定位区域,同时确保图像的至少一个维度(宽或高)等于或超过背景定位区域的相应维度。 */ span.style.backgroundSize = 'contain'; span.style.backgroundPosition = 'center center'; span.style.width = '20px'; span.style.height = '20px'; span.style.backgroundRepeat = 'no-repeat'; return span.outerHTML + value + '.' + row.fileExtension; }
复盘工作-2024-11-06
1.问题:sql向oracle库里某表加两个字段,分别是varchar2(32)和DATE类型
分析:语法:alter table 表名 add 列名 varchar2(30)(即类型);
alert table 表名 add 列名 date(即类型);
comment on column 表名.列名 is 注释;
/* 问题:sql向oracle库里某表加两个字段,分别是varchar2(32)和DATE类型 分析:语法:alter table 表名 add 列名 varchar2(30)(即类型); alert table 表名 add 列名 date(即类型); comment on column 表名.列名 is 注释; */ /* 我一开始写错了:alter table 表名 add column 列名 varchar2(30) 正确的:去掉column关键词:alter table 表名 add 列名 varchar2(30)*/ alter table practice_rwc add delay_person_id varchar2(32); /* 我一开始写错了:comment on practice_rwc.delay_person_id is '延期人ID'; 正确的:要加column关键词:comment on column practice_rwc.delay_person_id is '延期人ID';*/ comment on column practice_rwc.delay_person_id is '延期人ID'; /* date类型 */ alter table practice_rwc add delay_create_date date; comment on column practice_rwc.delay_create_date is '延期创建时间';
复盘工作-2024-11-07
1.问题:测试发现前端请求无法进入后端接口里的断点
原因:控制器接口应该定义为public而不是private
分析:springmvc使用反射机制来查找和调用控制器方法。反射机制要求被调用的方法必须是可访问的。而java中用private访问修饰符修饰的方法,对于类外部(包括框架代码)是不可访问的。spring通过依赖注入和aop(面向切面编程)代理来管理bean的生命周期和行为。这些机制也依赖于反射机制来工作,同样受到java访问控制规则的限制。 所以使用springmvc框架时,控制器接口都应声明为public。
/** * 问题:测试发现前端请求无法进入后端接口里的断点 * 原因:控制器接口应该定义为public而不是private * 分析:springmvc使用反射机制来查找和调用控制器方法。 * 反射机制要求被调用的方法必须是可访问的。 * 而java中用private访问修饰符修饰的方法,对于类外部(包括框架代码)是不可访问的。 * * spring通过依赖注入和aop(面向切面编程)代理来管理bean的生命周期和行为。 * 这些机制也依赖于反射机制来工作,同样受到java访问控制规则的限制。 * * 所以使用springmvc框架时,控制器接口都应声明为public。 * @param swbm * @return */ @RequestMapping(method = RequestMethod.GET,value = "checkSwbm") @ResponseBody public Result checkSwbm(String swbm){ // 省略具体业务代码 }
2.问题:访问功能,后端报错:org.hibernate.hql.internal.ast.QuerySyntaxException:PmSjbQrCodeEntity is not mapped [from PmSjbQrCodeEntity where sbId=?
解决:这种错误通常是hibernate没扫描到实体类造成的。
分析:在spring配置文件中,当使用通配符(例如*.)来指定hibernate应该扫描的包时, 这个通配符只能匹配直接子包,而不能递归地匹配多层子包。 即<value>com.pmis.sbtz.*.entity</value>只能匹配com.pmis.sbtz.somepackage.entity包下的实体。 所以要加上<value>com.pmis.sbtz.tz.*.entity</value>这样才能匹配到com.pmis.sbtz.tz.somepackage.entity包下的实体。
spring-mvc-hibernate.xml:
<property name="packagesToScan"> <list> <value>com.pmis.sbtz.*.entity</value><!-- pmis --> <!-- 问题:访问功能,后端报错:org.hibernate.hql.internal.ast.QuerySyntaxException: PmSjbQrCodeEntity is not mapped [from PmSjbQrCodeEntity where sbId=? 解决:这种错误通常是hibernate没扫描到实体类造成的。 分析:在spring配置文件中,当使用通配符(例如*.)来指定hibernate应该扫描的包时, 这个通配符只能匹配直接子包,而不能递归地匹配多层子包。 即<value>com.pmis.sbtz.*.entity</value>只能匹配com.pmis.sbtz.somepackage.entity包下的实体。 即匹配不到PmSjbQrCodeEntity在com.pmis.sbtz.sjbsb.tz.entity包下的实体类, 所以要加上<value>com.pmis.sbtz.sjbsb.*.entity</value>这样才能匹配到com.pmis.sbtz.sjbsb.somepackage.entity包下的实体--> <value>com.pmis.sbtz.sjbsb.*.entity</value> </list> </property>
3.idea里怎样快速查看某个controller有哪些方法:编辑窗口打开该controller后,在idea左下角点击“Structure”选项卡就能看到类的方法。
4.问题:(1)在jsp里写样式:用<style><style>标签;(2)图片背景时,url的写法;(3)display属性为inline-block,而不是我一开始错误的:设置position属性为inline-block
<script type="text/javascript"> function addBackground(value, row, index) { return "<span class='fileExtensionBackground'></span>" + value + "." + row.fileExtension; } </script> <!-- 我一开始写错了:<script type="text/css">...这种不对,应该在<style></style>里写样式 --> <style> .fileExtensionBackground{ /* 一开始我写做:background-image: url("/images/archive/pdf.png"); 报错:GET http://localhost:8092/images/archive/pdf.png 404 (Not Found) 分析: 1.当写做/images时,浏览器会尝试从网站根目录开始查找图片,实际上图片并不在网站根目录下的/images/archive/文件夹中, 所以会报错404 not defined 2.正确的:写做url("images/archive/pdf.png");时,浏览器会相对于当前HTML文件的位置来查找图片。这个我就记住就行了。 */ background-image: url("images/archive/pdf.png"); background-repeat: no-repeat; background-position: center center; background-size: contain; /* 我一开始写错了:position: inline-block;这样图片无法展示, 应该是display: inline-block; 分析:display属性控制元素的显示类型。 其取值例如:block即块级元素;inline即内联元素; inline-block:像内联元素一样不会占据整行,但可以设置宽高。 而position属性决定了元素的定位方式。 其取值例如:relative即相对定位;absolute即绝对定位。*/ display: inline-block; width: 20px; height: 20px; transform: translateY(5px); } </style>
复盘工作2024-11-14:
1.<f7-list-input class="block-input" placeholder="暂无数据" :disabled="!getEditStatus('bdepBcaqcs')||ticketWriteType!true||ticketType!1?'disabled':'is-required data-item-required'" resizable label="补充安全措施" type="textarea" :value="mainTable.bdepBcaqcs" @input="(event) => this.handleInput(event, 'list-item-8', 'bdepBcaqcs')"></f7-list-input>报错:Invalid prop: type check failed for prop "disabled". Expected Boolean, got String with value "is-required data-item-required".
分析:disabled属性需要提供个true/false值,而不是这里提供的字符串’disabled'和'is-required data-item-required'。
这里可以根据具体需求,改为如下两种:
1.1:disabled="!getEditStatus('bdepBcaqcs')||ticketWriteType!true||ticketType!1" 这种控制是否disabled
1.2:class="!getEditStatus('bdepBcaqcs')||ticketWriteType!true||ticketType!1?'disabled':'is-required data-item-required'" 这种是根据条件使用不同的样式,备注:项目里已分别定义了’disabled'和'is-required data-item-required'这两种样式。
2.业务js里怎样使用常量js里定义的常量:
分析:常量js里定义的常量是全局作用域,而业务js同样是定义在全局作用域,所以直接使用即可。但要注意在引入业务js的业务jsp页面里,需要先引入常量js,再引入业务js,这里涉及到js加载顺序问题。
业务jsp里:
<!-- 引入常量js --> <script src='webpage/common/js/constants.js'></script> <!-- 引入业务js --> <script src='webpage/com/项目名/common/workflow/wfTodoTasksList.js'></script>
常量js里:即constants.js:
// 山东 const PRACTICE_PROVINCE_SHANDONG = 'Shandong';
业务js里:即wfTodoTasksList.js:
function practiceMethod() { // 直接使用即可 console.log(PRACTICE_PROVINCE_SHANDONG); }
3.<f7-list inline-labels no-hairlines-md class="first-page">这代码里inline-labels no-hairlines-md是什么?
分析:这是framework7里,两个类名,定义了<f7-list>的某些样式或行为。我知道这点就够了。
4.<f7-list-input label="单位" type="text" :value="mainTable.sysCompanyName" @input="mainTable.sysCompanyName = $event.target.value" disabled></f7-list-input>和<f7-list-input readonly label="单位" type="text" :value="mainTable.sysCompanyName" @input="mainTable.sysCompanyName = $event.target.value"></f7-list-input>有什么异同?哪个好
分析:这个我记住就行:disabled和readonly都能实现禁用编辑;基于实际使用场景,readonly更好,因为readonly允许用户选中input框的内容(后续可以用于复制等操作)。
并且当设置disabled或readonly后,如果确定业务上始终不可编辑,则建议删掉冗余的input事件:@input="mainTable.sysCompanyName = $event.target.value"
5.关于项目里常用的<f7-list>、<f7-list-item>、<f7-accordion-content>、<f7-list-input>结构:分析如下,我记住即可:accordion /əˈkɔːdiən/ 手风琴
<!-- f7-list定义一个列表 --> <f7-list> <!-- f7-list-item定义列表项 --> <f7-list-item> <!-- f7-accordion-content定义手风琴样式 --> <f7-accordion-content> <!-- f7-list-input定义输入框组件,例如文本输入框、选择器、单选多选 --> <f7-list-input></f7-list-input> </f7-accordion-content> </f7-list-item> </f7-list>
6.关于ul li:last-child{background: green;}和ul li:last{background: green;}的区别:
分析:ul li:last-child是标准的css选择器,选中ul下最后一个li元素,注意:如果ul下的最后一个子元素不是li,则这个li不会被选中。
ul li:last不是标准的css选择器,而是jQuery提供的选择器,其选中ul的最后一个li元素,注意:即使这li不是ul的最后一个子元素,这个li也会被选中。
复盘工作2024-11-15:
1.PmLpGzpEntity pmLpGzpEntity = new PmLpGzpEntity();然后代码中手动设置id为uuid,最后调框架的saveOrUpdate();方法,运行时后端报错:org.hibernate.HibernateException: identifier of an instance of com.pmis.lp.entity.PmLpChildlistEntity was altered from 4028e5389292f67c019293475dd80004 to null
分析:当实体id字段的getId()方法上有自动生成id的注解时(即给id设置了自动生成策略),如果代码中手动再设置id,然后调用saveOrUpdate方法,因为手动设置了id,hibernate在缓存中根据此代码里手动生成的id查找不到对象,就会报这错。
解决方法:PmLpGzpEntity pmLpGzpEntity = new PmLpGzpEntity();创建对象后,代码中不手动设置id,设置其他业务字段后,直接调用save()方法;然后通过pmLpGzpEntity.getId()方法获取id,用于后续业务操作。
实体上配置的id的自动生成策略:
@Id @GeneratedValue(generator = "paymentableGenerator") @GenericGenerator(name = "paymentableGenerator", strategy = "uuid") @Column(name ="ID",nullable=false,length=32) public java.lang.String getId(){ return this.id; }
复盘工作-2024-11-20
1.练习:前后端请求路径映射,以及前端向后端传数组参数,以“发布计划”接口为例
后端:
@Controller @RequestMapping("/pmYjXsZnxsjhController") public class PmYjXsZnxsjhController extends BaseController { @RequestMapping(params = "practiceDoFbjh") @ResponseBody public AjaxJson practiceDoFbjh(@RequestParam(value = "ids[]", required = false) List<String> ids) { System.out.println(ids); AjaxJson ajaxJson = new AjaxJson(); ajaxJson.setMsg("计划发布成功"); ajaxJson.setSuccess(true); return ajaxJson; } /** * 练习:前后端请求路径映射,以及前端向后端传数组参数,以“发布计划”接口为例 * * 问题1:路径映射: * @RequestMapping(params = "prefixpracticeDoFbjh")会检查请求路径中是否含有参数:prefixpracticeDoFbjh,若有则会匹配该请求。 * 即当url为'pmYjXsZnxsjhController.do?prefixpracticeDoFbjh'时,会请求到该接口。 * * 疑问:当分别有:practiceDoFbjh、prefixpracticeDoFbjh、practiceDoFbjhSuffix、prefixpracticeDoFbjhSuffix * 这4个@RequesMapping(params = "")的值时,是怎么匹配的呢? * 验证结果:当url为'pmYjXsZnxsjhController.do?prefixpracticeDoFbjh'时,会请求到prefixpracticeDoFbjh接口, * 而不会请求到practiceDoFbjh接口。 * 原因分析:1.当分别有:practiceDoFbjh、prefixpracticeDoFbjh、practiceDoFbjhSuffix、prefixpracticeDoFbjhSuffix * 这4个@RequesMapping(params = "")的值时,后端会检测路径中是否含有这4个值, * 当url为'pmYjXsZnxsjhController.do?prefixpracticeDoFbjh'时,practiceDoFbjh、prefixpracticeDoFbjh都满足, * 这时,springmvc会精确匹配到完全相同的路径:prefixpracticeDoFbjh。 * 2.我测试实际当后端没有 @RequestMapping(params = "prefixpracticeDoFbjh") 接口时, * 路径请求prefixpracticeDoFbjh接口后端会直接报错。 * 结论:结合例子试验,我就记住该怎么用就行了:@RequestMapping(params = "")这种路径映射,就可以理解为完全匹配。 * 即要请求到@RequestMapping(params = "methodA")接口,则url写法就应该是:'pmYjXsZnxsjhController.do?methodA'。 * * 问题2:前端向后端传数组参数 * 分析:@RequestParam(value = "ids[]", required = false) List<String> ids * springmvc中,@RequesParam用于将http请求参数区数据绑定到控制器方法的参数上, * value属性指定了请求参数的名称。value="ids[]",期望从http请求中获取名为ids的参数。 * []指明ids是数组,(在springmvc中,即使不使用[],主要参数类型为List或数组,spring也会将多个具有相同名称的参数值绑定到这个列表上。 * 因此value="ids"和value="ids[]"在绝大多数情况下是等效的,但value="ids[]"更清晰) * required=false指明ids这参数不是必传的。 * List<String> ids,指明期望接收一个String类型的列表。springmvc会将请求中名为ids[]的所有参数值转成字符串并添加到列表中。 * * 补充:@ResponseBody声明该方法的返回值应作为http响应体返回,而不是解析作为视图名。 * 由于@ResponseBody的存在,springmvc会自动将AjaxJson对象(这是jeecg框架封装的实体类用于后端返回信息)转换成JSON格式的字符串, * 并作为http响应体返回给前端。 */ @RequestMapping(params = "prefixpracticeDoFbjh") @ResponseBody public AjaxJson prefixpracticeDoFbjh(@RequestParam(value = "ids[]", required = false) List<String> ids) { System.out.println(ids); AjaxJson ajaxJson = new AjaxJson(); ajaxJson.setMsg("计划发布成功"); ajaxJson.setSuccess(true); return ajaxJson; } @RequestMapping(params = "practiceDoFbjhSuffix") @ResponseBody public AjaxJson practiceDoFbjhSuffix(@RequestParam(value = "ids[]", required = false) List<String> ids) { System.out.println(ids); AjaxJson ajaxJson = new AjaxJson(); ajaxJson.setMsg("计划发布成功"); ajaxJson.setSuccess(true); return ajaxJson; } @RequestMapping(params = "prefixpracticeDoFbjhSuffix") @ResponseBody public AjaxJson prefixpracticeDoFbjhSuffix(@RequestParam(value = "ids[]", required = false) List<String> ids) { System.out.println(ids); AjaxJson ajaxJson = new AjaxJson(); ajaxJson.setMsg("计划发布成功"); ajaxJson.setSuccess(true); return ajaxJson; } }
前端:
function practiceDoFbjh(title,url, id,width,height) { let rows = $('#' + id).datagrid('getSelections'); if (rows != null && rows.length > 0) { let selectedIds = []; for (let i = 0; i < rows.length; i++) { selectedIds.push(rows[i].id); } $.ajax({ url: 'pmYjXsZnxsjhController.do?practiceDoFbjh', type: 'post', data: { ids: selectedIds }, cache: false, success: function (jsonObject) { let d = $.parseJSON(jsonObject); if (d.success) { tip(d.msg); // 后续业务逻辑省略 } } }) } } function prefixpracticeDoFbjh(title,url, id,width,height) { let rows = $('#' + id).datagrid('getSelections'); // js获取数组长度,用.length属性:获取数组或字符串长度,使用.length属性 if (rows != null && rows.length > 0) { let selectedIds = []; for (let i = 0; i < rows.length; i++) { selectedIds.push(rows[i].id); } // $.ajax是jQuery库提供的一个方法。用于执行异步http(Ajax)请求 $.ajax({ // url 请求路径 url: 'pmYjXsZnxsjhController.do?prefixpracticeDoFbjh', // type设置请求类型 type: 'post', // data参数包含要发送到服务端的数据,这里是一个对象{ids: selectedIds} data: { // 传递id数组 ids: selectedIds }, // cache: false,不缓存请求的响应 cache: false, // success是回调函数,当请求成功时执行,接收服务端返回的数据作为参数 success: function (jsonObject) { // $.parseJSON将JSON字符串转换成js对象 let d = $.parseJSON(jsonObject); if (d.success) { tip(d.msg); // 后续业务逻辑省略 } } }) } }
浙公网安备 33010602011771号