package office import ( "fmt" "regexp" "strconv" "github.com/ssgo/u" "github.com/xuri/excelize/v2" ) type Excel struct { filename string password string excel *excelize.File } func OpenExcel(filename string, password *string) (*Excel, error) { xls := &Excel{ filename: filename, password: u.String(password), } var err error if u.FileExists(filename) { if xls.excel, err = excelize.OpenFile(filename, excelize.Options{Password: xls.password}); err != nil { xls.excel = excelize.NewFile() } } else { xls.excel = excelize.NewFile() } return xls, err } func (xls *Excel) Save(filename, password *string) error { if filename != nil { xls.filename = *filename } if password != nil { xls.password = *password } return xls.excel.SaveAs(xls.filename, excelize.Options{Password: xls.password}) } func (xls *Excel) WriteToBuffer() ([]byte, error) { buf, err := xls.excel.WriteToBuffer() return buf.Bytes(), err } var isIntMatcher = regexp.MustCompile(`^\d{1,8}$`) var isFloatMatcher = regexp.MustCompile(`^[\d.]{1,8}$`) func (xls *Excel) Set(sheetName string, table [][]any, start, end *string) error { sheetName = xls.GetSheetName(sheetName) startX, startY := ParseCellID(u.String(start)) endX, endY := ParseCellID(u.String(end)) for y, row := range table { if endY > 0 && startY+y > endY { break } for x, v := range row { if endX > 0 && startX+x > endX { break } // fmt.Println(startX+x, startY+y, MakeCellID(startX+x, startY+y), reflect.TypeOf(v).String(), v) if err := xls.excel.SetCellValue(sheetName, MakeCellID(startX+x, startY+y), v); err != nil { return err } // fmt.Println(xls.excel.GetCellType(sheetName, MakeCellID(startX+x, startY+y))) } } return nil } func (xls *Excel) RemoveSheet(sheetName string) error { sheetName = xls.GetSheetName(sheetName) return xls.excel.DeleteSheet(sheetName) } func (xls *Excel) Sheets() []string { return xls.excel.GetSheetList() } func (xls *Excel) SetColWidth(sheetName string, startCol string, endCol string, width float64) error { sheetName = xls.GetSheetName(sheetName) return xls.excel.SetColWidth(sheetName, startCol, endCol, width) } func (xls *Excel) SetColWidths(sheetName string, widths []float64) error { sheetName = xls.GetSheetName(sheetName) for i, w := range widths { col := MakeColID(i) if err := xls.excel.SetColWidth(sheetName, col, col, w); err != nil { return err } } return nil } func (xls *Excel) MakeStyle(style excelize.Style) (int, error) { fmt.Println(u.JsonP(style), 111) return xls.excel.NewStyle(&style) } func (xls *Excel) SetCellStyle(sheetName string, start string, end string, styleID int) error { sheetName = xls.GetSheetName(sheetName) return xls.excel.SetCellStyle(sheetName, start, end, styleID) } func (xls *Excel) SetPanes(sheetName string, panes excelize.Panes) error { sheetName = xls.GetSheetName(sheetName) return xls.excel.SetPanes(sheetName, &panes) } func (xls *Excel) SetAutoFilter(sheetName string, start, end string, option *[]excelize.AutoFilterOptions) error { sheetName = xls.GetSheetName(sheetName) var opt []excelize.AutoFilterOptions if option != nil { opt = *option } return xls.excel.AutoFilter(sheetName, start+":"+end, opt) } func (xls *Excel) GetSheetName(sheetName string) string { if sheetName == "" { sheetName = "Sheet1" } if isIntMatcher.MatchString(sheetName) { sheetName = xls.excel.GetSheetName(u.Int(sheetName)) } else { if idx, err := xls.excel.GetSheetIndex(sheetName); err != nil || idx == -1 { _, _ = xls.excel.NewSheet(sheetName) } } return sheetName } func (xls *Excel) Get(sheetName string, start, end *string) ([][]any, error) { sheetName = xls.GetSheetName(sheetName) rows, err := xls.excel.GetRows(sheetName) if err != nil { return nil, err } startX, startY := ParseCellID(u.String(start)) endX, endY := ParseCellID(u.String(end)) result := make([][]any, 0) for y, row := range rows { if startY > 0 && y < startY { continue } if endY > 0 && y > endY { break } // result[y-startY] = make([]any, len(row)-startX) rowData := make([]any, 0) for x, v := range row { if startX > 0 && x < startX { continue } if endX > 0 && x > endX { break } cellType, err := xls.excel.GetCellType(sheetName, MakeCellID(x, y)) if err != nil { return nil, err } switch cellType { case excelize.CellTypeInlineString, excelize.CellTypeSharedString: rowData = append(rowData, v) case excelize.CellTypeFormula: // TODO 获得公式结果 // formula, err := xls.excel.GetCellFormula(sheetName, MakeCellID(j, i)) rowData = append(rowData, v) case excelize.CellTypeNumber: if isFloatMatcher.MatchString(v) { rowData = append(rowData, u.Float64(v)) } else { rowData = append(rowData, u.Int64(v)) } case excelize.CellTypeBool: rowData = append(rowData, u.Bool(v)) case excelize.CellTypeDate: // TODO 获得日期结果 rowData = append(rowData, v) case excelize.CellTypeUnset: if isFloatMatcher.MatchString(v) { rowData = append(rowData, u.Float64(v)) } else if isIntMatcher.MatchString(v) { rowData = append(rowData, u.Int64(v)) } else { rowData = append(rowData, v) } case excelize.CellTypeError: rowData = append(rowData, v) default: rowData = append(rowData, v) } } result = append(result, rowData) } return result, nil } func (xls *Excel) SetData(sheetName string, data []map[string]any, start, end *string) error { table, err := xls.Get(sheetName, start, end) if err != nil { return err } fieldIndex := map[string]int{} fieldNum := 0 if len(table) > 0 { fieldNum = len(table[0]) for i, v := range table[0] { fieldIndex[u.String(v)] = i } } for i, item := range data { if len(table) <= i+1 { table = append(table, []any{}) } for k, v := range item { idx, idxOk := fieldIndex[k] if !idxOk { idx = fieldNum fieldIndex[k] = idx fieldNum++ for len(table[0]) < idx+1 { table[0] = append(table[0], "") } table[0][idx] = k } for len(table[i+1]) < idx+1 { table[i+1] = append(table[i+1], "") } table[i+1][idx] = v } } return xls.Set(sheetName, table, start, end) } func (xls *Excel) GetData(sheetName string, start, end *string) ([]map[string]any, error) { rows, err := xls.Get(sheetName, start, end) if err != nil { return nil, err } fields := []string{} if len(rows) > 0 { for _, v := range rows[0] { v1 := u.String(v) if v1 == "" { v1 = MakeCellID(len(fields), 0) } fields = append(fields, v1) } } data := make([]map[string]any, 0) for i := 1; i < len(rows); i++ { row := map[string]any{} for j := 0; j < len(fields); j++ { if j < len(rows[i]) { row[fields[j]] = rows[i][j] } else { row[fields[j]] = "" } } data = append(data, row) } return data, nil } func MakeCellID(col, row int) string { colName := "" for col >= 0 { i := col % 26 colName = string(rune(i+65)) + colName col = col/26 - 1 } return fmt.Sprint(colName, row+1) } func MakeColID(col int) string { colName := "" for col >= 0 { i := col % 26 colName = string(rune(i+65)) + colName col = col/26 - 1 } return fmt.Sprint(colName) } func ParseCellID(cell string) (col, row int) { row = 0 col = 0 for i, c := range cell { if c >= 'A' && c <= 'Z' { col = col*26 + int(c-'A'+1) } else if c >= 'a' && c <= 'z' { col = col*26 + int(c-'a'+1) } else { row, _ = strconv.Atoi(cell[i:]) break } } col-- row-- if col < 0 { col = 0 } if row < 0 { row = 0 } return col, row }