阿里easyExcel -- excel单元格自定义下拉选择(升级版)

背景

很久很久以前写了一篇类似的文章 阿里easyExcel – excel下载/导出/读取 (单元格自定义下拉选择、不支持图片) ,用了没多久就发现不好用,限制太多(以后遇到你就知道了),然后就有了现在迟到很久的文章,主要懒得写文章。

必看

此篇文章的单元格下拉支持 1级,2级,多级联动下拉等 ,比较复杂,需耐心看一下。
再写之前,先讲几个excel注意的点:

  1. 你的excel必须支持创建 名称管理器 ,如下图所示:
    在这里插入图片描述
  2. excel必须支持 INDIRECTCONCATENATEVLOOKUP 函数,要是excel版本太太太太低,可能没有这些函数,检查方式如下,一般=后面加函数名就会有提示:
    在这里插入图片描述

缺点或限制

  1. 当下拉的数据太多时,会导致创建excel的速度变慢
  2. 下拉的总数据不能超过 1048576 行,不能超过16384列

先简单看下效果

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

下拉的原理,怎么实现的下拉

创建一个sheet,然后把下拉数据放接某一列,如下:
在这里插入图片描述
当然也可以横着放,但是横着最多只能放 16384 列,而竖着可以放 1048576 行。
如果 只有1级下拉 那么不需要创建 名称管理器 ,如果是多级联动下拉,则除了最后1级,其余都要设置 名称管理器名称管理器 怎么设置下拉自己去百度。下面直接放代码,看不懂慢慢看,不想看直接复制使用,不想讲解了哈哈哈。

另外,由于 名称管理器 对name的设置要求很高,有些字符无法设置,为了实现下拉数据的千奇百怪,所以需要做一层转换。下面展示代码。

代码

ExcelUtils 工具:

public class ExcelUtils {/*** 下载** @param writeHandlers 处理器(可自定义,可为null)* @param os            输出流* @param clazz         操作对象字节* @param data          数据* @param sheetName     表名*/public static <T> void downLoad(List<WriteHandler> writeHandlers, OutputStream os, Class<T> clazz, List<T> data, String sheetName) {ExcelWriterSheetBuilder builder = EasyExcelFactory.write(os, clazz).sheet(sheetName);if (!CollectionUtils.isEmpty(writeHandlers)) {writeHandlers.forEach(builder::registerWriteHandler);}builder.doWrite(data);}
}

ExcelLinkageDropdown 多级下拉数据:

public class ExcelLinkageDropdown {/*** 是否允许设置其他的值。false:只能是下拉列表的值;true:允许列表之外的值*/private boolean isAllowOtherValue = false;/*** 表头名称(为bean对象时传字段名称,为map时且多个头用json:["头1","头2"])*/private String fieldName;/*** 第几列,为对象自动计算*/private Integer cellIndex;/*** 下拉内容,<上级,下级列表>,第一级的key为null,只有一级时key也为null*/private Map<String, List<String>> value = new HashMap<>();/*** 提示信息*/private String message = "只能选择列表中的值!!!";//...get set 自己生成
}

DropdownWriteHandler下拉处理器:

/*** 下拉处理器:单元格下拉列表格式* 最大行:1048576 / 65536* 最大列:16384 / 256*/
public class DropdownWriteHandler extends AbstractVerticalCellStyleStrategy implements SheetWriteHandler {private final Map<ExcelLinkageDropdown[], String> dropdowns = new HashMap<>(); //所有下拉值private final Class<?> clazz; //操作的类private final int headMax; //表头行数public DropdownWriteHandler(Class<?> clazz) {this.clazz = clazz;Field[] fields = clazz.getDeclaredFields();// 取表头行数this.headMax = Arrays.stream(fields).filter(field -> field.isAnnotationPresent(ExcelProperty.class)).map(field -> field.getAnnotation(ExcelProperty.class).value().length).reduce(Integer::max).orElse(0);}@Overridepublic void beforeSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) {}@Overridepublic void afterSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) {Workbook book = writeWorkbookHolder.getCachedWorkbook();Sheet sheet = writeSheetHolder.getSheet();// 设置固定区域sheet.createFreezePane(0, headMax, 0, headMax);DataValidationHelper helper = sheet.getDataValidationHelper();// 联动下拉校验if (!CollectionUtils.isEmpty(dropdowns)) {long l = System.currentTimeMillis();for (Map.Entry<ExcelLinkageDropdown[], String> dropdownMap : dropdowns.entrySet()) {ExcelLinkageDropdown[] dropdown = dropdownMap.getKey();String key = StringUtils.isNotBlank(dropdownMap.getValue()) ? dropdownMap.getValue() :"t_" + Arrays.stream(dropdown).map(t -> t.getCellIndex().toString()).collect(Collectors.joining("_"));if (key.length() > 30) {key = "s_" + IdGenerator.getInstance().getId();}//设置下拉及校验数据setDropdownsAndValidationData(book, sheet, helper, dropdown, key);}long l2 = System.currentTimeMillis();System.out.println("下拉耗时" + (l2 - l));}}/*** 设置下拉及校验数据*/private void setDropdownsAndValidationData(Workbook book, Sheet sheet, DataValidationHelper helper, ExcelLinkageDropdown[] dropdown, String key) {// 设置多级下拉if (book.getSheetIndex(key) == -1) {buildDropdownSheet(book, dropdown, key);}//设置一级下拉List<String> val = dropdown[0].getValue().get(null);if (Objects.nonNull(val) && !val.isEmpty()) {String ss = ExcelTools.getRangeByCel(2, 2, val.size()); // A和B被占用,从C开始dropdownValidationData(String.format("='%s'!%s", key, ss), helper, this.headMax, 1000000, dropdown[0], sheet);}//二级及之后的下拉for (int i = 1; i < dropdown.length; i++) {String ci = CellReference.convertNumToColString(dropdown[i - 1].getCellIndex());String format = "INDIRECT(CONCATENATE(\"_\",VLOOKUP($" + ci + (this.headMax + 1) + "," + key + "!A:B,2,0),\"_\",\"" + key + "\"))"; // A:B写死dropdownValidationData(format, helper, this.headMax, 1000001, dropdown[i], sheet);}}/*** 验证下拉数据** @param formula   公式* @param firstRow  第一行* @param lastRow   最后一行* @param dropdown1 下拉数据* @param sheet     表*/private static void dropdownValidationData(String formula, DataValidationHelper helper, int firstRow, int lastRow, ExcelLinkageDropdown dropdown1, Sheet sheet) {DataValidationConstraint constraint = helper.createFormulaListConstraint(formula);DataValidation dataValidation = helper.createValidation(constraint, new CellRangeAddressList(firstRow, lastRow, dropdown1.getCellIndex(), dropdown1.getCellIndex()));dataValidation.setSuppressDropDownArrow(false);if (dataValidation instanceof XSSFDataValidation) {dataValidation.setSuppressDropDownArrow(true);dataValidation.setShowErrorBox(!dropdown1.isAllowOtherValue());  // 输入无效值时是否显示错误框dataValidation.setShowPromptBox(!dropdown1.isAllowOtherValue());  // 设置无效值时 是否弹出提示框dataValidation.createPromptBox("温馨提示", dropdown1.getMessage());   // 设置无效值时的提示框内容dataValidation.createErrorBox("温馨提示", dropdown1.getMessage());   // 设置无效值时的提示框内容}sheet.addValidationData(dataValidation);}/*** 设置单级或多级联动下拉,按顺序(1级,2级,3级...),否则将出错* 调用多次将设置多个多级联动* 单级或1级的key为null** @param head clazz为map时需要传头,否则传null*/public void setLinkageDropdown(List<List<String>> head, String excelName, ExcelLinkageDropdown... dropdowns) throws Exception {if (Objects.isNull(dropdowns) || dropdowns.length == 0) {throw new Exception("至少设置一个下拉参数");}boolean isMap = this.clazz.isAssignableFrom(Map.class);if (isMap && CollectionUtils.isEmpty(head)) {throw new Exception("head参数不能为空");}if (StringUtils.isNotBlank(excelName) && excelName.length() > 30) {throw new Exception("excelName长度不能超过30");}if (isMap) {for (ExcelLinkageDropdown dropdown : dropdowns) {if (dropdown.getValue().isEmpty()) {continue;}List<String> heads = head.stream().map(t -> String.join(",", t)).collect(Collectors.toList());int i = heads.indexOf(dropdown.getFieldName());dropdown.setCellIndex(i);}this.dropdowns.put(dropdowns, excelName);return;}List<ExcelLinkageDropdown> ds = new ArrayList<>();for (ExcelLinkageDropdown dropdown : dropdowns) {if (dropdown.getValue().isEmpty()) {continue;}Field field;try {field = this.clazz.getDeclaredField(dropdown.getFieldName());} catch (Exception e) {throw new Exception("填写的字段不存在:" + dropdown.getFieldName() + "," + e.getMessage());}int index = field.getAnnotation(ExcelProperty.class).index(); // 获取头的位置if (index == -1) {for (Field f : clazz.getDeclaredFields()) {if (!f.isAnnotationPresent(ExcelProperty.class)) {continue;}index++;if (f.getName().equals(dropdown.getFieldName())) {break;}}}dropdown.setCellIndex(index);ds.add(dropdown);}this.dropdowns.put(ds.toArray(new ExcelLinkageDropdown[0]), excelName);}/*** 设置单级或多级联动下拉,按顺序(1级,2级,3级...),否则将出错* 调用多次将设置多个多级联动* 单级或1级的key为null** @param head clazz为map时需要传头,否则传null*/public void setLinkageDropdown(List<List<String>> head, ExcelLinkageDropdown... dropdowns) throws Exception {this.setLinkageDropdown(head, null, dropdowns);}/*** 构建下拉列表sheet页,用于下拉框展示的数据源** @param book 工作簿*/private void buildDropdownSheet(Workbook book, ExcelLinkageDropdown[] dropdowns, String hiddenArea) {//创建新的隐藏表Sheet hideSheet = createNewHideSheet(book, hiddenArea);// 设置map的key格式化, 设置一级下拉this.setKeyFormat(dropdowns, hideSheet);// 第4列开始将具体的数据写入到每一列中int col = 3;int startRow = 1; //开始行CellStyle cellStyle = this.getFixRed(book);for (int i = 1; i < dropdowns.length; i++) {ExcelLinkageDropdown dropdown = dropdowns[i];for (Map.Entry<String, List<String>> entry : dropdown.getValue().entrySet()) {int rows = entry.getValue().size();//起始行如果超出最大行数,则新增一列,起始行重新计算,当前列总行数重新计算;if (startRow >= 1048576 || startRow + rows >= 1048576) {startRow = 1;col++;}//当前列超出最大列数,报错if (col > 16384) {throw new RuntimeException("当前列超出最大列数");}//设置下拉值和名称管理器this.setNameName(book, hideSheet, hiddenArea, col, startRow, cellStyle, entry, rows);//重新计算起始行startRow = startRow + rows + 2;}}}/*** 设置下拉值和名称管理器*/private void setNameName(Workbook book, Sheet hideSheet, String hiddenArea, int col, int startRow, CellStyle cellStyle, Map.Entry<String, List<String>> entry, int rows) {Row row0 = Objects.isNull(hideSheet.getRow(startRow - 1)) ? hideSheet.createRow(startRow - 1) : hideSheet.getRow(startRow - 1);Cell cell = row0.createCell(col);cell.setCellValue(ExcelTools.replaceAscii(entry.getKey()));cell.setCellStyle(cellStyle); //设置样式,区分下拉值for (int j = 0; j < rows; j++) {int r = j + startRow;Row row = Objects.isNull(hideSheet.getRow(r)) ? hideSheet.createRow(r) : hideSheet.getRow(r);row.createCell(col).setCellValue(entry.getValue().get(j));}// 添加名称管理器String range = ExcelTools.getRangeByCel(col, startRow + 1, rows);String nameName = "_" + ExcelTools.replaceAscii(entry.getKey()) + "_" + hiddenArea;Name name = book.createName();name.setNameName(nameName); // key不可重复String formula = hiddenArea + "!" + range;name.setRefersToFormula(formula);}/*** 设置map的key格式化*/private void setKeyFormat(ExcelLinkageDropdown[] dropdowns, Sheet hideSheet) {// 第1-2列设置匹配表int rowId = 0;  // 设置区域的头行for (int i = 1; i < dropdowns.length; i++) {ExcelLinkageDropdown d = dropdowns[i];for (Map.Entry<String, List<String>> kv : d.getValue().entrySet()) {// 原始key-第一列Row row0 = hideSheet.createRow(rowId);row0.createCell(0).setCellValue(kv.getKey()); // 第一列row0.createCell(1).setCellValue(ExcelTools.replaceAscii(kv.getKey())); // 处理后的key-第二列rowId++;}}// 设置第1级List<String> dropdownVal = dropdowns[0].getValue().get(null); // 得到第一级Row r1 = Objects.isNull(hideSheet.getRow(0)) ? hideSheet.createRow(0) : hideSheet.getRow(0);r1.createCell(2).setCellValue(dropdowns[0].getFieldName());for (int i = 0; i < dropdownVal.size(); i++) {Row r1_ = Objects.isNull(hideSheet.getRow(i + 1)) ? hideSheet.createRow(i + 1) : hideSheet.getRow(i + 1);r1_.createCell(2).setCellValue(dropdownVal.get(i));}}/*** 创建新的隐藏表*/private Sheet createNewHideSheet(Workbook book, String hiddenArea) {// 创建一个专门用来存放下拉的隐藏sheet页Sheet hideSheet = book.createSheet(hiddenArea);// 这一行作用是将此sheet隐藏book.setSheetHidden(book.getSheetIndex(hideSheet), true);//如果是SXSSFWorkbook类型,转XSSFWorkbook类型,否则sheet.getRow()可能为空if (book instanceof SXSSFWorkbook) {SXSSFWorkbook sxssfWorkbook = (SXSSFWorkbook) book;hideSheet = sxssfWorkbook.getXSSFWorkbook().getSheetAt(book.getSheetIndex(hideSheet));}return hideSheet;}/*** 红色固定样式*/private CellStyle getFixRed(Workbook book) {CellStyle style = book.createCellStyle();Font font = book.createFont();font.setBold(true);font.setColor(IndexedColors.RED.getIndex());style.setFont(font);return style;}
}

工具:

public class ExcelTools {/*** 计算formula:纵向** @param offset   偏移量,如果给0,表示从A列开始,1,就是从B列* @param rowId    第几行开始* @param rowCount 一共多少行* @return 如果给入参 0,2,10. 表示从A2-A11。最终返回 $A$2:$A$11*/public static String getRangeByCel(int offset, int rowId, int rowCount) {String columnLetter1 = CellReference.convertNumToColString(offset);return String.format("$%s$%s:$%s$%s", columnLetter1, rowId, columnLetter1, rowId + rowCount - 1);}/*** 把非(中文、英文、下划线、点)替换为 ascii码,因为excel不支持其他字符设置名称管理器* 如:”审核订单(一级)(1)“ 替换为 ”审核订单.40.一级.41..40.1.41.“*/public static String replaceAscii(String str) {if (StringUtils.isBlank(str)) {return str;}StringBuilder sb = new StringBuilder();for (String s : str.split("")) {if (s.matches("[^\\d\\u4e00-\\u9fa5.a-zA-Z_]")) {s = "." + (int) s.charAt(0) + ".";}sb.append(s);}return sb.toString();}
}

测试

@HeadStyle(fillForegroundColor = 1)
public class User implements Serializable {private static final long serialVersionUID = 1L;@ExcelProperty(value = {"一级"})private String borth;@ExcelProperty(value = {"二级"})@HeadStyle(fillForegroundColor = 29)private String name;@ExcelProperty(value = {"三级"})private String url;@ExcelProperty(value = {"性别"})private String sex;// 日期校验@ExcelProperty(value = "图片")private WriteCellData<Void> imgs;// 日期校验@ExcelProperty(value = "字节图片")private byte[] byteArray;
}
	//测试excel多级联动下拉@GetMapping("/test4")public void test4(HttpServletResponse response, int n) throws Exception {long l = System.currentTimeMillis();Map<String, List<String>> m1 = new HashMap<>();m1.put(null, new ArrayList<>());for (int i = 0; i < n; i++) {m1.get(null).add("A" + (i == 0 ? "" : i));m1.get(null).add("AA" + (i == 0 ? "" : i));m1.get(null).add("AAA" + (i == 0 ? "" : i));m1.get(null).add("AAAA" + (i == 0 ? "" : i));}Map<String, List<String>> m2 = new HashMap<>();for (String s : m1.get(null)) {List<String> a = new ArrayList<>();for (int i = 1; i < 11; i++) {a.add(s + "-" + i + "B");}m2.put(s, a);}List<String> v2 = m2.values().stream().flatMap(List::stream).collect(Collectors.toList());Map<String, List<String>> m3 = new HashMap<>();for (String s : v2) {int t = 50001;List<String> a = new ArrayList<>();for (int i = 1; i < t; i++) {a.add(s + "-" + i + "C");if (i > 1) {t = 1;}}m3.put(s, a);System.out.println(s);}DropdownWriteHandler handler = new DropdownWriteHandler(User.class);ExcelLinkageDropdown dropdown11 = new ExcelLinkageDropdown();dropdown11.setFieldName("borth");dropdown11.setValue(m1);ExcelLinkageDropdown dropdown111 = new ExcelLinkageDropdown();dropdown111.setFieldName("name");dropdown111.setValue(m2);ExcelLinkageDropdown dropdown1111 = new ExcelLinkageDropdown();dropdown1111.setFieldName("url");dropdown1111.setValue(m3);Map<String, List<String>> sexMap = new HashMap<>();sexMap.put(null, m3.values().stream().flatMap(List::stream).collect(Collectors.toList()));ExcelLinkageDropdown sex = new ExcelLinkageDropdown();sex.setFieldName("sex");sex.setValue(sexMap);long l2 = System.currentTimeMillis();System.out.println("设置耗时" + (l2 - l));handler.setLinkageDropdown(null, dropdown11, dropdown111, dropdown1111);handler.setLinkageDropdown(null, sex);long l3 = System.currentTimeMillis();System.out.println("设置耗时" + (l3 - l2));ExcelUtils.downLoad(Lists.newArrayList(handler), ExcelTools.getOutputStream("测试excel多级联动下拉", response), User.class, null, "测试excel多级联动下拉");}

好了,又不想写了,就到此为止吧,有问题评论留言,看到能回就回。

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

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

相关文章

python abs函数怎么用

abs()函数是Python的数字函数&#xff0c;用以返回数字的绝对值。 语法 以下是 abs() 方法的语法&#xff1a; abs( x ) 参数 x -- 数值表达式&#xff0c;可以是整数&#xff0c;浮点数&#xff0c;复数。 返回值 函数返回 x&#xff08;数字&#xff09;的绝对值&#x…

(十五)Servlet教程——Servlet文件上传

JSP和HTML标签一起使用&#xff0c;来允许用户把文件上传到服务器。 首先我们需要创建一个前端界面&#xff0c;创建上传文件表单时&#xff0c;需要注意以下几点&#xff1a; (1) 表单的method属性必须设置为POST方法&#xff0c; 不能使用GET方法。 (2) 表单enctype属性应该…

accelerator入门

一、目录 1 定义 2. DP、DPP的区别 3 实现 4. 测试比较 二、实现 定义 accelerator 是由大名鼎鼎的huggingface发布的&#xff0c;专门适用于Pytorch的分布式训练框架,是torchrun 的封装。 GitHub: https://github.com/huggingface/accelerate 官网教程&#xff1a;https://…

8、卷积Convolutions (CNN)

Today we finish off our study of collaborative filtering by looking closely at embeddings—a critical building block of many deep learning algorithms. Then we’ll dive into convolutional neural networks (CNNs) and see how they really work. We’ve used plen…

kaggle叶子分类比赛(易理解)

说实话网上很多关于叶子分类比赛的代码能取得的成绩都很好,但对于我这个业余人员太专业了&#xff0c;而且很多文章都有自己的想法&#xff0c;这让我这个仿写沐神代码的小菜鸡甚是头痛。 但好在我还是完成了&#xff0c;虽然结果并不是很好&#xff0c;但是如果跟着沐神走的同…

AI编码时代到来?实现编程梦想的利器—Baidu Comate测评

文章目录 Comate智能编码是什么&#xff1f;Comate支持的环境 Comate应用安装实际操作对话式生成代码生成代码注释智能单测项目测试调优功能 总结 Comate智能编码是什么&#xff1f; 在如今这个拥抱AI的时代&#xff0c;市面上已经产出了很多Ai代码助手&#xff0c;如果你还没…

【LeetCode算法】28. 找出字符串中第一个匹配项的下标

提示&#xff1a;此文章仅作为本人记录日常学习使用&#xff0c;若有存在错误或者不严谨得地方欢迎指正。 文章目录 一、题目二、思路三、解决方案四、JAVA截取字符串的常用方法4.1 通过subString()截取字符串* 一、题目 给你两个字符串 haystack 和 needle &#xff0c;请你在…

鸿蒙OpenHarmony南向:【Hi3516标准系统入门(IDE方式)】

Hi3516标准系统入门&#xff08;IDE方式&#xff09; 注意&#xff1a; 从3.2版本起&#xff0c;标准系统不再针对Hi3516DV300进行适配验证&#xff0c;建议您使用RK3568进行标准系统的设备开发。 如您仍然需要使用Hi3516DV300进行标准系统相关开发操作&#xff0c;则可能会出现…

CST电磁仿真查看模型的截面结构和生成Spice模型【入门教程】

通过Logfile查看仿真统计 一次性了解仿真统计! Post-Processing > Manage Results > Logfile 利用CPU Threads、Mesh Cells、Time Steps以及Total Solver Time等Logfile&#xff0c;可以一目了然地了解仿真统计。 &#xff08;1&#xff09;点击Post-Processing选项卡…

PPT如何录制视频?看这里,让你轻松上手!

在现代社会&#xff0c;演示文稿&#xff08;ppt&#xff09;已成为我们工作、学习和生活中不可或缺的一部分。无论是商务报告、教育培训还是产品展示&#xff0c;ppt都能以其直观、生动的形式有效地传达信息。可是你知道ppt如何录制视频吗&#xff1f;本文将为您介绍两种ppt录…

pycharm code行太长显示波浪线取消

实际操作如下&#xff1a;个人比较合适的位置为160,180时有点多 效果&#xff1a;

《视觉十四讲》例程运行记录(3)——运行ch6的例程中Ceres和g2o库的安装

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 一、安装Ceres1. 安装依赖2. 编译安装 二、安装g2o1. 安装依赖项2. 编译安装3. 可能出现的报错(1) 报错一 一、安装Ceres 1. 安装依赖 终端输入&#xff1a; sud…

计算有效声压

计算有效声压 clear all; %%----------------------------------------------读取文件------------------------------------------ % 从wav文件读入语音数据&#xff0c;该语音采样率16k&#xff0c;故信号最高频率8k。 [x,fs]audioread(C2_3_y.wav); % 取x的一个通道 xx(:,1)…

Google准备好了吗?OpenAI发布ChatGPT驱动搜索引擎|TodayAI

在科技界波澜壮阔的发展中&#xff0c;OpenAI正式宣布其最新突破——一个全新的基于ChatGPT技术的搜索引擎&#xff0c;旨在直接挑战谷歌在搜索领域的统治地位。这一创新将可能彻底改变用户上网搜索的方式。 据悉&#xff0c;这款AI驱动的搜索引擎利用了ChatGPT的强大功能&…

免费SSL证书?轻松申请攻略来了!

在当今的互联网时代&#xff0c;网络安全已经成为一个不容忽视的重要课题。随着在线交流和交易活动的增加&#xff0c;保护网站和用户信息的重要性日益突显。SSL证书&#xff0c;即安全套接字层证书&#xff0c;它为互联网通信提供了加密服务&#xff0c;确保数据的安全性和完整…

淘宝扭蛋机小程序开发:转动幸运,开启无限惊喜

一、探索未知&#xff0c;开启全新扭蛋体验 淘宝扭蛋机小程序&#xff0c;为您带来一场前所未有的扭蛋盛宴。在这个充满神秘与乐趣的平台上&#xff0c;每一次点击都将引领您走进未知的宝藏世界&#xff0c;每一次旋转都可能揭示出意想不到的惊喜。 二、海量商品&#xff0c;…

Kubernetes 教程:在 Containerd 容器中使用 GPU

原文链接:Kubernetes 教程:在 Containerd 容器中使用 GPU 云原生实验室本文介绍了如何在使用 Containerd 作为运行时的 Kubernetes 集群中使用 GPU 资源。https://fuckcloudnative.io/posts/add-nvidia-gpu-support-to-k8s-with-containerd/ 前两天闹得沸沸扬扬的事件不知道…

3D 交互展示该怎么做?

在博维数孪&#xff08;Bowell&#xff09;平台制作3D交互展示的流程相对简单&#xff0c;主要分为以下几个步骤&#xff1a; 1、准备3D模型&#xff1a;首先&#xff0c;你需要有一个3D模型。如果你有3D建模的经验&#xff0c;可以使用3ds Max或Blender等软件自行创建。如果没…

护眼台灯十大品牌哪个好?热销榜护眼灯十大品牌推荐

护眼台灯十大品牌哪个好&#xff1f;在这篇文章中&#xff0c;我将向大家介绍十大护眼台灯品牌&#xff0c;其中包括书客、松下、飞利浦等知名品牌。我精心挑选这些品牌&#xff0c;旨在为大家提供明智的选择参考。这些品牌的护眼台灯拥有的功能比较多&#xff0c;提供的光线也…

揭秘软胶囊品质的秘密武器:西奥机电CHT-01软胶囊弹性硬度测试仪

揭秘软胶囊品质的秘密武器&#xff1a;西奥机电CHT-01软胶囊弹性硬度测试仪 在医药行业中&#xff0c;软胶囊作为一种常见的药品剂型&#xff0c;因其独特的封装方式和便利性而受到广泛青睐。然而&#xff0c;软胶囊的质量问题也一直是制药企业关注的焦点。为了确保软胶囊的质量…