Vue

Vue前端页面日期时间控件修改以及后端Java程序相关修改

一 项目架构说明

新手直接上手项目,前端采用Vue框架+后端springboot架构。硬着头皮边学边用,天天都是瓶颈。

二 问题说明

在前面记录分享的两个案例中「Vue前端页面错误[Vue warn]: Invalid prop: custom validator check failed for prop “value”.解决

Warning: [antdv: Each record in table should have a unique key prop,or set rowKey to an unique primary key.]错误处理」,

前端页面上使用的是日期控件:<a-range-picker></a-range-picker>。这样,当前端选择起始时间和终止时间相同的话,则后端Java程序收到的SQL语句类似于:

2021-06-28 17:24:55.716 [http-nio-7083-exec-8] DEBUG org.postgresql.jdbc.PgConnection -  -  -  -   setAutoCommit = false
2021-06-28 17:24:55.717 [http-nio-7083-exec-8] DEBUG com.onlyou.sam.flow.mapper.SamInterfaceRelMapper.countOperationLogByCondition -  -  -  - ==>  Preparing: SELECT id FROM t_es_sam_interface_rel WHERE 1 = 1 AND create_ts between ? and ? 
2021-06-28 17:24:55.737 [http-nio-7083-exec-8] DEBUG com.onlyou.sam.flow.mapper.SamInterfaceRelMapper.countOperationLogByCondition -  -  -  - ==> Parameters: 2021-04-27 08:00:00.0(Timestamp), 2021-04-27 08:00:00.0(Timestamp)
2021-06-28 17:24:55.742 [http-nio-7083-exec-8] DEBUG com.onlyou.sam.flow.mapper.SamInterfaceRelMapper.countOperationLogByCondition -  -  -  - <==      Total: 0
2021-06-28 17:24:55.744 [http-nio-7083-exec-8] DEBUG org.postgresql.jdbc.PgConnection -  -  -  -   setAutoCommit = true

那么问题就来了,如果我们的数据库中有满足条件的数据,结果却查询不到数据。因为,从后台程序可以日志可以看到,前端页面上选择的时间,最后传递过来变成了:2021-04-27 08:00:00.0(Timestamp), 2021-04-27 08:00:00.0(Timestamp)。

三 分析问题

1 从后端来看,首先是前端传递过来的参数格式没有带时分秒格式

chrome控制台上看到传递给后端的参数:控制台选择network选项卡,点击请求页面地址,选择Headers,在最下方查看。

{pageSize: 10, currentPage: 1, keyWord: "", condition: {beginDt: "2021-06-28", endDt: "2021-06-28"}}
condition: {beginDt: "2021-06-28", endDt: "2021-06-28"}
beginDt: "2021-06-28"
endDt: "2021-06-28"
currentPage: 1
keyWord: ""
pageSize: 10
2 更改前端页面空间,可以输入时分秒

搜索引擎搜索,或者ant design官网,搜索:datepicker

https://www.antdv.com/components/date-picker-cn/#DatePicker-

找到有可以输入日期+时间的控件,看看示例的源代码,然后,结合我们当前的页面,进行对应的更改。

//示例代码:
<template>
  <div>
    <a-date-picker show-time placeholder="Select Time" @change="onChange" @ok="onOk" />
    <br />
    <a-range-picker
      :show-time="{ format: 'HH:mm' }"
      format="YYYY-MM-DD HH:mm"
      :placeholder="['Start Time', 'End Time']"
      @change="onChange"
      @ok="onOk"
    />
  </div>
</template>
<script>
export default {
  methods: {
    onChange(value, dateString) {
      console.log('Selected Time: ', value);
      console.log('Formatted Selected Time: ', dateString);
    },
    onOk(value) {
      console.log('onOk: ', value);
    },
  },
};
</script>
//我的代码
<a-form-model-item >
              <a-range-picker
                v-model="searchDates"
                format="YYYY-MM-DD "
                class="select-col"
                :placeholder="['创建开始时间', '结束时间']"
                allowClear
              />
            </a-form-model-item>

这样,我照着官网的实例,将我的代码format=”YYYY-MM-DD “改为format=”YYYY-MM-DD HH:mm:ss”。然后保存代码,在chrome页面,时间控件上,就可以选择时间了。

但是,此时,传递给后端的时间格式还是没有时分秒的。

3 控制前端页面可以传递时分秒给后端Java

分析代码之后,看到watch监控代码中,控制时间组件的方法:

  watch: {
    searchDates: {
      handler(dates) {
        //如果清除掉日期,dates的值为空数组
        if (!Array.isArray(dates)) {
          this.searchPage.beginDt = ''
          this.searchPage.endDt = ''
          return
        }
        if (dates.length > 0) {
          //因为目前antd返回的是 moment类型,所以需要进行格式化
          let startDateMoment = this.$moment(dates[0])
          let endDateMoment = this.$moment(dates[1])
          this.searchPage.beginDt = startDateMoment.format('YYYY-MM-DD')
          this.searchPage.endDt = endDateMoment.format('YYYY-MM-DD')
        } else {
          this.searchPage.beginDt = '';
          this.searchPage.endDt = '';
        }
      },
    },
  },

将其中的2行代码:

this.searchPage.beginDt = startDateMoment.format('YYYY-MM-DD'),
this.searchPage.endDt = endDateMoment.format('YYYY-MM-DD')

改为:

this.searchPage.beginDt = startDateMoment.format('YYYY-MM-DD HH:mm:ss'),
this.searchPage.endDt = endDateMoment.format('YYYY-MM-DD HH:mm:ss')

都加上时间格式就好了,这样前端传递给后台的数据中,时间就包含了年月日时分秒的格式了。

{pageSize: 10, currentPage: 1, keyWord: "",…}
condition: {beginDt: "2021-06-29 10:00:12", endDt: "2021-06-29 10:00:12"}
beginDt: "2021-06-29 10:00:12"
endDt: "2021-06-29 10:00:12"
currentPage: 1
keyWord: ""
pageSize: 10

四 Java后端修改

前端的日期数据格式处理好了之后,接下来开始处理后端Java程序了。

1 后端接收到的数据处理格式报错
2021-06-29 10:00:37.370 [http-nio-7083-exec-3] WARN  org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver -  -  -  - Resolved [org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot deserialize value of type `java.util.Date` from String "2021-06-29 10:00:12": not a valid representation (error: Failed to parse Date value '2021-06-29 10:00:12': Cannot parse date "2021-06-29 10:00:12": while it seems to fit format 'yyyy-MM-dd'T'HH:mm:ss.SSSZ', parsing fails (leniency? null)); nested exception is com.fasterxml.jackson.databind.exc.InvalidFormatException: Cannot deserialize value of type `java.util.Date` from String "2021-06-29 10:00:12": not a valid representation (error: Failed to parse Date value '2021-06-29 10:00:12': Cannot parse date "2021-06-29 10:00:12": while it seems to fit format 'yyyy-MM-dd'T'HH:mm:ss.SSSZ', parsing fails (leniency? null))
 at [Source: (PushbackInputStream); line: 1, column: 68] (through reference chain: com.onlyou.sam.common.vo.PageVO["condition"]->com.onlyou.sam.flow.vo.InterfaceRelVO["beginDt"])]

从Google搜索引擎上获取相关的参考:

https://stackoverflow.com/questions/55227544/cannot-deserialize-value-of-type-java-util-date-from-string

https://facingissuesonit.com/2019/07/19/com-fasterxml-jackson-databind-exc-invalidformatexception-cannot-deserialize-value-of-type-java-util-date-from-string-mm-dd-yyyy-not-a-valid-representation-for-date-format/

于是找到后端Java代码里对应的View Object类,将其对应接收数据的属性,加上响应的注解:

2 修改Java类上相关字段添加JSON注解

修改前:

   /**
     * 添加带时间范围查询字段
     * 2021.06.25 Asher Huang
     */
    private Date beginDt;
    private Date endDt;

修改后:

/**
     * 添加带时间范围查询字段
     * 2021.06.25 Asher Huang
     */
    //添加@JsonFormat解决前端传递过来的时间格式,之前传递的是日期,现在改为时间日期格式。
    @JsonFormat(pattern="yyyy-MM-dd HH:mm:ss")
    private Date beginDt;
​
    @JsonFormat(pattern="yyyy-MM-dd HH:mm:ss")
    private Date endDt 

修改后,测试发现,后端执行了下述的SQL语句,没有返回任何数据给前端。

2021-06-29 10:12:40.922 [http-nio-7083-exec-2] DEBUG com.onlyou.sam.flow.mapper.SamInterfaceRelMapper.countOperationLogByCondition -  -  -  - ==>  Preparing: SELECT id FROM t_es_sam_interface_rel WHERE 1 = 1 AND create_ts between ? and ? 
2021-06-29 10:12:40.924 [http-nio-7083-exec-2] DEBUG com.onlyou.sam.flow.mapper.SamInterfaceRelMapper.countOperationLogByCondition -  -  -  - ==> Parameters: 2021-06-29 18:00:12.0(Timestamp), 2021-06-29 18:00:12.0(Timestamp)
2021-06-29 10:12:40.926 [http-nio-7083-exec-2] DEBUG com.onlyou.sam.flow.mapper.SamInterfaceRelMapper.countOperationLogByCondition -  -  -  - <==      Total: 0
2021-06-29 10:12:40.927 [http-nio-7083-exec-2] DEBUG org.postgresql.jdbc.PgConnection -  -  -  -   setAutoCommit = true

究其原因是,前端发送的时间数据是:”2021-06-29 10:00:12″,后端接收到的数据是:2021-06-29 18:00:12.0(Timestamp),差了8个时区,进而导致数据不对。

3 Java类字段添加JSON的时区注解

继续Google之后,发现给VO类的时间字段的json注解,添加时区属性即可。

/**
     * 添加带时间范围查询字段
     * 2021.06.25 Asher Huang
     */
    //添加@JsonFormat解决前端传递过来的时间格式,之前传递的是日期,现在改为时间日期格式。带上时区,解决时差8小时的问题
    @JsonFormat(pattern="yyyy-MM-dd HH:mm:ss",timezone = "GMT+8")
    private Date beginDt;
​
    @JsonFormat(pattern="yyyy-MM-dd HH:mm:ss",timezone = "GMT+8")
    private Date endDt;    

然后,经过测试,后端程序接收到的数据就正常了,和前端发送的数据保持一致了。

4 有没有更优雅的办法

当前情形是,在每个后端要用到的VO上,都需要手工添加json的注解。如果用到的VO少还好处理,如果多的话,岂不是比较麻烦。看到有网友提供的方案,在全局配置文件:application.properties配置文件中加配置。

https://www.jianshu.com/p/d616d31c75d6

## default config
spring:
  jackson:
    time-zone: "GMT+8"
    date-format: "yyyy-MM-dd HH:mm:ss"

经过测试验证,该方案并不生效。即使用该全局配置,在VO上注释掉@JsonFormat注解。会报错,进而说明,该方法不对,或者我的环境上还有其它的配置没有修改过来。暂时,先到这儿吧。先记下来,后面找到更好的方案时,再来完善总结更新。

五 小结

新手学前端Vue,学着写后端Java,遇到错误,自己动手尝试解决,请教同事,积累成长,还蛮有意思的的。

留言