前端技术:Vue3 + TypeScript + Element Plus
后端技术:Java + Spring Boot + MyBatis
应用效果:
原来方案
1、前端日期控件使用 el-date-picker,日期显示格式和日期值返回格式都为:YYYY-MM-DD
<el-form :model="queryObj" :inline="true"><el-form-item label="流转时间:"><el-date-pickerv-model="queryObj.transactionTime"type="daterange"start-placeholder="开始时间"range-separator="至"end-placeholder="结束时间"format="YYYY-MM-DD"value-format="YYYY-MM-DD"unlink-panelsstyle="width: 240px"></el-date-picker></el-form-item></el-form>
2、前端查询对象中,流转时间 transactionTime,数据定义为 string[]
// 交易查询对象
export interface ITransactionsQueryObj {// 分仓id,1:试剂耗材;2:标准物质warehouseId: number | null;// 交易类型,1:入库;2:出库transactionType: number | null;// 流转时间transactionTime: string[];// 物资名称materialName: string;
}
3、后端查询对象中, 流转时间开始和流转时间结束,数据定义为 LocalDateTime
package com.weiyu.pojo;import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;import java.time.LocalDateTime;
import java.util.List;/*** 分仓交易(入库/出库)记录查询 DTO*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class BranchWarehouseTransactionsQueryDTO {// 分仓id,1:试剂耗材;2:标准物质private Integer warehouseId;// 交易类型,1:入库;2:出库private Integer transactionType;// 流转时间private List<String> transactionTime;// 物资名称private String materialName;// 流转时间开始private LocalDateTime transactionTimeBegin;// 流转时间结束private LocalDateTime transactionTimeEnd;
}
4、后端接收前端传递过来的流转时间 transactionTime(数据格式为:[2025-06-01, 2025-06-10]),使用 DateTimeFormatter 解析为 LocalDate,再通过 atStartOfDay() 和 atTime(LocalTime.MAX) 转换为 LocalDateTime
BranchWarehouseServiceImpl.java
// 查询试剂交易(入库/出库)记录@Overridepublic List<BranchWarehouseTransactions> queryForReagent(BranchWarehouseTransactionsQueryDTO queryDTO) {if ((queryDTO.getTransactionTime() == null || queryDTO.getTransactionTime().isEmpty()) &&queryDTO.getTransactionType() == null &&(queryDTO.getMaterialName() == null || queryDTO.getMaterialName().isEmpty())) {throw new RuntimeException("请输入查询条件!");}// 处理字符日期数组,transactionTime=[2025-06-01, 2025-06-10],需处理为 LocalDateTime 格式的开始时间和结束时间if (queryDTO.getTransactionTime() != null && queryDTO.getTransactionTime().size() == 2) {DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");queryDTO.setTransactionTimeBegin(LocalDate.parse(queryDTO.getTransactionTime().get(0), formatter).atStartOfDay());queryDTO.setTransactionTimeEnd(LocalDate.parse(queryDTO.getTransactionTime().get(1), formatter).atTime(LocalTime.MAX));}return branchWarehouseMapper.selectForReagent(queryDTO, PublicUtils.getUserName());}
4、查询
BranchWarehouseMapper.java
// 查询试剂交易(入库/出库)记录List<BranchWarehouseTransactions> selectForReagent(BranchWarehouseTransactionsQueryDTO queryDTO, String userName);
BranchWarehouseMapper.xml
<!-- 查询试剂交易(入库/出库)记录 --><select id="selectForReagent" resultType="com.weiyu.pojo.BranchWarehouseTransactionsQueryVO">select<include refid="selectForReagentBranchWarehouseTransactionsFields"/>from BranchWarehouseTransactions t1inner join Reagent t2on t2.rea_ID = t1.material_id<where><include refid="warehouse_is_reagent"/><if test="queryDTO.transactionTimeBegin != null">and t1.transaction_time >= #{queryDTO.transactionTimeBegin}</if><if test="queryDTO.transactionTimeEnd != null">and t1.transaction_time <= #{queryDTO.transactionTimeEnd}</if><if test="queryDTO.transactionType != null">and t1.transaction_type = #{queryDTO.transactionType}</if><if test="queryDTO.materialName != null and queryDTO.materialName != ''">and t2.rea_Name like '%' + #{queryDTO.materialName} + '%'</if>and exists (select 1from Employee t3where t3.emp_ID = #{userName}and t3.emp_DeptID = t2.rea_DeptID)</where>order by t1.id asc</select>
调整方案1
调整内容:
1、前端日期控件使用 el-date-picker,日期显示格式为:YYYY-MM-DD,日期值返回格式为:YYYY-MM-DD HH:mm:ss,增加:default-time="[new Date(0, 0, 0, 0, 0, 0), new Date(0, 0, 0, 23, 59, 59)]"
<el-form :model="queryObj" :inline="true"><el-form-item label="流转时间:"><el-date-pickerv-model="queryObj.transactionTime"type="daterange"start-placeholder="开始时间"range-separator="至"end-placeholder="结束时间"format="YYYY-MM-DD"value-format="YYYY-MM-DD HH:mm:ss":default-time="[new Date(0, 0, 0, 0, 0, 0), new Date(0, 0, 0, 23, 59, 59)]"unlink-panelsstyle="width: 240px"></el-date-picker></el-form-item></el-form>
4、后端接收前端传递过来的流转时间 transactionTime(数据格式为:[2025-06-01 00:00:00, 2025-06-10 23:59:59]),使用 DateTimeFormatter 直接解析为LocalDateTime
BranchWarehouseServiceImpl.java
// 查询试剂交易(入库/出库)记录@Overridepublic List<BranchWarehouseTransactions> queryForReagent(BranchWarehouseTransactionsQueryDTO queryDTO) {if ((queryDTO.getTransactionTime() == null || queryDTO.getTransactionTime().isEmpty()) &&queryDTO.getTransactionType() == null &&(queryDTO.getMaterialName() == null || queryDTO.getMaterialName().isEmpty())) {throw new RuntimeException("请输入查询条件!");}// 处理字符日期数组,transactionTime=[2025-06-01 00:00:00, 2025-06-10 23:59:59],需处理为 LocalDateTime 格式的开始时间和结束时间if (queryDTO.getTransactionTime() != null && queryDTO.getTransactionTime().size() == 2) {DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");queryDTO.setTransactionTimeBegin(LocalDateTime.parse(queryDTO.getTransactionTime().get(0), formatter));queryDTO.setTransactionTimeEnd(LocalDateTime.parse(queryDTO.getTransactionTime().get(1), formatter));}return branchWarehouseMapper.selectForReagent(queryDTO, PublicUtils.getUserName());}
调整方案2
调整内容:
1、前端日期控件使用 el-date-picker,日期显示格式为:YYYY-MM-DD,日期值返回格式为:x,增加:default-time="[new Date(0, 0, 0, 0, 0, 0), new Date(0, 0, 0, 23, 59, 59)]"
<el-form :model="queryObj" :inline="true"><el-form-item label="流转时间:"><el-date-pickerv-model="queryObj.transactionTime"type="daterange"start-placeholder="开始时间"range-separator="至"end-placeholder="结束时间"format="YYYY-MM-DD"value-format="x":default-time="[new Date(0, 0, 0, 0, 0, 0), new Date(0, 0, 0, 23, 59, 59)]"unlink-panelsstyle="width: 240px"></el-date-picker></el-form-item></el-form>
4、后端接收前端传递过来的流转时间 transactionTime(数据格式为:[1748707200000, 1749571199000]),先将时间戳 转换为 Instant (UTC时间),再转换为LocalDateTime
BranchWarehouseServiceImpl.java
// 查询试剂交易(入库/出库)记录@Overridepublic List<BranchWarehouseTransactions> queryForReagent(BranchWarehouseTransactionsQueryDTO queryDTO) {if ((queryDTO.getTransactionTime() == null || queryDTO.getTransactionTime().isEmpty()) &&queryDTO.getTransactionType() == null &&(queryDTO.getMaterialName() == null || queryDTO.getMaterialName().isEmpty())) {throw new RuntimeException("请输入查询条件!");}// 处理字符日期数组,transactionTime=[1748707200000, 1749571199000],需处理为 LocalDateTime 格式的开始时间和结束时间if (queryDTO.getTransactionTime() != null && queryDTO.getTransactionTime().size() == 2) {// 先将时间戳 转换为 Instant (UTC时间)Instant instant1 = Instant.ofEpochMilli(Long.parseLong(queryDTO.getTransactionTime().get(0)));Instant instant2 = Instant.ofEpochMilli(Long.parseLong(queryDTO.getTransactionTime().get(1)));queryDTO.setTransactionTimeBegin(LocalDateTime.ofInstant(instant1, ZoneId.systemDefault()));queryDTO.setTransactionTimeEnd(LocalDateTime.ofInstant(instant2, ZoneId.systemDefault()));}return branchWarehouseMapper.selectForReagent(queryDTO, PublicUtils.getUserName());}
最终方案
特点:前端随便传,后端都兼容
已测试的前端传递方式
value-format="YYYY-MM-DD",前端传递日期数据样式:[2025-06-01, 2025-06-10]
format="YYYY-MM-DD"value-format="YYYY-MM-DD"
value-format="YYYY-MM-DD",前端传递日期数据样式:[2025-06-01, 2025-06-10]
format="YYYY-MM-DD"value-format="YYYY-MM-DD":default-time="[new Date(0, 0, 0, 0, 0, 0), new Date(0, 0, 0, 23, 59, 59)]"
value-format="YYYY-MM-DD HH:mm:ss",前端传递时间数据样式:[2025-06-01 00:00:00, 2025-06-10 00:00:00]
format="YYYY-MM-DD"value-format="YYYY-MM-DD HH:mm:ss"
value-format="YYYY-MM-DD HH:mm:ss",前端传递时间数据样式:[2025-06-01 00:00:00, 2025-06-10 23:59:59]
format="YYYY-MM-DD"value-format="YYYY-MM-DD HH:mm:ss":default-time="[new Date(0, 0, 0, 0, 0, 0), new Date(0, 0, 0, 23, 59, 59)]"
value-format="x" ,前端传递时间戳数据样式:[1748707200000, 1749484800000]
format="YYYY-MM-DD"value-format="x"
value-format="x" ,前端传递时间戳数据样式:[1748707200000, 1749571199000]
format="YYYY-MM-DD"value-format="x":default-time="[new Date(0, 0, 0, 0, 0, 0), new Date(0, 0, 0, 23, 59, 59)]"
后端:BranchWarehouseServiceImpl.java
// 查询试剂交易(入库/出库)记录@Overridepublic List<BranchWarehouseTransactions> queryForReagent(BranchWarehouseTransactionsQueryDTO queryDTO) {if ((queryDTO.getTransactionTime() == null || queryDTO.getTransactionTime().isEmpty()) &&queryDTO.getTransactionType() == null &&(queryDTO.getMaterialName() == null || queryDTO.getMaterialName().isEmpty())) {throw new RuntimeException("请输入查询条件!");}if (queryDTO.getTransactionTime() != null && queryDTO.getTransactionTime().size() == 2) {String dateStyle = getDateStyle(queryDTO);switch (dateStyle) {// 处理字符日期数组,transactionTime=[2025-06-01, 2025-06-10],需处理为 LocalDateTime 格式的开始时间和结束时间case "日期" -> {DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");queryDTO.setTransactionTimeBegin(LocalDate.parse(queryDTO.getTransactionTime().get(0), formatter).atStartOfDay());queryDTO.setTransactionTimeEnd(LocalDate.parse(queryDTO.getTransactionTime().get(1), formatter).atTime(LocalTime.MAX));}// 处理字符日期数组,transactionTime=[2025-06-01 00:00:00, 2025-06-10 00:00:00],需处理为 LocalDateTime 格式的开始时间和结束时间case "日期零时" -> {DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");queryDTO.setTransactionTimeBegin(LocalDate.parse(queryDTO.getTransactionTime().get(0).substring(0,10), formatter).atStartOfDay());queryDTO.setTransactionTimeEnd(LocalDate.parse(queryDTO.getTransactionTime().get(1).substring(0,10), formatter).atTime(LocalTime.MAX));}// 处理字符日期数组,transactionTime=[2025-06-01 00:00:00, 2025-06-10 23:59:59],需处理为 LocalDateTime 格式的开始时间和结束时间case "日期时间" -> {DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");queryDTO.setTransactionTimeBegin(LocalDateTime.parse(queryDTO.getTransactionTime().get(0), formatter));queryDTO.setTransactionTimeEnd(LocalDateTime.parse(queryDTO.getTransactionTime().get(1), formatter));}// 处理字符日期数组,transactionTime=[1748707200000, 1749484800000] 或 [1748707200000, 1749571199000],需处理为 LocalDateTime 格式的开始时间和结束时间case "时间戳" -> {// 先将时间戳 转换为 Instant (UTC时间)Instant instant1 = Instant.ofEpochMilli(Long.parseLong(queryDTO.getTransactionTime().get(0)));Instant instant2 = Instant.ofEpochMilli(Long.parseLong(queryDTO.getTransactionTime().get(1)));// 因为时间戳有可能是没有时间部分内容的,所以这里统一先转为 LocalDate,再通过 atStartOfDay() 和 atTime(LocalTime.MAX) 转为 LocalDateTimequeryDTO.setTransactionTimeBegin(LocalDate.ofInstant(instant1, ZoneId.systemDefault()).atStartOfDay());queryDTO.setTransactionTimeEnd(LocalDate.ofInstant(instant2, ZoneId.systemDefault()).atTime(LocalTime.MAX));}default -> {List<LocalDateTime> dateTimeList = DateUtils.parseDateTimeRange(queryDTO.getTransactionTime());queryDTO.setTransactionTimeBegin(dateTimeList.get(0));queryDTO.setTransactionTimeEnd(dateTimeList.get(1));}}}return branchWarehouseMapper.selectForReagent(queryDTO, PublicUtils.getUserName());}// 获取日期格式private static String getDateStyle(BranchWarehouseTransactionsQueryDTO queryDTO) {String dateStr = queryDTO.getTransactionTime().get(1);int len = dateStr.length();return switch (len) {// dateStr=[2025-06-01, 2025-06-10]case 10 -> "日期";// dateStr=[2025-06-01 00:00:00, 2025-06-10 00:00:00] 或 [2025-06-01 00:00:00, 2025-06-10 23:59:59]case 19 -> {// dateStr=[2025-06-01 00:00:00, 2025-06-10 00:00:00]if (dateStr.endsWith("00:00:00")) {yield "日期零时";}// dateStr=[2025-06-01 00:00:00, 2025-06-10 23:59:59]else {yield "日期时间";}}// dateStr=[1748707200000, 1749484800000] 或 [1748707200000, 1749571199000]case 13 -> "时间戳";default -> throw new IllegalStateException("Unexpected value: " + dateStr);};}
后端优化:封装日期解析逻辑为日期工具类 DateUtils.java
package com.weiyu.utils;import com.weiyu.utils.enums.DateEnumDirection;import java.time.*;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.time.format.ResolverStyle;
import java.util.ArrayList;
import java.util.List;public class DateUtils {// 标准模式// private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd");private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("uuuu-MM-dd")// 启用严格模式,使用 ResolverStyle.STRICT 严格模式,拒绝无效日期(如 2023-02-29),自动处理闰年、月份天数等复杂逻辑// 启用严格模式,ofPattern必设置为"uuuu-MM-dd").withResolverStyle(ResolverStyle.STRICT);private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("uuuu-MM-dd HH:mm:ss").withResolverStyle(ResolverStyle.STRICT);/*** 有效检查* @param dateStr 字符串* @return 提示信息*/public static String isValidDate(String dateStr) {// 1. 空值检查if (dateStr == null) return "日期字符串不能为null";// 2. 空白检查if (dateStr.isEmpty()) {return "日期字符串不能为空";}return "";}/*** 转换为 LocalDate 格式的日期* @param dateStr 字符串* @return LocalDate 格式的日期*/public static LocalDate parseDate(String dateStr) {// 检查日期字符串格式String msg = isValidDate(dateStr);if (!msg.isEmpty()) {throw new RuntimeException(msg);}try {// 解析日期return LocalDate.parse(dateStr, DATE_FORMATTER);} catch (DateTimeParseException e) {// 格式错误处理throw new IllegalArgumentException("日期格式错误,应为 yyyy-MM-dd,并且为有效日期", e);}}/*** 将字符数组转换为日期数组,如:["2025-06-01", "2026-06-10"] 转换为 [beginDate, endDate]* @param dateStrList 字符数组* @return 日期数组,只限开始日期和结束日期,[beginDate, endDate]*/public static List<LocalDate> parseDateRange(List<String> dateStrList) {List<LocalDate> dateList = new ArrayList<>();if (dateStrList == null || dateStrList.size() != 2) {dateList.add(LocalDate.now());dateList.add(LocalDate.now());} else {try {dateList.add(parseDate(dateStrList.get(0)));dateList.add(parseDate(dateStrList.get(1)));} catch (DateTimeParseException e) {dateList.add(LocalDate.now());dateList.add(LocalDate.now());}}// 检查日期逻辑关系,开始时间大于结束时间if (dateList.get(0).isAfter(dateList.get(1))) {LocalDate maxDate = dateList.get(0);dateList.set(0, dateList.get(1));dateList.set(1,maxDate);}return dateList;}/*** 解析日期字符串范围为日期范围* @param dateStrRange 日期字符串范围,如:[2025-06-01, 2025-06-10]、[2025-06-01 00:00:00, 2025-06-10 23:59:59]、[1748707200000, 1749571199000]* @return List<LocalDateTime> 日期范围,如:[beginDateTime, endDateTime]*/public static List<LocalDateTime> parseDateTimeRange(List<String> dateStrRange) {List<LocalDateTime> dateTimeRange = new ArrayList<>();if (dateStrRange != null && dateStrRange.size() == 2) {DateEnumDirection dateDirection = getDateDirection(dateStrRange.get(1));switch (dateDirection) {// 处理字符日期数组,transactionTime=[2025-06-01, 2025-06-10],需处理为 LocalDateTime 格式的开始时间和结束时间case DATE -> {dateTimeRange.add(LocalDate.parse(dateStrRange.get(0), DATE_FORMATTER).atStartOfDay());dateTimeRange.add(LocalDate.parse(dateStrRange.get(1), DATE_FORMATTER).atTime(LocalTime.MAX));}// 处理字符日期数组,transactionTime=[2025-06-01 00:00:00, 2025-06-10 00:00:00],需处理为 LocalDateTime 格式的开始时间和结束时间case DATE_TIME -> {dateTimeRange.add(LocalDate.parse(dateStrRange.get(0).substring(0,10), DATE_FORMATTER).atStartOfDay());dateTimeRange.add(LocalDate.parse(dateStrRange.get(1).substring(0,10), DATE_FORMATTER).atTime(LocalTime.MAX));}// 处理字符日期数组,transactionTime=[2025-06-01 00:00:00, 2025-06-10 23:59:59],需处理为 LocalDateTime 格式的开始时间和结束时间case DATE_ZERO_TIME -> {dateTimeRange.add(LocalDateTime.parse(dateStrRange.get(0), DATE_TIME_FORMATTER));dateTimeRange.add(LocalDateTime.parse(dateStrRange.get(1), DATE_TIME_FORMATTER));}// 处理字符日期数组,transactionTime=[1748707200000, 1749484800000] 或 [1748707200000, 1749571199000],需处理为 LocalDateTime 格式的开始时间和结束时间case TIME_STAMP -> {// 先将时间戳 转换为 Instant (UTC时间)Instant instant1 = Instant.ofEpochMilli(Long.parseLong(dateStrRange.get(0)));Instant instant2 = Instant.ofEpochMilli(Long.parseLong(dateStrRange.get(1)));// 因为时间戳有可能是没有时间部分内容的,所以这里统一先转为 LocalDate,再通过 atStartOfDay() 和 atTime(LocalTime.MAX) 转为 LocalDateTimedateTimeRange.add(LocalDate.ofInstant(instant1, ZoneId.systemDefault()).atStartOfDay());dateTimeRange.add(LocalDate.ofInstant(instant2, ZoneId.systemDefault()).atTime(LocalTime.MAX));}default -> {List<LocalDate> dateList = parseDateRange(dateStrRange);dateTimeRange.add(dateList.get(0).atStartOfDay());dateTimeRange.add(dateList.get(1).atTime(LocalTime.MAX));}}}return dateTimeRange;}/*** 日期时间格式方向枚举* @param dateStr 日期字符串* @return 日期时间格式方向枚举 DateEnumDirection */public static DateEnumDirection getDateDirection(String dateStr) {int dateStrLen = dateStr.length();return switch (dateStrLen) {// dateStr=[2025-06-01, 2025-06-10]case 10 -> DateEnumDirection.DATE;// dateStr=[2025-06-01 00:00:00, 2025-06-10 00:00:00] 或 [2025-06-01 00:00:00, 2025-06-10 23:59:59]case 19 -> {// dateStr=[2025-06-01 00:00:00, 2025-06-10 00:00:00]if (dateStr.endsWith("00:00:00")) {yield DateEnumDirection.DATE_ZERO_TIME;}// dateStr=[2025-06-01 00:00:00, 2025-06-10 23:59:59]else {yield DateEnumDirection.DATE_TIME;}}// dateStr=[1748707200000, 1749484800000] 或 [1748707200000, 1749571199000]case 13 -> DateEnumDirection.TIME_STAMP;default -> DateEnumDirection.OTHER;};}
}
// 查询试剂交易(入库/出库)记录@Overridepublic List<BranchWarehouseTransactions> queryForReagent(BranchWarehouseTransactionsQueryDTO queryDTO) {if ((queryDTO.getTransactionTime() == null || queryDTO.getTransactionTime().isEmpty()) &&queryDTO.getTransactionType() == null &&(queryDTO.getMaterialName() == null || queryDTO.getMaterialName().isEmpty())) {throw new RuntimeException("请输入查询条件!");}// 日期字符串数组,transactionTime=[2025-06-01, 2025-06-10] 或 [2025-06-01 00:00:00, 2025-06-10 23:59:59] 或 [1748707200000, 1749571199000]// 使用日期工具 DateUtils.parseDateTimeRange 转换为 LocalDateTime 格式的开始时间和结束时间queryDTO.setTransactionTimeBegin(DateUtils.parseDateTimeRange(queryDTO.getTransactionTime()).get(0));queryDTO.setTransactionTimeEnd(DateUtils.parseDateTimeRange(queryDTO.getTransactionTime()).get(1));return branchWarehouseMapper.selectForReagent(queryDTO, PublicUtils.getUserName());}