package workbook import ( "fmt" "time" ) // Worksheet 表示Excel工作簿中的工作表 type Worksheet struct { Name string SheetID int Cells map[string]*Cell Columns []*Column Rows []*Row MergedCells []*MergedCell } // NewWorksheet 创建一个新的工作表 func NewWorksheet(name string) *Worksheet { return &Worksheet{ Name: name, SheetID: 1, Cells: make(map[string]*Cell), Columns: make([]*Column, 0), Rows: make([]*Row, 0), MergedCells: make([]*MergedCell, 0), } } // Cell 表示工作表中的单元格 type Cell struct { Value interface{} Formula string Style *CellStyle DataType string // s: 字符串, n: 数字, b: 布尔值, d: 日期, e: 错误 } // NewCell 创建一个新的单元格 func NewCell() *Cell { return &Cell{ Style: NewCellStyle(), } } // CellStyle 表示单元格样式 type CellStyle struct { FontID int FillID int BorderID int NumberFormatID int Alignment *Alignment } // NewCellStyle 创建一个新的单元格样式 func NewCellStyle() *CellStyle { return &CellStyle{ Alignment: &Alignment{ Horizontal: "general", Vertical: "bottom", }, } } // Alignment 表示对齐方式 type Alignment struct { Horizontal string // left, center, right, fill, justify, centerContinuous, distributed Vertical string // top, center, bottom, justify, distributed WrapText bool } // Column 表示工作表中的列 type Column struct { Min int Max int Width float64 Style *CellStyle Hidden bool } // Row 表示工作表中的行 type Row struct { Index int Height float64 Cells []*Cell Style *CellStyle Hidden bool } // MergedCell 表示合并的单元格 type MergedCell struct { TopLeftRef string // 例如: "A1" BottomRightRef string // 例如: "B2" } // AddRow 添加一个新的行 func (ws *Worksheet) AddRow() *Row { row := &Row{ Index: len(ws.Rows) + 1, Cells: make([]*Cell, 0), Style: NewCellStyle(), } ws.Rows = append(ws.Rows, row) return row } // AddColumn 添加一个新的列 func (ws *Worksheet) AddColumn(min, max int, width float64) *Column { col := &Column{ Min: min, Max: max, Width: width, Style: NewCellStyle(), } ws.Columns = append(ws.Columns, col) return col } // AddCell 在指定位置添加一个单元格 func (ws *Worksheet) AddCell(cellRef string, value interface{}) *Cell { cell := NewCell() // 根据值类型设置数据类型 switch v := value.(type) { case string: cell.DataType = "s" cell.Value = value case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, float32, float64: cell.DataType = "n" cell.Value = value case bool: cell.DataType = "b" cell.Value = value case time.Time: // 日期和时间在Excel中表示为序列号 // 数字部分代表天数,小数部分代表一天中的时间 cell.DataType = "n" // 使用正确的Excel日期转换函数 // GetExcelSerialDate已经处理了日期转换的细节,包括1900年2月29日的Excel错误 serialDate := GetExcelSerialDate(v) // 需要保留小数部分以表示时间 hours := float64(v.Hour()) / 24.0 minutes := float64(v.Minute()) / (24.0 * 60.0) seconds := float64(v.Second()) / (24.0 * 60.0 * 60.0) // 组合日期和时间部分 cell.Value = serialDate + hours + minutes + seconds default: cell.DataType = "s" cell.Value = fmt.Sprintf("%v", value) } ws.Cells[cellRef] = cell return cell } // SetCellFormula 设置单元格公式 func (ws *Worksheet) SetCellFormula(cellRef string, formula string) *Cell { cell, ok := ws.Cells[cellRef] if !ok { cell = ws.AddCell(cellRef, "") } cell.Formula = formula // 公式单元格的初始值设为空,让Excel自动计算 // 确保DataType不是字符串,这会导致Excel无法正确计算公式 if cell.DataType == "s" { cell.DataType = "" // 让Excel自动判断类型 } return cell } // SetCellStyle 设置单元格样式 func (ws *Worksheet) SetCellStyle(cellRef string, style *CellStyle) *Cell { cell, ok := ws.Cells[cellRef] if !ok { cell = ws.AddCell(cellRef, "") } // 直接设置样式,确保样式引用正确 cell.Style = style return cell } // ToXML 将工作表转换为XML func (ws *Worksheet) ToXML(sharedStrings *SharedStrings) string { xml := "\n" xml += "\n" // 列定义 if len(ws.Columns) > 0 { xml += " \n" for _, col := range ws.Columns { xml += fmt.Sprintf(" rowIndices[j] { rowIndices[i], rowIndices[j] = rowIndices[j], rowIndices[i] } } } // 输出行和单元格 for _, rowIdx := range rowIndices { // 查找是否有显式添加的行 var rowHeight float64 var rowHidden bool for _, r := range ws.Rows { if r.Index == rowIdx { rowHeight = r.Height rowHidden = r.Hidden break } } xml += fmt.Sprintf(" 0 { xml += fmt.Sprintf(" ht=\"%f\" customHeight=\"1\"", rowHeight) } if rowHidden { xml += " hidden=\"1\"" } xml += ">\n" // 输出该行的单元格 if cells, ok := rowMap[rowIdx]; ok { // 对单元格按列排序 cellRefs := make([]string, 0, len(cells)) for cellRef := range cells { cellRefs = append(cellRefs, cellRef) } // 简单排序,确保单元格按列顺序输出 for i := 0; i < len(cellRefs)-1; i++ { for j := i + 1; j < len(cellRefs); j++ { _, col1, _ := ParseCellRef(cellRefs[i]) _, col2, _ := ParseCellRef(cellRefs[j]) if col1 > col2 { cellRefs[i], cellRefs[j] = cellRefs[j], cellRefs[i] } } } for _, cellRef := range cellRefs { cell := cells[cellRef] // 使用标准化的单元格引用 xml += fmt.Sprintf(" 0 { styleID = cell.Style.NumberFormatID } else { // 如果没有NumberFormatID,则按优先级使用其他样式ID if cell.Style.FontID > 0 { styleID = cell.Style.FontID } else if cell.Style.FillID > 0 { styleID = cell.Style.FillID } else if cell.Style.BorderID > 0 { styleID = cell.Style.BorderID } } if styleID > 0 { xml += fmt.Sprintf(" s=\"%d\"", styleID) } } // 设置数据类型 if cell.DataType != "" { // 确保数据类型是有效的Excel类型 switch cell.DataType { case "s": xml += " t=\"s\"" // 字符串类型 case "b": xml += " t=\"b\"" // 布尔类型 case "n": // 数字类型不需要特殊的t属性 default: xml += " t=\"s\"" } } xml += ">" // 添加公式 if cell.Formula != "" { xml += fmt.Sprintf("%s", cell.Formula) // 公式单元格不应该标记为字符串类型,除非明确需要 // 让Excel自动根据公式计算结果决定单元格类型 if cell.Value != nil { xml += fmt.Sprintf("%v", cell.Value) } } else if cell.Value != nil { // 添加值(仅当没有公式时) switch cell.DataType { case "s": // 字符串 strValue, ok := cell.Value.(string) if ok { index := sharedStrings.AddString(strValue) xml += fmt.Sprintf("%d", index) } case "n": // 数字 (包括日期,日期仅是有特殊格式的数字) xml += fmt.Sprintf("%v", cell.Value) case "b": // 布尔值 boolValue, ok := cell.Value.(bool) if ok { if boolValue { xml += "1" } else { xml += "0" } } default: // 默认作为字符串处理 strValue := fmt.Sprintf("%v", cell.Value) index := sharedStrings.AddString(strValue) xml += fmt.Sprintf("%d", index) } } xml += "\n" } } xml += " \n" } xml += " \n" // 合并单元格 if len(ws.MergedCells) > 0 { xml += " \n" for _, mergedCell := range ws.MergedCells { xml += " \n" } xml += " \n" } xml += "" return xml } // MergeCells 合并单元格 func (ws *Worksheet) MergeCells(topLeftRef, bottomRightRef string) *MergedCell { mergedCell := &MergedCell{ TopLeftRef: topLeftRef, BottomRightRef: bottomRightRef, } ws.MergedCells = append(ws.MergedCells, mergedCell) return mergedCell }