office/Excel.go
2024-11-22 16:33:58 +08:00

287 lines
6.5 KiB
Go

package office
import (
"fmt"
"regexp"
"strconv"
"github.com/ssgo/u"
"github.com/xuri/excelize/v2"
)
// func WriteExcel(excelFile, sheetName string, table [][]string, password *string) error {
// var f *excelize.File
// var err error
// if f, err = excelize.OpenFile(excelFile, excelize.Options{Password: u.String(password)}); err != nil {
// f = excelize.NewFile()
// }
// if sheetName == "" {
// sheetName = "Sheet1"
// }
// if sheetName >= "0" && sheetName <= "9" {
// sheetName = f.GetSheetName(u.Int(sheetName))
// }
// for y, row := range table {
// for x, v := range row {
// _ = f.SetCellStr(sheetName, MakeCellID(x, y), v)
// }
// }
// if err == nil {
// err = f.Save()
// } else {
// err = f.SaveAs(excelFile, excelize.Options{Password: u.String(password)})
// }
// return err
// }
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})
}
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 {
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 {
if _, err = xls.excel.NewSheet(sheetName); err != nil {
return err
}
} else {
return err
}
}
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) Sheets() []string {
return xls.excel.GetSheetList()
}
func (xls *Excel) Get(sheetName string, start, end *string) ([][]any, error) {
if sheetName >= "0" && sheetName <= "9" {
sheetName = xls.excel.GetSheetName(u.Int(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 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
}