EasyExcel 導出合并層級單元格

EasyExcel 導出合并層級單元格

一、案例

案例一

  • 1.相同訂單號單元格進行合并

image-20250207145806662

合并結果

image-20250207145841217

案例二

  • 1.相同訂單號的單元格進行合并
  • 2.相同訂單號的總數和總金額進行合并

image-20250207150108895

合并結果

image-20250207150033347

案例三

  • 1.相同訂單號的單元格進行合并
  • 2.相同訂單號的商品分類進行合并
  • 3.相同訂單號的總數和總金額進行合并
  • 4.相同訂單號和相同商品分類的分類總數、分類總金額進行合并

image-20250207150213477

合并結果

image-20250207150139989

二、代碼實現

相關依賴

<dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>3.2.1</version>
</dependency><dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.8.16</version>
</dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.26</version>
</dependency>
2.1 AbstractMergeStrategy
import com.alibaba.excel.write.handler.CellWriteHandler;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellType;
import org.apache.poi.ss.usermodel.Sheet;
public abstract class AbstractMergeStrategy implements CellWriteHandler {/*** 最大行索引*/public final static int EXCEL_LAST_INDEX = 1048575;/*** 默認合并起始行*/public final static int DEFAULT_START_ROW_INDEX = 1;/*** 合并抽象方法* @param sheet* @param cell*/public abstract void merge(Sheet sheet, Cell cell);/*** 獲取單元格值* @param cell */public Object getCellValue(Cell cell) {return cell.getCellType() == CellType.STRING ? cell.getStringCellValue() : cell.getNumericCellValue();}
}
2.2 ColumnMergeStrategy
import cn.hutool.core.collection.CollUtil;
import com.alibaba.excel.metadata.Head;
import com.alibaba.excel.metadata.data.WriteCellData;
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
import com.alibaba.excel.write.metadata.holder.WriteTableHolder;
import com.easy.excel.demo.model.MergeRowColumn;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.util.CellRangeAddress;import java.util.*;
import java.util.stream.Collectors;/*** 合并單元格策略:適用于列合并*/
@Slf4j
public class ColumnMergeStrategy extends AbstractMergeStrategy {/*** 合并起始行索引*/private int mergeStartRowIndex;/*** 合并結束行索引*/private int mergeEndRowIndex;/*** 待合并的列(如果沒有指定,則所有的列都會進行合并)*/private List<Integer> mergeColumnIndexList;/*** 待合并的列父級依賴關系 <需要合并的列索引, 依賴的父級列索引>* key 需要合并的列, value 所依賴的父級列的列表    */private Map<Integer, List<Integer>> mergeColumnIndexMap = new HashMap<>();/*** 合并的行列索引數據(存儲每列的數據合并的行列索引范圍)*/private Map<Integer, MergeRowColumn> mergeRowColumnMap = new HashMap<>();private Sheet sheet;public ColumnMergeStrategy() {this(DEFAULT_START_ROW_INDEX, EXCEL_LAST_INDEX);}public ColumnMergeStrategy(List<Integer> mergeColumnIndexList) {this(DEFAULT_START_ROW_INDEX, EXCEL_LAST_INDEX, mergeColumnIndexList);}public ColumnMergeStrategy(Map<Integer, List<Integer>> mergeColumnIndexMap) {this.mergeColumnIndexMap = mergeColumnIndexMap;this.mergeColumnIndexList = mergeColumnIndexMap.keySet().stream().collect(Collectors.toList());this.mergeStartRowIndex = DEFAULT_START_ROW_INDEX;this.mergeEndRowIndex = EXCEL_LAST_INDEX;}public ColumnMergeStrategy(int mergeStartRowIndex) {this(mergeStartRowIndex, EXCEL_LAST_INDEX);}public ColumnMergeStrategy(int mergeStartRowIndex, int mergeEndRowIndex) {this(mergeStartRowIndex, mergeEndRowIndex, new ArrayList<>());}public ColumnMergeStrategy(int mergeStartRowIndex, int mergeEndRowIndex, List<Integer> mergeColumnIndexList) {this.mergeStartRowIndex = mergeStartRowIndex;this.mergeEndRowIndex = mergeEndRowIndex;this.mergeColumnIndexList = mergeColumnIndexList;}@Overridepublic void afterCellDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, List<WriteCellData<?>> list, Cell cell, Head head, Integer integer, Boolean isHead) {// 頭不參與合并if (isHead) {return;}// 初始化 sheetif (sheet == null) {this.sheet = writeSheetHolder.getSheet();}// 如果當前行大于合并起始行則進行合并if (cell.getRowIndex() >= mergeStartRowIndex && cell.getRowIndex() <= mergeEndRowIndex) {// 判斷是否是全列合并或者當前列在需要合并列中if (CollUtil.isEmpty(mergeColumnIndexList) || (CollUtil.isNotEmpty(mergeColumnIndexList) && mergeColumnIndexList.contains(cell.getColumnIndex()))) {// 合并單元格this.merge(writeSheetHolder.getSheet(), cell);}}}@Overridepublic void merge(Sheet sheet, Cell cell) {// 當前單元格行、列索引int curRowIndex = cell.getRowIndex();int curColumnIndex = cell.getColumnIndex();// 當前單元格的值為Object curCellValue = this.getCellValue(cell);// 上一行的行索引int aboveRowIndex = curRowIndex - 1;if (aboveRowIndex < 0 || aboveRowIndex < mergeStartRowIndex) {// 初始化當前列的 合并區域范圍MergeRowColumn mergeRowColumn = new MergeRowColumn(curRowIndex, curRowIndex, curColumnIndex, curColumnIndex);mergeRowColumnMap.put(curColumnIndex, mergeRowColumn);return;}// 獲取上一個單元格Cell aboveCell = sheet.getRow(aboveRowIndex).getCell(curColumnIndex);// 上一個單元格的值Object aboveCellValue = this.getCellValue(aboveCell);// 判斷上一個單元格是否能合并if (Objects.equals(curCellValue, aboveCellValue)) {boolean needMerge = true;// 父級列 列表List<Integer> parentColumnIndexList = mergeColumnIndexMap.get(curColumnIndex);if (parentColumnIndexList != null && !parentColumnIndexList.isEmpty()) {for (Integer parentColumnIndex : parentColumnIndexList) {Cell mainCell = sheet.getRow(curRowIndex).getCell(parentColumnIndex);Cell aboveMainCell = sheet.getRow(aboveRowIndex).getCell(parentColumnIndex);Object mainCellValue = this.getCellValue(mainCell);Object aboveMainCellValue = this.getCellValue(aboveMainCell);// 所有主列都需要滿足合并條件才能合并副列if (!Objects.equals(mainCellValue, aboveMainCellValue)) {needMerge = false;break;}}}// 允許合并if (needMerge){// 修改當前列的行合并索引范圍MergeRowColumn mergeRowColumn = mergeRowColumnMap.get(curColumnIndex);mergeRowColumn.setEndRowIndex(curRowIndex);} else {// 合并已有的單元格,修改行索引指向mergeRowColumnCell(sheet, curRowIndex,curColumnIndex);}} else {// 合并已有的單元格,修改行索引指向mergeRowColumnCell(sheet, curRowIndex,curColumnIndex);}}/*** 檢查給定的單元格是否在一個或多個合并區域中。** @return 如果指定單元格是合并區域的一部分,則返回 true;否則返回 false。*/private boolean isMergedRegion(Sheet sheet, Integer rowIndex, Integer columnIndex) {// 獲取當前工作表中的所有合并區域數量int numMergedRegions = sheet.getNumMergedRegions();// 遍歷所有合并區域for (int i = 0; i < numMergedRegions; i++) {CellRangeAddress region = sheet.getMergedRegion(i);// 檢查指定的單元格是否在當前合并區域內if (region.isInRange(rowIndex, columnIndex)) {return true;}}return false;}/*** 合并區域單元格** @param sheet* @param curRowIndex* @param curColumnIndex*/private void mergeRowColumnCell(Sheet sheet, Integer curRowIndex, Integer curColumnIndex) {// 獲取當前的列的合并區域索引對象MergeRowColumn mergeRowColumn = mergeRowColumnMap.get(curColumnIndex);// 合并單元格mergeCell(sheet, mergeRowColumn, curRowIndex, curColumnIndex);}/*** 手動合并最后的單元格* (最后一段單元格需要手動合并)*/public void finalMergeCell() {// 遍歷所有列的合并索引,合并最后的單元格for (Map.Entry<Integer, MergeRowColumn> entry : mergeRowColumnMap.entrySet()) {Integer columnIndex = entry.getKey();MergeRowColumn mergeRowColumn = entry.getValue();Integer endRowIndex = mergeRowColumn.getEndRowIndex();mergeCell(sheet, mergeRowColumn, endRowIndex, columnIndex);}}/*** 合并單元格** @param sheet* @param mergeRowColumn* @param curRowIndex* @param curColumnIndex*/private void mergeCell(Sheet sheet, MergeRowColumn mergeRowColumn,Integer curRowIndex, Integer curColumnIndex) {// 獲取合并的行起始索引Integer startRowIndex = mergeRowColumn.getStartRowIndex();// 獲取合并的行結束索引Integer endRowIndex = mergeRowColumn.getEndRowIndex();// 合并單元格(至少有兩個單元格以上才能進行合并)if (startRowIndex < endRowIndex) {CellRangeAddress cellAddresses = new CellRangeAddress(startRowIndex, endRowIndex, curColumnIndex, curColumnIndex);// 判斷起始單元格是否已經合并過了boolean mergedRegion = isMergedRegion(sheet, startRowIndex, curColumnIndex);if (!mergedRegion) {// 合并指定區域的單元格sheet.addMergedRegion(cellAddresses);}}// 重置合并索引(當前列的行指針下移)mergeRowColumn.setStartRowIndex(curRowIndex);mergeRowColumn.setEndRowIndex(curRowIndex);}
}

源碼分析

  • mergeRowColumnMap 是進行合并的關鍵所在,存儲了所有需要合并的列的行合并區域索引,在遍歷數據過程中,根據情況進行單元格合并然后偏移指針,或者只修改指針,這樣就不需要頻繁的進行合并操作。
2.3 MergeRowColumn
@NoArgsConstructor
@AllArgsConstructor
@Data
public class MergeRowColumn {/*** 開始行索引*/private Integer startRowIndex;/*** 結束行索引*/private Integer endRowIndex;/*** 開始列索引*/private Integer startColumnIndex;/*** 結束列索引*/private Integer endColumnIndex;
}

三、測試

案例測試代碼

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.util.DateUtils;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.easy.excel.demo.handler.ColumnMergeStrategy;
import com.easy.excel.demo.model.OrderDetailEntity;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;import java.io.File;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;@Slf4j
@SpringBootTest
public class EasyExcelDemoTest2 {/*** 案例一*/@Testpublic void testMerge1(){File file = new File("order1.xlsx");WriteSheet writeSheet = EasyExcel.writerSheet("sheet1").head(OrderDetailEntity.class).registerWriteHandler(new ColumnMergeStrategy(Arrays.asList(0)))// 訂單號.build();ExcelWriter excelWriter = EasyExcel.write(file).build();// 寫入數據excelWriter.write(data(), writeSheet);// 手動合并最后的單元格(最后的單元格沒有辦法合并,需要手動進行合并)writeSheet.getCustomWriteHandlerList().stream().filter(handler -> handler instanceof ColumnMergeStrategy).map(handler -> (ColumnMergeStrategy) handler).forEach(handler -> handler.finalMergeCell());excelWriter.finish();}/*** 案例二*/@Testpublic void testMerge2(){// 輸出文件路徑File file = new File("order2.xlsx");// 初始化列合并列父級依賴關系Map<Integer, List<Integer>> mergeColumnIndexMap = new HashMap<>();mergeColumnIndexMap.put(0, new ArrayList<>());//訂單號mergeColumnIndexMap.put(10, Arrays.asList(0));//總數 ==> 訂單號mergeColumnIndexMap.put(11, Arrays.asList(0));//總金額 ==> 訂單號WriteSheet writeSheet = EasyExcel.writerSheet("sheet1").head(OrderDetailEntity.class).registerWriteHandler(new ColumnMergeStrategy(mergeColumnIndexMap)).build();ExcelWriter excelWriter = EasyExcel.write(file).build();excelWriter.write(data(), writeSheet);// 手動合并最后的單元格(分段數據注入時最后的單元格沒有辦法合并,需要手動進行合并)writeSheet.getCustomWriteHandlerList().stream().filter(handler -> handler instanceof ColumnMergeStrategy).map(handler -> (ColumnMergeStrategy) handler).forEach(handler -> handler.finalMergeCell());excelWriter.finish();}/*** 案例三*/@Testpublic void testMerge3(){// 輸出文件路徑File file = new File("order3.xlsx");// 初始化列合并上級依賴關系Map<Integer, List<Integer>> mergeColumnIndexMap = new HashMap<>();mergeColumnIndexMap.put(0, new ArrayList<>());//訂單號mergeColumnIndexMap.put(2, Arrays.asList(0));//商品分類 ==> 訂單號mergeColumnIndexMap.put(8, Arrays.asList(0, 2));//分類總數 ==> 訂單號,商品分類mergeColumnIndexMap.put(9, Arrays.asList(0, 2));//分類總金額 ==> 訂單號,商品分類mergeColumnIndexMap.put(10, Arrays.asList(0));//總數 ==> 訂單號mergeColumnIndexMap.put(11, Arrays.asList(0));//總金額 ==> 訂單號WriteSheet writeSheet = EasyExcel.writerSheet("sheet1").head(OrderDetailEntity.class).registerWriteHandler(new ColumnMergeStrategy(mergeColumnIndexMap)).build();ExcelWriter excelWriter = EasyExcel.write(file).build();// 模擬分頁查詢數據for (int i = 0; i < 3; i++) {excelWriter.write(data(), writeSheet);}// 手動合并最后的單元格(分段數據注入時最后的單元格沒有辦法合并,需要手動進行合并)writeSheet.getCustomWriteHandlerList().stream().filter(handler -> handler instanceof ColumnMergeStrategy).map(handler -> (ColumnMergeStrategy) handler).forEach(handler -> handler.finalMergeCell());excelWriter.finish();}/*** 隨機生成測試數據* @return*/private Collection<?> data() {Map<String, List<String>> productMap = getProductMap();List<String> statusList = Arrays.asList("待發貨", "已發貨", "運輸中", "待取貨", "已完成");List<OrderDetailEntity> dataList = new ArrayList<>();Random random = new Random();int orderCount = random.nextInt(2) + 5;for (int i = 0; i < orderCount; i++) {String orderCode = "PL" + DateUtils.format(new Date(), "yyyyMMddHHmm") + "000" + i;int orderDetailCount = random.nextInt(10) + 1;List<OrderDetailEntity> detailEntities = new ArrayList<>();Map<String, BigDecimal> categoryTotalQuantityMap = new HashMap<>();Map<String, BigDecimal> categoryTotalPriceMap = new HashMap<>();BigDecimal totalQuantity = BigDecimal.ZERO;BigDecimal totalPrice = BigDecimal.ZERO;for (int j = 0; j < orderDetailCount; j++) {String orderDetailCode = UUID.randomUUID().toString();String productCategory = new ArrayList<String>(productMap.keySet()).get(random.nextInt(productMap.size()));List<String> productList = productMap.get(productCategory);String productCode = "SKU" + (random.nextInt(1000)+1000);String productName = productList.get(random.nextInt(productList.size())) + "-A" + random.nextInt(50);BigDecimal price = new BigDecimal(random.nextInt(2000) + 800);BigDecimal quantity = new BigDecimal(random.nextInt(5) + 1);String status = statusList.get(random.nextInt(statusList.size()));String key = orderCode + "-" + productCategory;BigDecimal categoryTotalQuantity = categoryTotalQuantityMap.get(key);if (categoryTotalQuantity == null) {categoryTotalQuantity = quantity;} else {categoryTotalQuantity = categoryTotalQuantity.add(quantity);}categoryTotalQuantityMap.put(key, categoryTotalQuantity);BigDecimal categoryTotalPrice = categoryTotalPriceMap.get(key);if (categoryTotalPrice == null) {categoryTotalPrice = price.multiply(quantity);} else {categoryTotalPrice = categoryTotalPrice.add(price.multiply(quantity));}categoryTotalPriceMap.put(key, categoryTotalPrice);totalQuantity = totalQuantity.add(quantity);totalPrice = totalPrice.add(price.multiply(quantity));detailEntities.add(OrderDetailEntity.builder().orderCode(orderCode).orderDetailCode(orderDetailCode).productCategory(productCategory).productCode(productCode).productName(productName).price(price).quantity(quantity).status(status).build());}for (OrderDetailEntity item : detailEntities) {String key = item.getOrderCode() + "-" + item.getProductCategory();item.setCategoryTotalQuantity(categoryTotalQuantityMap.get(key));item.setCategoryTotalPrice(categoryTotalPriceMap.get(key));item.setTotalQuantity(totalQuantity);item.setTotalPrice(totalPrice);}detailEntities = detailEntities.stream().sorted(Comparator.comparing(OrderDetailEntity::getOrderCode).thenComparing(OrderDetailEntity::getProductCategory)).collect(Collectors.toList());dataList.addAll(detailEntities);}return dataList;}private Map<String, List<String>> getProductMap() {Map<String, List<String>> productMap = new HashMap<>();// 家電List<String> householdList = new ArrayList<>();householdList.add("電視機");householdList.add("冰箱");householdList.add("洗衣機");householdList.add("空調");productMap.put("家電", householdList);// 數碼產品List<String> digitalList = new ArrayList<>();digitalList.add("手機");digitalList.add("攝影機");digitalList.add("電腦");digitalList.add("照相機");digitalList.add("投影儀");digitalList.add("智能手表");productMap.put("數碼產品", digitalList);// 健身器材List<String> gymEquipmentList = new ArrayList<>();gymEquipmentList.add("動感單車");gymEquipmentList.add("健身椅");gymEquipmentList.add("跑步機");productMap.put("健身器材", gymEquipmentList);return productMap;}
}

OrderDetailEntity

import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.annotation.write.style.*;
import com.alibaba.excel.enums.poi.BorderStyleEnum;
import com.alibaba.excel.enums.poi.FillPatternTypeEnum;
import com.alibaba.excel.enums.poi.HorizontalAlignmentEnum;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;import java.math.BigDecimal;@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
// 頭背景設置
@HeadStyle(fillPatternType = FillPatternTypeEnum.SOLID_FOREGROUND, horizontalAlignment = HorizontalAlignmentEnum.CENTER, borderLeft = BorderStyleEnum.THIN, borderTop = BorderStyleEnum.THIN, borderRight = BorderStyleEnum.THIN, borderBottom = BorderStyleEnum.THIN)
//標題高度
@HeadRowHeight(30)
//內容高度
@ContentRowHeight(20)
//內容居中,左、上、右、下的邊框顯示
@ContentStyle(horizontalAlignment = HorizontalAlignmentEnum.CENTER, borderLeft = BorderStyleEnum.THIN, borderTop = BorderStyleEnum.THIN, borderRight = BorderStyleEnum.THIN, borderBottom = BorderStyleEnum.THIN)
public class OrderDetailEntity {@ExcelProperty(value = "訂單號")@ColumnWidth(25)private String orderCode;@ExcelProperty(value = "訂單明細")@ColumnWidth(40)private String orderDetailCode;@ExcelProperty(value = "商品分類")@ColumnWidth(20)private String productCategory;@ExcelProperty(value = "商品編碼")@ColumnWidth(20)private String productCode;@ExcelProperty(value = "商品名稱")@ColumnWidth(20)private String productName;@ExcelProperty(value = "單價")@ColumnWidth(10)private BigDecimal price;@ExcelProperty(value = "數量")@ColumnWidth(10)private BigDecimal quantity;@ExcelProperty(value = "狀態")@ColumnWidth(10)private String status;@ExcelProperty(value = "分類總數")@ColumnWidth(20)private BigDecimal categoryTotalQuantity;@ExcelProperty(value = "分類總金額")@ColumnWidth(20)private BigDecimal categoryTotalPrice;@ExcelProperty(value = "總數")@ColumnWidth(10)private BigDecimal totalQuantity;@ExcelProperty(value = "總金額")@ColumnWidth(10)private BigDecimal totalPrice;
}

參考文章

https://blog.csdn.net/xhmico/article/details/141814528

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/895149.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/895149.shtml
英文地址,請注明出處:http://en.pswp.cn/news/895149.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

cs106x-lecture3(Autumn 2017)

打卡cs106x(Autumn 2017)-lecture3 1、streamErrors Suppose an input file named streamErrors-data.txt contains the following text: Donald Knuth M 76 Stanford U. The code below attempts to read the data from the file, but each section has a bug. Correct th…

C++模板編程——typelist的實現

文章最后給出了匯總的代碼&#xff0c;可直接運行 1. typelist是什么 typelist是一種用來操作類型的容器。和我們所熟知的vector、list、deque類似&#xff0c;只不過typelist存儲的不是變量&#xff0c;而是類型。 typelist簡單來說就是一個類型容器&#xff0c;能夠提供一…

springboot 事務管理

在Spring Boot中&#xff0c;事務管理是通過Spring框架的事務管理模塊來實現的。Spring提供了聲明式事務管理和編程式事務管理兩種方式。通常&#xff0c;我們使用聲明式事務管理&#xff0c;因為它更簡潔且易于維護。 1. 聲明式事務管理 聲明式事務管理是通過注解來實現的。…

windows通過網絡向Ubuntu發送文件/目錄

由于最近要使用樹莓派進行一些代碼練習&#xff0c;但是好多東西都在windows里或虛擬機上&#xff0c;就想將文件傳輸到樹莓派上&#xff0c;但試了發現u盤不能簡單傳送&#xff0c;就在網絡上找到了通過windows 的scp命令傳送 前提是樹莓派先開啟ssh服務&#xff0c;且Window…

字節跳動后端一面

&#x1f4cd;1. Gzip壓縮技術詳解 Gzip是一種流行的無損數據壓縮格式&#xff0c;它使用DEFLATE算法來減少文件大小&#xff0c;廣泛應用于網絡傳輸和文件存儲中以提高效率。 &#x1f680; 使用場景&#xff1a; ? 網站優化&#xff1a;通過壓縮HTML、CSS、JavaScript文件來…

Leetcode 3448. Count Substrings Divisible By Last Digit

Leetcode 3448. Count Substrings Divisible By Last Digit 1. 解題思路2. 代碼實現 題目鏈接&#xff1a;3448. Count Substrings Divisible By Last Digit 1. 解題思路 這一題的話我們走的是一個累積數組的思路。 首先&#xff0c;我們使用一個cache數組記錄下任意段數字…

三維模擬-機械臂自翻車

機械仿真 前言效果圖后續 前言 最近在研究Unity機械仿真&#xff0c;用Unity實現其運動學仿真展示的功能&#xff0c;發現一個好用的插件“MGS-Machinery-master”&#xff0c;完美的解決了Unity關節定義缺少液壓缸伸縮關節功能&#xff0c;內置了多個場景&#xff0c;講真的&…

USB子系統學習(四)用戶態下使用libusb讀取鼠標數據

文章目錄 1、聲明2、HID協議2.1、描述符2.2、鼠標數據格式 3、應用程序4、編譯應用程序5、測試6、其它 1、聲明 本文是在學習韋東山《驅動大全》USB子系統時&#xff0c;為梳理知識點和自己回看而記錄&#xff0c;全部內容高度復制粘貼。 韋老師的《驅動大全》&#xff1a;商…

2月9日QT

優化登錄框&#xff1a; 當用戶點擊取消按鈕&#xff0c;彈出問題對話框&#xff0c;詢問是否要確定退出登錄&#xff0c;并提供兩個按鈕&#xff0c;yes|No&#xff0c;如果用戶點擊的Yes&#xff0c;則關閉對話框&#xff0c;如果用戶點擊的No&#xff0c;則繼續登錄 當用戶…

安卓路由與aop 以及 Router-api

安卓路由&#xff08;Android Router&#xff09;和AOP&#xff08;面向切面編程&#xff09;是兩個在Android開發中常用的概念。下面我將詳細講解這兩個概念及其在Android開發中的應用。 一、安卓路由 安卓路由主要用于在應用程序中管理不同組件之間的導航和通信。它可以簡化…

大模型賦能網絡安全整體應用流程概述

一、四個階段概述 安全大模型的應用大致可以分為四個階段: 階段一主要基于開源基礎模型訓練安全垂直領域的模型; 階段二主要基于階段一訓練出來的安全大模型開展推理優化、蒸餾等工序,從而打造出不同安全場景的專家模型,比如數據安全領域、安全運營領域、調用郵件識別領…

nexus部署及配置https訪問

1. 使用docker-compose部署nexus docker-compose-nexus.yml version: "3" services:nexus:container_name: my-nexusimage: sonatype/nexus3:3.67.1hostname: my-nexusnetwork_mode: hostports:- 8081:8081deploy:resources:limits:cpus: 4memory: 8192Mreservations…

史上最快 Python版本 Python 3.13 安裝教程

Python3.13安裝和配置 一、Python的下載 1. 網盤下載地址 (下載速度比較快&#xff0c;推薦&#xff09; Python3.13.0下載&#xff1a;Python3.13.0下載地址&#xff08;windows&#xff09;3.13.0下載地址&#xff08;windows&#xff09; 點擊下面的下載鏈接&#xff0c…

Docker從入門到精通- 容器化技術全解析

第一章&#xff1a;Docker 入門 一、什么是 Docker&#xff1f; Docker 就像一個超級厲害的 “打包神器”。它能幫咱們把應用程序和它運行所需要的東東都整整齊齊地打包到一起&#xff0c;形成一個獨立的小盒子&#xff0c;這個小盒子在 Docker 里叫容器。以前呢&#xff0c;…

ProcessingP5js數據可視化

折線圖繪制程序設計說明 可以讀取表格數據&#xff0c;并轉換成折線圖&#xff0c;條形圖和餅狀圖&#xff0c;并設計了銜接動畫效果 1. 功能概述 本程序使用 Processing 讀取 CSV 文件數據&#xff0c;并繪制帶有坐標軸和數據點的折線圖。橫坐標&#xff08;X 軸&#xff09…

使用云計算,企業的數據監管合規問題如何解決?

使用云計算&#xff0c;企業的數據監管合規問題如何解決&#xff1f; 在當今這個信息化、數字化的時代&#xff0c;數據無疑成為了企業最寶貴的資產之一。隨著云計算的普及&#xff0c;企業將大量數據存儲在云端&#xff0c;不僅提升了效率&#xff0c;也帶來了更多靈活性。然…

AWS Fargate

AWS Fargate 是一個由 Amazon Web Services (AWS) 提供的無服務器容器計算引擎。它使開發者能夠運行容器化應用程序&#xff0c;而無需管理底層的服務器或虛擬機。簡而言之&#xff0c;AWS Fargate 讓你只需關注應用的容器本身&#xff0c;而不需要管理運行容器的基礎設施&…

vue3+vite+eslint|prettier+elementplus+國際化+axios封裝+pinia

文章目錄 vue3 vite 創建項目如果創建項目選了 eslint prettier從零教你使用 eslint prettier第一步&#xff0c;下載eslint第二步&#xff0c;創建eslint配置文件&#xff0c;并下載好其他插件第三步&#xff1a;安裝 prettier安裝后配置 eslint (2025/2/7 補充) 第四步&am…

vLLM V1 重磅升級:核心架構全面革新

本文主要是 翻譯簡化個人評讀&#xff0c;原文請參考&#xff1a;vLLM V1: A Major Upgrade to vLLM’s Core Architecture vLLM V1 開發背景 2025年1月27日&#xff0c;vLLM 開發團隊推出 vLLM V1 alpha 版本&#xff0c;這是對框架核心架構的里程碑式升級。基于過去一年半的…

Jupyter Notebook自動保存失敗等問題的解決

一、未生成配置文件 需要在命令行中&#xff0c;執行下面的命令自動生成配置文件 jupyter notebook --generate-config 執行后會在 C:\Users\用戶名\.jupyter目錄中生成文件 jupyter_notebook_config.py 二、在網頁端打開Jupyter Notebook后文件保存失敗&#xff1b;運行代碼…