java 导出excel表格POI - XSSFWorkbook,自定义各种格式,多级表格,同一个sheet按照天往后排列

news2024/12/29 10:31:18

maven的依赖

    <dependency>
      <groupId>org.apache.poi</groupId>
      <artifactId>poi</artifactId>
      <version>4.1.2</version>
      <scope>compile</scope>
    </dependency>
    <dependency>
      <groupId>org.apache.poi</groupId>
      <artifactId>poi-ooxml</artifactId>
      <version>4.1.2</version>
      <scope>compile</scope>
    </dependency>
    <dependency>
      <groupId>org.apache.poi</groupId>
      <artifactId>poi-ooxml-schemas</artifactId>
      <version>4.1.2</version>
      <scope>compile</scope>
    </dependency>

基础版的截图 三级表头,每天对应一大列

实战复杂的截图 三级表头,每天对应一大列

表格分为横向排列和纵向排列,比较复杂,

还涉及多个表头,编写代码太复杂,历时4天完成。

基础版实现代码

	public static void main(String[] args) {
		String base = "温度,湿度,气压";
		String identifyStr = "日期,设备" + "," + base;
		String head = base + "," + "时间段";
		String[] identifyArray = Func.toStrArray(",", identifyStr);
		String[] headArray = Func.toStrArray(",", head);
		XSSFWorkbook workbook = new XSSFWorkbook();
		XSSFSheet sheet = workbook.createSheet("设备属性数据表 ");
		//  数据
		Map<String, List<Map<String, Object>>> dailyData = getDailyData(identifyArray, 3);
		String header0 = "";
		int i = 0;
		for (Map.Entry<String, List<Map<String, Object>>> entry : dailyData.entrySet()) {
			String day = entry.getKey();
			List<Map<String, Object>> dataList = entry.getValue();
			// 合并单元格
			int lastCellNum = identifyArray.length;
			int cellst = i * (lastCellNum + 3);   // 0-5 7-12 14-19 21-26
			int cellend = i * (lastCellNum + 3) + lastCellNum;
			i++;
			sheet.addMergedRegion(new CellRangeAddress(0, 0, cellst, cellend)); // 合并表头一的单元格
			sheet.addMergedRegion(new CellRangeAddress(1, 1, cellst, cellend)); // 合并表头二的单元格  导出时间
			sheet.addMergedRegion(new CellRangeAddress(2, 2, cellst, cellend)); // 合并表头三的单元格  汇总数据
			Row headerRow1 = sheet.getRow(0);
			if (headerRow1 == null) {
				headerRow1 = sheet.createRow(0);
			}
			// 设置边框样式
			CellStyle borderStyle = workbook.createCellStyle();
			borderStyle.setBorderBottom(BorderStyle.THIN);
			borderStyle.setBorderTop(BorderStyle.THIN);
			borderStyle.setBorderLeft(BorderStyle.THIN);
			borderStyle.setBorderRight(BorderStyle.THIN);
			borderStyle.setAlignment(HorizontalAlignment.CENTER);
			borderStyle.setVerticalAlignment(VerticalAlignment.CENTER);
			header0 = StringUtil.format("至{}设备数据导出", "设备名66");
			headerRow1.createCell(cellst).setCellValue(header0);
			Row dateRow = sheet.getRow(1);
			if (dateRow == null) {
				dateRow = sheet.createRow(1);
			}
			dateRow.createCell(cellst).setCellValue("导出时间:" + DateUtil.formatDateTime(DateUtil.now()));
			Row dateRow2 = sheet.getRow(2);
			if (dateRow2 == null) {
				dateRow2 = sheet.createRow(2);
			}
			dateRow2.createCell(cellst).setCellValue("汇总数据");
			Row dateRow3 = sheet.getRow(3);
			if (dateRow3 == null) {
				dateRow3 = sheet.createRow(3);
			}
			dateRow3.createCell(cellst).setCellValue("统计类型");
			Row dateRow4 = sheet.getRow(4);
			if (dateRow4 == null) {
				dateRow4 = sheet.createRow(4);
			}
			dateRow4.createCell(cellst).setCellValue("合计");
			Row dateRow5 = sheet.getRow(5);
			if (dateRow5 == null) {
				dateRow5 = sheet.createRow(5);
			}
			dateRow5.createCell(cellst + 0).setCellValue("平均值");
			Row dateRow6 = sheet.getRow(6);
			if (dateRow6 == null) {
				dateRow6 = sheet.createRow(6);
			}
			dateRow6.createCell(cellst + 0).setCellValue("最大值");
			Row dateRow7 = sheet.getRow(7);
			if (dateRow7 == null) {
				dateRow7 = sheet.createRow(7);
			}
			dateRow7.createCell(cellst + 0).setCellValue("最小值");
			Row dateRow8 = sheet.getRow(8);
			if (dateRow8 == null) {
				dateRow8 = sheet.createRow(8);
			}
			dateRow8.createCell(cellst + 0).setCellValue("异常波动情况");
			Row dateRow9 = sheet.getRow(9);
			if (dateRow9 == null) {
				dateRow9 = sheet.createRow(9);
			}
			dateRow9.createCell(cellst + 0).setCellValue("报警情况");
			Row dateRow10 = sheet.getRow(10);
			if (dateRow10 == null) {
				dateRow10 = sheet.createRow(10);
			}
			dateRow10.createCell(cellst).setCellValue("预警情况");
			for (int cells = 1; cells <= headArray.length; cells++) {
				dateRow3.createCell(cellst + cells).setCellValue(headArray[cells - 1]);  // 统计类型 表头
				dateRow4.createCell(cellst + cells).setCellValue("4_" + cells);
				dateRow5.createCell(cellst + cells).setCellValue("5_" + cells);
				dateRow6.createCell(cellst + cells).setCellValue("6_" + cells);
				dateRow7.createCell(cellst + cells).setCellValue("7_" + cells);
				dateRow8.createCell(cellst + cells).setCellValue("8_" + cells);
				dateRow9.createCell(cellst + cells).setCellValue("9_" + cells);
				dateRow10.createCell(cellst + cells).setCellValue("10_" + cells);
			}
			// 写入数据
			int rowNum = 15;
			int arrayLength = 0;
			for (Map<String, Object> data : dataList) {
				// 创建表头三
				Row headerRow = sheet.getRow(14);
				if (headerRow == null) {
					headerRow = sheet.createRow(14);
				}
				Row row = sheet.getRow(rowNum);
				if (row == null) {
					row = sheet.createRow(rowNum);
				}
				for (int c = 0; c < identifyArray.length; c++) {
					Cell headerCell2 = headerRow.createCell(cellst + c);
					headerCell2.setCellValue(identifyArray[c]);
					row.createCell(cellst + c).setCellValue(data.get(identifyArray[c]).toString());
				}
				rowNum++;
				arrayLength++;
			}
		}

		// 写入到文件
		try (FileOutputStream fileOut = new FileOutputStream("C:\\Users\\xu\\Desktop\\表格\\daily_data.xlsx")) {
			workbook.write(fileOut);
		} catch (IOException e) {
			e.printStackTrace();
		}
		// 关闭工作簿
		try {
			workbook.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}


	// 模拟数据
	private static Map<String, List<Map<String, Object>>> getDailyData(String[] array, int timeDay) {
		Map<String, List<Map<String, Object>>> data = new HashMap<>();
		for (int i = 0; i < timeDay; i++) {
			List<Map<String, Object>> day1Data = new ArrayList<>();
			for (int c = 0; c < 5; c++) {
				Map<String, Object> day1Row1 = new HashMap<>();
				for (String str : array) {
					day1Row1.put(str, 1 + i);
				}
				day1Data.add(day1Row1);
			}
			data.put("Day" + i, day1Data);
		}
		return data;
	}

实战复杂案例代码

里面有模拟的数据,可以直接运行,无需编写代码即可导出

package com.bluebird.iot.util;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.bluebird.common.enums.iot.AlarmRuleTypeEnum;
import com.bluebird.common.model.TimeSplit;
import com.bluebird.core.log.exception.ServiceException;
import com.bluebird.core.tool.utils.DateUtil;
import com.bluebird.core.tool.utils.Func;
import com.bluebird.core.tool.utils.StringUtil;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

import java.io.FileOutputStream;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.*;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.regex.Pattern;
 
public class ExcelExporter {


	// 报警情况
	private static final Integer ALARM_ANALYSIS = 5;
	// 预警情况
	private static final Integer WARNING_ANALYSIS = 6;
	// 异常波动情况
	private static final Integer ABNORMAL_ANALYSIS = 7;

	// 计算大小值
	private static final Integer MIN_NUM = 2147483647;
	//计算最大值
	private static final Integer MAX_NUM = -2147483648;


	@Data
	public static class ModelItem   {
		private static final long serialVersionUID = 1L;

		@ApiModelProperty(value = "中文名称 ")
		private String name;

		@ApiModelProperty(value = "属性标识符")
		private String identifier;

		@ApiModelProperty(value = "单位")
		private String unit;

	}


	@Data
	public static class Value   {
		private static final long serialVersionUID = 1L;

		private String identify;
		private String value;
	}

	@Data
	public static class AlarmData {
		private static final long serialVersionUID = 1L;
		private int ruleType;
		private long time;
		private List<Value> value;
	}



	// 处理表头模拟数据
	private static List<ModelItem>  getModelItemList( ) {
		// 雷(L)	风(F)	气	水	雨(Y)	电
		List<ModelItem> modelItemList = new ArrayList<>();
		ModelItem feng = new ModelItem();
		feng.setName("风");
		feng.setIdentifier("feng");
		feng.setUnit("F");

		ModelItem yu = new ModelItem();
		yu.setName("雨");
		yu.setIdentifier("yu");
		yu.setUnit("Y");

		ModelItem lei = new ModelItem();
		lei.setName("雷");
		lei.setIdentifier("lei");
		lei.setUnit("L");

		ModelItem dian = new ModelItem();
		dian.setName("电");
		dian.setIdentifier("dian");
		dian.setUnit("D");


		ModelItem qi = new ModelItem();
		qi.setName("气");
		qi.setIdentifier("qi");
		qi.setUnit("Q");

		ModelItem shui = new ModelItem();
		shui.setName("水");
		shui.setIdentifier("shui");
		shui.setUnit("L");

		modelItemList.add( feng );
		modelItemList.add( yu );
		modelItemList.add( lei );
		modelItemList.add( qi );
		modelItemList.add( shui );
		return modelItemList;
	}


	// 模拟表头二 报警信息
	private static List<AlarmData>  getAlarmDataList( ){
		List<AlarmData> alarmDataList = new ArrayList<>();
		AlarmData alarmData1 = new AlarmData();
		alarmData1.setTime(1718359244437L);
		alarmData1.setRuleType(1);
		List<Value> value_1 = new ArrayList<>();
		Value value1 = new Value();
		value1.setValue("30");
		value1.setIdentify("qi");
		Value value2 = new Value();
		value2.setValue("70");
		value2.setIdentify("shui");
		Value value3 = new Value();
		value3.setValue("88");
		value3.setIdentify("feng");
		value_1.add( value1 );
		value_1.add( value2 );
		value_1.add( value3 );
		alarmData1.setValue( value_1 );




		AlarmData alarmData3 = new AlarmData();
		alarmData3.setTime(1718359244437L);
		alarmData3.setRuleType(2);
		List<Value> value_3 = new ArrayList<>();
		Value value7 = new Value();
		value7.setValue("30");
		value7.setIdentify("qi");
		Value value8 = new Value();
		value8.setValue("70");
		value8.setIdentify("shui");
		Value value9  = new Value();
		value9.setValue("88");
		value9.setIdentify("feng");
		value_3.add( value7 );
		value_3.add( value8 );
		value_3.add( value9 );
		alarmData3.setValue( value_3 );


		AlarmData alarmData = new AlarmData();
		alarmData.setTime(1718359244437L);
		alarmData.setRuleType(2);
		List<Value> value_2 = new ArrayList<>();
		Value value4 = new Value();
		value4.setValue("30");
		value4.setIdentify("qi");
		Value value5 = new Value();
		value5.setValue("70");
		value5.setIdentify("shui");
		Value value6 = new Value();
		value6.setValue("88");
		value6.setIdentify("feng");
		value_2.add( value4 );
		value_2.add( value5 );
		value_2.add( value6 );
		alarmData.setValue( value_2 );


		alarmDataList.add( alarmData );
		alarmDataList.add( alarmData1 );
		alarmDataList.add( alarmData3 );
		return alarmDataList;
	}


	// 处理模拟表头三 的刘表
	// {"identifies":["lei","feng","qi","shui","yu","dian"],"values":["50","60","40","70","10","15"],"_id":1718359477276,"time":1718359477276,"deviceSn":"wsd01"}

	private static List<JSONObject> getList(){
		List<JSONObject> aggregatedList = new ArrayList<>();
		JSONObject jsonObject = new JSONObject();
		String[] identifies = {"lei","feng","qi","shui","yu","dian"};
		jsonObject.put("identifies", identifies);
		String[] values = {"50","60","40","70","10","15"};
		jsonObject.put("values", values);
		jsonObject.put("time", "1718359477276");
		jsonObject.put("deviceSn", "wsd01");
		aggregatedList.add( jsonObject );

		return aggregatedList;
	}


	public static Date strToDate(String dateStr) {
		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
		Date date = null;
		try {
			date = sdf.parse(dateStr);
		} catch (ParseException e) {
			e.printStackTrace();
		}
		return date;
	}


	public static void main(String[] args) {
		toExportUtils(  );
	}

	public static String dateToString(Object date ) {
		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
		return sdf.format(date);
	}

	public static List<TimeSplit> getStartAndEndTimesForEachDay(Date startDate, Date endDate) {
		List<TimeSplit> timeSplits = new ArrayList<>();

		// 将开始日期转为LocalDate
		ZonedDateTime startZonedDateTime = startDate.toInstant().atZone(ZoneId.systemDefault());
		LocalDate startLocalDate = startZonedDateTime.toLocalDate();

		// 将结束日期转为LocalDate
		ZonedDateTime endZonedDateTime = endDate.toInstant().atZone(ZoneId.systemDefault());
		LocalDate endLocalDate = endZonedDateTime.toLocalDate();

		// 检查开始日期和结束日期是否在同一天
		if (startLocalDate.isEqual(endLocalDate)) {
			timeSplits.add(new TimeSplit(startDate, endDate));
			return timeSplits;
		}

		// 将确切的startDateTime添加第一天
		ZonedDateTime endOfFirstDay = startLocalDate.atTime(LocalTime.MAX).atZone(ZoneId.systemDefault());
		timeSplits.add(new TimeSplit(startDate, Date.from(endOfFirstDay.toInstant())));

		// 迭代剩余的天数,直到endLocalDate的前一天
		LocalDate current = startLocalDate.plusDays(1);
		while (current.isBefore(endLocalDate)) {
			ZonedDateTime startOfDay = current.atStartOfDay(ZoneId.systemDefault());
			ZonedDateTime endOfDay = current.atTime(LocalTime.MAX).atZone(ZoneId.systemDefault());

			// Add the current day's start and end times to the list
			timeSplits.add(new TimeSplit(Date.from(startOfDay.toInstant()), Date.from(endOfDay.toInstant())));

			// Move to the next day
			current = current.plusDays(1);
		}

		// Handle the last day separately to include the exact endDateTime
		ZonedDateTime startOfLastDay = endLocalDate.atStartOfDay(ZoneId.systemDefault());
		if (startOfLastDay.isBefore(endZonedDateTime)) {
			timeSplits.add(new TimeSplit(Date.from(startOfLastDay.toInstant()), endDate));
		}

		return timeSplits;
	}


	public static void toExportUtils(  ) {

		long startExecutionTime = System.currentTimeMillis();
		Date startTime =  strToDate("2024-06-16 01:00:00 ") ;
 		Date endTime =    strToDate("2024-06-18 01:00:00") ;
		String[] identifys = Func.toStrArray(",", "feng,yu,lei,qi,shui");

		// 获取用户选择的统计类型
		String statisticsTypesStr = "7,6,5,2,1,3,4";
		List<Integer> statisticsTypes = Func.toIntList(",", statisticsTypesStr);
		// 处理 这4个表头 合计:1,平均值:2,最大值:3,最小值:4 由于有顺序或者是否查询此表头 需要特殊处理下
		List<Integer> topFourList = new ArrayList<>();
		Map<Integer, String> integerStringMap = toMap();
		for (Integer statisticsType : statisticsTypes) {
			if (statisticsType < 5) {
				topFourList.add(statisticsType);
			}
		}

		List<TimeSplit> timeSplitList =  getStartAndEndTimesForEachDay(startTime, endTime);
		if (timeSplitList != null && timeSplitList.size() > 7) {
			throw new ServiceException("导出时间不能超过7天,请修改时间后再导出!");
		}

		List<ModelItem> modelItemList = getModelItemList();

		if (modelItemList == null || modelItemList.size() == 0) {
			throw new ServiceException("物模型不存在");
		}
		String base = "";
		String identifyKeyStr = "日期,设备" + ",";
		Map<String, String> mapHead = new HashMap<>();
		Map<String, String> mapSum = new HashMap<>();
		String sumKey = "";
		Map<String, ModelItem> modelItemMap = new HashMap<>();
		if (identifys != null && identifys.length > 0) {
			for (int i = 0; i < identifys.length; i++) {
				String identify = identifys[i];
				ModelItem item = new ModelItem();
				for (ModelItem modelItem : modelItemList) {
					if (identify.equals(modelItem.getIdentifier())) {
						item = modelItem;
						break;
					}
				}
				identifyKeyStr = identifyKeyStr + item.getIdentifier() + ",";
				sumKey = sumKey + item.getIdentifier() + ",";
				if (StringUtil.isNotBlank(item.getUnit())) {
					base = base + item.getName() + "(" + item.getUnit() + ")" + ",";
					mapHead.put(item.getIdentifier(), item.getName() + "(" + item.getUnit() + ")");
					mapSum.put(item.getIdentifier(), item.getName() + "(" + item.getUnit() + ")");
				} else {
					base = base + item.getName() + ",";
					mapHead.put(item.getIdentifier(), item.getName());
					mapSum.put(item.getIdentifier(), item.getName());
				}
				modelItemMap.put(item.getIdentifier(), item);
			}
			mapHead.put("日期", "日期");
			mapHead.put("设备", "设备");
		}
		String identifyStr = "日期,设备" + "," + base;
		String head = base + "时间段";
		String[] identifyKey = Func.toStrArray(",", identifyKeyStr);
		//  列表数据 使用的 带 日期,设备 的字段
		String[] identifyArray = Func.toStrArray(",", identifyStr);
		// 统计使用的 带 时间段 的字段
		String[] headArray = Func.toStrArray(",", head);
		// 前端传过来的物属性
		String[] sumArray = Func.toStrArray(",", sumKey);
		// 处理总数据
		Map<String, List<Map<String, Object>>> dailyData = new LinkedHashMap<>();

		// 处理统计告警等信息
		Map<String, List<AlarmData>> alarmMapData = new LinkedHashMap<>();


		// 将计算的 告警、统计等综合记录下,便于下个集合遍历使用
		Map<String, Integer> excelMapCount = new LinkedHashMap<>();

		// 查询每天的总数据量 由于表头、合计、最值、告警等会占用一定的行数 所以这里最多按照 excelCount 计算 如果超过 excelCount 就返回提示
		for (int i = 0; i < timeSplitList.size(); i++) {
			TimeSplit timeSplit = timeSplitList.get(i);
			// 自定义设备名称 例如: ....设备_2024-06-14至2024-06-18
			String keyMap = "";
			// 计算小时数
			LocalDateTime startDateTime = LocalDateTime.ofInstant(startTime.toInstant(), ZoneId.systemDefault());
			LocalDateTime endDateTime = LocalDateTime.ofInstant(endTime.toInstant(), ZoneId.systemDefault());
			long hours = ChronoUnit.HOURS.between(startDateTime, endDateTime);
			if (hours <= 24) {
				keyMap = StringUtil.format("{}_{}小时", "设备名称", hours);
			} else {
				keyMap = StringUtil.format("{}_{}至{}",
					"设备名称", DateUtil.formatDate(timeSplit.getStartDateTime()), DateUtil.formatDate(timeSplit.getEndDateTime()));
			}
			List<AlarmData> alarmDataProjectionList = null;
			//查询报警、预警、异常等数据
			if (statisticsTypes.contains(ALARM_ANALYSIS) || statisticsTypes.contains(WARNING_ANALYSIS) || statisticsTypes.contains(ABNORMAL_ANALYSIS)) {
				alarmDataProjectionList = getAlarmDataList(); // getAlarmData(statisticsTypes, identifys, serviceId, productKey, deviceSn, timeSplit);
				alarmMapData.put(keyMap, alarmDataProjectionList);
			}

			// 查询数据总量  我这 用200模拟下
			long deviceDayCount = 200L ;

			System.out.println( "================== 第 "+dateToString(timeSplit.getStartDateTime())+" 天的消息总数量为:"+deviceDayCount+ " 条" );
			//  表格总行数 65535 - 表头和统计以及空格 按照 12计算 = 65523
			int excelCount = 65523;
			if (alarmDataProjectionList != null) {
				excelCount = excelCount - alarmDataProjectionList.size();
			}
			if (deviceDayCount > excelCount) {
				throw new ServiceException(dateToString(timeSplit.getStartDateTime()) + " 当天总数量超过 65535 的最大限制了,不能导出!");
			}
			excelMapCount.put(keyMap, Integer.parseInt(String.valueOf(deviceDayCount)));

		}

		// 根据时间天 查询数据
		for (int i = 0; i < timeSplitList.size(); i++) {
			TimeSplit timeSplit = timeSplitList.get(i);

			// 自定义设备名称 例如: 演示能耗设备_2024-05-30至2024-05-30
			String keyMap = "";
			// 计算小时数
			LocalDateTime startDateTime = LocalDateTime.ofInstant(startTime.toInstant(), ZoneId.systemDefault());
			LocalDateTime endDateTime = LocalDateTime.ofInstant(endTime.toInstant(), ZoneId.systemDefault());
			long hours = ChronoUnit.HOURS.between(startDateTime, endDateTime);
			if (hours <= 24) {
				keyMap = StringUtil.format("{}_{}小时", "设备名称", hours);
			} else {
				keyMap = StringUtil.format("{}_{}至{}", "设备名称", DateUtil.formatDate(timeSplit.getStartDateTime()), DateUtil.formatDate(timeSplit.getEndDateTime()));
			}
			// 拿到最大值
			int maxRow = excelMapCount.get(keyMap);
			// 每次查询的数据量
			int offset = 0;
			int batchSize = 10;

			//计算遍历次数
			int divisionResult = maxRow / batchSize;
			//计算最后一次 取余
			int remainder = maxRow % batchSize;
			List<Map<String, Object>> dayData = new ArrayList<>();
			if (maxRow > 0) {
				for (int max = 0; max <= divisionResult; max++) {
					//  计算最后一次的偏移量
					if (max == divisionResult && remainder != 0) {
						batchSize = remainder;
					}
					// 查询表头三列表数据 TODO
					List<JSONObject> aggregatedList = getList();// getDeviceParseLog(identifys, serviceId, productKey, deviceSn, timeSplit, offset, batchSize, endTime);
					for (JSONObject jsonObject : aggregatedList) {
						Map<String, Object> dayRow = new HashMap<>();
						dayRow.put("日期", dateToString(new Date()));
						dayRow.put("设备", jsonObject.get("deviceSn"));
						Object identifies = jsonObject.get("identifies");
						Object values = jsonObject.get("values");
						List<String> identifiesList = JSONArray.parseArray(JSON.toJSONString(identifies), String.class);
						List<String> objectList = JSONArray.parseArray(JSON.toJSONString(values), String.class);
						for (int j = 0; j < identifiesList.size(); j++) {
							String str = identifiesList.get(j);
							int val = 0;
							if (isNumber(objectList.get(j))) {
								val = Integer.parseInt(objectList.get(j));
							} else {
								val = 0;
							}
							dayRow.put(str, val);
						}
						dayData.add(dayRow);
					}
					offset += batchSize;
				}
			}
			dailyData.put(keyMap, dayData);
		}

		String header0 = "";
		int i = 0;
		XSSFWorkbook workbook = new XSSFWorkbook();
		XSSFSheet sheet = workbook.createSheet("设备属性数据表 ");
		// 遍历天
		for (Map.Entry<String, List<Map<String, Object>>> entry : dailyData.entrySet()) {
			String nameTime = entry.getKey();
			List<AlarmData> alarmDataProjectionList = alarmMapData.get(nameTime);
			// 处理统计信息
			Map<String, List<Object>> listMap = new HashMap<>();

			List<String> stringList = Arrays.asList(sumArray);
			for (String keyPY : stringList) {
				int sum = 0;
				int count = 0;
				int max = MAX_NUM;
				BigDecimal avg = null;
				int min = MIN_NUM;
				// 处理最大值和最小值、总和
				for (Map<String, Object> stringObjectMap : entry.getValue()) {
					for (Map.Entry<String, Object> stringObjectEntry : stringObjectMap.entrySet()) {
						if (keyPY.equals(stringObjectEntry.getKey())) {
							int value = Integer.parseInt(stringObjectEntry.getValue().toString());
							sum += value;
							count++;
							if (max < value) {
								max = value;
							}
							if (min > value) {
								min = value;
							}
						}
					}
				}

				if (count != 0) {
					BigDecimal num = new BigDecimal(sum);
					BigDecimal denom = new BigDecimal(count);
					avg = num.divide(denom, 2, RoundingMode.HALF_UP);
				}

				List<Object> list = new ArrayList<>();
				// 时间段
				List<Object> listTime = new ArrayList<>();
				for (int top = 0; top < topFourList.size(); top++) {
					int topNum = topFourList.get(top);
					// 合计:1,平均值:2,最大值:3,最小值:4
					String headName = integerStringMap.get(topNum);
					if ("合计".equals(headName)) {
						list.add(sum);
					} else if ("平均值".equals(headName)) {
						list.add(avg);
					} else if ("最大值".equals(headName)) {
						list.add(max);
					} else if ("最小值".equals(headName)) {
						list.add(min);
					}
					listTime.add(0);
				}


				// 处理 统计的数据  时间和其他物模型
				if (alarmDataProjectionList != null && alarmDataProjectionList.size() > 0) {
					for (int j = 0; j < alarmDataProjectionList.size(); j++) {
						AlarmData alarmDataProjection = alarmDataProjectionList.get(j);
						String time = dateToString(alarmDataProjection.getTime());
						if (alarmDataProjection.getRuleType() == AlarmRuleTypeEnum.ONE.getValue()) {
							listTime.add(time); // 报警情况
						} else if (alarmDataProjection.getRuleType() == AlarmRuleTypeEnum.TWO.getValue()) {
							listTime.add(time);  // 预警情况
						} else if (alarmDataProjection.getRuleType() == AlarmRuleTypeEnum.THREE.getValue()) {
							listTime.add(time); // 异常情况
						}
						List<Value> valueList = alarmDataProjection.getValue();
						if (valueList != null && valueList.size() > 0) {
							int value = 0;
							for (Value identifyValue : valueList) {
								if (keyPY.equals(identifyValue.getIdentify())) {
									value = Integer.parseInt(identifyValue.getValue());
								}
							}
							list.add(value);
						}
					}
				}
				String keyName = mapSum.get(keyPY);
				listMap.put(keyName, list);
				listMap.put("时间段", listTime);

			}

			// 每天的数据
			List<Map<String, Object>> dataList = entry.getValue();
			// 合并单元格
			int lastCellNum = identifyArray.length;
			int cellst = i * (lastCellNum + 3);
			int cellend = i * (lastCellNum + 3) + lastCellNum;
			i++;
			// 设置边框样式
			CellStyle borderStyle = workbook.createCellStyle();
			borderStyle.setBorderBottom(BorderStyle.THIN);  // 下边框加粗
			borderStyle.setBorderTop(BorderStyle.THIN);  // 上边框加粗
			borderStyle.setBorderLeft(BorderStyle.THIN);  // 左边框加粗
			borderStyle.setBorderRight(BorderStyle.THIN);  // 右边框加粗
			borderStyle.setAlignment(HorizontalAlignment.CENTER);  // 水平对齐方式为居中
			borderStyle.setVerticalAlignment(VerticalAlignment.CENTER);  // 垂直对齐方式为居中


			sheet.addMergedRegion(new CellRangeAddress(0, 0, cellst, cellend)); // 合并表头一的单元格
			sheet.addMergedRegion(new CellRangeAddress(1, 1, cellst, cellend)); // 合并表头二的单元格  导出时间
			sheet.addMergedRegion(new CellRangeAddress(2, 2, cellst, cellend)); // 合并表头三的单元格  汇总数据
			sheet.addMergedRegion(new CellRangeAddress(3, 3, cellst, cellst + 1)); // 合并表头四的单元格  统计类型

			Row headerRow0 = sheet.getRow(0);
			if (headerRow0 == null) {
				headerRow0 = sheet.createRow(0);
			}
			header0 = StringUtil.format("{}设备数据导出", nameTime);
			headerRow0.createCell(cellst).setCellValue(header0);
			Row dateRow1 = sheet.getRow(1);
			if (dateRow1 == null) {
				dateRow1 = sheet.createRow(1);
			}
			dateRow1.createCell(cellst).setCellValue("导出时间:" + DateUtil.formatDateTime(DateUtil.now()));

			Row dateRow2 = sheet.getRow(2);
			if (dateRow2 == null) {
				dateRow2 = sheet.createRow(2);
			}
			Cell cell2 = dateRow2.createCell(cellst);
			cell2.setCellValue("汇总数据");

			int cellCount = 2 + headArray.length;
			// 对 汇总数据合并的后边的行处理样式 边框加粗
			for (int j = 0; j < cellCount; j++) {
				Cell cell_j = dateRow2.createCell(cellst + j );
				cell_j.setCellStyle(borderStyle);
			}

			Row dateRow3 = sheet.getRow(3);
			if (dateRow3 == null) {
				dateRow3 = sheet.createRow(3);
			}
			Cell cell3 = dateRow3.createCell(cellst);
			cell3.setCellValue("统计类型");
			cell3.setCellStyle(borderStyle);

			// 对 统计类型合并的后边一行处理样式 边框加粗
			Cell cell_1 = dateRow3.createCell(cellst + 1 );
			cell_1.setCellStyle(borderStyle);

			List<Row> rowList = new ArrayList<>();
			for (int top = 0; top < topFourList.size(); top++) {
				int topNum = topFourList.get(top);
				// 合计:1,平均值:2,最大值:3,最小值:4
				String headName = integerStringMap.get(topNum);
				Row dateRowHead = sheet.getRow(top + 4);
				if (dateRowHead == null) {
					dateRowHead = sheet.createRow(top + 4);
				}
				Cell cellHead = dateRowHead.createCell(cellst);
				cellHead.setCellValue(headName);
				cellHead.setCellStyle(borderStyle);
				// 对 合计、平均值、最大值、最小值 合并的后边一行处理样式 边框加粗
				Cell cellHead_1 = dateRowHead.createCell(cellst + 1 );
				cellHead_1.setCellStyle(borderStyle);
				rowList.add(dateRowHead);
				sheet.addMergedRegion(new CellRangeAddress(4 + top , 4 + top, cellst, cellst + 1)); // 合并表头 6的单元格   平均值
			}
			// 设置红色背景样式  异常波动情况
			CellStyle abnormalBackgroundStyle = workbook.createCellStyle();
			abnormalBackgroundStyle.setFillForegroundColor(IndexedColors.YELLOW.getIndex());
			abnormalBackgroundStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
			abnormalBackgroundStyle.setAlignment(HorizontalAlignment.CENTER);
			abnormalBackgroundStyle.setVerticalAlignment(VerticalAlignment.CENTER);

			// 设置蓝色背景样式   报警情况
			CellStyle alarmBackgroundStyle = workbook.createCellStyle();
			alarmBackgroundStyle.setFillForegroundColor(IndexedColors.RED.getIndex());
			alarmBackgroundStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
			alarmBackgroundStyle.setAlignment(HorizontalAlignment.CENTER);
			alarmBackgroundStyle.setVerticalAlignment(VerticalAlignment.CENTER);

			// 设置黄色背景样式   预警情况
			CellStyle warningBackgroundStyle = workbook.createCellStyle();
			warningBackgroundStyle.setFillForegroundColor(IndexedColors.ORANGE.getIndex());
			warningBackgroundStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
			warningBackgroundStyle.setAlignment(HorizontalAlignment.CENTER);
			warningBackgroundStyle.setVerticalAlignment(VerticalAlignment.CENTER);

			int size = topFourList.size();
			// 合计 动态处理 异常情况、报警情况、预警情况 的表格
			if (alarmDataProjectionList != null && alarmDataProjectionList.size() > 0) {
				for (int j = 0; j < alarmDataProjectionList.size(); j++) {
					AlarmData  alarmDataProjection = alarmDataProjectionList.get(j);

					Row dateRowJ = sheet.getRow(4 + size + j);
					if (dateRowJ == null) {
						dateRowJ = sheet.createRow(4 + size + j);
					}
					Cell cellJ = dateRowJ.createCell(cellst);
					if (alarmDataProjection.getRuleType() == AlarmRuleTypeEnum.ONE.getValue()) {
						sheet.addMergedRegion(new CellRangeAddress(4 + size + j, 4 + size + j, cellst, cellst + 1)); // 合并表头 10的单元格   报警情况
						cellJ.setCellValue("报警情况");
						cellJ.setCellStyle(alarmBackgroundStyle);
					} else if (alarmDataProjection.getRuleType() == AlarmRuleTypeEnum.TWO.getValue()) {
						sheet.addMergedRegion(new CellRangeAddress(4 + size + j, 4 + size + j, cellst, cellst + 1)); // 合并表头 11的单元格   预警情况
						cellJ.setCellValue("预警情况");
						cellJ.setCellStyle(warningBackgroundStyle);
					} else if (alarmDataProjection.getRuleType() == AlarmRuleTypeEnum.THREE.getValue()) {
						sheet.addMergedRegion(new CellRangeAddress(4 + size + j, 4 + size + j, cellst, cellst + 1)); // 合并表头 9的单元格   异常波动情况
						cellJ.setCellValue("异常波动情况");
						cellJ.setCellStyle(abnormalBackgroundStyle);
					}
				}
			}

			//  表头二 统计类型、属性、时间段等
			for (int cells = 0; cells < headArray.length; cells++) {
				List<Object> objects = listMap.get(headArray[cells]);
				Cell cell_3 = dateRow3.createCell(cellst + cells + 2); // 统计类型 表头
				if ( "时间段".equals( headArray[cells] )) {
					sheet.setColumnWidth(cellst + cells + 2, 20 * 256);  // 时间段 加宽
				}
				cell_3.setCellValue(headArray[cells]);
				cell_3.setCellStyle(borderStyle);

				// 处理统计的数据
				for (int row = 0; row < rowList.size(); row++) {
					int topNum = topFourList.get(row);
					// 合计:1,平均值:2,最大值:3,最小值:4
					String headName = integerStringMap.get(topNum);
					Row cellsDate = rowList.get(row);
					Cell cell_Dat = cellsDate.createCell(cellst + cells + 2);
					if ("平均值".equals(headName)) {
						cell_Dat.setCellValue(toDateStr(objects.get(row), 1)); // 平均值
					} else {
						cell_Dat.setCellValue(toDateStr(objects.get(row), 0));   // 合计、最大值、最小值
					}
					cell_Dat.setCellStyle(borderStyle);
				}
				// 动态处理 异常情况、报警情况、预警情况
				if (alarmDataProjectionList != null && alarmDataProjectionList.size() > 0) {
					for (int j = 0; j < alarmDataProjectionList.size(); j++) {
						AlarmData alarmDataProjection = alarmDataProjectionList.get(j);
						Row dateRowJ = sheet.getRow(4 + size  + j);
						if (dateRowJ == null) {
							dateRowJ = sheet.createRow(4 + size + j);
						}
						Cell cell_J = dateRowJ.createCell(cellst + cells + 2);
						if (alarmDataProjection.getRuleType() == AlarmRuleTypeEnum.ONE.getValue()) {
							cell_J.setCellValue(toDateStr(objects.get(size + j), 0)); //  报警情况
							cell_J.setCellStyle(alarmBackgroundStyle);
						} else if (alarmDataProjection.getRuleType() == AlarmRuleTypeEnum.TWO.getValue()) {
							cell_J.setCellValue(toDateStr(objects.get(size + j), 0)); //   预警情况
							cell_J.setCellStyle(warningBackgroundStyle);
						} else if (alarmDataProjection.getRuleType() == AlarmRuleTypeEnum.THREE.getValue()) {
							cell_J.setCellValue(toDateStr(objects.get(size + j), 0));  //    异常波动情况
							cell_J.setCellStyle(abnormalBackgroundStyle);
						}
					}
				}
			}

			int addNum = alarmDataProjectionList == null ? 0 : alarmDataProjectionList.size();
			// 写入数据
			int rowNum = 8 + size  + addNum;
			if (dataList == null || dataList.size() == 0) {
				// 创建表头三
				Row headerRow = sheet.getRow(7  +  size + addNum);
				if (headerRow == null) {
					headerRow = sheet.createRow(7  +  size + addNum);
				}
				Row row = sheet.getRow(rowNum);
				if (row == null) {
					row = sheet.createRow(rowNum);
				}
				for (int c = 0; c < identifyKey.length; c++) {
					String headName = identifyKey[c];
					Cell headerCell2 = headerRow.createCell(cellst + c);
					String cellValue = mapHead.get(headName);
					headerCell2.setCellValue(cellValue);
					// 设置列宽度为默认值的两倍
					if ("日期".equals(headName) || "设备".equals(headName)) {
						sheet.setColumnWidth(cellst + c, 20 * 256);
					}
				}
			} else {
				for (Map<String, Object> data : dataList) {
					// 创建表头三
					Row headerRow = sheet.getRow(7  +  size  + addNum);
					if (headerRow == null) {
						headerRow = sheet.createRow(7  +  size   + addNum);
					}
					Row row = sheet.getRow(rowNum);
					if (row == null) {
						row = sheet.createRow(rowNum);
					}
					for (int c = 0; c < identifyKey.length; c++) {
						String headName = identifyKey[c];
						Cell headerCell2 = headerRow.createCell(cellst + c);
						String cellValue = mapHead.get(headName);
						headerCell2.setCellValue(cellValue);
						row.createCell(cellst + c).setCellValue(data.get(headName) == null ? "0" : data.get(headName).toString());
						// 设置列宽度为默认值的两倍
						if ("日期".equals(headName) || "设备".equals(headName)) {
							sheet.setColumnWidth(cellst + c, 20 * 256);
						}
					}
					rowNum++;
				}
			}
		}

		// 写入到文件
		try (FileOutputStream fileOut = new FileOutputStream("C:\\Users\\xu\\Desktop\\表格\\daily_data333333.xlsx")) {
			workbook.write(fileOut);
		} catch (IOException e) {
			e.printStackTrace();
		}
		// 关闭工作簿
		try {
			workbook.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
		// 记录结束执行此方法的时间
		long endExecutionTime = System.currentTimeMillis();
		double executionTime = (endExecutionTime - startExecutionTime) / 1000.0;
		System.out.println("================= 共执行了 " + executionTime +" 秒============");
	}



	// 处理统计和前端传值的对应 前端写死的
	public static Map<Integer,String> toMap(){
		Map<Integer,String>  map = new HashMap<>();
		map.put( 1, "合计");
		map.put( 2, "平均值");
		map.put( 3, "最大值");
		map.put( 4, "最小值");
		return map;
	}


	// 判断字符串是否是整数或浮点数
	private static boolean isNumber(String str) {
		if (str == null || str.isEmpty()) {
			return false;
		}
		// 正则表达式匹配整数或浮点数
		String numberRegex = "^-?\\d+(\\.\\d+)?$";
		return Pattern.matches(numberRegex, str);
	}


	private static String toDateStr(Object dateStr, int type) {
		if (dateStr instanceof Integer) {
			if (dateStr == null || MIN_NUM == Integer.parseInt(dateStr.toString()) || MAX_NUM == Integer.parseInt(dateStr.toString()) || 0 == Integer.parseInt(dateStr.toString())) {
				return "/";
			}
		} else if (dateStr instanceof Double) {
			if (dateStr == null || dateStr == "" || 0 == Double.valueOf(dateStr.toString())) {
				return "/";
			}
		} else {
			if (dateStr == null || dateStr == "") {
				return "/";
			}
		}
		return String.valueOf(dateStr);
	}

 
}

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

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

相关文章

超级会员卡积分收银小程序源码系统,在线充值+商家核销+在线下单 附带源代码+搭建部署教程

系统概述 在当今数字化快速发展的时代&#xff0c;移动支付已经成为人们生活中不可或缺的一部分。为了满足商家和消费者对于便捷、高效支付体验的需求&#xff0c;超级会员卡积分收银小程序源码系统应运而生。本文将深入介绍该源码系统的开发背景及其特色功能&#xff0c;附带…

【Linux环境下Hadoop部署】—报错“Unit ntpd.service could not be found.“

项目场景&#xff1a; 执行 “systemctl status ntpd” 命令。 问题描述 报错&#xff1a;Unit ntpd.service could not be found. 原因分析&#xff1a; 没有安装ntp 解决方案&#xff1a; 执行 “yum install ntp” 命令&#xff0c;再次执行 “systemctl status ntpd” 命令…

python 【包含数据预处理】基于词频生成词云图

基于词频生成词云图 背景目的 有一篇中文文章&#xff0c;或者一本小说。想要根据词频来生成词云图。 为什么中文需要分词 中文分词是理解和处理中文文本的关键步骤&#xff0c;它直接影响到后续的文本分析和信息提取的准确性和有效性。 无明显单词分隔&#xff1a;中文文本不…

HTML5基本语法

文章目录 HTML5基本语法一、基础标签1、分级标题2、段标签3、换行及水平线标签4、文本格式标签 二、图片标签1、格式2、属性介绍 三、音频标签1、格式2、属性介绍 四、视频标签1、格式2、属性介绍 五、链接标签1、格式2、显示特点3、属性介绍4、补充&#xff08;空链接&#xf…

【C++】Template模板

创作不易&#xff0c;本篇文章如果帮助到了你&#xff0c;还请点赞 关注支持一下♡>&#x16966;<)!! 主页专栏有更多知识&#xff0c;如有疑问欢迎大家指正讨论&#xff0c;共同进步&#xff01; &#x1f525;c系列专栏&#xff1a;C/C零基础到精通 &#x1f525; 给大…

Boom3D软件下载-Boom3D音效增强工具下载附加详细安装步骤

Boom3D音效增强工具是一款便捷的为用户们进行音频处理和编辑的软件&#xff0c;支持用户们轻松的进行音频的使用&#xff0c;支持超多的音频格式让你可以轻松的进行使用&#xff0c;Boom3D音效增强工具拥有多种音频特效的功能&#xff0c;让你可以在Boom3D音效增强工具轻松的进…

Python办公自动化实例--照片挑选

实例背景 学院举行毕业晚会&#xff0c;要制作照片墙&#xff0c;让学生自己上传一直没有多少回应&#xff0c;上传的没有几张&#xff08;学院的号召力是真的拉&#xff09;&#xff0c;需要从整个学校的学生证件照中挑选出外面学院的同学&#xff0c;于是这个“艰巨”的任务…

Avalonia:一个.NET跨平台UI框架

概述 Avalonia是一个强大的框架&#xff0c;使开发人员能够使用. NET创建跨平台应用程序。它使用自己的渲染引擎来绘制UI控件&#xff0c;确保在各种平台上保持一致的外观和行为&#xff0c;包括Windows&#xff0c;macOS&#xff0c;Linux&#xff0c;Android&#xff0c;iOS…

轻轻松松上手的LangChain学习说明书

本文为笔者学习LangChain时对官方文档以及一系列资料进行一些总结&#xff5e;覆盖对Langchain的核心六大模块的理解与核心使用方法&#xff0c;全文篇幅较长&#xff0c;共计50000字&#xff0c;可先码住辅助用于学习Langchain。 一、Langchain是什么&#xff1f; 如今各类AI…

大模型面试指南:掌握关键技术与策略,成功应对面试挑战

随着人工智能技术的快速发展&#xff0c;大型预训练模型在自然语言处理、计算机视觉、语音识别等领域取得了显著成果。这些模型通过在海量数据上进行预训练&#xff0c;能够捕捉到丰富的特征信息&#xff0c;为各种下游任务提供强大的支持。在求职面试中&#xff0c;掌握大模型…

OpenAI新开放了这些好用的API功能(附AI学习指南)

OpenAI近期召开了开发者大会&#xff0c;同时也发布和开放了一些新的功能特性&#xff0c;比如新版本GPT-4 Turbo&#xff0c;支持128k上下文&#xff0c;知识截止更新到2023年4月&#xff0c;视觉能力、DALLE3&#xff0c;文字转语音TTS等等全都对API开放&#xff0c;GPTs商店…

【Unity拖拽物体】实现对点中的3D物体进行拖拽的功能

场景结构&#xff0c;两个普通模型 第一种 脚本所挂载的物体才可以被拖拽 【PC鼠标版本】 using UnityEngine;// 这个脚本实现了&#xff0c;本脚本所在的游戏物体能够被拖拽 public class DragObjectT : MonoBehaviour {private Vector3 screenPoint; // 存储物体在屏幕上的位…

多行文本的文字展示全部和收起功能

组件代码&#xff1a; <template><!-- 外层容器&#xff0c;使用相对定位 --><div class"relative"><!-- 文本容器&#xff0c;根据 expanded 状态决定是否应用 line-clamp-4 类 --><div :class"{ line-clamp-4: !expanded }"…

没有二十年功力,写不出这一行代码!

这篇文章要从一个奇怪的注释说起&#xff0c;就是下面这张图&#xff1a; 我们可以不用管具体的代码逻辑&#xff0c;只是单单看这个 for 循环。 在循环里面&#xff0c;专门有个变量 j&#xff0c;来记录当前循环次数。 第一次循环以及往后每 1000 次循环之后&#xff0c;进…

阻力支撑相对强度(RSRS)选股系列报告之三

https://download.csdn.net/download/SuiZuoZhuLiu/89447699?spm1001.2014.3001.5503https://download.csdn.net/download/SuiZuoZhuLiu/89447699?spm1001.2014.3001.5503

电脑密码忘记了怎么办?3步教你找回密码!

在日常使用电脑的过程中&#xff0c;忘记密码是一件令人头痛的事情。如果您不慎忘记了电脑的登录密码&#xff0c;无法进入系统进行工作和娱乐&#xff0c;这时需要找到合适的解决方案来恢复对电脑的访问权限。那么电脑密码忘记了怎么办呢&#xff1f;本文将介绍三种解决方法&a…

Uncaught TypeError: Cannot read properties of null (reading ‘isCE‘)

问题描述 使用 view-ui-plus 加 vue3 开发项目&#xff0c;本地启动项目正常&#xff0c;但其他人将代码拉下来&#xff0c;启动项目时报错 Uncaught TypeError: Cannot read properties of null (reading isCE)&#xff1a; 原因分析&#xff1a; 尝试将 mode_nodules 文件删…

C++实时检测耳机的插入与拔出(附源码)

目录 1、实现继承于IMMNotificationClient接口类的CMMNotificationClient类,实时感知音频设备变化的通知事件 2、在CMMNotificationClient的构造函数中初始化多媒体设备COM接口,设置回调类指针 3、通过获取音频设备接口下外设的KSJACK_DESCRIPTION 信息判断耳机的连接状态…

ArcGIS arcpy代码工具——关于工具使用的软件环境说明

系列文章目录 ArcGIS arcpy代码工具——批量对MXD文件的页面布局设置修改 ArcGIS arcpy代码工具——数据驱动工具批量导出MXD文档并同步导出图片 ArcGIS arcpy代码工具——将要素属性表字段及要素截图插入word模板 ArcGIS arcpy代码工具——定制属性表字段输出表格 ArcGIS arc…

鸿蒙实现自定义Tabbar样式,显示数字红点提示

前言&#xff1a; DevEco Studio版本&#xff1a;4.0.0.600 Tabs的链接参考&#xff1a;OpenHarmony Tabs TabContent的链接参考&#xff1a;OpenHarmony TabContent 通过查看链接参考我们知道可以通过TabContent的tabBar来实现自定义TabBar样式&#xff08;CustomBuilder&…