Vue项目使用Univer Sheets

发布于:2025-07-25 ⋅ 阅读:(19) ⋅ 点赞:(0)

Univer Excel主页链接:安装步骤

1. 安装

使用预设模式的包管理器安装
- 预设模式:可以理解为开包即用的模式,省去很多配置,当然自由度不如插件模式

pnpm add @univerjs/presets @univerjs/preset-sheets-core

2. 前端代码

<template>
  <div ref="container" style="height: 70vh"></div>
</template>

<script lang="ts" setup>
  import { onMounted, onBeforeUnmount, ref, watch, reactive } from 'vue';
  import { createUniver, FUniver, LocaleType, merge, Univer } from '@univerjs/presets';
  import { UniverSheetsCorePreset } from '@univerjs/preset-sheets-core';
  import UniverPresetSheetsCoreZhCN from '@univerjs/preset-sheets-core/lib/locales/zh-CN';

  import '@univerjs/preset-sheets-core/lib/index.css';

  const props = defineProps({
    excelData: { // 父组件传递的excel数据
      type: Object,
      default: () => ({}),
    },
    isEdit: { // 是否是编辑模式
      type: Boolean,
      default: false,
    },
  });
  const container = ref<HTMLElement | null>(null);

  let univerInstance: Univer | null = null;
  let univerAPIInstance: FUniver | null = null;

  onMounted(() => {
    const { univer, univerAPI } = createUniver({
      locale: LocaleType.ZH_CN,
      locales: {
        [LocaleType.ZH_CN]: merge({}, UniverPresetSheetsCoreZhCN),
      },
      presets: [
        UniverSheetsCorePreset({
          container: container.value as HTMLElement,
          // 设置excel上方的menu菜单
          menu: {
            // 隐藏加粗按钮
            // 'sheet.command.set-range-bold': {
            //   hidden: true,
            // },
          },
        }),
      ],
    });

    // 监听attachments变化,重新加载数据
    watch(
      () => props.excelData,
      (newAttachments) => {
        if (newAttachments) {
          // univerAPI.createWorkbook(newAttachments);
          univerAPI.createWorkbook(workbook1);  // 开始可以使用数据workbook0或者workboot1来测试是否正常加载数据
          const permission = univerAPI.getPermission();
          const workbookEditablePermission = permission.permissionPointsDefinition.WorkbookEditablePermission;
          // unitId 是工作簿的 id, WorkbookEditablePermission 是权限点位, false 表示该权限不可使用
          // 权限:如果是查看则不可编辑,由props.isEdit确定是编辑或查看模式
          permission.setWorkbookPermissionPoint(newAttachments.id, workbookEditablePermission, props.isEdit);
        }
      }
    );

    univerInstance = univer;
    univerAPIInstance = univerAPI;
  });
  
  // 添加一个方法来获取当前表格数据
  const getExcelData = () => {
    if (!univerAPIInstance) return null;

    // 获取当前活动的工作簿
    const workbook = univerAPIInstance.getActiveWorkbook();

    if (!workbook) return null;

    // 获取工作簿数据(JSON格式)
    const workbookData = workbook.getSnapshot();

    return workbookData;
  };
  // 如果此组件是在modal中展示,则用户在点击modal的确认按钮后,会调用此方法获取当前表格数据
  defineExpose({
    getExcelData,
  });
  
  
  // 最简单的可显示的数据,最重要的地方是sheelt1和里面Id的字符串必须一致
  const workbook0 = reactive({
    id: '1',
    sheets: {
      sheelt1: {
        id: 'sheelt1',
        name: '工作簿的名字1',
        cellData: {
          // 第一行
          0: {
            // 第一列
            0: { v: 'A1' },
            // 第二列
            1: { v: 'B1' },
          },
          // 第二行
          1: {
            // 第一列
            0: { v: 'A2' },
            // 第二列
            1: { v: 'B2' },
          },
        },
      },
    },
  });
  
  // 官网提供的数据,比较复杂,包含了很多样式
  //  const workbook1 = reactive({
  //     id: 'gyI0JO',
  //     sheetOrder: ['RSfWjJFv4opmE1JaiRj80'],
  //     name: '',
  //     appVersion: '0.5.0',
  //     locale: 'zhCN',
  //     styles: {},
  //     sheets: {
  //       RSfWjJFv4opmE1JaiRj80: {
  //         id: 'RSfWjJFv4opmE1JaiRj80',
  //         name: '测试',
  //         tabColor: '',
  //         hidden: 0,
  //         rowCount: 30,
  //         columnCount: 10,
  //         zoomRatio: 1,
  //         freeze: {
  //           startRow: -1,
  //           startColumn: -1,
  //           ySplit: 0,
  //           xSplit: 0,
  //         },
  //         scrollTop: 0,
  //         scrollLeft: 0,
  //         defaultColumnWidth: 73,
  //         defaultRowHeight: 23,
  //         mergeData: [],
  //         cellData: {
  //           // 第一行
  //           0: {
  //             // 第一列
  //             0: { v: 'A1' },
  //             // 第二列
  //             1: { v: 'B1' },
  //           },
  //           // 第二行
  //           1: {
  //             // 第一列
  //             0: { v: 'A2' },
  //             // 第二列
  //             1: { v: 'B2' },
  //           },
  //         },
  //         rowData: {},
  //         columnData: {
  //           0: {
  //             w: 125,
  //             hd: 0,
  //           },
  //           1: {
  //             w: 125,
  //             hd: 0,
  //           },
  //           2: {
  //             w: 125,
  //             hd: 0,
  //           },
  //           3: {
  //             w: 125,
  //             hd: 0,
  //           },
  //           4: {
  //             w: 125,
  //             hd: 0,
  //           },
  //           5: {
  //             w: 125,
  //             hd: 0,
  //           },
  //           6: {
  //             w: 125,
  //             hd: 0,
  //           },
  //           7: {
  //             w: 125,
  //             hd: 0,
  //           },
  //           8: {
  //             w: 125,
  //             hd: 0,
  //           },
  //           9: {
  //             w: 125,
  //             hd: 0,
  //           },
  //         },
  //         showGridlines: 1,
  //         rowHeader: {
  //           width: 46,
  //           hidden: 0,
  //         },
  //         columnHeader: {
  //           height: 20,
  //           hidden: 0,
  //         },
  //         selections: ['A1'],
  //         rightToLeft: 0,
  //       },
  //     },
  //     resources: [
  //       {
  //         name: 'SHEET_DEFINED_NAME_PLUGIN',
  //         data: '',
  //       },
  //     ],
  //   });
  onBeforeUnmount(() => {
    univerInstance?.dispose();
    univerAPIInstance?.dispose();
    univerInstance = null;
    univerAPIInstance = null;
  });
</script>

特别需要注意的地方

在这里插入图片描述

单元格样式

单元格样式链接

工具栏菜单项

工具栏菜单项链接

3. 文件的保存

官网收费项目
前端发送数据给后端,后端通过判断各种样式将数据使用poi保存成文件,在此只实现了简单的样式保存,供参考

文件实体类

class ExcelVo {
	private String id;
    private String fileName;
    private Map<String, Object> jsonExcel;
}

保存和查看文件的方法

import java.io.*;
import java.lang.reflect.Method;
import java.util.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.regex.Pattern;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.hssf.usermodel.HSSFFont;
import org.apache.poi.hssf.usermodel.HSSFPalette;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.hssf.util.HSSFColor;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.xssf.usermodel.XSSFCellStyle;
import org.apache.poi.xssf.usermodel.XSSFColor;
import org.apache.poi.xssf.usermodel.XSSFFont;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.web.multipart.MultipartFile;

/**
 * @Description: Excel文件操作服务
 * @Date:   2025-06-15
 * @Version: V1.0
 * 
 */
@Slf4j
@Service
public class ExcelSavaAndReadServiceImpl {
    // 读取yml配置文件中的路径
    @Value("${jeecg.path.upload:}")
    private String uploadPath;

    // save&updata数据
    public void updateExcelJson(ExcelVo fileData) {
        if(StringUtils.isBlank(fileData.getId())) {
            // 此处应该使用自定义错误
            throw new Exception("id为空");
        }
        String fileName = fileData.getFileName();
        Map<String, Object> univerData = fileData.getJsonExcel();
       // log.info("univerData==:" + univerData);
        if (StringUtils.isBlank(fileName)) {
            throw new Exception("文件名称为空");
        }
        Path filePath = Paths.get(uploadPath, fileName);
        Workbook workbook = null;

        try {
            if (Files.exists(filePath)) {
                // 文件存在:打开并更新
                try (InputStream is = new FileInputStream(filePath.toFile())) {
                    workbook = WorkbookFactory.create(is);
                }
                updateWorkbookFromUniver(workbook, univerData, false); // 更新模式
            } else {
                // 文件不存在:新建完整工作簿
                workbook = createWorkbook(fileName); // 根据扩展名创建空工作簿
                updateWorkbookFromUniver(workbook, univerData, true); // 完整创建模式
            }

            // 保存工作簿
            try (FileOutputStream fos = new FileOutputStream(filePath.toFile())) {
                workbook.write(fos);
            }
            log.info("Excel文件{}成功: {}", Files.exists(filePath) ? "更新" : "创建", fileName);
        } catch (Exception e) {
            log.error("处理Excel文件失败: {}", fileName, e);
            throw new RuntimeException("Excel操作失败: " + e.getMessage(), e);
        } finally {
            if (workbook != null) {
                try {
                    workbook.close();
                } catch (IOException e) {
                    log.error("关闭工作簿失败", e);
                }
            }
        }
    }
    // 读取excel数据
    public Map<String, Object> getUniverSheetData(String fileName) {
        //获取文件
        Path filePath = Paths.get(uploadPath + "/" + fileName);
        if (!Files.exists(filePath)) {
            throw new RuntimeException("Excel文件不存在");
        }
        try (FileInputStream fis = new FileInputStream(filePath.toFile());
             //从输入流 fis 中创建一个 Excel 工作簿对象 Workbook,用于后续读取和解析 Excel 文件内容。
             //根据文件输入流自动识别 Excel 格式(如 .xls 或 .xlsx),并创建对应的 HSSFWorkbook 或 XSSFWorkbook 实例
             Workbook workbook = WorkbookFactory.create(fis)) {
            //解析Excel文件 转成 Univer识别格式
            return convertToUniverFormat(workbook);
        } catch (Exception e) {
            throw new RuntimeException("Excel解析失败", e);
        }
    }

    private Map<String, Object> convertToUniverFormat(Workbook workbook) {
        Map<String, Object> univerData = new LinkedHashMap<>();
        univerData.put("id", "workbook-" + UUID.randomUUID());
        Map<String, Map<String, Object>> sheetsMap = new LinkedHashMap<>();

        HSSFPalette hssfPalette = null;
        if (workbook instanceof HSSFWorkbook) {
            hssfPalette = ((HSSFWorkbook) workbook).getCustomPalette();
        }

        for (int i = 0; i < workbook.getNumberOfSheets(); i++) {
            Sheet sheet = workbook.getSheetAt(i);
            String sheetId = "sheet_" + i;

            Map<String, Object> sheetData = new LinkedHashMap<>();
            sheetData.put("id", sheetId);
            sheetData.put("name", sheet.getSheetName());

            // 合并单元格处理
            List<Map<String, Integer>> mergeData = new ArrayList<>();
            for (int j = 0; j < sheet.getNumMergedRegions(); j++) {
                CellRangeAddress region = sheet.getMergedRegion(j);
                Map<String, Integer> mergeInfo = new HashMap<>();
                mergeInfo.put("startRow", region.getFirstRow());
                mergeInfo.put("startColumn", region.getFirstColumn());
                mergeInfo.put("endRow", region.getLastRow());
                mergeInfo.put("endColumn", region.getLastColumn());
                mergeData.add(mergeInfo);
            }
            sheetData.put("mergeData", mergeData);

            Map<String, Map<String, Map<String, Object>>> cellData = new HashMap<>();
            Map<String, Map<String, Object>> cellStyleMap = new HashMap<>();
            List<CellRangeAddress> mergedRegions = sheet.getMergedRegions();

            // 公式共享ID生成器
            Map<String, String> formulaIdMap = new HashMap<>();

            for (int r = 0; r <= sheet.getLastRowNum(); r++) {
                Row row = sheet.getRow(r);
                if (row == null) continue;

                Map<String, Map<String, Object>> rowData = new HashMap<>();

                for (int c = 0; c < row.getLastCellNum(); c++) {
                    Cell cell = row.getCell(c, Row.MissingCellPolicy.CREATE_NULL_AS_BLANK);
                    Map<String, Object> cellInfo = new HashMap<>();

                    // 处理单元格值
                    switch (cell.getCellType()) {
                        case STRING:
                            cellInfo.put("v", cell.getStringCellValue());
                            cellInfo.put("t", "text");
                            break;
                        case NUMERIC:
                            if (DateUtil.isCellDateFormatted(cell)) {
                                cellInfo.put("v", cell.getDateCellValue().getTime());
                                cellInfo.put("t", "date");
                            } else {
                                cellInfo.put("v", cell.getNumericCellValue());
                                cellInfo.put("t", "number");
                            }
                            break;
                        case BOOLEAN:
                            cellInfo.put("v", cell.getBooleanCellValue());
                            cellInfo.put("t", "boolean");
                            break;
                        case FORMULA:
                            // 添加=前缀
                            String formula = "=" + cell.getCellFormula();
                            cellInfo.put("f", formula);
                            cellInfo.put("t", "formula");

                            // 生成公式共享ID
                            String formulaKey = r + ":" + c + ":" + formula;
                            if (!formulaIdMap.containsKey(formulaKey)) {
                                formulaIdMap.put(formulaKey, "fid_" + UUID.randomUUID().toString().substring(0, 8));
                            }
                            cellInfo.put("si", formulaIdMap.get(formulaKey));

                            try {
                                switch (cell.getCachedFormulaResultType()) {
                                    case NUMERIC:
                                        cellInfo.put("v", cell.getNumericCellValue());
                                        break;
                                    case STRING:
                                        cellInfo.put("v", cell.getStringCellValue());
                                        break;
                                    case BOOLEAN:
                                        cellInfo.put("v", cell.getBooleanCellValue());
                                        break;
                                    default:
                                        cellInfo.put("v", "");
                                }
                            } catch (Exception e) {
                                cellInfo.put("v", "");
                            }
                            break;
                        default:
                            cellInfo.put("v", "");
                            cellInfo.put("t", "text");
                    }

                    // ============== 样式重构 ==============
                    CellStyle style = cell.getCellStyle();
                    Map<String, Object> styleMap = new HashMap<>();
                    Font font = workbook.getFontAt(style.getFontIndex());
                    String fontColor = getFontColor(workbook, font);

                    // 1. 背景色
                    if (style.getFillPattern() != FillPatternType.NO_FILL) {
                        String bgColorHex = getColorHex(style.getFillForegroundColor(), hssfPalette);
                        if (bgColorHex != null) {
                            Map<String, String> bgColorMap = new HashMap<>();
                            bgColorMap.put("rgb", bgColorHex);
                            styleMap.put("bg", bgColorMap);
                        }
                    }

                    // 2. 字体样式 - 使用文档要求的属性名
                    //Map<String, Object> fontMap = new HashMap<>();
                    styleMap.put("ff", font.getFontName()); // 字体名称
                    styleMap.put("fs", font.getFontHeightInPoints()); // 字体大小
                    styleMap.put("bl", font.getBold() ? 1 : 0); // 加粗
                    styleMap.put("it", font.getItalic() ? 1 : 0); // 斜体

                    // 3. 下划线样式 - 符合Univer格式
                    Map<String, Object> ulMap = new HashMap<>();
                    ulMap.put("s", (font.getUnderline() != Font.U_NONE) ? 1 : 0); // 是否显示
                    ulMap.put("c", 0); // 颜色不跟随字体
                    ulMap.put("t", 0); // 下划线类型
                    if (fontColor != null) {
                        Map<String, String> ulColorMap = new HashMap<>();
                        ulColorMap.put("rgb", fontColor);
                        ulMap.put("cl", ulColorMap);
                    }
                    styleMap.put("ul", ulMap);

                    // 4. 删除线样式 - 符合Univer格式
                    Map<String, Object> stMap = new HashMap<>();
                    stMap.put("s", font.getStrikeout() ? 1 : 0); // 是否显示
                    stMap.put("c", 0); // 颜色不跟随字体
                    stMap.put("t", 0); // 删除线类型
                    if (fontColor != null) {
                        Map<String, String> stColorMap = new HashMap<>();
                        stColorMap.put("rgb", fontColor);
                        stMap.put("cl", stColorMap);
                    }
                    styleMap.put("st", stMap);

                    // 5. 上标/下标
                    short typeOffset = font.getTypeOffset();
                    if (typeOffset == Font.SS_SUPER) {
                        styleMap.put("va", 3); // 上标
                    } else if (typeOffset == Font.SS_SUB) {
                        styleMap.put("va", 2); // 下标
                    } else {
                        styleMap.put("va", 1); // 正常
                    }

                    //styleMap.put("font", fontMap);

                    // 6. 文字颜色
                    if (fontColor != null) {
                        Map<String, String> textColorMap = new HashMap<>();
                        textColorMap.put("rgb", fontColor);
                        styleMap.put("cl", textColorMap);
                    }

                    // 7. 文字旋转
                    Map<String, Object> trMap = new HashMap<>();
                    trMap.put("a", style.getRotation()); // 旋转角度
                    trMap.put("v", 0); // 0=水平,1=垂直
                    styleMap.put("tr", trMap);

                    // 8. 对齐方式
                    HorizontalAlignment alignment = style.getAlignment();
                    VerticalAlignment verticalAlignment = style.getVerticalAlignment();

                    if (alignment != null) {
                        switch (alignment) {
                            case LEFT:
                                styleMap.put("ht", 1);
                                break;
                            case CENTER:
                            case CENTER_SELECTION:
                                styleMap.put("ht", 2);
                                break;
                            case RIGHT:
                                styleMap.put("ht", 3);
                                break;
                            default:
                                styleMap.put("ht", 1);
                        }
                    } else {
                        styleMap.put("ht", 1);
                    }

                    if (verticalAlignment != null) {
                        switch (verticalAlignment) {
                            case TOP:
                                styleMap.put("vt", 1);
                                break;
                            case CENTER:
                                styleMap.put("vt", 2);
                                break;
                            case BOTTOM:
                                styleMap.put("vt", 3);
                                break;
                            default:
                                styleMap.put("vt", 2);
                        }
                    } else {
                        styleMap.put("vt", 2);
                    }

                    // 9. 文本截断方式
                    styleMap.put("tb", style.getWrapText() ? 3 : 1);

                    // 10. 内边距(示例值)
                    Map<String, Integer> pdMap = new HashMap<>();
                    pdMap.put("t", 2); // 上
                    pdMap.put("b", 2); // 下
                    pdMap.put("l", 3); // 左
                    pdMap.put("r", 3); // 右
                    styleMap.put("pd", pdMap);

                    // 11. 边框样式
                    Map<String, Map<String, Object>> borderMap = new HashMap<>();
                    addBorderStyle(borderMap, "t", style.getBorderTop(), style.getTopBorderColor(), hssfPalette);
                    addBorderStyle(borderMap, "r", style.getBorderRight(), style.getRightBorderColor(), hssfPalette);
                    addBorderStyle(borderMap, "b", style.getBorderBottom(), style.getBottomBorderColor(), hssfPalette);
                    addBorderStyle(borderMap, "l", style.getBorderLeft(), style.getLeftBorderColor(), hssfPalette);

                    if (!borderMap.isEmpty()) {
                        styleMap.put("bd", borderMap);
                    }

                    cellInfo.put("s", styleMap);
                    // ============== 样式结束 ==============

                    cellStyleMap.put(r + ":" + c, styleMap);
                    rowData.put(String.valueOf(c), cellInfo);
                }

                if (!rowData.isEmpty()) {
                    cellData.put(String.valueOf(r), rowData);
                }
            }

            // 处理合并单元格样式
            for (CellRangeAddress region : mergedRegions) {
                int firstRow = region.getFirstRow();
                int firstCol = region.getFirstColumn();
                String sourceKey = firstRow + ":" + firstCol;
                Map<String, Object> sourceStyle = cellStyleMap.get(sourceKey);

                if (sourceStyle != null) {
                    for (int r = region.getFirstRow(); r <= region.getLastRow(); r++) {
                        for (int c = region.getFirstColumn(); c <= region.getLastColumn(); c++) {
                            if (r == firstRow && c == firstCol) continue;

                            String rowKey = String.valueOf(r);
                            String colKey = String.valueOf(c);

                            if (cellData.containsKey(rowKey)) {
                                Map<String, Map<String, Object>> rowData = cellData.get(rowKey);
                                if (rowData.containsKey(colKey)) {
                                    rowData.get(colKey).put("s", sourceStyle);
                                } else {
                                    Map<String, Object> newCell = new HashMap<>();
                                    newCell.put("v", "");
                                    newCell.put("t", "text");
                                    newCell.put("s", sourceStyle);
                                    rowData.put(colKey, newCell);
                                }
                            } else {
                                Map<String, Map<String, Object>> newRow = new HashMap<>();
                                Map<String, Object> newCell = new HashMap<>();
                                newCell.put("v", "");
                                newCell.put("t", "text");
                                newCell.put("s", sourceStyle);
                                newRow.put(colKey, newCell);
                                cellData.put(rowKey, newRow);
                            }
                        }
                    }
                }
            }

            sheetData.put("cellData", cellData);
            sheetsMap.put(sheetId, sheetData);
        }

        univerData.put("sheets", sheetsMap);
        return univerData;
    }

    // 添加边框样式(符合Univer格式)
    private void addBorderStyle(Map<String, Map<String, Object>> borderMap, String position,
                                BorderStyle borderStyle, short colorIndex, HSSFPalette hssfPalette) {
        if (borderStyle == BorderStyle.NONE) return;

        Map<String, Object> borderStyleMap = new HashMap<>();
        borderStyleMap.put("s", convertBorderStyle(borderStyle));

        String colorHex = getColorHex(colorIndex, hssfPalette);
        if (colorHex == null) colorHex = "#000000";

        Map<String, String> colorMap = new HashMap<>();
        colorMap.put("rgb", colorHex);
        borderStyleMap.put("cl", colorMap);

        borderMap.put(position, borderStyleMap);
    }

    // 边框样式转换方法(返回数字枚举)
    private int convertBorderStyle(BorderStyle borderStyle) {
        if (borderStyle == null) return 0;

        switch (borderStyle) {
            case NONE: return 0;       // 无边框
            case THIN: return 1;       // 细实线
            case MEDIUM: return 2;     // 中等实线
            case DASHED: return 3;     // 虚线
            case DOTTED: return 4;     // 点线
            case THICK: return 5;      // 粗实线
            case DOUBLE: return 6;     // 双实线
            case HAIR: return 7;       // 极细线
            case MEDIUM_DASHED: return 8;          // 中等虚线
            case DASH_DOT: return 9;               // 点划线
            case MEDIUM_DASH_DOT: return 10;       // 中等点划线
            case DASH_DOT_DOT: return 11;          // 点点划线
            case MEDIUM_DASH_DOT_DOT: return 12;   // 中等点点划线
            case SLANTED_DASH_DOT: return 13;      // 斜点划线
            default: return 0;
        }
    }

    // 获取字体颜色(支持XSSF和HSSF)
    private String getFontColor(Workbook workbook, Font font) {
        // 处理XSSF格式(.xlsx)
        if (workbook instanceof XSSFWorkbook) {
            try {
                Method getXSSFColorMethod = font.getClass().getMethod("getXSSFColor");
                XSSFColor color = (XSSFColor) getXSSFColorMethod.invoke(font);
                if (color != null && color.getRGB() != null) {
                    return String.format("#%02X%02X%02X",
                            color.getRGB()[0] & 0xFF,
                            color.getRGB()[1] & 0xFF,
                            color.getRGB()[2] & 0xFF);
                }
            } catch (Exception e) {
                return "#000000";
            }
        }
        // 处理HSSF格式(.xls)
        else if (workbook instanceof HSSFWorkbook && font instanceof HSSFFont) {
            HSSFFont hssfFont = (HSSFFont) font;
            if (hssfFont.getColor() == HSSFFont.COLOR_NORMAL) {
                return "#000000";
            }
            HSSFColor color = ((HSSFWorkbook) workbook).getCustomPalette().getColor(hssfFont.getColor());
            if (color != null) {
                short[] rgb = color.getTriplet();
                return String.format("#%02X%02X%02X", rgb[0], rgb[1], rgb[2]);
            }
        }
        return "#000000";
    }

    // 统一颜色处理方法
    private String getColorHex(Color color, HSSFPalette hssfPalette) {
        if (color == null) return null;

        if (color instanceof XSSFColor) {
            byte[] rgb = ((XSSFColor) color).getRGB();
            if (rgb != null) {
                return String.format("#%02X%02X%02X", rgb[0] & 0xFF, rgb[1] & 0xFF, rgb[2] & 0xFF);
            }
        }
        else if (color instanceof HSSFColor) {
            HSSFColor hssfColor = (HSSFColor) color;
            short[] rgb = hssfColor.getTriplet();
            if (rgb != null) {
                return String.format("#%02X%02X%02X", rgb[0], rgb[1], rgb[2]);
            }
        }
        return null;
    }

    // 获取颜色值的重载方法(支持索引)
    private String getColorHex(short colorIndex, HSSFPalette hssfPalette) {
        if (hssfPalette == null) return null;

        HSSFColor color = hssfPalette.getColor(colorIndex);
        return (color != null) ?
                String.format("#%02X%02X%02X",
                        color.getTriplet()[0],
                        color.getTriplet()[1],
                        color.getTriplet()[2]) :
                null;
    }



    // 读取已存在的Excel文件
    private Workbook getExistingWorkbook(Path filePath) throws Exception {
        try (InputStream is = new FileInputStream(filePath.toFile())) {
            return WorkbookFactory.create(is);
        }
    }

    // 单元格样式创建方法
    private Workbook createWorkbook(String fileName) throws Exception {
        // 根据文件扩展名创建对应类型的工作簿
        if (fileName.toLowerCase().endsWith(".xlsx")) {
            return new XSSFWorkbook();
        } else if (fileName.toLowerCase().endsWith(".xls")) {
            return new HSSFWorkbook();
        } else {
            throw new IllegalArgumentException("不支持的文件类型: " + fileName);
        }
    }

    @SuppressWarnings("unchecked")
    private void updateWorkbookFromUniver(Workbook workbook, Map<String, Object> univerData, boolean isNewFile) {
        // 获取工作表顺序和所有工作表数据
        List<String> sheetOrder = (List<String>) univerData.get("sheetOrder");
        Map<String, Map<String, Object>> sheets = (Map<String, Map<String, Object>>) univerData.get("sheets");
        Map<String, Map<String, Object>> styles = (Map<String, Map<String, Object>>) univerData.get("styles");

        Map<String, CellStyle> styleCache = new HashMap<>();

        for (String sheetId : sheetOrder) {
            Map<String, Object> sheetData = sheets.get(sheetId);
            if (sheetData == null) continue;

            String sheetName = (String) sheetData.get("name");
            Sheet sheet;

            if (isNewFile) {
                sheet = workbook.createSheet(sheetName);

                // 设置默认行高列宽
                int defaultRowHeight = getInt(sheetData, "defaultRowHeight", 24);
                int defaultColumnWidth = getInt(sheetData, "defaultColumnWidth", 88);
                sheet.setDefaultRowHeightInPoints(defaultRowHeight);
                sheet.setDefaultColumnWidth(defaultColumnWidth);
            } else {
                sheet = workbook.getSheet(sheetName);
                if (sheet == null) {
                    log.warn("工作表不存在: {}, 跳过更新", sheetName);
                    continue;
                }
            }

            // ========= 关键修复1:正确处理合并单元格 =========
            List<Map<String, Integer>> mergeData = (List<Map<String, Integer>>) sheetData.get("mergeData");
            if (mergeData != null) {
                // 移除现有合并区域
                for (int i = sheet.getNumMergedRegions() - 1; i >= 0; i--) {
                    sheet.removeMergedRegion(i);
                }

                // 添加新合并区域
                for (Map<String, Integer> mergeInfo : mergeData) {
                    CellRangeAddress region = new CellRangeAddress(
                            mergeInfo.getOrDefault("startRow", 0),
                            mergeInfo.getOrDefault("endRow", 0),
                            mergeInfo.getOrDefault("startColumn", 0),
                            mergeInfo.getOrDefault("endColumn", 0)
                    );
                    sheet.addMergedRegion(region);
                }
            }

            // 处理单元格数据
            Map<String, Map<String, Map<String, Object>>> cellData =
                    (Map<String, Map<String, Map<String, Object>>>) sheetData.get("cellData");

            if (cellData != null) {
                for (Map.Entry<String, Map<String, Map<String, Object>>> rowEntry : cellData.entrySet()) {
                    int rowIndex = safeParseInt(rowEntry.getKey(), -1);
                    if (rowIndex < 0) continue;

                    Row row = sheet.getRow(rowIndex);
                    if (row == null) {
                        row = sheet.createRow(rowIndex);
                    }

                    Map<String, Map<String, Object>> rowData = rowEntry.getValue();
                    for (Map.Entry<String, Map<String, Object>> cellEntry : rowData.entrySet()) {
                        int colIndex = safeParseInt(cellEntry.getKey(), -1);
                        if (colIndex < 0) continue;

                        Cell cell = row.getCell(colIndex);
                        if (cell == null) {
                            cell = row.createCell(colIndex);
                        }

                        Map<String, Object> cellInfo = cellEntry.getValue();
                        updateCellFromUniver(workbook, cell, cellInfo, styles, styleCache, isNewFile);
                    }
                }
            }
        }
    }


    private void updateCellFromUniver(Workbook workbook, Cell cell, Map<String, Object> cellInfo,
                                      Map<String, Map<String, Object>> styles,
                                      Map<String, CellStyle> styleCache, boolean isNewFile) {
        // 设置单元格值
        String cellType = String.valueOf(cellInfo.get("t"));
        Object value = cellInfo.get("v");

        switch (cellType) {
            case "1": // 文本
                cell.setCellValue(value != null ? value.toString() : "");
                break;
            case "2": // 数字
                if (value instanceof Number) {
                    cell.setCellValue(((Number) value).doubleValue());
                } else if (value != null) {
                    try {
                        cell.setCellValue(Double.parseDouble(value.toString()));
                    } catch (NumberFormatException e) {
                        cell.setCellValue(value.toString());
                    }
                }
                break;
            case "3": // 布尔值
                if (value instanceof Boolean) {
                    cell.setCellValue((Boolean) value);
                } else if ("true".equalsIgnoreCase(String.valueOf(value))) {
                    cell.setCellValue(true);
                } else if ("false".equalsIgnoreCase(String.valueOf(value))) {
                    cell.setCellValue(false);
                }
                break;
            case "date": // 日期
                if (value instanceof Number) {
                    cell.setCellValue(new Date(((Number) value).longValue()));
                }
                break;
            case "formula": // 公式
                if (cellInfo.containsKey("f")) {
                    String formula = (String) cellInfo.get("f");
                    if (formula.startsWith("=")) {
                        formula = formula.substring(1);
                    }
                    cell.setCellFormula(formula);
                }
                break;
            default:
                // 保持原值
        }

        // ========= 关键修复2:支持内联样式和样式引用 =========
        Object styleObj = cellInfo.get("s");
        if (styleObj != null) {
            Map<String, Object> styleMap = null;

            // 情况1:直接内联样式 (如: s={bg={...}, cl={...}})
            if (styleObj instanceof Map) {
                styleMap = (Map<String, Object>) styleObj;
            }
            // 情况2:样式ID引用 (如: s="b4_FtO")
            else if (styleObj instanceof String) {
                String styleId = (String) styleObj;
                if (!"null".equals(styleId) && styles != null) {
                    styleMap = styles.get(styleId);
                }
            }

            if (styleMap != null) {
                CellStyle newStyle = workbook.createCellStyle();

                // 克隆原有样式(更新模式)
                if (!isNewFile && cell.getCellStyle() != null) {
                    newStyle.cloneStyleFrom(cell.getCellStyle());
                }

                applyFullStyle(workbook, newStyle, styleMap);
                cell.setCellStyle(newStyle);
            }
        }
    }

    private byte convertUnderlineType(int univerType) {
        switch (univerType) {
            case 0: return FontUnderline.SINGLE.getByteValue();
            case 1: return FontUnderline.DOUBLE.getByteValue();
            case 2: return FontUnderline.SINGLE_ACCOUNTING.getByteValue();
            case 3: return FontUnderline.DOUBLE_ACCOUNTING.getByteValue();
            default: return FontUnderline.SINGLE.getByteValue();
        }
    }

    // 新建文件时应用完整样式
    @SuppressWarnings("unchecked")
    private void applyFullStyle(Workbook workbook, CellStyle cellStyle, Map<String, Object> styleMap) {
        // 字体处理
        // 1. 处理字体
        Font font = workbook.createFont();
        boolean fontModified = false;

        // 字体属性可能在顶层,也可能在font子对象中
       /* Map<String, Object> fontMap = null;
        if (styleMap.containsKey("font")) {
            fontMap = (Map<String, Object>) styleMap.get("font");
        } else {
            // 检查顶层是否有字体属性
            if (styleMap.containsKey("ff") || styleMap.containsKey("fs") ||
                    styleMap.containsKey("bold") || styleMap.containsKey("italic")) {
                fontMap = styleMap;
            }
        }*/
        // 字体属性可能在顶层,也可能在font子对象中
        Map<String, Object> fontMap = styleMap.containsKey("font") ?
                (Map<String, Object>) styleMap.get("font") : styleMap;
        // 字体名称
        if (fontMap.containsKey("ff") || fontMap.containsKey("name")) {
            String fontName = getString(fontMap, "ff", getString(fontMap, "name", "Arial"));
            font.setFontName(fontName);
            fontModified = true;
        }
        if (fontMap != null) {
            // 字体名称
            if (fontMap.containsKey("ff") || fontMap.containsKey("name")) {
                String fontName = getString(fontMap, "ff", getString(fontMap, "name", "Arial"));
                font.setFontName(fontName);
                fontModified = true;
            }

            // 字体大小
            if (fontMap.containsKey("fs") || fontMap.containsKey("size")) {
                Object size = fontMap.get("fs");
                if (size == null) size = fontMap.get("size");
                if (size instanceof Number) {
                    font.setFontHeightInPoints(((Number) size).shortValue());
                    fontModified = true;
                }
            }

            // 粗体 (bl 或 bold)
            if (fontMap.containsKey("bl") || fontMap.containsKey("bold")) {
                Object bold = fontMap.get("bl");
                if (bold == null) bold = fontMap.get("bold");
                if (bold instanceof Boolean) {
                    font.setBold((Boolean) bold);
                    fontModified = true;
                } else if (bold instanceof Number) {
                    font.setBold(((Number) bold).intValue() == 1);
                    fontModified = true;
                }
            }

            // 斜体 (it 或 italic)
            if (fontMap.containsKey("it") || fontMap.containsKey("italic")) {
                Object italic = fontMap.get("it");
                if (italic == null) italic = fontMap.get("italic");
                if (italic instanceof Boolean) {
                    font.setItalic((Boolean) italic);
                    fontModified = true;
                } else if (italic instanceof Number) {
                    font.setItalic(((Number) italic).intValue() == 1);
                    fontModified = true;
                }
            }

            // 下划线 (ul)
            if (fontMap.containsKey("ul")) {
                Object ulObj = fontMap.get("ul");
                if (ulObj instanceof Map) {
                    Map<String, Object> ulMap = (Map<String, Object>) ulObj;
                    if (ulMap.containsKey("s") && getInt(ulMap, "s", 0) == 1) {
                        // 设置下划线类型
                        int underlineType = getInt(ulMap, "t", FontUnderline.SINGLE.getByteValue());
                        font.setUnderline((byte) underlineType);

                        // 设置下划线颜色(如果指定)
                        if (ulMap.containsKey("cl") && !getBoolean(ulMap, "c", true)) {
                            Object clObj = ulMap.get("cl");
                            if (clObj instanceof Map) {
                                Map<String, String> clMap = (Map<String, String>) clObj;
                                String rgb = clMap.get("rgb");
                                if (isValidHexColor(rgb)) {
                                    setFontColor(workbook, font, rgb);
                                }
                            }
                        }
                        fontModified = true;
                    }
                }
            }

            // 删除线 (st)
            if (fontMap.containsKey("st")) {
                Object stObj = fontMap.get("st");
                if (stObj instanceof Map) {
                    Map<String, Object> stMap = (Map<String, Object>) stObj;
                    if (stMap.containsKey("s") && getInt(stMap, "s", 0) == 1) {
                        font.setStrikeout(true);

                        // 设置删除线颜色(如果指定)
                        if (stMap.containsKey("cl") && !getBoolean(stMap, "c", true)) {
                            Object clObj = stMap.get("cl");
                            if (clObj instanceof Map) {
                                Map<String, String> clMap = (Map<String, String>) clObj;
                                String rgb = clMap.get("rgb");
                                if (isValidHexColor(rgb)) {
                                    // 删除线颜色通过字体颜色设置(POI限制)
                                    setFontColor(workbook, font, rgb);
                                }
                            }
                        }
                        fontModified = true;
                    }
                }
            } else if (fontMap.containsKey("strikeThrough")) {
                // 兼容旧版
                Object strike = fontMap.get("strikeThrough");
                if (strike instanceof Boolean) {
                    font.setStrikeout((Boolean) strike);
                    fontModified = true;
                }
            }

            // 上划线 (ol) - POI不支持,但我们可以模拟实现
            if (fontMap.containsKey("ol")) {
                Object olObj = fontMap.get("ol");
                if (olObj instanceof Map) {
                    Map<String, Object> olMap = (Map<String, Object>) olObj;
                    if (olMap.containsKey("s") && getInt(olMap, "s", 0) == 1) {
                        // POI不支持上划线,使用下划线并调整位置模拟
                        font.setUnderline((byte) 8); // 使用双下划线模拟
                        fontModified = true;
                    }
                }
            }

            // 字体颜色 (顶层cl属性)
            if (styleMap.containsKey("cl")) {
                Object clObj = styleMap.get("cl");
                if (clObj instanceof Map) {
                    Map<String, String> clMap = (Map<String, String>) clObj;
                    String rgb = clMap.get("rgb");
                    if (isValidHexColor(rgb)) {
                        setFontColor(workbook, font, rgb);
                        fontModified = true;
                    }
                }
            }

            if (fontModified) {
                cellStyle.setFont(font);
            }

            // 2. 背景色 (顶层bg属性)
            if (styleMap.containsKey("bg")) {
                Object bgObj = styleMap.get("bg");
                if (bgObj instanceof Map) {
                    Map<String, String> bgMap = (Map<String, String>) bgObj;
                    String rgb = bgMap.get("rgb");
                    if (isValidHexColor(rgb)) {
                        setBackgroundColor(workbook, cellStyle, rgb);
                    }
                }
            }

            // 3. 水平对齐 (顶层ht属性)
            if (styleMap.containsKey("ht")) {
                Object ht = styleMap.get("ht");
                if (ht instanceof Number) {
                    int align = ((Number) ht).intValue();
                    switch (align) {
                        case 1:
                            cellStyle.setAlignment(HorizontalAlignment.LEFT);
                            break;
                        case 2:
                            cellStyle.setAlignment(HorizontalAlignment.CENTER);
                            break;
                        case 3:
                            cellStyle.setAlignment(HorizontalAlignment.RIGHT);
                            break;
                        case 4:
                            cellStyle.setAlignment(HorizontalAlignment.JUSTIFY);
                            break;
                        case 5:
                            cellStyle.setAlignment(HorizontalAlignment.FILL);
                            break;
                    }
                }
            }

            // 4. 垂直对齐 (顶层vt属性)
            if (styleMap.containsKey("vt")) {
                Object vt = styleMap.get("vt");
                if (vt instanceof Number) {
                    int verticalAlign = ((Number) vt).intValue();
                    switch (verticalAlign) {
                        case 1:
                            cellStyle.setVerticalAlignment(VerticalAlignment.TOP);
                            break;
                        case 2:
                            cellStyle.setVerticalAlignment(VerticalAlignment.CENTER);
                            break;
                        case 3:
                            cellStyle.setVerticalAlignment(VerticalAlignment.BOTTOM);
                            break;
                        case 4:
                            cellStyle.setVerticalAlignment(VerticalAlignment.JUSTIFY);
                            break;
                    }
                }
            }

            // 5. 自动换行 (顶层wrap属性)
            if (styleMap.containsKey("wrap")) {
                Object wrap = styleMap.get("wrap");
                if (wrap instanceof Boolean) {
                    cellStyle.setWrapText((Boolean) wrap);
                } else if (wrap instanceof Number) {
                    cellStyle.setWrapText(((Number) wrap).intValue() == 1);
                }
            }

            // 6. 文字旋转 (tr)
            if (styleMap.containsKey("tr")) {
                Object trObj = styleMap.get("tr");
                if (trObj instanceof Map) {
                    Map<String, Object> trMap = (Map<String, Object>) trObj;
                    // 垂直文本
                    if (trMap.containsKey("v") && getInt(trMap, "v", 0) == 1) {
                        cellStyle.setRotation((short) 255); // 垂直文本
                    }
                    // 旋转角度
                    else if (trMap.containsKey("a")) {
                        int angle = getInt(trMap, "a", 0);
                        // POI角度范围:-90° 到 90°,转换为0-180度
                        if (angle > 90) angle = 90;
                        if (angle < -90) angle = -90;
                        cellStyle.setRotation((short) angle);
                    }
                }
            }

            // 7. 边框处理 (顶层bd属性)
            if (styleMap.containsKey("bd")) {
                Map<String, Map<String, Object>> borderMap = (Map<String, Map<String, Object>>) styleMap.get("bd");
                for (Map.Entry<String, Map<String, Object>> entry : borderMap.entrySet()) {
                    String position = entry.getKey();
                    Map<String, Object> borderStyle = entry.getValue();

                    Object styleObj = borderStyle.get("s");
                    int borderStyleValue = styleObj instanceof Number ? ((Number) styleObj).intValue() : 0;

                    String rgb = null;
                    Object colorObj = borderStyle.get("cl");
                    if (colorObj instanceof Map) {
                        Map<String, String> colorMap = (Map<String, String>) colorObj;
                        rgb = colorMap.get("rgb");
                    }

                    applyBorderStyle(cellStyle, position, borderStyleValue, rgb, workbook);
                }
            }

            // 8. 文本方向 (tb) - 1: 从左到右, 2: 从右到左
            if (styleMap.containsKey("tb")) {
                Object tb = styleMap.get("tb");
                if (tb instanceof Number) {
                    int textDirection = ((Number) tb).intValue();
                    if (textDirection == 2) {
                        cellStyle.setRotation((short) 180); // 从右到左
                    }
                }
            }

            // 9. 内边距 (pd)
            if (styleMap.containsKey("pd")) {
                Object pdObj = styleMap.get("pd");
                if (pdObj instanceof Map) {
                    Map<String, Object> pdMap = (Map<String, Object>) pdObj;
                    // POI 不支持直接设置内边距,但可以通过缩进模拟
                    int left = getInt(pdMap, "l", 0);
                    if (left > 0) {
                        cellStyle.setIndention((short) (left / 3)); // 近似转换
                    }
                }
            }
        }
    }

    // 新增辅助方法
    private boolean getBoolean(Map<String, Object> map, String key, boolean defaultValue) {
        Object value = map.get(key);
        if (value instanceof Boolean) {
            return (Boolean) value;
        } else if (value instanceof Number) {
            return ((Number) value).intValue() == 1;
        } else if (value != null) {
            return "true".equalsIgnoreCase(value.toString());
        }
        return defaultValue;
    }

    private String getString(Map<String, Object> map, String key, String defaultValue) {
        Object value = map.get(key);
        return value != null ? value.toString() : defaultValue;
    }

    // 只更新特定的样式属性
    @SuppressWarnings("unchecked")
    private void applyPartialStyle(Workbook workbook, CellStyle newStyle, Map<String, Object> styleMap) {
        // 字体处理(只更新提供的属性)
        if (styleMap.containsKey("font")) {
            Font font = workbook.createFont();
            Map<String, Object> fontMap = (Map<String, Object>) styleMap.get("font");

            // 保留原字体
            Font originalFont = workbook.getFontAt(newStyle.getFontIndex());
            if (originalFont != null) {
                font.setFontName(originalFont.getFontName());
                font.setFontHeightInPoints(originalFont.getFontHeightInPoints());
                font.setBold(originalFont.getBold());
                font.setItalic(originalFont.getItalic());
                font.setUnderline(originalFont.getUnderline());
                font.setColor(originalFont.getColor());
            }

            // 更新Univer提供的属性
            if (fontMap.containsKey("name")) {
                font.setFontName(fontMap.get("name").toString());
            }
            if (fontMap.containsKey("size")) {
                Object size = fontMap.get("size");
                if (size instanceof Number) {
                    font.setFontHeightInPoints(((Number) size).shortValue());
                }
            }
            if (fontMap.containsKey("bold")) {
                Object bold = fontMap.get("bold");
                if (bold instanceof Boolean) {
                    font.setBold((Boolean) bold);
                }
            }
            if (fontMap.containsKey("italic")) {
                Object italic = fontMap.get("italic");
                if (italic instanceof Boolean) {
                    font.setItalic((Boolean) italic);
                }
            }
            if (fontMap.containsKey("underline")) {
                Object underline = fontMap.get("underline");
                if (underline instanceof Number) {
                    font.setUnderline(((Number) underline).byteValue());
                }
            }
            if (fontMap.containsKey("color")) {
                Object colorObj = fontMap.get("color");
                if (colorObj instanceof Map) {
                    Map<String, String> colorMap = (Map<String, String>) colorObj;
                    String rgb = colorMap.get("rgb");
                    if (isValidHexColor(rgb)) {
                        setFontColor(workbook, font, rgb);
                    }
                }
            }
            newStyle.setFont(font);
        }

        // 背景色(只更新提供的)
        if (styleMap.containsKey("bg")) {
            Object bgObj = styleMap.get("bg");
            if (bgObj instanceof Map) {
                Map<String, String> bgMap = (Map<String, String>) bgObj;
                String rgb = bgMap.get("rgb");
                if (isValidHexColor(rgb)) {
                    setBackgroundColor(workbook, newStyle, rgb);
                }
            }
        }

        // 对齐方式(只更新提供的)
        if (styleMap.containsKey("ht")) {
            Object ht = styleMap.get("ht");
            if (ht instanceof Number) {
                int align = ((Number) ht).intValue();
                switch (align) {
                    case 1: newStyle.setAlignment(HorizontalAlignment.LEFT); break;
                    case 2: newStyle.setAlignment(HorizontalAlignment.CENTER); break;
                    case 3: newStyle.setAlignment(HorizontalAlignment.RIGHT); break;
                }
            }
        }

        // 垂直对齐(只更新提供的)
        if (styleMap.containsKey("vt")) {
            Object vt = styleMap.get("vt");
            if (vt instanceof Number) {
                int verticalAlign = ((Number) vt).intValue();
                switch (verticalAlign) {
                    case 1: newStyle.setVerticalAlignment(VerticalAlignment.TOP); break;
                    case 2: newStyle.setVerticalAlignment(VerticalAlignment.CENTER); break;
                    case 3: newStyle.setVerticalAlignment(VerticalAlignment.BOTTOM); break;
                }
            }
        }

        // 自动换行(只更新提供的)
        if (styleMap.containsKey("wrap")) {
            Object wrap = styleMap.get("wrap");
            if (wrap instanceof Boolean) {
                newStyle.setWrapText((Boolean) wrap);
            }
        }
    }

    @SuppressWarnings("unchecked")
    private void applyUniverStyleToCell(Workbook workbook, CellStyle cellStyle, Map<String, Object> styleMap) {
        // 字体处理
        if (styleMap.containsKey("font")) {
            Map<String, Object> fontMap = (Map<String, Object>) styleMap.get("font");
            Font font = workbook.createFont();

            Object name = fontMap.get("name");
            if (name != null) font.setFontName(name.toString());

            Object size = fontMap.get("size");
            if (size instanceof Number) {
                font.setFontHeightInPoints(((Number) size).shortValue());
            } else if (size != null) {
                try {
                    font.setFontHeightInPoints(Short.parseShort(size.toString()));
                } catch (NumberFormatException ignored) {}
            }

            Object bold = fontMap.get("bold");
            if (bold instanceof Boolean) {
                font.setBold((Boolean) bold);
            } else if ("true".equalsIgnoreCase(String.valueOf(bold))) {
                font.setBold(true);
            }

            Object italic = fontMap.get("italic");
            if (italic instanceof Boolean) {
                font.setItalic((Boolean) italic);
            } else if ("true".equalsIgnoreCase(String.valueOf(italic))) {
                font.setItalic(true);
            }

            Object underline = fontMap.get("underline");
            if (underline instanceof Number) {
                font.setUnderline(((Number) underline).byteValue());
            } else if (underline != null) {
                try {
                    font.setUnderline(Byte.parseByte(underline.toString()));
                } catch (NumberFormatException ignored) {}
            }

            // 字体颜色
            if (fontMap.containsKey("color")) {
                Object colorObj = fontMap.get("color");
                if (colorObj instanceof Map) {
                    Map<String, String> colorMap = (Map<String, String>) colorObj;
                    String rgb = colorMap.get("rgb");
                    if (rgb != null && isValidHexColor(rgb)) {
                        setFontColor(workbook, font, rgb);
                    }
                }
            }

            cellStyle.setFont(font);
        }

        // 背景颜色
        if (styleMap.containsKey("bg")) {
            Object bgObj = styleMap.get("bg");
            if (bgObj instanceof Map) {
                Map<String, String> bgMap = (Map<String, String>) bgObj;
                String rgb = bgMap.get("rgb");
                if (rgb != null && isValidHexColor(rgb)) {
                    setBackgroundColor(workbook, cellStyle, rgb);
                }
            }
        }

        // 对齐方式
        Object ht = styleMap.get("ht");
        if (ht instanceof Number) {
            int align = ((Number) ht).intValue();
            switch (align) {
                case 1: cellStyle.setAlignment(HorizontalAlignment.LEFT); break;
                case 2: cellStyle.setAlignment(HorizontalAlignment.CENTER); break;
                case 3: cellStyle.setAlignment(HorizontalAlignment.RIGHT); break;
                default: cellStyle.setAlignment(HorizontalAlignment.LEFT);
            }
        }

        Object vt = styleMap.get("vt");
        if (vt instanceof Number) {
            int verticalAlign = ((Number) vt).intValue();
            switch (verticalAlign) {
                case 1: cellStyle.setVerticalAlignment(VerticalAlignment.TOP); break;
                case 2: cellStyle.setVerticalAlignment(VerticalAlignment.CENTER); break;
                case 3: cellStyle.setVerticalAlignment(VerticalAlignment.BOTTOM); break;
                default: cellStyle.setVerticalAlignment(VerticalAlignment.CENTER);
            }
        }

        // 自动换行
        Object wrap = styleMap.get("wrap");
        if (wrap instanceof Boolean) {
            cellStyle.setWrapText((Boolean) wrap);
        } else if ("true".equalsIgnoreCase(String.valueOf(wrap))) {
            cellStyle.setWrapText(true);
        } else if ("false".equalsIgnoreCase(String.valueOf(wrap))) {
            cellStyle.setWrapText(false);
        }

        // 边框处理
        if (styleMap.containsKey("bd")) {
            Map<String, Map<String, Object>> borderMap =
                    (Map<String, Map<String, Object>>) styleMap.get("bd");

            for (Map.Entry<String, Map<String, Object>> entry : borderMap.entrySet()) {
                String position = entry.getKey();
                Map<String, Object> borderStyle = entry.getValue();

                Object styleObj = borderStyle.get("s");
                int borderStyleValue = styleObj instanceof Number ? ((Number) styleObj).intValue() : 0;

                String rgb = null;
                Object colorObj = borderStyle.get("cl");
                if (colorObj instanceof Map) {
                    Map<String, String> colorMap = (Map<String, String>) colorObj;
                    rgb = colorMap.get("rgb");
                }

                applyBorderStyle(cellStyle, position, borderStyleValue, rgb, workbook);
            }
        }
    }

    private void applyBorderStyle(CellStyle cellStyle, String position, int styleValue, String rgb, Workbook workbook) {
        BorderStyle borderStyle = convertToPoiBorderStyle(styleValue);

        // 处理无边框情况
        if (borderStyle == BorderStyle.NONE) {
            switch (position) {
                case "t": cellStyle.setBorderTop(BorderStyle.NONE); break;
                case "r": cellStyle.setBorderRight(BorderStyle.NONE); break;
                case "b": cellStyle.setBorderBottom(BorderStyle.NONE); break;
                case "l": cellStyle.setBorderLeft(BorderStyle.NONE); break;
            }
            return;
        }

        if (workbook instanceof XSSFWorkbook) {
            // XSSF (xlsx) 处理
            XSSFCellStyle xssfStyle = (XSSFCellStyle) cellStyle;
            XSSFColor color = null;

            if (rgb != null && isValidHexColor(rgb)) {
                byte[] rgbBytes = parseHexColor(rgb);
                color = new XSSFColor(rgbBytes, null);
            }

            switch (position) {
                case "t":
                    xssfStyle.setBorderTop(borderStyle);
                    if (color != null) xssfStyle.setTopBorderColor(color);
                    break;
                case "r":
                    xssfStyle.setBorderRight(borderStyle);
                    if (color != null) xssfStyle.setRightBorderColor(color);
                    break;
                case "b":
                    xssfStyle.setBorderBottom(borderStyle);
                    if (color != null) xssfStyle.setBottomBorderColor(color);
                    break;
                case "l":
                    xssfStyle.setBorderLeft(borderStyle);
                    if (color != null) xssfStyle.setLeftBorderColor(color);
                    break;
            }
        } else {
            // HSSF (xls) 处理
            short colorIndex = IndexedColors.BLACK.getIndex();
            if (rgb != null && isValidHexColor(rgb)) {
                colorIndex = getColorIndex(workbook, rgb);
            }

            switch (position) {
                case "t":
                    cellStyle.setBorderTop(borderStyle);
                    cellStyle.setTopBorderColor(colorIndex);
                    break;
                case "r":
                    cellStyle.setBorderRight(borderStyle);
                    cellStyle.setRightBorderColor(colorIndex);
                    break;
                case "b":
                    cellStyle.setBorderBottom(borderStyle);
                    cellStyle.setBottomBorderColor(colorIndex);
                    break;
                case "l":
                    cellStyle.setBorderLeft(borderStyle);
                    cellStyle.setLeftBorderColor(colorIndex);
                    break;
            }
        }
    }

    private BorderStyle convertToPoiBorderStyle(int styleValue) {
        // 根据Univer官方文档映射边框样式
        switch (styleValue) {
            case 0: return BorderStyle.NONE;       // 无边框
            case 1: return BorderStyle.THIN;       // 细实线
            case 2: return BorderStyle.MEDIUM;     // 中等实线
            case 3: return BorderStyle.DASHED;     // 虚线
            case 4: return BorderStyle.DOTTED;     // 点线
            case 5: return BorderStyle.THICK;      // 粗实线
            case 6: return BorderStyle.DOUBLE;     // 双实线
            case 7: return BorderStyle.HAIR;       // 极细线
            case 8: return BorderStyle.MEDIUM_DASHED;          // 中等虚线
            case 9: return BorderStyle.DASH_DOT;               // 点划线
            case 10: return BorderStyle.MEDIUM_DASH_DOT;       // 中等点划线
            case 11: return BorderStyle.DASH_DOT_DOT;          // 点点划线
            case 12: return BorderStyle.MEDIUM_DASH_DOT_DOT;   // 中等点点划线
            case 13: return BorderStyle.SLANTED_DASH_DOT;      // 斜点划线
            default: return BorderStyle.NONE;
        }
    }

    private short getColorIndex(Workbook workbook, String rgb) {
        if (!isValidHexColor(rgb)) {
            return IndexedColors.BLACK.getIndex();
        }

        try {
            byte[] rgbBytes = parseHexColor(rgb);
            int r = rgbBytes[0] & 0xFF;
            int g = rgbBytes[1] & 0xFF;
            int b = rgbBytes[2] & 0xFF;

            if (workbook instanceof HSSFWorkbook) {
                HSSFWorkbook hssfWorkbook = (HSSFWorkbook) workbook;
                HSSFPalette palette = hssfWorkbook.getCustomPalette();
                return findOrCreateHSSFColor(palette, r, g, b);
            } else if (workbook instanceof XSSFWorkbook) {
                // XSSF使用直接RGB值,返回任意值(实际使用XSSFColor)
                return IndexedColors.BLACK.getIndex();
            }
        } catch (Exception e) {
            log.error("颜色转换失败: {}", rgb, e);
        }
        return IndexedColors.BLACK.getIndex();
    }

    private void setBackgroundColor(Workbook workbook, CellStyle cellStyle, String rgb) {
        if (!isValidHexColor(rgb)) return;

        try {
            byte[] rgbBytes = parseHexColor(rgb);
            int r = rgbBytes[0] & 0xFF;
            int g = rgbBytes[1] & 0xFF;
            int b = rgbBytes[2] & 0xFF;

            if (workbook instanceof XSSFWorkbook) {
                XSSFColor color = new XSSFColor(new java.awt.Color(r, g, b), null);
                ((XSSFCellStyle) cellStyle).setFillForegroundColor(color);
                cellStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
            } else if (workbook instanceof HSSFWorkbook) {
                HSSFWorkbook hssfWorkbook = (HSSFWorkbook) workbook;
                HSSFPalette palette = hssfWorkbook.getCustomPalette();

                // 查找相似颜色或创建新索引
                short colorIndex = findOrCreateHSSFColor(palette, r, g, b);
                cellStyle.setFillForegroundColor(colorIndex);
                cellStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
            }
        } catch (Exception e) {
            log.error("设置背景色失败: {}", rgb, e);
        }
    }

    // 辅助方法:处理HSSF颜色
    private short findOrCreateHSSFColor(HSSFPalette palette, int r, int g, int b) {
        // 尝试查找相似颜色
        HSSFColor color = palette.findSimilarColor(r, g, b);
        if (color != null) {
            return color.getIndex();
        }

        // 创建新颜色(从56开始的自定义索引)
        for (short i = 56; i < 64; i++) {
            if (palette.getColor(i) == null) {
                palette.setColorAtIndex(i, (byte) r, (byte) g, (byte) b);
                return i;
            }
        }

        // 没有可用槽位则使用默认
        return IndexedColors.GREY_25_PERCENT.getIndex();
    }

    private void setFontColor(Workbook workbook, Font font, String rgb) {
        if (!isValidHexColor(rgb)) return;

        try {
            byte[] rgbBytes = parseHexColor(rgb);

            if (workbook instanceof XSSFWorkbook && font instanceof XSSFFont) {
                XSSFColor color = new XSSFColor(rgbBytes, null);
                ((XSSFFont) font).setColor(color);
            } else if (workbook instanceof HSSFWorkbook) {
                HSSFWorkbook hssfWorkbook = (HSSFWorkbook) workbook;
                HSSFPalette palette = hssfWorkbook.getCustomPalette();
                HSSFColor color = palette.findSimilarColor(
                        rgbBytes[0] & 0xFF,
                        rgbBytes[1] & 0xFF,
                        rgbBytes[2] & 0xFF
                );
                font.setColor(color.getIndex());
            }
        } catch (Exception e) {
            log.error("设置字体颜色失败: {}", rgb, e);
        }
    }

    // 辅助方法
    private boolean isValidHexColor(String color) {
        return color != null && Pattern.matches("^#[0-9A-Fa-f]{6}$", color);
    }

    private byte[] parseHexColor(String hexColor) {
        if (!isValidHexColor(hexColor)) {
            return new byte[]{0, 0, 0}; // 默认黑色
        }
        return new byte[]{
                (byte) Integer.parseInt(hexColor.substring(1, 3), 16),
                (byte) Integer.parseInt(hexColor.substring(3, 5), 16),
                (byte) Integer.parseInt(hexColor.substring(5, 7), 16)
        };
    }

    private int safeParseInt(String str, int defaultValue) {
        try {
            return Integer.parseInt(str);
        } catch (NumberFormatException e) {
            return defaultValue;
        }
    }

    private int getInt(Map<String, Object> map, String key, int defaultValue) {
        Object value = map.get(key);
        if (value instanceof Number) {
            return ((Number) value).intValue();
        } else if (value != null) {
            try {
                return Integer.parseInt(value.toString());
            } catch (NumberFormatException e) {
                return defaultValue;
            }
        }
        return defaultValue;
    }
}

网站公告

今日签到

点亮在社区的每一天
去签到