xlsx/export/xlsx.go

186 lines
4.0 KiB
Go
Raw Normal View History

2024-05-15 15:09:48 +08:00
package export
import (
"fmt"
"reflect"
"strconv"
"strings"
"github.com/xuri/excelize/v2"
)
type Exporter struct {
File string
Path string
Sheets []string
Titles []Title
Data interface{}
xlsx *excelize.File
GlobalTitleStyle *excelize.Style
2024-05-16 16:53:29 +08:00
GlobalCellStyle *excelize.Style
preLocation *CellLocation
2024-05-15 15:09:48 +08:00
}
func NewExporter() *Exporter {
return &Exporter{
xlsx: excelize.NewFile(),
}
}
func DefaultExporter() *Exporter {
return &Exporter{
xlsx: excelize.NewFile(),
GlobalTitleStyle: DefaultTitleStyle(),
2024-05-16 16:53:29 +08:00
GlobalCellStyle: DefaultCellStyle(),
2024-05-15 15:09:48 +08:00
Sheets: []string{"Sheet1"},
}
}
func (e *Exporter) newSheet() error {
for _, sheet := range e.Sheets {
_, err := e.xlsx.NewSheet(sheet)
if err != nil {
return fmt.Errorf("init sheet error:%s", err)
}
}
return nil
}
func (e *Exporter) Export(sheetIndex int) error {
if len(e.Sheets) == 0 {
return fmt.Errorf("excel file has no sheet")
}
err := e.newSheet()
if err != nil {
return err
}
if len(e.Titles) == 0 {
return fmt.Errorf("excel title is null")
}
sheet := e.Sheets[sheetIndex]
e.SetTitle(sheet)
v := reflect.ValueOf(e.Data)
if v.Kind() != reflect.Pointer {
return fmt.Errorf("data must be pointer")
}
2024-05-16 16:53:29 +08:00
switch v.Elem().Kind() {
case reflect.Slice:
for i := 0; i < v.Elem().Len(); i++ {
value := v.Elem().Index(i)
if value.Kind() != reflect.Struct {
return fmt.Errorf("element must be struct")
}
e.dealElement(sheet, value)
2024-05-15 15:09:48 +08:00
}
2024-05-16 16:53:29 +08:00
case reflect.Struct:
e.dealElement(sheet, v.Elem())
default:
return fmt.Errorf("data must be slice or struct")
2024-05-15 15:09:48 +08:00
}
2024-05-16 16:53:29 +08:00
return e.Save()
2024-05-15 15:09:48 +08:00
}
2024-05-16 16:53:29 +08:00
func (e *Exporter) dealElement(sheet string, data reflect.Value) {
2024-05-15 15:09:48 +08:00
for i := 0; i < data.NumField(); i++ {
field := data.Field(i)
2024-05-15 17:09:50 +08:00
t := data.Type().Field(i)
tag, ok := t.Tag.Lookup("export")
2024-05-15 15:09:48 +08:00
switch field.Kind() {
case reflect.Struct:
2024-05-15 17:09:50 +08:00
// 如果是结构体类型但是没有export标签则进行递归否则直接处理例如time.Time类型是结构体但是可能需要导出
if !ok {
2024-05-16 16:53:29 +08:00
e.dealElement(sheet, field)
2024-05-15 17:09:50 +08:00
} else {
2024-05-16 16:53:29 +08:00
e.writeCell(sheet, field, tag)
2024-05-15 17:09:50 +08:00
}
2024-05-15 15:09:48 +08:00
// 该逻辑有待验证
case reflect.Slice:
for j := 0; j < field.Len(); j++ {
value := field.Index(j)
if value.Kind() == reflect.Struct {
2024-05-16 16:53:29 +08:00
e.dealElement(sheet, value)
2024-05-15 15:09:48 +08:00
}
}
default:
if ok {
2024-05-16 16:53:29 +08:00
e.writeCell(sheet, field, tag)
2024-05-15 15:09:48 +08:00
}
2024-05-15 17:09:50 +08:00
}
2024-05-15 15:09:48 +08:00
}
}
2024-05-16 16:53:29 +08:00
func (e *Exporter) writeCell(sheet string, field reflect.Value, tag string) {
2024-05-15 17:09:50 +08:00
tagMap := dealTag(tag)
x, _ := strconv.Atoi(tagMap["x"])
y, _ := strconv.Atoi(tagMap["y"])
colSpan, _ := strconv.Atoi(tagMap["colspan"])
rowSpan, _ := strconv.Atoi(tagMap["rowspan"])
2024-05-16 16:53:29 +08:00
var location Location
if e.preLocation != nil {
if x > e.preLocation.RightX {
y = e.preLocation.TopY
} else if x <= e.preLocation.RightX {
y = e.preLocation.BottomY + 1
}
}
location = Location{
2024-05-15 17:09:50 +08:00
X: x,
2024-05-16 16:53:29 +08:00
Y: y,
2024-05-15 17:09:50 +08:00
}
value := value(field)
cell := Cell{
Location: location,
Rowspan: rowSpan,
Colspan: colSpan,
Value: value,
}
e.SetCell(sheet, cell)
}
func value(field reflect.Value) (val string) {
mthV := field.MethodByName("String")
if !mthV.IsValid() && field.CanAddr() {
mthV = field.Addr().MethodByName("String")
}
if mthV.IsValid() && mthV.Kind() == reflect.Func && mthV.Type().NumIn() == 0 {
callV := mthV.Call(nil)
if len(callV) > 0 {
val = fmt.Sprintf("%s", callV[0].String())
return
}
}
if field.CanUint() {
val = fmt.Sprintf("%d", field.Uint())
} else if field.CanInt() {
val = fmt.Sprintf("%d", field.Int())
} else if field.CanFloat() {
val = fmt.Sprintf("%f", field.Float())
} else {
val = fmt.Sprintf("%s", field.String())
}
return
}
2024-05-15 15:09:48 +08:00
func dealTag(tag string) map[string]string {
tag_list := strings.Split(tag, ",")
tag_map := make(map[string]string)
for _, tag := range tag_list {
tag_kv := strings.Split(tag, ":")
if len(tag_kv) != 2 {
continue
}
tag_map[tag_kv[0]] = tag_kv[1]
}
return tag_map
}
func (e *Exporter) Save() error {
return e.xlsx.SaveAs(fmt.Sprintf("%s/%s", e.Path, e.File))
}