package iot_excelize import ( "reflect" "github.com/gogf/gf/v2/util/gconv" "github.com/xuri/excelize/v2" ) // BuildByStructs creates an Excel workbook from a struct slice without writing // it to HTTP response or disk. func BuildByStructs(tags []string, list interface{}, sheetName string) (f *excelize.File, err error) { return BuildByStructsWithOption(tags, list, sheetName, nil) } // BuildByStructsWithOption creates an Excel workbook and applies optional // styles or advanced customizations. func BuildByStructsWithOption(tags []string, list interface{}, sheetName string, option *BuildOption) (f *excelize.File, err error) { if sheetName == "" { sheetName = "Sheet1" } f = excelize.NewFile() if err = f.SetSheetName("Sheet1", sheetName); err != nil { _ = f.Close() return nil, err } if err = f.SetSheetRow(sheetName, "A1", &tags); err != nil { _ = f.Close() return nil, err } length := len(tags) rowNum := 1 for _, item := range gconv.Interfaces(list) { row := structValues(item) rowNum++ if err = f.SetSheetRow(sheetName, "A"+gconv.String(rowNum), &row); err != nil { _ = f.Close() return nil, err } } if err = applyBuildOption(f, sheetName, length, rowNum, option); err != nil { _ = f.Close() return nil, err } return f, nil } func applyBuildOption(f *excelize.File, sheetName string, colCount int, rowCount int, option *BuildOption) error { if option == nil { return nil } if option.HeaderHeight > 0 { if err := f.SetRowHeight(sheetName, 1, option.HeaderHeight); err != nil { return err } } if colCount > 0 && option.ColWidth > 0 { lastCol := letter(colCount)[colCount-1] if err := f.SetColWidth(sheetName, "A", lastCol, option.ColWidth); err != nil { return err } } if colCount > 0 && option.HeaderStyle != nil { styleID, err := f.NewStyle(option.HeaderStyle) if err != nil { return err } lastCell, err := excelize.CoordinatesToCellName(colCount, 1) if err != nil { return err } if err = f.SetCellStyle(sheetName, "A1", lastCell, styleID); err != nil { return err } } if colCount > 0 && rowCount > 1 && option.RowStyle != nil { styleID, err := f.NewStyle(option.RowStyle) if err != nil { return err } startCell := "A2" endCell, err := excelize.CoordinatesToCellName(colCount, rowCount) if err != nil { return err } if err = f.SetCellStyle(sheetName, startCell, endCell, styleID); err != nil { return err } } if option.AfterBuild != nil { return option.AfterBuild(f, sheetName) } return nil } func structValues(item interface{}) []interface{} { value := reflect.ValueOf(item) if !value.IsValid() { return nil } if value.Kind() == reflect.Ptr { if value.IsNil() { return nil } value = value.Elem() } if value.Kind() != reflect.Struct { return []interface{}{item} } return collectStructValues(value) } func collectStructValues(value reflect.Value) []interface{} { var row []interface{} for i := 0; i < value.NumField(); i++ { fieldValue := value.Field(i) fieldInfo := value.Type().Field(i) if !fieldInfo.IsExported() { continue } if fieldInfo.Anonymous && derefType(fieldInfo.Type).Kind() == reflect.Struct { if fieldValue.Kind() == reflect.Ptr { if fieldValue.IsNil() { continue } fieldValue = fieldValue.Elem() } row = append(row, collectStructValues(fieldValue)...) continue } row = append(row, fieldValue.Interface()) } return row }