1. 需求分析
前面我们已经完成了检查项管理、检查组管理、套餐管理等。接下来我们需要进行预约设置,其实就是设置每一天的体检预约最大数量。客户可以通过微信端在线预约,在线预约时需要选择体检的时间,如果客户选择的时间已经预约满则无法进行预约。
2. 批量导入预约设置信息
预约设置信息对应的数据表为t_ordersetting,预约设置操作对应的页面为ordersetting.html
t_ordersetting表结构:
orderDate:预约日期
number:可预约人数
reservations:已预约人数
批量导入预约设置信息操作过程:
- 点击模板下载按钮下载Excel模板文件
- 将预约设置信息录入到模板文件中
- 点击上传文件按钮将录入完信息的模板文件上传到服务器
- 通过POI读取上传文件的数据并保存到数据库
本章解析Excel文件使用的技术为 Apache POI
2.1 完善页面
2.1.1 提供模板文件
资料中已经提供了Excel模板文件ordersetting_template.xlsx,将文件放在health_backend工程的template目录
2.1.2 实现模板文件下载
为模板下载按钮绑定事件实现模板文件下载
<el-button style="margin-bottom: 20px;margin-right: 20px" type="primary"
@click="downloadTemplate()">模板下载</el-button>
//模板文件下载
downloadTemplate(){
window.location.href="../../template/ordersetting_template.xlsx";
}
2.1.3 文件上传
使用ElementUI的上传组件实现文件上传并绑定相关事件
<el-upload action="/ordersetting/upload.do"
name="excelFile"
:show-file-list="false"
:on-success="handleSuccess"
:before-upload="beforeUpload">
<el-button type="primary">上传文件</el-button>
</el-upload>
handleSuccess(response, file) {
if(response.flag){
this.$message({
message: response.message,
type: 'success'
});
}else{
this.$message.error(response.message);
}
}
beforeUpload(file){
const isXLS = file.type === 'application/vnd.ms-excel';
if(isXLS){
return true;
}
const isXLSX = file.type === 'application/vnd.openxmlformatsofficedocument.spreadsheetml.sheet';
if (isXLSX) {
return true;
}
this.$message.error('上传文件只能是xls或者xlsx格式!');
return false;
}
2.2 后台代码
2.2.1 Controller
将资料中的POIUtils工具类复制到health_common工程
在health_backend工程创建OrderSettingController并提供upload方法
import com.alibaba.dubbo.config.annotation.Reference;
import com.itheima.constant.MessageConstant;
import com.itheima.entity.Result;
import com.itheima.pojo.OrderSetting;
import com.itheima.service.OrderSettingService;
import com.itheima.utils.POIUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import javax.xml.ws.handler.MessageContext;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
/**
* @author 嘿嘿嘿1212
* @version 1.0
* @date 2019/10/18 19:39
*/
@RestController
@RequestMapping("ordersetting")
public class OrderSettingController {
@Reference
private OrderSettingService orderSettingService;
/**
* 文件上传,实现预约设置数据批量导入
*
* @param excelFile
* @return
*/
@RequestMapping("upload")
public Result upload(@RequestParam("excelFile") MultipartFile excelFile) {
try {
//使用POI解析Excel表格
List<String[]> excel = POIUtils.readExcel(excelFile);
//转换数据类型
List<OrderSetting> list = new LinkedList<OrderSetting>();
for (String[] strings : excel) {
String orderDate = strings[0];
String number = strings[1];
OrderSetting orderSetting = new OrderSetting(new Date(orderDate), Integer.parseInt(number));
list.add(orderSetting);
}
orderSettingService.add(list);
} catch (Exception e) {
e.printStackTrace();
return new Result(false, MessageConstant.IMPORT_ORDERSETTING_FAIL);
}
return new Result(true, MessageConstant.IMPORT_ORDERSETTING_SUCCESS);
}
}
- POIUtils
package com.itheima.utils;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.web.multipart.MultipartFile;
public class POIUtils {
private final static String xls = "xls";
private final static String xlsx = "xlsx";
private final static String DATE_FORMAT = "yyyy/MM/dd";
/**
* 读入excel文件,解析后返回
* @param file
* @throws IOException
*/
public static List<String[]> readExcel(MultipartFile file) throws IOException {
//检查文件
checkFile(file);
//获得Workbook工作薄对象
Workbook workbook = getWorkBook(file);
//创建返回对象,把每行中的值作为一个数组,所有行作为一个集合返回
List<String[]> list = new ArrayList<String[]>();
if(workbook != null){
for(int sheetNum = 0;sheetNum < workbook.getNumberOfSheets();sheetNum++){
//获得当前sheet工作表
Sheet sheet = workbook.getSheetAt(sheetNum);
if(sheet == null){
continue;
}
//获得当前sheet的开始行
int firstRowNum = sheet.getFirstRowNum();
//获得当前sheet的结束行
int lastRowNum = sheet.getLastRowNum();
//循环除了第一行的所有行
for(int rowNum = firstRowNum+1;rowNum <= lastRowNum;rowNum++){
//获得当前行
Row row = sheet.getRow(rowNum);
if(row == null){
continue;
}
//获得当前行的开始列
int firstCellNum = row.getFirstCellNum();
//获得当前行的列数
int lastCellNum = row.getPhysicalNumberOfCells();
String[] cells = new String[row.getPhysicalNumberOfCells()];
//循环当前行
for(int cellNum = firstCellNum; cellNum < lastCellNum;cellNum++){
Cell cell = row.getCell(cellNum);
cells[cellNum] = getCellValue(cell);
}
list.add(cells);
}
}
workbook.close();
}
return list;
}
//校验文件是否合法
public static void checkFile(MultipartFile file) throws IOException{
//判断文件是否存在
if(null == file){
throw new FileNotFoundException("文件不存在!");
}
//获得文件名
String fileName = file.getOriginalFilename();
//判断文件是否是excel文件
if(!fileName.endsWith(xls) && !fileName.endsWith(xlsx)){
throw new IOException(fileName + "不是excel文件");
}
}
public static Workbook getWorkBook(MultipartFile file) {
//获得文件名
String fileName = file.getOriginalFilename();
//创建Workbook工作薄对象,表示整个excel
Workbook workbook = null;
try {
//获取excel文件的io流
InputStream is = file.getInputStream();
//根据文件后缀名不同(xls和xlsx)获得不同的Workbook实现类对象
if(fileName.endsWith(xls)){
//2003
workbook = new HSSFWorkbook(is);
}else if(fileName.endsWith(xlsx)){
//2007
workbook = new XSSFWorkbook(is);
}
} catch (IOException e) {
e.printStackTrace();
}
return workbook;
}
public static String getCellValue(Cell cell){
String cellValue = "";
if(cell == null){
return cellValue;
}
//如果当前单元格内容为日期类型,需要特殊处理
String dataFormatString = cell.getCellStyle().getDataFormatString();
if(dataFormatString.equals("m/d/yy")){
cellValue = new SimpleDateFormat(DATE_FORMAT).format(cell.getDateCellValue());
return cellValue;
}
//把数字当成String来读,避免出现1读成1.0的情况
if(cell.getCellType() == Cell.CELL_TYPE_NUMERIC){
cell.setCellType(Cell.CELL_TYPE_STRING);
}
//判断数据的类型
switch (cell.getCellType()){
case Cell.CELL_TYPE_NUMERIC: //数字
cellValue = String.valueOf(cell.getNumericCellValue());
break;
case Cell.CELL_TYPE_STRING: //字符串
cellValue = String.valueOf(cell.getStringCellValue());
break;
case Cell.CELL_TYPE_BOOLEAN: //Boolean
cellValue = String.valueOf(cell.getBooleanCellValue());
break;
case Cell.CELL_TYPE_FORMULA: //公式
cellValue = String.valueOf(cell.getCellFormula());
break;
case Cell.CELL_TYPE_BLANK: //空值
cellValue = "";
break;
case Cell.CELL_TYPE_ERROR: //故障
cellValue = "非法字符";
break;
default:
cellValue = "未知类型";
break;
}
return cellValue;
}
}
2.2.2 服务接口
创建OrderSettingService服务接口并提供新增方法
package com.itheima.service;
import com.itheima.pojo.OrderSetting;
import java.util.List;
public interface OrderSettingService {
public void add(List<OrderSetting> list) throws Exception;
}
2.2.3 服务实现类
创建服务实现类OrderSettingServiceImpl并实现新增方法
package com.itheima.service.impl;
import com.alibaba.dubbo.config.annotation.Service;
import com.itheima.dao.OrderSettingDao;
import com.itheima.pojo.OrderSetting;
import com.itheima.service.OrderSettingService;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.List;
/**
* @author 嘿嘿嘿1212
* @version 1.0
* @date 2019/10/18 20:09
*/
@Service(interfaceClass = OrderSettingService.class)
public class OrderSettingServiceImpl implements OrderSettingService {
@Autowired
private OrderSettingDao orderSettingDao;
@Override
public void add(List<OrderSetting> list) throws Exception {
//批量导入预约人数
if (list != null && list.size() > 0) {
for (OrderSetting orderSetting : list) {
//判断当前日期是否已经进行了预约设置
long countByOrderDate = orderSettingDao.findCountByOrderDate(orderSetting.getOrderDate());
if (countByOrderDate > 0) {
//已经进行了预约设置,执行更新操作
orderSettingDao.editNumberByOrderDate(orderSetting);
} else {
//没有进行预约设置,执行插入操作
orderSettingDao.add(orderSetting);
}
}
}
}
}
2.2.4 Dao接口
创建Dao接口OrderSettingDao并提供更新和新增方法与查询
package com.itheima.dao;
import com.itheima.pojo.OrderSetting;
import java.util.Date;
/**
* @author 嘿嘿嘿1212
* @version 1.0
* @date 2019/10/18 20:17
*/
public interface OrderSettingDao {
/**
* 根据预约时间查询是否存在预约
* @param orderDate
* @return
*/
public long findCountByOrderDate(Date orderDate);
/**
* 添加预约
* @param orderSetting
*/
public void add(OrderSetting orderSetting);
/**
* 根据预约时间修改预约人数
* @param orderSetting
*/
public void editNumberByOrderDate(OrderSetting orderSetting);
}
2.2.5 Mapper映射文件
创建Mapper映射文件OrderSettingDao.xml并提供相关SQL
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.itheima.dao.OrderSettingDao">
<!--根据预约时间查询-->
<select id="findCountByOrderDate" resultType="long" parameterType="date">
select count(*)
from t_ordersetting
where orderDate = #{orderDate}
</select>
<!--添加预约数据-->
<insert id="add" parameterType="com.itheima.pojo.OrderSetting">
insert into
t_ordersetting (orderDate, number, reservations)
values
(#{orderDate}, #{number}, #{reservations})
</insert>
<!--根据预约时间修改可预约人数-->
<update id="editNumberByOrderDate" parameterType="com.itheima.pojo.OrderSetting">
update t_ordersetting
set number = #{number}
where orderDate = #{orderDate}
</update>
</mapper>
3. 日历展示预约设置信息
前面已经完成了预约设置功能,现在就需要通过日历的方式展示出来每天设置的预约人数。
在页面中已经完成了日历的动态展示,我们只需要查询当前月份的预约设置信息并展示到日历中即可,同时在日历中还需要展示已经预约的人数,效果如下:
3.1 完善页面
3.1.1 使用静态数据调试
为了能够快速看到效果,我们可以先使用静态数据模拟,然后再改为发送ajax请求查询数据库。
实现步骤:
(1)预约设置数据对应的模型数据为leftobj,在initData方法最后为leftobj模型数据赋值:
this.leftobj = [
{ date: 1, number: 120, reservations: 1 },
{ date: 3, number: 120, reservations: 1 },
{ date: 4, number: 120, reservations: 120 },
{ date: 6, number: 120, reservations: 1 },
{ date: 8, number: 120, reservations: 1 }
];
其中date表示日期,number表示可预约人数,reservations表示已预约人数
(2)使用VUE的v-for标签遍历上面的leftobj模型数据,展示到日历上:
<template>
<template v-for="obj in leftobj">
<template v-if="obj.date == dayobject.day.getDate()">
<template v-if="obj.number > obj.reservations">
<div class="usual">
<p>可预约{{obj.number}}人</p>
<p>已预约{{obj.reservations}}人</p>
</div>
</template>
<template v-else>
<div class="fulled">
<p>可预约{{obj.number}}人</p>
<p>已预约{{obj.reservations}}人</p>
<p>已满</p>
</div>
</template>
</template>
</template>
<button v-if="dayobject.day > today" @click="handleOrderSet(dayobject.day)"
class="orderbtn">设置
</button>
</template>
</div>
</template>
3.1.2 发送ajax获取动态数据
将上面的静态模拟数据去掉,改为发送ajax请求,根据当前页面对应的月份查询数据库获取预约设置信息,将查询结果赋值给leftobj模型数据
//发送ajax请求,根据当前页面对应的月份查询预约设置信息
axios.post(
"/ordersetting/getOrderSettingByMonth.do?date="+this.currentYear+'-'+this.currentMonth
).then((response)=>{
if(response.data.flag){
//为模型数据赋值,通过双向绑定展示到日历中
this.leftobj = response.data.data;
}else{
this.$message.error(response.data.message);
}
});
3.2 后台代码
3.2.1 Controller
在OrderSettingController中提供getOrderSettingByMonth方法,根据月份查询预约设置信息
/**
* 根据日期查询预约设置数据(获取指定日期所在月份的预约设置数据)
* @param date
* @return
*/
@RequestMapping("/getOrderSettingByMonth")
public Result getOrderSettingByMonth(String date){//参数格式为:2019-03
try{
List<Map> list = orderSettingService.getOrderSettingByMonth(date);
//获取预约设置数据成功
return new Result(true,MessageConstant.GET_ORDERSETTING_SUCCESS,list);
}catch (Exception e){
e.printStackTrace();
//获取预约设置数据失败
return new Result(false,MessageConstant.GET_ORDERSETTING_FAIL);
}
}
注意:List<Map>
中Map
是为灵活的进行返回数据绑定,指定属性名称,减少封装实体类
3.2.2 服务接口
在OrderSettingService服务接口中扩展方法getOrderSettingByMonth
public List<Map> getOrderSettingByMonth(String date);//参数格式为:2019-03
3.2.3 服务实现类
在OrderSettingServiceImpl服务实现类中实现方法getOrderSettingByMonth
//根据日期查询预约设置数据
public List<Map> getOrderSettingByMonth(String date) {//2019-3
String begin = date + "-1";
//无论是哪个月份都是小于31号的
String end = date + "-31";
//进行封装查询数据
Map<String, String> map = new HashMap<String, String>(2);
map.put("begin", begin);
map.put("end", end);
//调用orderSettingDao进行查询
List<OrderSetting> list = orderSettingDao.getOrderSettingByMonth(map);
//进行封装数据
List<Map<String, Integer>> result = new ArrayList<Map<String, Integer>>();
if (list != null && list.size() > 0) {
for (OrderSetting orderSetting : list) {
Map<String, Integer> left = new HashMap<String, Integer>();
//获取需要的数据
int day = orderSetting.getOrderDate().getDate();
int number = orderSetting.getNumber();
int reservations = orderSetting.getReservations();
//封装到指定属性名中
left.put("date", date);
left.put("number", number);
left.put("reservations", reservations);
result.add(left);
}
}
return result
}
3.2.4 Dao接口
在OrderSettingDao接口中扩展方法getOrderSettingByMonth
public List<OrderSetting> getOrderSettingByMonth(Map date);
3.2.5 Mapper映射文件
在OrderSettingDao.xml文件中扩展SQL
<!--根据月份查询预约设置信息-->
<select id="getOrderSettingByMonth"
parameterType="map"
resultType="com.itheima.pojo.OrderSetting">
select *
from t_ordersetting
where orderDate between #{dateBegin} and #{dateEnd}
</select>
4. 基于日历实现预约设置
本章节要完成的功能为通过点击日历中的设置按钮来设置对应日期的可预约人数。效果如下:
4.1 完善页面
4.1.1 为设置按钮绑定事件
为日历中的设置按钮绑定单击事件,当前日期作为参数
<button v-if="dayobject.day > today"
@click="handleOrderSet(dayobject.day)" class="orderbtn">设置</button>
//预约设置
handleOrderSet(day){
alert(day);
}
4.1.2 弹出预约设置窗口并发送ajax请求
完善handleOrderSet方法,弹出预约设置窗口,用户点击确定按钮则发送ajax请求
//预约设置
handleOrderSet(day) {
this.$prompt('请输入可预约人数', '预约设置', {
confirmButtonText: '确定',
cancelButtonText: '取消',
inputPattern: /^[0-9]*[1-9][0-9]*$/,
inputErrorMessage: '只能输入正整数'
}).then(({value}) => {
//发送ajax请求根据日期修改可预约人数
axios.post("/ordersetting/editNumberByDate.do", {
orderDate: this.formatDate(day.getFullYear(), day.getMonth() + 1, day.getDate()), //日期
number: value //可预约人数
}).then((response) => {
if (response.data.flag) {
this.initData(this.formatDate(day.getFullYear(), day.getMonth() + 1,
1));
this.$message({
type: 'success',
message: response.data.message
});
} else {
this.$message.error(response.data.message);
}
});
}).catch(() => {
this.$message({
type: 'info',
message: '已取消'
});
});
4.2 后台代码
4.2.1 Controller
在OrderSettingController中提供方法editNumberByDate
/**
* 根据指定日期修改可预约人数
* @param orderSetting
* @return
*/
@RequestMapping("editNumberByDate")
public Result editNumberByDate(@RequestBody OrderSetting orderSetting) {
try {
orderSettingService.editNumberByDate(orderSetting);
return new Result(true, MessageConstant.ORDERSETTING_SUCCESS);
} catch (Exception e) {
e.printStackTrace();
return new Result(false, MessageConstant.ORDERSETTING_FAIL);
}
}
4.2.2 服务接口
在OrderSettingService服务接口中提供方法editNumberByDate
public void editNumberByDate(OrderSetting orderSetting);
4.2.3 服务实现类
在OrderSettingServiceImpl服务实现类中实现editNumberByDate
//根据日期修改可预约人数
@Override
public void editNumberByDate(OrderSetting orderSetting) {
//查询当前日期是否进行了预约设置
long count = orderSettingDao.findCountByOrderDate(orderSetting.getOrderDate());
if (count > 0) {
//当前日期已经进行了预约设置,需要进行修改操作
orderSettingDao.editNumberByOrderDate(orderSetting);
} else {
//当前日期未进行预约设置,需要进行插入操作
orderSettingDao.add(orderSetting);
}
}
4.2.4 Dao接口
在OrderSettingDao接口中提供方法
public void editNumberByOrderDate(OrderSetting orderSetting);
public long findCountByOrderDate(Date orderDate);
public void add(OrderSetting orderSetting);
4.2.5 Mapper映射文件
在OrderSettingDao.xml映射文件中提供SQL
<!--根据日期更新可预约人数-->
<update id="editNumberByOrderDate" parameterType="com.itheima.pojo.OrderSetting">
update t_ordersetting
set number = #{number}
where orderDate = #{orderDate}
</update>
<!--根据预约日期查询-->
<select id="findCountByOrderDate" parameterType="java.util.Date" resultType="long">
select count(*)
from t_ordersetting
where orderDate = #{orderDate}
</select>