从0开始的springboot项目

目录

关于前端传到后端格式和参数

后端设置拦截器

addInterceptors

addResourceHandlers

addViewControllers

 实战之利用拦截器实现访问日志路径和参数打印

Token之JWT技术

Token如何存储到客服端? JSESSIONID是什么?

获取cookies中存放的token

重点:利用TOKEN实现拦截功能

index首页的布局

freemark的使用

layui路径导入的坑

前后端结合

table渲染

Jquery 利用each的取值

关于form.on 

后端的部分组件

如何降低代码的耦合度

定时器的使用

手机验证码

七牛云图片上传

微信支付

微信支付超级详细配图讲解——1(基本配置的获取和密钥讲解)

支付宝支付

导入项目如果

<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的实现。

Spring Boot拦截器(Interceptor)详解

如果我注册自定义拦截器的时候,不设置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之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、

微信支付之 Native 支付——(第2集)

3、

微信V3APP支付2022,全网最新+踩坑(已实现)

4、 微信支付的坑

微信支付调起失败或者llegal key size解密大坑(已解决)

支付宝支付

  • 30
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值