xlsx/export/xlsx.go

172 lines
3.7 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

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
}
func NewExporter() *Exporter {
return &Exporter{
xlsx: excelize.NewFile(),
}
}
func DefaultExporter() *Exporter {
return &Exporter{
xlsx: excelize.NewFile(),
GlobalTitleStyle: DefaultTitleStyle(),
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")
}
if v.Elem().Kind() != reflect.Slice {
return fmt.Errorf("data must be 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, i)
}
return nil
}
func (e *Exporter) dealElement(sheet string, data reflect.Value, index int) error {
for i := 0; i < data.NumField(); i++ {
field := data.Field(i)
t := data.Type().Field(i)
tag, ok := t.Tag.Lookup("export")
switch field.Kind() {
case reflect.Struct:
// 如果是结构体类型但是没有export标签则进行递归否则直接处理例如time.Time类型是结构体但是可能需要导出
if !ok {
e.dealElement(sheet, field, index)
} else {
e.writeCell(sheet, field, tag, index)
}
// 该逻辑有待验证
case reflect.Slice:
for j := 0; j < field.Len(); j++ {
value := field.Index(j)
if value.Kind() == reflect.Struct {
e.dealElement(sheet, value, index)
}
}
default:
if ok {
e.writeCell(sheet, field, tag, index)
}
}
}
return e.Save()
}
func (e *Exporter) writeCell(sheet string, field reflect.Value, tag string, index int) {
tagMap := dealTag(tag)
x, _ := strconv.Atoi(tagMap["x"])
y, _ := strconv.Atoi(tagMap["y"])
colSpan, _ := strconv.Atoi(tagMap["colspan"])
rowSpan, _ := strconv.Atoi(tagMap["rowspan"])
location := Location{
X: x,
Y: y + index,
}
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
}
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))
}