目录
导入项目如果
<artifactId>spring-boot-maven-plugin</artifactId>报错,那么就把这里的version
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.4.4</version> <relativePath/> <!-- lookup parent from repository --> </parent>
复制到这里,秒不爆红
<plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <version>2.4.4</version>
关于前端变量取不到值的问题
注意变量别直接放到layui.use的下面 ,而是要放到form.on的下面 在取值 ,不然取值为空。
因为layui.use在页面一加载,就会去执行。
layui.use(['form'],function () {
var form = layui.form;
/* 千万别把变量放在这,否则会取不到数据。
原因是,一打开这个页面,就会加载layui.use,那个时候,用户还没有输入数据呢!
UsernameOrSnumber = $("#UsernameOrSnumber").val();
password = $("#password").val();
auto = $('input:radio[name="auto"]:checked').val(); */
//监听提交
form.on('submit(*)', function(){
//变量要放在这才能拿到数据,因为放这里,只有点击表单提交,才会去获取数据。
UsernameOrSnumber = $("#UsernameOrSnumber").val();
password = $("#password").val();
auto = $('input:radio[name="auto"]:checked').val();
$.ajax({
url: '/user/login.action',
data: {UsernameOrSnumber:UsernameOrSnumber, password:password,auto:auto },
type: 'post',
dataType: 'JSON',
})
return false;
});
});
mybatis-x 自动生成,如何正确的使用
根据我惨痛的教学总结。
1.数据库和实体类的字段,一般来说 是用驼峰来的。 比如 user_name = userName
最好要就写一样的,要就驼峰的。 因为你想要他自动生成, 就得这样。
如果你把 user_去掉了 ,那么他生成的字段,就是没有 user_的,那样就会出现不匹配的问题。
在这里也可以改变他的 include refid来改变他的 字段。
2. 如果前端传递参数都后端,如果数据量大的话,一般使用实体类来传参。 因为在你后端的修改,插入中,一般也是利用实体类来传参的,而不是一个个参数传进去。 用实体类就省去很大的麻烦。
3.如果你某个表单input不想提交进入参数, 那么你只需要不给他设置 name 属性就行了
关于前端传到后端格式和参数
前端传数据到后端,表单提交,默认是 application/x-www-form-urlencoded,可以理解为它主要用于key-value提交、
而multipart/form-data 主要是当 表单中有 file文件的时候用。
表单传参的name ,对应你的对象属性名。
而application/json ,主要用于对象形式提交,也就是 JSON.stringfly(data.field);然后后台的对象用 @RequestBody 对象 接收参数。
所以你如果要传递对象的时候, 你的contentType用:application/json(默认 application/x-www-form-urlencoded) 。这个一定要记住,不然会报错。
还有 dataType: 'json' 不是 dataType: JSON
还有 跳转页面用controller跳转 window.location.href='/login'; 不是 直接跳转到那个页面 window.location.href='/login.html';
$.ajax({
url: '/user/register.action',
type: 'POST',
dataType: 'json',
contentType: 'application/json',
data: JSON.stringify(data.field),
success: function (res) {
if (res.status==200){
layer.msg('注册成功',{
icon: 1,
time: 2000
});
window.location.href='/login';
}else{
layer.msg('注册失败',{
icon:2,
time:2000
})
}
}
,error: function (res) {
layer.msg('异常错误',{
icon: 2,
time: 2000
})
}
})
注意layui的$ 和 jQuery的$是不一样的。
如果你在下面这 。代表的是layui的。那么你请求的ajax就会跟jQuery请求的ajax不同
layui.use(['form','jquery'],function () { var form = layui.form; var $ = layui.jquery;
主要体现在method这。
$.ajax({ url: '/user/login.action', data: {UsernameOrSnumber:UsernameOrSnumber, password:password }, method: 'post',
而jQuery是用type
$.ajax({ url: '/user/register.action', type: 'POST', dataType: 'json', contentType: 'application/json',
页面加载完后执行这条语句 $(document).ready(function () {}
var regexNumber = /^\d{13}$/;
regexNumber.test(number) 利用正则表达式。
mybatis 默认使用的是驼峰规则命名。 假如你数据库是 user_name ,那么你对应的实体类就应该是 userName 。 否则会对应不上名字。 你也可以通过yml那修改配置
map-underscore-to-camel-case: false (取消驼峰命名。默认true)
也可以不用下划线,就是你数据库什么名,实体类就要什么名。
mybatis-plus:
mapper-locations: classpath*:/mapper/**/*.Mapper.xml
type-aliases-package: com.example.webdemo.entity
configuration:
#这个日志输出,就是输出在控制台那
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
map-underscore-to-camel-case: true
如果报,mapper映射不到的错误。大坑
如果是多子模块的项目,Mapper.xml文件是在子模块项目中,那么记得在classpath后面加*
mybatis-plus:
mapper-locations: classpath*:/mapper/**/*.Mapper.xml 注意这里 是 /*.Mapper.xml 注意小数点啊,上次给小数点搞了一天。
记住,使用mybatis-x和mybatis-plus ,如果他们直接能映射,说明 mapper层和xml层之间是没问题的。 也就是有那个小鸟标志。
还有你的实体类改变, 不会影响mybatis-plus 的 BaseMapper<User>,所以不要浪费时间在这些身上。 mybatis-x自动生成的xml是没问题的。
所以如果报找不到那个方法的错误。
要就是 mapper-locations: classpath*:/mapper/**/*.Mapper.xml 这里没有设置对路径。
要就是你的mapper没有加@mapper或者 mapperscan
namespace也可以点进去看看有没有成功。
注意 resultType 是返回一个的时候使用的,无论是对象,还是你自己定义的对象。
当你要返回多个时,比如list,map等等就要用resultMap。
注意接受的后端返回的参数。 信息是,res.message不是res.msg
success: function (res) { if (res.status==200){ layer.msg(res.message,{icon: 1,time:1000}); window.location.href='/index'; }else{ layer.msg(res.message,{icon:2,time:1000}); } }
后端设置拦截器
addInterceptors
return true代表允许通过,false代表不允许。
通常,我们可以用拦截器来设置日志,比如用户访问了哪个方法。
如下。
代码实现
private Logger logger = LoggerFactory.getLogger("access");
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
logger.info("Request URL"+request.getRequestURI());
return true;
}
或者用户有没有登录等等。其实就是AOP的实现。
如果我注册自定义拦截器的时候,不设置excludePathPatterns,那么他会把静态资源路径也进行拦截。这会导致程序运行的效率大大下降
过滤器是 servlet 规范中的一部分,任何 java web 工程都可以使用。
拦截器是 SpringMVC 框架自己的,只有使用了 SpringMVC 框架的工程才能用。
addResourceHandlers
代表设置 静态资源的访问
spring.mvc.static-path-pattern 代表着,只有访问这个路径,才算静态资源。
“spring.resources.static-locations”用于告诉Spring Boot应该在何处查找静态资源文件,这是一个列表性的配置,查找文件时会依赖于配置的先后顺序依次进行,默认的官方配置如下:
优先级顺序为:META-INF/resources > resources > static > public
spring.resources.static-locations=classpath:/static,classpath:/public,classpath:/resources,classpath:/META-INF/resources
继续以上面的请求地址为例,“http://localhost:8080/resources/jquery.js”就会在上述的四个路径中依次查找是否存在“jquery.js”文件,如果找到了,则返回此文件,否则返回404错误。
Spring Boot配置静态资源的地址与访问路径(spring.mvc.static-path-pattern和spring.web.resources.static-locations)
mvc:
static-path-pattern: /**
resources:
static-locations: classpath:/META-INF/resources/,classpath:/resources/, classpath:/static/
上面这个其实和下面的代码就是一样的。 只是一个通过类来配置,一个直接在yml配置。
@Override
//需要告知系统,这是要被当成静态文件的!
public void addResourceHandlers(ResourceHandlerRegistry registry) {
// 设置文件上传的文件不拦截
// registry.addResourceHandler("/upload/**").addResourceLocations("file:"+ TaleUtils.getUplodFilePath()+"upload/");
//第一个方法设置访问路径前缀,第二个方法设置资源路径
//addResoureHandler指的是对外暴露的访问路径,addResourceLocations指的是文件放置的目录
registry.addResourceHandler("/static/**").addResourceLocations("classpath:/static/");
}
addViewControllers
等同于controller中的跳转。如果你不需要 Model,只是单纯想跳转,可以使用这个方法。
addViewController 相当于设置requestMapping 路径 。
setViewName 相当于要跳转的html。
//等同于controller跳转
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/login").setViewName("login");
registry.addViewController("/index").setViewName("index");
registry.addViewController("/register").setViewName("register");
registry.addViewController("/recoverpasswd").setViewName("recoverpasswd");
}
实战之利用拦截器实现访问日志路径和参数打印
@Slf4j 注解(需要lombok插件)
此处的{}其实是Slf4j的占位符,注意的是info括号内用逗号连接而不是加号
关于下面这个,你是填写类名,还是像我代码那样填写字符串,其实没关系的。都一样
如果是类名,就显示类名而已。
private Logger logger = LoggerFactory.getLogger(MyInterceptor.class);
如果随便写一个字符串。输入也是你写的那个字符串而已。只是为了辨识而已。
private Logger logger = LoggerFactory.getLogger("a");
package com.example.webdemo.config;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Enumeration;
import java.util.WeakHashMap;
@Slf4j
public class MyInterceptor implements HandlerInterceptor {
private Logger logger = LoggerFactory.getLogger("access");
// //弱引用 ,它有自清理的机制
private static WeakHashMap<String,String> para = new WeakHashMap<>();
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
Enumeration names = request.getParameterNames();
while (names.hasMoreElements()){
String paraname = (String) names.nextElement();
para.put(paraname,request.getParameter(paraname));
}
logger.info("请求路径为:{} -- 参数为:{}",request.getServletPath(),para);
para.clear();
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
}
}
Token之JWT技术
为了减少容量,我就另外放了。
Token如何存储到客服端? JSESSIONID是什么?
Token 在用户登录成功之后返回给客户端,客户端组要有三种存储方式
储存在 localStorage 中,每次调用接口时放在http请求头里面,长期有效
储存在 sessionStorage 中,每次调用接口时,把它当为一个字段传给后台,浏览器关闭自动清除
储存在 cookie 中,每次调用接口会自动发送,不过缺点是不能跨域,可以设置过期时间。如果不设置,那么浏览器关闭就结束生命。
- 当客户端首次访问时,创建一个新的session对象.并同时生成一个sessionId,并在此次响应中将sessionId以响应报文的方式送回客户端浏览器内存或以重写url方式送回客户端,来保持整个会话;
vue中访问后台所携带的token值为什么会放在cookie中,而不是放在localStorage和sessionStorage中
TokenInfo tokenInfo =new TokenInfo(user.getUserId(),user.getUserPhone(),user.getUserNumber());
String token = JwtUtils.generateToken(JSONUtils.objToJson(tokenInfo));
Cookie cookie = new Cookie("Authorization", token);
cookie.setMaxAge(3600);
cookie.setPath("/");
response.addCookie(cookie);
session.setAttribute("user",user);
浏览器的application中cookies就可以看到这些。
获取cookies中存放的token
好了,设置cookies我们会了,那么我们如果获取cookies中存放的token呢?
打开浏览器,可以看到,请求头中的cookie。
也就是这里的 name和value都放到请求头上了。
并且每一个name和value都会作为类似数组,又像Map中的一个数。
我们只需要先获取所有cookie生成的数组。
Cookie[] cookieList = request.getCookies();
然后遍历,
if (cookieList[i].getName().equals(cookieName)) 这样,然后getValue就可以找到那个token对应的值了。
public static String getCookieValueByName(HttpServletRequest request, String cookieName, Boolean isDecoder) {
Cookie[] cookieList = request.getCookies();
if (cookieList == null || cookieName == null) {
return null;
}
String retValueString = null;
try {
for (int i = 0; i < cookieList.length; i++) {
if (cookieList[i].getName().equals(cookieName)) {
if (isDecoder) {
retValueString = URLDecoder.decode(
cookieList[i].getValue(), "utf-8");
} else {
retValueString = cookieList[i].getValue();
}
}
}
} catch (Exception e) {
LOGGER.error("Cookie Decode Error : ", e);
e.printStackTrace();
}
return retValueString;
}
重点:利用TOKEN实现拦截功能
我们学会了jwt技术和拦截器,那么我们如何安全是去实现拦截呢?
我觉得,利用redis 存储token ,如何和客服端传来的token来比对,如果token一样,就放行,如果不一样就拦截。 这样还可以不用把密码等重要信息放到cookie或者localstore 等等地方,即安全又高效。
redis技术我就不说了,如果不会的就去学。
下面的代码我都省略其他无关的。 我默认你看了我上面的东西
1.首先我在 请求登录接口上, 存放token到redis中,并设置过期时间。
2. 注意这个token的key不能固定,因为用户不止一个。如果大家都用一个token,那么token就会被覆盖!
TokenInfo tokenInfo =new TokenInfo(user.getUserId(),user.getUserPhone(),user.getUserNumber());
String token = JwtUtils.generateToken(JSONUtils.objToJson(tokenInfo));
//突然想到,把token放到redis,然后在这拿redis,
// 一对比这两个是不是一样的就简单多了,还安全,不用把密码放到token中
//注意这个key不能固定,因为用户不止一个。
String keyName = "TOKEN"+user.getUserId();
redisTemplateUtil.set(keyName,token,UserConstant.COOKIEMAXAGE);
//设置cookie到客服端
CookieUtils.setCookie(response, UserConstant.HEADER_TOKEN,token,UserConstant.COOKIEMAXAGE,false);
定义拦截器(拦截器可以有多个)
package com.example.webdemo.config;
import com.example.webdemo.util.*;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Component
public class JwtAuthenticationInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
boolean flag = TokenVerify.judgeToken(request);
if (!flag) {
System.out.println("拦截了");
response.sendRedirect("/login");
return false;
}
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
}
}
然后一定要记得去 WebMvcConfigurer上注册,你刚定义的拦截器。并设置排除不拦截的路径。
比如静态路径。 还有 登录页面,请求登录接口,注册页面,找回密码页面等等。
registry.addInterceptor(new JwtAuthenticationInterceptor())
.excludePathPatterns("/login","/user/login.action","/register","/recoverpasswd","/images/**","/js/**",
"/css/**","/dist/**","/lib/**","/layui/**","/fonts/**");
定义一个工具类,来判断token是否合法
注意这一步很重要
private static RedisTemplateUtil redisTemplateUtil;
@Autowired
public void setRedisTemplateUtil(RedisTemplateUtil redisTemplateUtil) {
TokenVerify.redisTemplateUtil = redisTemplateUtil;
}
如果你是直接注入bean。 你在静态方法中,是无法使用的。
因为被static修饰变量,是不属于任何实例化的对象拥有,spring的依赖注入只能在对象层级上进行依赖注入,所以不能直接使用@autowired标签进行注入。
@Autowired
private RedisTemplateUtil redisTemplateUtil;
package com.example.webdemo.util;
import com.example.webdemo.dto.TokenInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
@Component
public class TokenVerify {
private static RedisTemplateUtil redisTemplateUtil;
@Autowired
public void setRedisTemplateUtil(RedisTemplateUtil redisTemplateUtil) {
TokenVerify.redisTemplateUtil = redisTemplateUtil;
}
public static boolean judgeToken(HttpServletRequest request){
//拿到客服端的token
String token = CookieUtils.getCookieValueByName(request, UserConstant.HEADER_TOKEN,false);
if (token==null||"".equals(token)){
return false;
}
// 解密token
String tokenInfo= JwtUtils.validateToken(token);
TokenInfo tokenInfo1 =JSONUtils.jsonToObj(tokenInfo, TokenInfo.class);
Long userId = tokenInfo1.getUserId();
String redisToken = null;
try {
//从redis中拿到token
redisToken = (String) redisTemplateUtil.get(keyName);
} catch (NullPointerException e) {
e.printStackTrace();
return false;
}
if (!token.equals(redisToken)){
return false;
}
return true;
}
}
上面cookiesutils的方法
public static String getCookieValueByName(HttpServletRequest request, String cookieName, Boolean isDecoder) {
Cookie[] cookieList = request.getCookies();
if (cookieList == null || cookieName == null) {
return null;
}
String retValueString = null;
try {
for (int i = 0; i < cookieList.length; i++) {
if (cookieList[i].getName().equals(cookieName)) {
if (isDecoder) {
retValueString = URLDecoder.decode(
cookieList[i].getValue(), "utf-8");
} else {
retValueString = cookieList[i].getValue();
}
}
}
} catch (Exception e) {
LOGGER.error("Cookie Decode Error : ", e);
e.printStackTrace();
}
return retValueString;
}
对了对了,还有很重要的一步,redis要序列化。如果不序列化,那么存入的key在redis上会变成乱码 \ue 啥的
package com.example.webdemo.config;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
@Configuration
public class RedisConfig {
/**
* reids默认的template不能序列化value为自定义的对象,故需要自己注入替换
* * retemplate相关配置
* * 序列化配置,如果没有配置序列化的话可能会出现往redis存了值,但是获取不到
* @param factory
* @return
*/
@Bean
@SuppressWarnings("all")
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String, Object> template = new RedisTemplate<String, Object>();
template.setConnectionFactory(factory);
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(om);
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
// key采用String的序列化方式
template.setKeySerializer(stringRedisSerializer);
// hash的key也采用String的序列化方式
template.setHashKeySerializer(stringRedisSerializer);
// value序列化方式采用jackson
template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
// hash的value序列化方式采用jackson
template.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());
template.afterPropertiesSet();
return template;
}
}
注意小细节
//这是解密前
String token = CookieUtils.getCookieValueByName(request, UserConstant.HEADER_TOKEN,false);
//这是解密后的信息,已经被解析成json形式了 String tokenInfo= JwtUtils.validateToken(token);
所以redis中取出的要和解密前进行比较。
对了, 关于redis,前期的时候,如果报,redis连接超时,那就是timeout的原因。初始阶段,你可以设置几秒 ,或者 timeout :0 表示无限时间。 最好还是5000 表示五秒。
session的生命周期
request.getSession().setAttribute("xxx",xxx);//储存到session中
request.getSession().getAttribute("xxx",xxx);//读取session中的值
session.invalidate();//注销该request的所有session
- Session何时失效
1.服务器会把长时间没有活动的Session从服务器内存中清除,此时Session便失效。Tomcat中Session的默认失效时间为20分钟。
2.调用Session的invalidate方法。
3.session的过期时间是从什么时候开始计算的?是从一登录就开始计算还是说从停止活动开始计算?
从session不活动的时候开始计算,如果session一直活动,session就总不会过期。
从该Session未被访问,开始计时; 一旦Session被访问,计时清0;
request.getSession().setMaxInactiveInterval(-1);//永不过期
[注]:若要移除Session中特定的值,可以调用removeAttribute方法,但request中的SessionID仍不变,只是其中的值发生了改变,Session还是原来的Session。
index首页的布局
freemark的使用
layui路径导入的坑
首先先理解一下 layui.use() 模块的引入问题 ,使用了才会被引入。可以看下面的文章。
layui的引用js踩坑 - 穿黑风衣的牛奶 - 博客园 (cnblogs.com)
然后下面这个,是一个超级恶心,但又很容易解决的bug。
假如你按f12发现, 路径后面多了一个不知道哪里来的 %22,比如下面的代码
<script src=../static/layui/layui.js" charset="utf-8"></script>
你找了半天,没找到答案,网上也没有。 再看看正确的引入
<script src="../static/layui/layui.js" charset="utf-8"></script>
你可能发现不了有什么区别。 认真一看,就是左边少了一个 引号 " 。 是不是很无语。
随便说一下,如果你是直接打开静态页面, 他默认是在templates下去寻找的。
如果你启动服务器,那么他默认 会直接在static下去寻找,也就是这样
所以就不用加static了。 直接这样就行了。
<script src="/layui/layui.js" charset="utf-8"></script>
如果不是上面的问题,而是明明看到你有这个路径,但是又说引入不了。那么终极方法看你的classpath ,也就是编译后的地方。 这才是运行时候,真正的路径。
如果是上面的错误,那么这里肯定是没有那个路径的。 主要原因就是缓存
只需要,clean然后complile就行了。 你再去classpath看看,你会发现路径又出现了。
最后,注意一点。 如果你是使用 别人做好的框架 ,比如X-admin等等。他们的css和js 看看是不是自己的,还有layui官方的,别引用错了。导致没有反应
前后端结合
弄完上面的东西,终于可以开始了
关于,login页面,还有index页面,那些,我先不放页面。占用太大地方了,如果要项目代码,就找我cpdd,秃头唯一
table渲染
几个注意的地方。
1. field 对应后端返回的字段
2. 下面这代码一定要写, 特别是 "data": res.data //解析数据列表 。不然拿不到数据。
, response: {
statusCode: 200 //重新规定成功的状态码为 200,table 组件默认为 0
}
, parseData: function (res) { //将原始数据解析成 table 组件所规定的数据
return {
"code": res.status, //解析接口状态
"msg": res.msg, //解析提示文本
"count": res.data.total, //解析数据长度
"data": res.data //解析数据列表
};
}
解释一下:
这个代表三个
左侧头工具条
,defaultToolbar: ['filter', 'exports', 'print', { //自定义头部工具栏右侧图标。如无需自定义,去除该参数即可
title: '提示'
,layEvent: 'LAYTABLE_TIPS'
,icon: 'layui-icon-tips'
}]
,toolbar: '#toolbarDemo' //开启头部工具栏,并为其绑定左侧模板
这里绑定。
<script type="text/html" id="toolbarDemo">
<div class="layui-btn-container">
<button class="layui-btn layui-btn-sm" lay-event="getCheckData">获取选中行数据</button>
<button class="layui-btn layui-btn-sm" lay-event="getCheckLength">获取选中数目</button>
<button class="layui-btn layui-btn-sm" lay-event="isAll">验证是否全选</button>
</div>
</script>
列中的工具条。
,{fixed: 'right', title:'操作', toolbar: '#barDemo', width:150}
<script type="text/html" id="barDemo"> <a class="layui-btn layui-btn-xs" lay-event="edit">编辑</a> <a class="layui-btn layui-btn-danger layui-btn-xs" lay-event="del">删除</a> </script>
table.render({
elem: '#test'
,url:'/user/getAllUser'
,toolbar: '#toolbarDemo' //开启头部工具栏,并为其绑定左侧模板
,defaultToolbar: ['filter', 'exports', 'print', { //自定义头部工具栏右侧图标。如无需自定义,去除该参数即可
title: '提示'
,layEvent: 'LAYTABLE_TIPS'
,icon: 'layui-icon-tips'
}]
,title: '用户数据表'
, page: {
layout: ['limit', 'count', 'prev', 'page', 'next', 'skip'],
groups: 5 //只显示 1 个连续页码
, first: false //不显示首页
, last: false //不显示尾页
}
,cols: [[
{type: 'checkbox', fixed: 'left'}
,{field:'userName', title:'用户名', width:120}
,{field:'userIcon', title:'头像', width:100}
,{field:'userPhone', title:'电话', width:80, sort: true}
,{field:'userNumber', title:'学号'}
,{field:'userSex', title:'性别', width:80, sort: true}
,{field:'userMail', title:'邮箱', width:150}
,{field:'userBirthday', title:'生日', width:100, sort: true}
,{field:'userAddress', title:'地址', width:120}
,{fixed: 'right', title:'操作', toolbar: '#barDemo', width:150}
]]
, response: {
statusCode: 200 //重新规定成功的状态码为 200,table 组件默认为 0
}
, parseData: function (res) { //将原始数据解析成 table 组件所规定的数据
return {
"code": res.status, //解析接口状态
"msg": res.msg, //解析提示文本
"count": res.data.total, //解析数据长度
"data": res.data //解析数据列表
};
}
});
Jquery 利用each的取值
HTML checkbox 的使用(取值、赋值、判断是否选中)
// radio标签的checked属性是一个布尔值,只要点击了,就是true。
$("input:checkbox[name='familyPermission']:checked").each(function(i){ arr[i] = $(this).val(); }); if(adminRoleReal=="admin"){ //超级管理员创建族谱管理员 $("input:radio[name='templateFamily']:checked").each(function(i){ templateFamilyId = $(this).val(); }); familyAddress = province+city+area; }
关于form.on
<input type="radio" checked="checked" name="changeAdmin" id="changeAdmin" lay-skin="primary" lay-filter="changeAdmin" title="族谱管理员" lay-text="族谱管理员">
form.on(标签名(lay-filter的值))
form.on('radio(changeAdmin)',function(data){}
注意事项 ,form.on 必须要点击才能监听到。 如果我不点,那么就不会去执行。
有时候可以利用freemark取值,然后if判断,不一定死板的表单取值。
后端的部分组件
如何降低代码的耦合度
比如我们要用一个东西,并且那个东西是到处都能用到的。 我们就最好定义一个全局变量 ,或者aop 等等。
比如日志操作。或者全局变量
举个栗子:假如我要用微信支付, 里面的一些配置过期了, 那么我就得更换很多使用到配置的地方。 而如果定义一个全局静态变量, 就可以直接在那个变量换掉,就行了。
再或者我要弄一个页面跳转 ,我想在全部 跳转页面加上一个 /page,方便识别
我可以在全局静态变量类这里定义一个常量。 我如果以后要改的话,直接改这一个就好了。
而且这样还可以防止出错,以免少改。
这就是降低代码耦合度的好处,可以很好的改变,扩展。
定时器的使用
直接看我下面的文章。
手机验证码
七牛云图片上传
微信支付
三部曲。
1、
微信支付超级详细配图讲解——1(基本配置的获取和密钥讲解)
2、
3、
4、 微信支付的坑
微信支付调起失败或者llegal key size解密大坑(已解决)