ajax实际上是通过XMLHttpRequest来向服务器发送异步请求的,从服务器获取数据,然后使用JS来更新页面,这也就是常说的局部刷新实现方式,所以ajax请求之后,服务器返回的都是纯文本流,客户端的浏览器在获取ajax异步结果时,不是直接显示在页面上,而是要通过js来进行处理,js处理完以后才能显示在页面上,所以这才导致了controller中的ModelAndView对象不能直接返回视图
解决方法:
将页面跳转的控制放到前端页面的js中来进行跳转,即kk = 'xxxxxxx’
$.ajax({
url: "toMain",//请求路径
type: "post",//请求方式
data: {"username": name, "password": pwd},
success: function (data)
{
alert(data);
if(data==1)
{
window.location.href="toRealMain";
}
else
{
window.location.href="/";
}
}
});
<!-- 数据表格 -->
<table id="demo" lay-filter="test"></table>
</div>
</div>
</div>
<!-- 引入 layui.js -->
<script src="../js/layui.js"></script>
<script>
layui.use(['element','table'], function(){
var element = layui.element;
var table = layui.table;
//第一个实例
table.render({
elem: '#demo'
,height: 400//容器高度
,url: 'getAllUsers' //数据接口
,page: true //开启分页
,cols: [
[ //表头
{field: 'id', title: 'ID', width:150, sort: true, fixed: 'left'}
,{field: 'useraccount', title: '用户名', width:150,sort:true}
,{field: 'userpassword', title: '密码', width:150, sort: true}
,{field: 'flag', title: '权限', width: 100,sort:true}
,{field: 'powerofcourse', title: '课程权限', width: 200,sort:true}
,{field: 'information', title: '信息', width: 200, sort: true}
]
]
, method: "post"
, page: true
, limits: [3, 5, 10] //一页选择显示3,5或10条数据
, limit: 5 //一页显示10条数据
, parseData: function (res) { //将原始数据解析成 table 组件所规定的数据,res为从url中get到的数据
var result;
if (this.page.curr) {
result = res.data.slice(this.limit * (this.page.curr - 1), this.limit * this.page.curr);
} else {
result = res.data.slice(0, this.limit);
}
return {
"code": res.code, //解析接口状态
"msg": res.msg, //解析提示文本
"count": res.count, //解析数据长度
"data": result //解析数据列表
};
}
});
});
//退出登录按钮点击
$("#dropDown").click(function (){
//置空令牌
window.localStorage.xfxAdminToken="null";
//发送请求跳转到登录页面
//跳转到登录页面
window.location.href="/";
window.event.returnValue=false;
})
</script>
controller层代码:
//获取所有用户,并以json数组形式返回
@ResponseBody
@PostMapping("/getAllUsers")
public String getAllManagers()
{
//获取所有管理员
List<User> users = userService.list();
String jsonString = JSON.toJSONString(users);
System.out.println(jsonString);
//状态码,响应消息,数量,数据
String allManagers ="{\"code\":"+0+",\"msg\":\"ok\",\"count\":"+users.size()+",\"data\":"+jsonString+"}";
return allManagers;
}
点了好多条都不知道说的什么,后面还是我自己总结出了解决的办法。table的渲染提供了一个后端接口的参数
然而这个接口要求后端的数据进行格式化的处理,然而这个会和分页功能产生冲突导致分页失效。layui还提供了data参数去渲染数据,建议使用layui的表格组件时不用url,直接用data去渲染数据,就是先请求后台,返回数据再render。
,cols: [
[ //表头
{field: 'id', title: 'ID', width:150, sort: true, fixed: 'left'}
,{field: 'useraccount', title: '用户名', width:150,sort:true}
,{field: 'userpassword', title: '密码', width:150, sort: true}
,{field: 'flag', title: '权限', width: 100,sort:true}
,{field: 'powerofcourse', title: '课程权限', width: 200,sort:true}
,{field: 'information', title: '信息', width: 200, sort: true}
]
]
将[[…]]分割开来写
对于ajax,一般情况下我们都是在success函数中进行逻辑处理,
但是在某些特定的时候我们需要在ajax外对返回值进行处理,这时我们只需要进行下面两个操作:
1、async设置为false,也就是同步;
2、在方法内ajax外设置一个全局变量,用这个变量去接收success函数内的返回值。
//服务器端检测令牌是否可用
function VerifyServerToken(type)
{
if(type=="admin")
{
var ManagerName = "";
//检验令牌
$.ajax({
url: "getManageName",
type: "post",
async : false,
data: {"adminToken": window.localStorage.xfxAdminToken},
success: function (name)
{
if (name.length <=0){
//令牌置空
window.localStorage.xfxAdminToken = "null";
window.location.href = "/";
window.event.returnValue = true;
}
ManagerName=name;
}
});
return ManagerName;
}
}
ajax是异步请求,如果上面不让ajax变成同步请求,那么先执行完return语句,过了一会,ajax请求和成功回调函数才会执行完
<table class="layui-table" lay-data="{url:'******', id:'test3'}" lay-filter="test3">
<thead>
<tr>
<th lay-data="{type:'checkbox'}">ID</th>
<th lay-data="{field:'id', width:80}">ID</th>
<th lay-data="{field:'t1'}">测试项</th>
<th lay-data="{field:'t2', edit: 'text'}">标定参数</th>
<th lay-data="{field:'t3'}">标定值</th>
</tr>
</thead>
</table>
table.on('checkbox(test3)', function(obj){
var checkStatus = table.checkStatus('test3');
console.log(checkStatus.data)//选中行数据
console.log(checkStatus.data.length)
})
$('.ttes').click(function () {
var index = parent.layer.getFrameIndex(window.name);
var checkStatus = table.checkStatus('test3');
console.log(checkStatus.data)//选中行数据
var items = checkStatus.data;
//监听行工具事件---监听删除按钮
table.on('tool(test)', function (obj) {
var data = obj.data;
//如果删除按钮按下了
if (obj.event === 'del') {
layer.confirm('确认删除当前管理员吗?', {
btn: ['删除', '取消']
, yes: function (index, layero) {
//点击删除按钮
//发送ajax请求前往服务器,去数据库中删除当前选择的管理员
$.ajax({
url: "delManagerById",
type: "post",
data: {"id": data.id,"adminToken":adminToken},
success: function (data) {
if (data == 0) {
//删除成功
layer.alert('删除成功', {icon: 1});
obj.del(); //删除对应行(tr)的DOM结构,并更新缓存
}
else if(data==-1){
//出现异常,删除失败
layer.msg('出现异常,删除失败', {icon: 5});
}
else if(data==-2)
{
//令牌失效
window.localStorage.xfxAdminToken="null";
window.location.href="/";
window.event.returnValue=false;
}
}
});
layer.close(index);
}
, btn2: function (index, layero) {
//点击取消按钮
layer.close(index);
}
});
}
});
//复选框勾选事件
table.on('checkbox(test)', function (obj) {
var checkStatus = table.checkStatus('test');
//删除被勾选的行元素集合
//checkStatus.data:被选中的元素集合
//checkStatus.data.length:被选中的元素个数
//当头部工具栏的删除按钮被点击时,获取被选中元素集合,进行批量删除操作
$("#topDelBtn").click(function () {
//弹出确认是否删除的按钮
layer.confirm('确认批量删除选中的管理员?', {
btn: ['确认', '取消'] //可以无限个按钮
, yes: function (index, layero) {
//删除
//获取删除用户的id集合
var arr = new Array();
for (i = 0; i < checkStatus.data.length; i++) {
arr[i] = checkStatus.data[i].id;
}
//发送ajax请求前往服务器执行批量删除的操作
$.ajax({
url: "delManagerByIds",
type: "post",
data: {"ids": arr,"adminToken":adminToken},
success: function (data) {
if (data == 0) {
//删除成功
layer.msg('删除成功', {icon: 6});
//刷新当前页面
window.location.href = "toListAll";
window.event.returnValue = false;
}
else if (data == -1)
{
//出现异常删除失败
layer.msg('出现异常,删除失败,请重新尝试', {icon: 2});
}
else if(data==-2)
{
//令牌失效
window.localStorage.xfxAdminToken="null";
window.location.href="/";
window.event.returnValue=false;
}
}
});
},
btn2: function (index, layero) {
//取消
layer.msg('删除失败', {icon: 5});
}
});
});
});
});
html网页什么样的字体最好看,css设置各种中文字体样式代码
cookie,session,localStorage,sessionStorage的区别
style里面设置内样式:
1.display:none;
2.visibility:hidden;
/!*
如果是页面层
*/
layer.open({
type: 1,
content: '传入任意的文本或html' //这里content是一个普通的String
});
layer.open({
type: 1,
content: $('#id') //这里content是一个DOM,注意:最好该元素要存放在body最外层,否则可能被其它的相对元素所影响
});
//Ajax获取
$.post('url', {}, function(str){
layer.open({
type: 1,
content: str //注意,如果str是object,那么需要字符拼接。
});
});
/!*
如果是iframe层
*/
layer.open({
type: 2,
content: 'http://sentsin.com' //这里content是一个URL,如果你不想让iframe出现滚动条,你还可以content: ['http://sentsin.com', 'no']
});
/!*
如果是用layer.open执行tips层
*/
layer.open({
type: 4,
content: ['内容', '#id'] //数组第二项即吸附元素选择器或者DOM
});
<div id="classInfo" style="padding:20px;display:none">
<table class="layui-hide" id="selectClassInfo" lay-filter="selectClassInfo"></table>
</div>
先写一个隐藏的div,里边写一个隐藏的table
原因:jquery 冲突
解决方法:将你html页面引入的jquery删掉,直接使用layui内置的jquery
删除页面引入的jquery
使用layui自带的jquery
或者:
,end: function(index, layero){
layer.close(index);
$('#divcontent').css({'display':'none'});
return false;
}
引入js
<!--引入下拉多级选择框组件-->
<script src="../js/xm-select.js"></script>
后端返回的数据格式:
//发送axios获取到user的数据
@GetMapping("/getCourses")
@ResponseBody
public String getCourses()
{
List<CourseName> courseNames = courseService.getALLCourseName();
//转换为JSON格式,返回给前端
String jsonString = JSONObject.toJSONString(courseNames);
String allCourses ="{\"code\":"+0+",\"msg\":\"success\",\"data\":"+jsonString+"}";
return allCourses;
}
前端处理:
<div class="layui-form-item">
<label class="layui-form-label">课程</label>
<div class="layui-input-block">
<div id="course" class="xm-select-demo"></div>
</div>
</div>
......
//渲染下拉多选框的数据
var demo1 = xmSelect.render({
el: '#course',
toolbar: {show: true},
prop: {
name: 'name',
value: 'id',
},
data: [],
tips: '为当前用户开通课程权限',
empty: '当前没有课程可以选择',
searchTips: '搜索课程',
paging: true,
pageSize: 3,
filterable: true,
pageEmptyShow: false,
theme: {
color: '#0081ff',
},
autoRow: true,
})
//使用axios发送ajax请求
axios({
method: 'get',
url: 'getCourses',
}).then(response =>
{
var res = response.data;
console.log(res);
demo1.update({
data: res.data,
autoRow: true,
})
});
也可以发送ajax请求,来获取数据,回显在多选下拉框中
使用ajax发送请求,记得设置响应数据的格式为json,不然默认是text文本
//课程分类的下拉多选框--courseType
var types = xmSelect.render({
el: '#courseType',
toolbar: {show: true},
data: [],
prop: {
name: 'type',
value: 'id',
},
tips: '课程分类',
empty: '课程分类为空',
searchTips: '搜索分类',
paging: true,
pageSize: 3,
filterable: true,
pageEmptyShow: false,
theme: {
color: '#0081ff',
},
autoRow: true,
})
//发送ajxa请求,用于展示所有课程数据
$.ajax({
url:"getCourseTypes",
type:"get",
dataType:"json",
success:function (data)
{
console.log(data.data);
//渲染多选下拉框
types.update({
data:data.data,
autoRow: true,
})
}
});
返回的data:
只需要将data.data交给多选下拉框渲染即可:
也就是说,其实后端也只需要返回data部分的格式数据即可,可以不用带着code和msg
后端,返回数据:
//返回所有课程的类型
@GetMapping("/getCourseTypes")
public String getCourseTypes()
{
List<Map<Integer, String>> courseTypes = courseService.getCourseTypes();
String jsonString = JSONObject.toJSONString(courseTypes);
String Types ="{\"code\":"+0+",\"msg\":\"success\",\"data\":"+jsonString+"}";
return Types;
}
1.你的mapper写在了java目录里面。例如下图:这样会出现一个问题,即使你在properties里面配置了 mybatis.mapper-locations= classpath:com/lihaoyu/demo/dao/*.xml,也没有用,因为编译的时候这个xml文件并没有被自动拉到target里面,毕竟编译的是.java文件而不是xml嘛,所以这时候应该在pom文件里面加上:
</build>
<resources>
<resource>
<directory>src/main/java</directory><!--所在的目录-->
<includes><!--包括目录下的.properties,.xml文件都会扫描到-->
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
</build>
如果你把xml放到了resources文件下,那么就只需要配置mybatis.mapper-locations=classpath:/mapper/.xml 就可以了,因为构建的时候会把resources里的东西自动拉到classpath下,注意.classpath意思就是编译后target文件夹下的classes目录.
2.xml里面的namespace不对 或者id和mapper里面的方法名不一样,或者parameterType对应不上,都会出现这种问题。
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.yml</include>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.yml</include>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
application.yml
#################### mybatis-plus配置 ###################
mybatis-plus:
#外部化xml配置
#config-location: classpath:mybatis-config.xml
#指定外部化 MyBatis Properties 配置,通过该配置可以抽离配置,实现不同环境的配置部署
#configuration-properties: classpath:mybatis/config/properties
#xml扫描,多个目录用逗号或者分号分割(告诉 Mapper 所对应的 XML 文件位置)
mapper-locations: classpath*:com/wongoing/sys/mapper/xml/*.xml
#MyBatis 别名包扫描路径,通过该属性可以给包中的类注册别名,多个路径用逗号分割
type-aliases-package: com.wongoing.sys.model
#如果配置了该属性,则仅仅会扫描路径下以该类作为父类的域对象
type-aliases-super-type: java.lang.Object
#枚举类 扫描路径,如果配置了该属性,会将路径下的枚举类进行注入,让实体类字段能够简单快捷的使用枚举属性
#type-enums-package: com.wongoing.sys.model
#项目启动会检查xml配置存在(只在开发时打开)
check-config-location: true
#SIMPLE:该执行器类型不做特殊的事情,为每个语句的执行创建一个新的预处理语句,REUSE:改执行器类会复用预处理语句,BATCH:该执行器类型会批量执行所有的更新语句
default-executor-type: REUSE
configuration:
# 是否开启自动驼峰命名规则(camel case)映射,即从经典数据库列名 A_COLUMN(下划线命名) 到经典 Java 属性名 aColumn(驼峰命名) 的类似映射
map-underscore-to-camel-case: true
# 全局地开启或关闭配置文件中的所有映射器已经配置的任何缓存,默认为 true
cache-enabled: true
#懒加载
aggressive-lazy-loading: true
#none:不启用自动映射 partial:只对非嵌套的 resultMap 进行自动映射 full:对所有的 resultMap 都进行自动映射
auto-mapping-behavior: partial
#none:不做任何处理 (默认值)warning:以日志的形式打印相关警告信息 failing:当作映射失败处理,并抛出异常和详细信息
auto-mapping-unknown-column-behavior: none
#如果查询结果中包含空值的列,则 MyBatis 在映射的时候,会不会映射这个字段
call-setters-on-nulls: true #允许在resultType="map"时映射null值
#这个配置会将执行的sql打印出来,在开发或测试的时候可以用
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
#是否允许映射结果为多个数据集
multiple-result-sets-enabled: false
global-config:
db-config:
#表名下划线命名默认为true
table-underline: false
#id类型: 0 # 0:数据库ID自增 1:用户输入id 2:全局唯一id(IdWorker) 3:全局唯一ID(uuid)
id-type: auto
#是否开启大写命名,默认不开启
capital-mode: false
#逻辑已删除值,(逻辑删除下有效) 需要注入逻辑策略LogicSqlInjector 以@Bean方式注入
logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)
#逻辑未删除值,(逻辑删除下有效)
logic-delete-value: 1 # 逻辑已删除值(默认为 1)
application.properties
########################## mybatis-plus配置 begin################################
##
#外部化xml配置
#mybatis-plus.config-location = classpath:mybatis-config.xml
#指定外部化 MyBatis Properties 配置,通过该配置可以抽离配置,实现不同环境的配置部署
#mybatis-plus.configuration-properties = classpath:mybatis/config/properties
#xml扫描,多个目录用逗号或者分号分割(告诉 Mapper 所对应的 XML 文件位置)
mybatis-plus.mapper-locations = classpath*:mapper/*.xml
#MyBatis 别名包扫描路径,通过该属性可以给包中的类注册别名,多个路径用逗号分割
mybatis-plus.type-aliases-package = com.wongoing.*.model
#枚举类 扫描路径,如果配置了该属性,会将路径下的枚举类进行注入,让实体类字段能够简单快捷的使用枚举属性
#mybatis-plus.type-enums-package = com.wongoing.*.model
#如果配置了该属性,则仅仅会扫描路径下以该类作为父类的域对象
mybatis-plus.type-aliases-super-type = java.lang.Object
#项目启动会检查xml配置存在(只在开发时打开)
mybatis-plus.check-config-location = true
#SIMPLE:该执行器类型不做特殊的事情,为每个语句的执行创建一个新的预处理语句,REUSE:改执行器类会复用预处理语句,BATCH:该执行器类型会批量执行所有的更新语句
mybatis-plus.default-executor-type = REUSE
# 是否开启自动驼峰命名规则(camel case)映射,即从经典数据库列名 A_COLUMN(下划线命名) 到经典 Java 属性名 aColumn(驼峰命名) 的类似映射
mybatis-plus.configuration.map-underscore-to-camel-case = true
# 全局地开启或关闭配置文件中的所有映射器已经配置的任何缓存,默认为 true
mybatis-plus.configuration.cache-enabled = true
#懒加载
mybatis-plus.configuration.aggressive-lazy-loading = true
#none:不启用自动映射 partial:只对非嵌套的 resultMap 进行自动映射 full:对所有的 resultMap 都进行自动映射
mybatis-plus.configuration.auto-mapping-behavior = partial
#none:不做任何处理 (默认值)warning:以日志的形式打印相关警告信息 failing:当作映射失败处理,并抛出异常和详细信息
mybatis-plus.configuration.auto-mapping-unknown-column-behavior = none
#如果查询结果中包含空值的列,则 MyBatis 在映射的时候,会不会映射这个字段(#允许在resultType="map"时映射null值)
mybatis-plus.configuration.call-setters-on-nulls = true
#这个配置会将执行的sql打印出来,在开发或测试的时候可以用
mybatis-plus.configuration.log-impl = org.apache.ibatis.logging.stdout.StdOutImpl
#是否允许映射结果为多个数据集
mybatis-plus.configuration.multiple-result-sets-enabled = false
## mybatis-plus全局配置
#表名下划线命名默认为true
mybatis-plus.global-config.db-config.table-underline = false
#id类型: 0 # 0:数据库ID自增 1:用户输入id 2:全局唯一id(IdWorker) 3:全局唯一ID(uuid)
mybatis-plus.global-config.db-config.id-type = auto
#是否开启大写命名,默认不开启
mybatis-plus.global-config.db-config.capital-mode = false
#逻辑已删除值,(逻辑删除下有效) 需要注入逻辑策略LogicSqlInjector 以@Bean方式注入(逻辑未删除值(默认为 0))
mybatis-plus.global-config.db-config.logic-not-delete-value = 0
#逻辑未删除值,(逻辑删除下有效)(逻辑已删除值(默认为 1))
mybatis-plus.global-config.db-config.logic-delete-value = 1
########################## mybatis-plus配置 end #################################
table.render({
elem: '#demo'
, height: 400//容器高度
, url: 'getAllUsers' //数据接口
, page: true //开启分页
, cols: [
[ //表头
{field: 'id', title: 'ID', width: 150, sort: true, fixed: 'left'}
, {field: 'useraccount', title: '用户名', width: 150, sort: true}
, {field: 'userpassword', title: '密码', width: 150, sort: true}
, {field: 'flag', title: '封号开启', templet: fontColor,width: 200, sort: true}
, {field: 'powerofcourse', title: '课程权限', width: 150, sort: true,hide:true}
, {field: 'information', title: '信息', width: 270, sort: true}
, {fixed: 'right', width: 200, align: 'center', toolbar: '#barDemo'}
]
]
, method: "post"
, page: true
, limits: [3, 5, 10] //一页选择显示3,5或10条数据
, limit: 5 //一页显示10条数据
, parseData: function (res) { //将原始数据解析成 table 组件所规定的数据,res为从url中get到的数据
var result;
if (this.page.curr) {
result = res.data.slice(this.limit * (this.page.curr - 1), this.limit * this.page.curr);
} else {
result = res.data.slice(0, this.limit);
}
return {
"code": res.code, //解析接口状态
"msg": res.msg, //解析提示文本
"count": res.count, //解析数据长度
"data": result //解析数据列表
};
}
});
js代码:
//改变封号列的字体颜色
function fontColor(data) {
var flag = data.flag;
var btns = "";
if (flag == "0") {
btns += '<a class="" style="color:green">未封号</a>';
}
if (flag == "1") {
btns += '<a class="" style="color:#ed2a4a">封号中</a>';
}
return btns;
}
Error resolving template [JD/insert], template might not exist or might not be accessible by any of the configured Template Resolvers
修改接口的@Controller=>@RestController
//复选框批量勾选事件
table.on('checkbox(test)', function(obj)
{
console.log(obj); //当前行的一些常用操作集合
console.log(obj.checked); //当前是否选中状态
console.log(obj.data); //选中行的相关数据
console.log(obj.type); //如果触发的是全选,则为:all,如果触发的是单选,则为:one
});
通过回调函数的obj对象,只能获取到最后一次被勾选的一行的数据,无法获取到所有被勾选数据的集合
如果想获取到被勾选数据的集合,可以采用下面的方案:
//工具栏事件
table.on('toolbar(test)', function(obj){
var checkStatus = table.checkStatus(obj.config.id);
switch(obj.event){
case 'getCheckData':
//获取到所有被选中的数据集合
var data = checkStatus.data;
layer.alert(JSON.stringify(data));
break;
case 'getCheckLength':
var data = checkStatus.data;
//获取到被选中的元素个数
layer.msg('选中了:'+ data.length + ' 个');
break;
case 'isAll':
layer.msg(checkStatus.isAll ? '全选': '未全选')
break;
};
});
也可以这样写:
table.checkStatus(‘test’),这里的test是数据表格的id,不是lay-fliter对应的值
//复选框勾选事件
table.on('checkbox(test)', function (obj) {
var checkStatus = table.checkStatus('test');//这里传入的test是数据表格的id
//删除被勾选的行元素集合
//checkStatus.data:被选中的元素集合
//checkStatus.data.length:被选中的元素个数
}
test对应数据表格
<!--员工展示列表-->
<table class="layui-hide" id="test" lay-filter="test"></table>
这里obj,我觉得传入的是数据表格对象
头部工具栏的table.checkStatus(obj.config.id);就是获取数据表格中被选中数据
页面正常通过Key-Value的形式传值,数组保持原格式,后端使用@RequestParam注解标注接值的入参,注意@RequestParam里的value一定要带上中括号:
controller:
@RequestMapping("/test.htm")
@ResponseBody
public AjaxResp test(@RequestParam("dataList[]") List<String> dataList, String name){
//do something
}
下面的写法也可以,并且不一定非要是String类型数组接收,也可以是int数组
@RequestParam(value = "ids[]") String[] ids
页面js:
var dataList = [1,2,3];
$.get({
url: "/test.htm",
data: {dataList: dataList,name:'jack'},
success: function (data) {
//do something
}
});
报文:
dataList[]: 1
dataList[]: 2
dataList[]: 3
name: jack
种方式的好处是简单,方便。get请求和post请求都可以传值,并且后台接值的参数类型可以是List集合也可以用String[]数组。不过这种方式可能报文看起来比较奇怪。
页面正常通过Key-Value的形式传值,数组使用逗号分割的形式的字符串(可以使用toString()或join()将数组转成这种格式),后端使用String[]数组接值。
页面:
var dataList = "1,2,3";
$.get({
url: "/test.htm",
data: {dataList: dataList,name:'jack'},
success: function (data) {
//do something
}
});
controller:
@RequestMapping("/test.htm")
@ResponseBody
public AjaxResp test(String[] dataList, String name){
//do something
}
报文:
dataList: 1,2,3
name: jack
注意这种方式后端接口得用String[]数组来接,如果你直接用List集合会抛下面这个异常:
Could not instantiate bean class [java.util.List]: Specified class is an interface
加上**@RequestParam注解并且设置value**之后就可以用List集合类型了:
@RequestMapping("/test.htm")
@ResponseBody
public AjaxResp test(@RequestParam("dataList") List<String> dataList, String name){
//do something
}
这种方式同样支持get和post请求,并且报文也没那么奇怪,推荐使用。
前台使用json来传值,后台使用一个数据对象来接值:
public class Dto {
private List<String> dataList;
private String name;
//getters and setters
}
controller,记得加上@RequestBody,spring才能帮我们解析json对象:
@RequestMapping(value = "/test.htm", method = RequestMethod.POST)
@ResponseBody
public AjaxResp test(@RequestBody Dto dto){
//do something
}
页面js:
var payload = {};
payload.dataList = ["1","2","3"];
payload.name="jack";
$.post({
url: "test.htm",
contentType:"application/json",
data: JSON.stringify(payload),
success: function (data) {
//do something
}
});
报文:
{"dataList":["1","2","3"],"name":"jack"}
这种方式比较适合用post
请求传输多个复杂字段时候使用。记得发送请求的时候设置contentType:"application/json"
,表示给的是json
格式的数据。
原因:返回的不是JSON格式数据,或者返回的JSON格式数据不满足要求
返回的数据格式最好满足一下的要求:
//处理上传的文件
@PostMapping("/fileUp")
public String fileUpLoad(@RequestPart("file")MultipartFile file)
{
System.out.println("====================================");
System.out.println("收到文件");
String Types ="{\"code\":"+0+",\"msg\":\"success\",\"data\":"+"[{\"id\":1,\"type\":\"三岁\"},{\"id\":2,\"type\":\"四岁\"},{\"id\":3,\"type\":\"五岁\"},{\"id\":4,\"type\":\"六岁\"}]"+"}";
return Types;
}
去掉code和msg也没有问题
String Types ="{\"data\":"+"[{\"id\":1,\"type\":\"三岁\"},{\"id\":2,\"type\":\"四岁\"},{\"id\":3,\"type\":\"五岁\"},{\"id\":4,\"type\":\"六岁\"}]"+"}";
终止返回的数据类型必须是JSON格式才行
自定义开关模板:
<!--自定义开关的模板-->
<script type="text/html" id="delDemo">
<!-- 1是上架-->
{{# if(d.flag==1){ }}
<input type="checkbox" name="status" lay-skin="switch" lay-filter="switchTest" checked lay-text="上架|下架" value={{ d.status }}>
{{# } else { }}
<input type="checkbox" name="status" lay-skin="switch" lay-filter="switchTest" lay-text="上架|下架" value={{ d.status}}>
{{# } }}
</script>
数据表格:
toolbar和template都都支持laytpl 语法
后端返回给前端的数据中有flag字段数据,那么在就可以在对应的自定义模板出,使用d.属性名的方式,取值,进行数据的动态更新
如果想要绑定lay-event事件属性,需要使用a标签才会生效,或者可以给按钮加上该属性,外面无需a标签包裹
<!--自定义开关的模板-->
<script type="text/html" id="delDemo">
<!-- 1是上架-->
{{# if(d.flag==1){ }}
<a lay-event="switch">
<input type="checkbox" name="status" lay-skin="switch" lay-filter="switchTest"
checked lay-text="上架|下架" value={{ d.status }}>
</a>
{{# } else { }}
<a lay-event="switch">
<input type="checkbox" name="status" lay-skin="switch" lay-filter="switchTest"
lay-event="switch" lay-text="上架|下架" value={{ d.status}}>
</a>
{{# } }}
</script>
如果使用tooltar,就可以使用行工具栏事件,获取对应的事件了
table.on('tool(courseFilter)', function(obj){
if(obj.event==='switch')
}
form.on('switch(filter)', function(data){
console.log(data.elem); //得到checkbox原始DOM对象
console.log(data.elem.checked); //开关是否开启,true或者false
console.log(data.value); //开关value值,也可以通过data.elem.value得到
console.log(data.othis); //得到美化后的DOM对象
});
这里我们需要使用data.othis,即美化后的DOM对象,该DOM对象就是当前的开关的DOM对象,我们可以给其增添或者删除layui-form-switch属性,设置开关的开与否
//监听表单里面的开关事件
form.on('switch(switchTest)', function(data)
{
var chose=data.elem.checked; //开关是否开启,true或者false
if(chose)//上架
{
layer.confirm('确认上架?', {
btn: ['确认', '取消']
,yes: function(index, layero)
{
//返回当前课程id和flag=1
//发送ajax请求给后端
$.ajax({
url:'upAndDownCourse',
type:'get',
data:{'id':curID,'flag':1},
success:function(data)
{
if(data=="error")
{
layer.msg('上架失败', {icon: 5});
}
else if(data=="success")
{
layer.msg('上架成功', {icon: 1});
}
}
})
layer.close(index);
},
btn2:function (index,layero)
{
//将选中状态撤销
//点击取消之后,还是处于下架状态
var formSwitch=data.othis
//设置开关状态为开,即下架
formSwitch .find("em").text("下架")
formSwitch .prop("class","layui-unselect layui-form-switch")
layer.close(index);
}
});
}
else//下架
{
layer.confirm('确认下架?', {
btn: ['确认', '取消']
,yes: function(index, layero)
{
//返回当前课程id和flag=1
//发送ajax请求给后端
$.ajax({
url:'upAndDownCourse',
type:'get',
data:{'id':curID,'flag':0},
success:function(data)
{
if(data=="error")
{
layer.msg('下架失败', {icon: 5});
}
else if(data=="success")
{
layer.msg('下架成功', {icon: 1});
}
}
})
//返回当前课程id和flag=0
layer.close(index);
},
btn2:function (index,layero)
{
//将选中状态撤销
//点击取消之后,还是处于上架状态
var formSwitch=data.othis
//设置开关状态为开,即上架
formSwitch .find("em").text("上架")
formSwitch .prop("class","layui-unselect layui-form-switch layui-form-onswitch")
layer.close(index);
}
});
}
});
该方法设置的是所有的开关,不是当前行的开关
// 可以还原off状态
var formSwitch = $(".layui-form-switch");
formSwitch .find("em").text("OFF")
formSwitch .prop("class","layui-unselect layui-form-switch")
mid={{d.id}} 属性的增加,我们就可以很方便获取到当前行的开关,对应的id值,方便我们后面修改对应开关值状态
还可以将需要传递的数据,写入属性中
<!--自定义开关的模板-->
<script type="text/html" id="delDemo">
<!-- 1是上架-->
{{# if(d.flag==1){ }}
<input type="checkbox" name="status" lay-skin="switch" lay-filter="switchTest"
checked lay-text="上架|下架" value={{ d.status }} mid={{d.id}}>
{{# } else { }}
<!--0是下架-->
<input type="checkbox" name="status" lay-skin="switch" lay-filter="switchTest"
lay-event="switch" lay-text="上架|下架" value={{ d.status}} mid={{d.id}}>
{{# } }}
</script>
js代码:
//修改开关的状态---flag: 1-->设置为选中,即上架
//0-->设置为不选中,即下架
function checkStatus(flag,data)
{
//设置状态为选中,即上架
if(flag==1)
{
// 设置为原始状态(恢复状态)
data.elem.checked =true;
// 渲染方法1:渲染全部表单的checkbox
form.render('checkbox');
}
else if(flag==0)
{
// 设置为原始状态(恢复状态)
data.elem.checked =false;
// 渲染方法1:渲染全部表单的checkbox
form.render('checkbox');
}
}
//监听表单里面的开关事件
form.on('switch(switchTest)', function(data)
{
var curID;
// 通过属性获取绑定的id值
curID= $(this).attr('mid');
var chose=data.elem.checked; //开关是否开启,true或者false
if(chose)//上架
{
layer.confirm('确认上架?', {
btn: ['确认', '取消']
,yes: function(index, layero)
{
//返回当前课程id和flag=1
//发送ajax请求给后端
$.ajax({
url:'upAndDownCourse',
type:'get',
data:{'id':curID,'flag':1},
success:function(data)
{
if(data=="error")
{
layer.msg('上架失败', {icon: 5});
//如果上架失败了,那么我们还需要将上架的状态改为下架
checkStatus(0,data);
}
else if(data=="success")
{
layer.msg('上架成功', {icon: 1});
}
}
})
layer.close(index);
},
btn2:function (index,layero)
{
//下架
checkStatus(0,data);
layer.close(index);
},
//如果直接关闭弹出层,那么还是需要回复到下架状态
cancel: function(index, layero){
//下架
checkStatus(0,data);
}
});
}
else//下架
{
layer.confirm('确认下架?', {
btn: ['确认', '取消']
,yes: function(index, layero)
{
//返回当前课程id和flag=1
//发送ajax请求给后端
$.ajax({
url:'upAndDownCourse',
type:'get',
data:{'id':curID,'flag':0},
success:function(data)
{
if(data=="error")
{
layer.msg('下架失败', {icon: 5});
//需要将下架的状态改为上架
checkStatus(1,data);
}
else if(data=="success")
{
layer.msg('下架成功', {icon: 1});
}
}
})
//返回当前课程id和flag=0
layer.close(index);
},
btn2:function (index,layero)
{
//上架
checkStatus(1,data);
layer.close(index);
},
//如果直接关闭弹出层,那么还是需要回复到上架状态
cancel: function(index, layero){
//上架
checkStatus(1,data);
}
});
}
});
on事件里面的this对象,就是上面写的input输入框,可以获取到里面动态更新的id值
//监听表单里面的开关事件
form.on('switch(switchTest)', function(data)
{
var curID;
// 通过属性获取绑定的id值
curID= $(this).attr('mid');
console.log(this)
涉及知识点: Hutool解压缩工具类,springboot文件上传
//处理上传的文件
@PostMapping("/fileUp")
public String fileUpLoad(@RequestPart("file")MultipartFile file,@RequestParam("courseName")String courseName,
@RequestParam("courseType")String courseType)
{
//获取文件的类型
String type = file.getContentType();
//返回的不是压缩包,则不给过
if (!type.equals("application/x-zip-compressed")) {
return "{\"msg\":\"error\"}";
}
//文件解压到某个目录下
String path = "C:\\Users\\zdh\\Desktop\\课程体系\\" + courseType + "\\" + courseName;
//在上面的基础上规定了解压缩到对应目录下面,后压缩文件的名字
String zipPath=path + "\\" + file.getOriginalFilename();
File f = new File(path);
//创建多级目录
f.mkdirs();
try {
//将压缩文件放置到对应的目录下面
file.transferTo(new File(zipPath));
} catch (IOException e) {
e.printStackTrace();
}
//当前目录底下进行解压
ZipUtil.unzip(zipPath,path);
String Types = "{\"data\":\"大忽悠\"}";
return Types;
}
$("#upload").trigger('click');
<div class="layui-form-item">
<label class="layui-form-label">上传课件</label>
<div class="layui-input-inline">
<div class="layui-upload-drag" id="upload" ifUpLoad="false" style="margin-left: 20px">
<i class="layui-icon"></i>
<p>点击上传,或将文件拖拽到此处</p>
<div class="layui-hide" id="uploadDemoView">
<hr>
<img src="" alt="上传成功后渲染" style="max-width: 196px">
</div>
</div>
</div>
</div>
<div style="margin-left: 170px;margin-bottom: 10px">
<font id="noFile">(未选择任何文件)</font>
</div>
<!-- 进度条-->
<div class="layui-progress layui-progress-big" style="margin-left: 130px;width:170px" lay-filter="demo" lay-showPercent="true">
<div class="layui-progress-bar" lay-percent="0%" id="addProcess"></div>
</div>
//上传文件---增加课程
//获取当前进度条
var addpercent;
upload.render({
elem: '#upload' //绑定元素
,url: '/fileUp' //上传接口
,field:'file'
,accept:'file'
,data:{
courseName:function(){return $("#courseName").val()},courseType:function()
{return types.getValue()[0].type;}
}
,auto:false//不自动上传文件
,exts: 'zip|rar|7z'//只允许上传压缩格式的文件
,bindAction:"#addCourseUpFile"//按钮点击后,才会上传文件
,progress: function(n, elem, res, index){
addpercent= n + '%' //获取进度百分比
element.progress('demo', addpercent); //可配合 layui 进度条元素使用
element.progress('demo-'+ index, n + '%'); //进度条
},
choose: function(obj)
{
//设置文件上传状态为true
//说明上传了必要的课件
$("#upload").attr("ifUpLoad","true");
//遍历文件,显示文件名
obj.preview(function(index, file, result){
//显示文件名
$("#noFile").text(file.name);
});
}
,done: function(res, index, upload){
//上传成功后的回调
if(res.msg=="success")
{
//文件上传成功
layer.msg('文件上传成功', {icon: 1,time:500});
$.ajax
({
url:"addCourse",
type:"post",
data:{"coursename":$("#courseName").val(),"type_id":types.getValue()[0].id},
success:function (data)
{
if(data==='success')
{
//数据库更新成功,将当前页面对应的数据清空
$("#courseName").val("");
//删除多选下拉框中被选中的值---填入的是value值
//[]不能少
types.delete([types.getValue()[0].id]);
}
else if(data='error')
{
layer.msg('失败', {icon: 5,time:500});
}
}
});
//文件上传完毕
if(addpercent='100%')
{
//关闭增加课程页面
layer.close(addIndex);
}
}
if(res.msg=="error")
{
//文件上传失败
layer.msg('文件上传失败', {icon: 1,time:500});
}
//上传成功后,将ifUpLoad属性变为false
$("#upload").prop("ifUpLoad","false");
//文件未被选择
$("#noFile").text("未选择任何文件");
//进度条回到起点
element.progress('demo', '0%');
}
,error: function(){
//请求异常回调
layer.msg('出现异常', {icon: 5,time:500});
//文件未被选择
$("#noFile").text("未选择任何文件");
//进度条回到起点
element.progress('demo', '0%');
}
});
https://blog.csdn.net/qq_18432653/article/details/110931454
springboot保存图片到数据库和从数据库获取图片到前端
java.lang.IllegalArgumentException: MALFORMED
原因:
查看源码得知,源码中只判断了examole==null,忽略了""空字符串的情况!
解决方案:
<!--指定swagger-models版本,解决报错-->
<dependency>
<groupId>io.swagger</groupId>
<artifactId>swagger-annotations</artifactId>
<version>1.5.21</version>
</dependency>
<dependency>
<groupId>io.swagger</groupId>
<artifactId>swagger-models</artifactId>
<version>1.5.21</version>
</dependency>
项目当前使用的swagger版本及UI版本
<!--swagger-->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-boot-starter</artifactId>
<version>3.0.0</version>
</dependency>
<!-- swagger ui-->
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-spring-boot-starter</artifactId>
<version>3.0.2</version>
</dependency>
<!--指定swagger-models版本,解决报错-->
<dependency>
<groupId>io.swagger</groupId>
<artifactId>swagger-annotations</artifactId>
<version>1.5.21</version>
</dependency>
<dependency>
<groupId>io.swagger</groupId>
<artifactId>swagger-models</artifactId>
<version>1.5.21</version>
</dependency>
java 解压zip中文文件 出现错误
java.lang.IllegalArgumentException: MALFORMED
解决这个错误需要 jdk 1.7及以上版本支持
并在创建ZipFile 时指定中文字符集gbk
ZipFile zipFile = new ZipFile(file, Charset.forName("gbk"));
如果使用hutool工具,那么也需要设置:
ZipUtil.unzip(zipPath,path, Charset.forName("gbk"));
dataType值如果为’json’,jquery就会把后端返回的字符串尝试通过JSON.parse()尝试解析为js对象。
dataType值如果为’text’,结果弹出框直接显示后台返回的json字符串。
dataType值如果为’html’,结果弹出框直接显示后台返回的json字符串。
如果不进行设置,那么默认为text,后端传过来一个JSON字符串,不会解析为JS对象,而是作为纯文本展示
attr()?
首先要知道prop与attr的区别
prop() 用于检索固有属性,例如 DOM 属性(selectedIndex, tagName, nodeName, nodeType, ownerDocument, defaultChecked, 和 defaultSelected)。
实例: <a href="http://www.baidu.com" id="baidu">百度</a> 中的 herf、id 就属于固有属性,适合使用prop。
attr() 用于检索自定义DOM属性。
实例: <a href="http://www.baidu.com" id="baidu" extra-data="2">百度</a> 中的 extra-data 就属于自己定义的属性。
attr—>自定义属性
prop—>固有属性
上图中的例子,如果使用attr动态的添加 checked=“checked”, 代码能增加上去,但页面不会有相应改变的,但用prop就很完美的响应。别忘了在ajax后面加上form.render();动态渲染from表单
$.ajax({
urL: 'abc',
data: {name:'大忽悠'},
dataType: 'json',
success: function()
{}
})
location.reload();
上面的ajax请求,如果受到reload的影响,发送不成功
可以晚一点刷新页面,先让ajax请求发送出去
setTimeout(function () {
window.location.reload();
}, 3000);
<!-- 设置数据表格每一行的高度-->
<style>
.layui-table-cell{
height:70px;
line-height: 70px;
}
</style>