导入导出带下拉框模版(EasyExcel)

前言

        项目进行到新的一个迭代了,赶了1周需求,接口终于处理完了。分享记录下迭代中处理导入、导出、下载模版功能的细节吧。


一、场景

  1. EasyExcel(阿里)实现Excel数据处理
  2. 三层表头,第二、三层表头动态
  3. 数据根据第二、三层表头动态
  4. 导出的模版动态数据区域,下拉选择
  5. 导入4的模版,导入业务数据
    模版效果:
    在这里插入图片描述

        懂的都懂,没有其他啥好说的,直接上代码吧。

二、使用步骤

1.导出

controller导出方法

/*** 导出排班计划*/@ResourceAction(id = "exportShiftPlan", name = "导出排班计划")@GetMapping("/exportShiftPlan")public void exportShiftPlan(PotAttendShiftPlanQueryRequestVo requestVo, HttpServletResponse response) {try {// 设置响应头response.setContentType("application/vnd.ms-excel");response.setCharacterEncoding("utf-8");String fileName = URLEncoder.encode(DateUtil.format(DateUtil.date(), DatePattern.PURE_DATETIME_PATTERN) + "排班计划导出" + ".xlsx", StandardCharsets.UTF_8).replaceAll("\\+", "%20");response.setHeader("Content-disposition", "attachment;filename=" + fileName);//组织导出数据Map<String, Object> exportDataMap = service.exportShiftPlan(requestVo);List<List<String>> headList = (List<List<String>>) exportDataMap.get("headList");List<List<Object>> dataList = (List<List<Object>>) exportDataMap.get("dataList");ExcelWriterBuilder excelWriterBuilder = EasyExcel.write(response.getOutputStream());excelWriterBuilder.head(headList).sheet("排班计划").doWrite(dataList);} catch (Exception e) {throw new RuntimeException("导出排班计划失败");}}

exportShiftPlan方法实现

@Overridepublic Map<String, Object> exportShiftPlan(PotAttendShiftPlanQueryRequestVo requestVo) {Map<String, Object> dataMap = new HashMap<>();//处理动态列头List<List<String>> headList = new ArrayList<>();//前3列姓名、部门、员工账号List<String> xmHeard = new ArrayList<>();xmHeard.add("姓名");headList.add(xmHeard);List<String> bmHeard = new ArrayList<>();bmHeard.add("部门");headList.add(bmHeard);List<String> yghHeard = new ArrayList<>();yghHeard.add("员工账号");headList.add(yghHeard);//时间处理Date startTime = requestVo.getStartTime();Date endTime = requestVo.getEndTime();List<DateTime> rangeDateList = DateUtil.rangeToList(startTime, endTime, DateField.DAY_OF_MONTH);for (DateTime dateTime : rangeDateList) {List<String> dateHead = new ArrayList<>();//日期dateHead.add(DateUtil.format(dateTime, DatePattern.NORM_DATE_PATTERN));//星期int dayOfWeek = DateUtil.dayOfWeek(dateTime);String weekName = AttendConstant.weekDict.get(String.valueOf(dayOfWeek)).toString();dateHead.add(weekName);headList.add(dateHead);}//列头dataMap.put("headList", headList);//do your code,循环处理组织一行数据放到list,再放到map//动态列部分数据根据列头时间循环处理 dataMap.put("dataList", dataList);return dataMap;}

2.下载模版

controller方法

/*** 下载排班计划导入模版** @param response 响应*/@ResourceAction(id = "exportShiftPlanTemplate", name = "导出排班计划模版")@GetMapping("/exportShiftPlanTemplate")public void exportShiftPlanTemplate(PotAttendShiftPlanQueryRequestVo requestVo, HttpServletResponse response) {try {// 设置响应头response.setContentType("application/vnd.ms-excel");response.setCharacterEncoding("utf-8");String fileName = BusinessSerialNoUtil.genDateFmtSeqCode(redisService, "", "", 4, CommonConstant.SHIFT_EXPORT_TEMPLATE, "0", CommonConstant.COMPLET_BEFORE, DatePattern.PURE_DATE_PATTERN) + "排班计划模版.xlsx";//防止乱码fileName = URLEncoder.encode(fileName, StandardCharsets.UTF_8);response.setHeader("Content-disposition", "attachment;filename=" + fileName);//组织导出数据Map<String, Object> exportDataMap = service.exportShiftPlanTemplate(requestVo);List<List<String>> headList = (List<List<String>>) exportDataMap.get("headList");List<List<Object>> dataList = (List<List<Object>>) exportDataMap.get("dataList");List<PotAttendShiftDTO> shiftList = (List<PotAttendShiftDTO>) exportDataMap.get("shiftList");ExcelWriterBuilder excelWriterBuilder = EasyExcel.write(response.getOutputStream());//动态设置下拉框excelWriterBuilder.registerWriteHandler(new CustomCellWriteHeightStrategy((short) 30, (short) 30));excelWriterBuilder.registerWriteHandler(new ShiftSheetWriteHandler(shiftList, headList.size(), dataList.size()));ExcelWriterSheetBuilder excelWriterSheetBuilder = excelWriterBuilder.head(headList).sheet("排班计划");excelWriterSheetBuilder.doWrite(dataList);} catch (Exception e) {String errMsg = e.getMessage();if (StringUtils.isBlank(errMsg)) {errMsg = "导出导入模版失败";}throw new RuntimeException(errMsg);}}

        实际上就是在导出的基础上增加了一个样式核心拦截、一个数据处理的handler
CustomCellWriteHeightStrategy
        自定义单元格高度策略


/*** 自定义单元格高度策略* @author zwmac*/
public class CustomCellWriteHeightStrategy extends AbstractRowHeightStyleStrategy {/*** 默认高度*/private static final Integer DEFAULT_HEIGHT = 30;/*** 头部行高*/private Short headRowHeight = DEFAULT_HEIGHT.shortValue();/*** 内容行高*/private Short contentRowHeight = DEFAULT_HEIGHT.shortValue();public CustomCellWriteHeightStrategy(Short headRowHeight, Short contentRowHeight) {this.headRowHeight = headRowHeight;this.contentRowHeight = contentRowHeight;}@Overrideprotected void setHeadColumnHeight(Row row, int relativeRowIndex) {int maxHeight = autoCalMaxHeight(row);//row.setHeight((short) (maxHeight * DEFAULT_HEIGHT));row.setHeightInPoints(maxHeight * DEFAULT_HEIGHT);}@Overrideprotected void setContentColumnHeight(Row row, int relativeRowIndex) {int maxHeight = autoCalMaxHeight(row);//row.setHeight((short) (maxHeight * DEFAULT_HEIGHT));row.setHeightInPoints(maxHeight * DEFAULT_HEIGHT);}private int autoCalMaxHeight(Row row) {Iterator<Cell> cellIterator = row.cellIterator();if (!cellIterator.hasNext()) {return 1;}// 默认为 1行高度int maxHeight = 1;while (cellIterator.hasNext()) {Cell cell = cellIterator.next();switch (cell.getCellType()) {case STRING:if (cell.getStringCellValue().contains("\n")) {int length = cell.getStringCellValue().split("\n").length;maxHeight = Math.max(maxHeight, length);}break;default:break;}}return maxHeight;}
}

ShiftSheetWriteHandler
        下拉框处理,这里用的是隐藏域方式,之前其实分享过另一种处理方式,也提到了这种方式,下拉框处理


/*** 排班sheetHandler** @author zwmac*/
public class ShiftSheetWriteHandler implements SheetWriteHandler {/*** 一行的列数*/private Integer rowCelNum = 0;/*** 行数*/private Integer rowNum = 0;/*** 班次数据*/private List<PotAttendShiftDTO> shiftList;/*public ShiftSheetWriteHandler(List<PotAttendShiftDTO> shiftList, int rowCelNum) {this.shiftList = shiftList;this.rowCelNum = rowCelNum;}@Overridepublic void beforeSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) {}@Overridepublic void afterSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) {//班次转mapMap<String, String[]> mapDropDown = new HashMap<>();String[] shiftTimeArr = new String[shiftList.size()];for (int i = 0; i < shiftList.size(); i++) {PotAttendShiftDTO shift = shiftList.get(i);String shiftName = shift.getShiftName();List<PotAttendShiftTime> shiftTimeList = shift.getAttendShiftTimeList();StringBuffer sbf = new StringBuffer();if (CollectionUtil.isNotEmpty(shiftTimeList)) {for (int j = 0; j < shiftTimeList.size(); j++) {PotAttendShiftTime shiftTime = shiftTimeList.get(j);String time = DateUtil.format(shiftTime.getStartTime(), "HH:mm") + "-" + DateUtil.format(shiftTime.getEndTime(),"HH:mm");sbf.append(time);if (j != shiftTimeList.size() - 1) {sbf.append("跳");}}}//长度估计是长了,报异常//shiftTimeArr[i] = shiftName + " " + sbf;shiftTimeArr[i] = shiftName;}mapDropDown.put("1", shiftTimeArr);//获取工作簿Sheet sheet = writeSheetHolder.getSheet();///开始设置下拉框DataValidationHelper helper = sheet.getDataValidationHelper();//设置下拉框for (Map.Entry<String, String[]> entry : mapDropDown.entrySet()) {*//*起始行、终止行、起始列、终止列  起始行为1即表示表头不设置**//*//这里设置65535可能又问题,因为这个是excel的最大行数,如果数据量超过这个数,就会报错CellRangeAddressList addressList = new CellRangeAddressList(2, 65535, 3, rowCelNum - 1);*//*设置下拉框数据**//*DataValidationConstraint constraint = helper.createExplicitListConstraint(entry.getValue());DataValidation dataValidation = helper.createValidation(constraint, addressList);//阻止输入非下拉选项的值dataValidation.setErrorStyle(DataValidation.ErrorStyle.STOP);dataValidation.setShowErrorBox(true);dataValidation.setSuppressDropDownArrow(true);dataValidation.createErrorBox("提示", "输入值与单元格定义格式不一致");dataValidation.createPromptBox("填写说明", "填写内容只能为下拉数据集中的类型");sheet.addValidationData(dataValidation);}}*/private Map<Integer, List<String>> selectMap;private int index;private char[] alphabet = new char[]{'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L','M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'};public ShiftSheetWriteHandler(List<PotAttendShiftDTO> shiftList, int rowCelNum,Integer rowNum) {//转mapMap<Integer, List<String>> shiftMp = transShiftListToMap(shiftList);this.selectMap = shiftMp;this.index = 0;this.rowCelNum = rowCelNum;this.rowNum = rowNum;}private Map<Integer, List<String>> transShiftListToMap(List<PotAttendShiftDTO> shiftList) {Map<Integer, List<String>> mapDropDown = new HashMap<>();List<String> shiftTimeArr = new ArrayList<>();for (int i = 0; i < shiftList.size(); i++) {PotAttendShiftDTO shift = shiftList.get(i);String shiftName = shift.getShiftName();List<PotAttendShiftTime> shiftTimeList = shift.getAttendShiftTimeList();StringBuffer sbf = new StringBuffer();if (CollectionUtil.isNotEmpty(shiftTimeList)) {for (int j = 0; j < shiftTimeList.size(); j++) {PotAttendShiftTime shiftTime = shiftTimeList.get(j);String time = DateUtil.format(shiftTime.getStartTime(), "HH:mm") + "-" + DateUtil.format(shiftTime.getEndTime(), "HH:mm");sbf.append(time);if (j != shiftTimeList.size() - 1) {sbf.append("跳");}}}String fullName = DictTranslateor.translate(AttendConstant.SHIFT_NAME_DICT, shiftName);shiftTimeArr.add(fullName + " " + sbf);}mapDropDown.put(1, shiftTimeArr);return mapDropDown;}@Overridepublic void afterSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) {if (selectMap == null || selectMap.size() == 0) {return;}// 需要设置下拉框的sheet页Sheet curSheet = writeSheetHolder.getSheet();DataValidationHelper helper = curSheet.getDataValidationHelper();String dictSheetName = "字典sheet";Workbook workbook = writeWorkbookHolder.getWorkbook();// 数据字典的sheet页Sheet dictSheet = workbook.createSheet(dictSheetName);// 从第二个工作簿开始隐藏,为了用户的友好性,将字典sheet隐藏掉this.index++;// 设置隐藏workbook.setSheetHidden(this.index, true);for (Map.Entry<Integer, List<String>> entry : selectMap.entrySet()) {// 设置下拉单元格的首行、末行、首列、末列CellRangeAddressList rangeAddressList = new CellRangeAddressList(3, 3 + rowNum - 1, 3, rowCelNum - 1);int rowLen = entry.getValue().size();// 设置字典sheet页的值 每一列一个字典项for (int i = 0; i < rowLen; i++) {Row row = dictSheet.getRow(i);if (row == null) {row = dictSheet.createRow(i);}row.createCell(entry.getKey()).setCellValue(entry.getValue().get(i));}String excelColumn = getExcelColumn(entry.getKey());// 下拉框数据来源 eg:字典sheet!$B1:$B2String refers = dictSheetName + "!$" + excelColumn + "$1:$" + excelColumn + "$" + rowLen;// 创建可被其他单元格引用的名称Name name = workbook.createName();// 设置名称的名字name.setNameName("dict" + entry.getKey());// 设置公式name.setRefersToFormula(refers);// 设置引用约束DataValidationConstraint constraint = helper.createFormulaListConstraint("dict" + entry.getKey());// 设置约束DataValidation validation = helper.createValidation(constraint, rangeAddressList);if (validation instanceof HSSFDataValidation) {validation.setSuppressDropDownArrow(false);} else {validation.setSuppressDropDownArrow(true);validation.setShowErrorBox(true);}// 阻止输入非下拉框的值validation.setErrorStyle(DataValidation.ErrorStyle.STOP);validation.createErrorBox("提示", "此值与单元格定义格式不一致!");validation.setShowErrorBox(true);validation.setSuppressDropDownArrow(true);validation.createPromptBox("填写说明", "填写内容只能为下拉数据集中的类型");// 添加下拉框约束writeSheetHolder.getSheet().addValidationData(validation);}}/*** 将数字列转化成为字母列** @param num* @return*/private String getExcelColumn(int num) {String column = "";int len = alphabet.length - 1;int first = num / len;int second = num % len;if (num <= len) {column = alphabet[num] + "";} else {column = alphabet[first - 1] + "";if (second == 0) {column = column + alphabet[len] + "";} else {column = column + alphabet[second - 1] + "";}}return column;}
}

        service实现部分就不贴了,跟导出的思路一致,多了一个下拉框数据shiftList而已。

3、导入

controller方法

/*** 导入排班计划2** @param file 文件*/@OperateLog(moduleName = moduleName, operateContent = "导入排班计划")@RequestMapping(value = "/importShiftPlan/{parkId}", method = RequestMethod.POST)public RestResponse importShiftPlan2(@RequestParam("file") MultipartFile file, @PathVariable("parkId") Long parkId) {try {service.importShiftPlan(file, parkId);return RestResponse.success();} catch (Exception e) {log.error("导入排班计划失败", e);throw new RuntimeException("导入排班计划失败");}}

importShiftPlan方法实现

@Overridepublic void importShiftPlan(MultipartFile file, Long parkId) throws IOException {//参数校验Assert.notNull(file, "文件为空");Assert.notNull(parkId, "园区id为空");//解析文件//EasyExcel.read(file.getInputStream(), AttendShiftPlanExcelDto.class, new AttendShiftPlanListener()).sheet(0).doRead();//List<Map<Integer,Object>> list = EasyExcel.read(file.getInputStream(), AttendShiftPlanExcelDto.class, new AttendShiftPlanListener()).sheet(0).doReadSync();//headRowNumber设置表示设置的行与之前的行都是列头,监听里的数据处理就从这个行之后开始读AttendShiftPlanListener attendShiftPlanListener = new AttendShiftPlanListener();List<Map<Integer, Object>> list = EasyExcel.read(file.getInputStream(), attendShiftPlanListener).sheet(0).headRowNumber(3).doReadSync();List<String> headList = attendShiftPlanListener.getHeaders();Assert.notEmpty(list, "导入数据为空");Assert.notEmpty(headList, "表头信息为空");//do your code//有表头了,有数据了,剩下的就是大家表演了}

AttendShiftPlanListener
        监听处理数据,其实保存数据可以在监听里就处理了。

/*** 排班计划导入监听器** @author zwmac*/
@Slf4j
public class AttendShiftPlanListener extends AnalysisEventListener<Map<Integer, String>> {private static final int BATCH_COUNT = 5;private List<AttendShiftPlanExcelDto> cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);@Getterprivate List<String> headers = new ArrayList<>();// 存储自定义表头Map<Integer, String> customHeaders = new HashMap<>();@Overridepublic void onException(Exception exception, AnalysisContext context) throws Exception {super.onException(exception, context);}@Overridepublic void invokeHead(Map<Integer, ReadCellData<?>> headMap, AnalysisContext context) {//列头处理Integer rowIndex = context.readRowHolder().getRowIndex();if(rowIndex == 1) {for (int i = 3; i < headMap.size(); i++) {String header = headMap.get(i).getStringValue();if (!"null".equals(header) && !isFixedHeader(header)) {customHeaders.put(i, header);}}Map<Integer, String> hdMap = ConverterUtils.convertToStringMap(headMap, context);for (int i = 0; i < hdMap.size(); i++) {headers.add(hdMap.get(i));}}}/*** 判断是否是固定表头*/private boolean isFixedHeader(String header) {//定义固定表头集合List<String> list = Arrays.asList("姓名", "部门", "账号");return list.contains(header);}@Overridepublic void invoke(Map<Integer, String> data, AnalysisContext context) {//数据处理AttendShiftPlanExcelDto shiftPlanExcelDto = new AttendShiftPlanExcelDto();StringBuilder sb = new StringBuilder();shiftPlanExcelDto.setUserName(data.get(0));shiftPlanExcelDto.setOrgName(data.get(1));shiftPlanExcelDto.setUserAccount(data.get(2));JSONObject jsonObject = new JSONObject();for (int i = 0; i < data.size() - 1; i++) {jsonObject.put(customHeaders.get(i), data.get(i));}shiftPlanExcelDto.setShiftPlanDataJson(jsonObject.toJSONString());cachedDataList.add(shiftPlanExcelDto);if (cachedDataList.size() >= BATCH_COUNT) {//saveData();其实这里就可以直接处理保存cachedDataList.clear();} else {log.info("解析到一条数据:{}", data);}}@Overridepublic void doAfterAllAnalysed(AnalysisContext context) {}
}

AttendShiftPlanExcelDto


import com.alibaba.excel.annotation.ExcelIgnore;
import com.alibaba.excel.annotation.ExcelProperty;
import lombok.Data;/*** @author zwmac*/
@Data
public class AttendShiftPlanExcelDto {@ExcelProperty(value = "姓名", index = 0)private String userName;@ExcelProperty(value = "部门", index = 1)private String orgName;@ExcelProperty(value = "账号", index = 2)private String userAccount;/*** 动态数据列*/@ExcelIgnoreprivate String shiftPlanDataJson;
}

        前3列是固定的,后面是动态,当成一个字段,实际是json串


总结

  • EasyExcel阿里系的还是挺强,目前我们的微服务也是阿里系的(其实我个人不太喜欢阿里系的微服务)
  • 导出可以使用模版,这里横向、纵向都是动态,感觉太麻烦,还不如编码
  • 其实主要一点还是要自己有大致的思路,自己要敢想
            还是那句话,思路很重要,就分享到这里,希望能帮到大家!uping!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/856434.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

RabbitMQ(六)仲裁队列、流式队列、异地容灾(联邦队列Federation Queue)

文章目录 仲裁队列1、创建交换机2、创建仲裁队列3、验证主节点宕机不影响消息发送和接收 流式队列&#xff08;不推荐&#xff0c;Kafka主场&#xff09;概念 异地容灾一、Federation插件概述 二、Federation交换机1、总体说明2、准备工作3、启用联邦插件4、添加上游连接端点5、…

NetSuite Inventory Transfer Export Saved Search

用户之前有提出一个实际的需求&#xff0c;大致意思是想要导出Inventory Transfer的相关明细行信息&#xff0c;且要包含From Location&#xff0c;To Location&#xff0c;Quantity等信息。 我们知道From Location和To Location在IT Form中应该是在Main的部分&#xff0c;在D…

java学习--集合(大写一.1)

看尚硅谷视频做的笔记 1.集合框架概述 1.1生活中的容器 首先知道集合是来解决什么问题的&#xff1f; 1.1.1内存层面需要针对于多个数据进行存储&#xff0c;此时&#xff0c;可以考虑的容器有&#xff1a;数组、集合类 对于内存层面的来说&#xff0c;断电后数据就不复存…

后端学习笔记:Python基础

后端学习笔记&#xff1a;Python基础 数据类型&#xff1a; Python中主要有以下几种常用的基本数据类型&#xff1a; String 字符串类型&#xff0c;用单引号或者双引号引用Number 数字类型&#xff0c;包括浮点数&#xff0c;整数&#xff0c;长整数和复数List 列表项&…

界面构件开发之RC文件

代码; #include <gtk-2.0/gtk/gtk.h> #include <gtk-2.0/gdk/gdkkeysyms.h> #include <glib-2.0/glib.h> #include <stdio.h>int main(int argc, char *argv[]) {gtk_init(&argc, &argv);gtk_rc_parse("./mainrc");GtkWidget *winN…

【2024最新华为OD-C/D卷试题汇总】[支持在线评测] 连续字母长度(100分) - 三语言AC题解(Python/Java/Cpp)

&#x1f36d; 大家好这里是清隆学长 &#xff0c;一枚热爱算法的程序员 ✨ 本系列打算持续跟新华为OD-C/D卷的三语言AC题解 &#x1f4bb; ACM银牌&#x1f948;| 多次AK大厂笔试 &#xff5c; 编程一对一辅导 &#x1f44f; 感谢大家的订阅➕ 和 喜欢&#x1f497; &#x1f…

某棋牌渗透测试

前言 由于传播、利用此文所提供的信息而造成的任何直接或者间接的后果及损失&#xff0c;均由使用者本人负责&#xff0c;文章作者不为此承担任何责任。 一、信息收集 这里通过fofa进行收集&#xff0c;语法为&#xff1a;body某棋牌 && titlexxx 图1-1 fofa资产收集 …

Human4DiT:使用 4D Diffusion Transformer 生成自由视角人物视频。

Human4DiT&#xff0c;使用 4D Diffusion Transformer 生成自由视角人物视频。又一个根据一张图&#xff0c;生成人物动画视频的项目&#xff0c;效果如下视频所示。 给定参考图像、SMPL序列和摄像机参数&#xff0c;Human4DiT能够生成自由视图动态人体视频。 相关链接 项目地…

神经网络模型---LeNet-5

一、LeNet-5 1.定义LeNet-5模型 model models.Sequential([1.1添加一个二维卷积层&#xff0c;有6个过滤器&#xff0c;每个过滤器的尺寸是5x5。输入图像尺寸是28x28像素&#xff0c;具有1个颜色通道,激活函数是relu layers.Conv2D(6, (5, 5), activationrelu, input_shape…

【Codesys】-计算开机通电运行时间,累计正常使用时间,故障停机时间

应客户要求&#xff0c;在程序添加了这个用来计算开机运行时间&#xff0c;原理就是取当前时间减去一开始记录的时间&#xff0c;没什么特别要求&#xff0c;记录一下使用的变量类型和数据写法&#xff0c;防止忘记了。 下文只写了一个开机通电运行时间的写法&#xff0c;累计…

ROS操作过程中的报错

文章目录 错误&#xff1a;E: Unable to locate package ros-noetic-desktop-full报错问题报错原因解决方法 错误2&#xff1a;ERROR: cannot download default source list from:报错问题错误原因解决办法 错误&#xff1a;E: Unable to locate package ros-noetic-desktop-fu…

养殖自动化温控系统:现代养殖场的智能守护神

现代农业养殖业中&#xff0c;养殖自动化温控系统已经成为提高生产效率和保障动物福利的关键技术之一。本篇文章将深入介绍养殖自动化温控系统的原理、组成、优势及其在不同类型养殖场中的应用实例&#xff0c;并展望该技术的未来发展。 一、养殖自动化温控系统概述 养殖自动…

智能制造 v3.13.12 发布,ERP、在线课堂、表白墙更新

智能制造一体化管理系统 [SpringBoot2 - 快速开发平台]&#xff0c;适用于制造业、建筑业、汽车行业、互联网、教育、政府机关等机构的管理。包含文件在线操作、工作日志、多班次考勤、CRM、ERP 进销存、项目管理、EHR、拖拽式生成问卷、日程、笔记、工作计划、行政办公、薪资模…

Hive笔记-3

3.2.2 查看表 1) 展示所有表 (1) 语法: 语法: SHOW TABLES [IN database_name] LIKE [identifier_with_wildcards]; In database_name 写的是查哪个数据库,一般不写默认是当前数据库 Like 后面跟通配符表达式 (2) 案例: 查看在 db_hive1 数据库里有没有以 stu 开头的表 …

C++第二学期期末考试选择题题库(qlu题库,自用)

又到了期末周&#xff0c;突击一下c吧— 第一次实验 1、已知学生记录的定义为&#xff1a; struct student { int no; char name[20]; char sex; struct 注意年月日都是结构体&#xff0c;不是student里面的 { int year; int month; …

如何在springboot项目中引入knife4j接口文档

开发框架&#xff0c;帮助后端开发人员做后端接口测试 knife4j是为Java MVC框架集成Swagger生成Api文档的增强解决方案 引入依赖 <dependency><groupId>com.github.xiaoymin</groupId><artifactId>knife4j-spring-boot-starter</artifactId>&…

模电电流源相关知识总结

模电书版本为第五版&#xff1a; 一、基本电流源 1、镜像电流源 电路图&#xff1a; 电路图很好理解&#xff0c;T0和T1两个晶体管参数相近&#xff0c;即当施加相同的Ube时&#xff0c;对应的Ic也相同。 可用图中的电阻R来控制电流源的电流。 计算公式&#xff1a;Ic&…

C# WPF入门学习番外篇——C#使用WPF连接MySQL数据库

在 C# 中使用 WPF 连接 MySQL 数据库涉及几个步骤&#xff0c;包括安装必要的库&#xff0c;配置连接字符串&#xff0c;编写代码以执行数据库操作等。下面是一个详细的入门教程&#xff0c;帮助你理解如何在 WPF 应用程序中使用 MySQL 数据库。 1. 安装 MySQL 数据库连接器 …

来都来了,8个JavaScript技巧奉上

吆喝一声&#xff0c;如果你计算机、软件工程、电子等相关专业本科及以上学历&#xff0c;欢迎来共事。前后端/测试可投&#xff0c;技术大厂。 JavaScript 作为最流行的语言之一&#xff0c;其语法灵活且每年都在不断吸纳新特性&#xff0c;即使是一个从业多年的老手&#xff…

el-table表格变更前后根据数据值改变背景颜色

需求&#xff1a; 1.左侧变更前表格数据不可以编辑&#xff0c;并且背景色加灰 2.右侧变更后表格数据可被编辑&#xff0c;编辑后变更前与变更后行数据不一致&#xff0c;添加背景色区分 3.点击删除的时候&#xff0c;给变更后表格当前行&#xff0c;添加背景色和删除的中横…