Skip to content

Commit

Permalink
feat: Support Map Columns
Browse files Browse the repository at this point in the history
  • Loading branch information
NX-Official committed Mar 27, 2024
1 parent 2e7dd73 commit 7992ec2
Show file tree
Hide file tree
Showing 3 changed files with 132 additions and 97 deletions.
95 changes: 60 additions & 35 deletions book_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,51 +35,76 @@ type (
}
)

func Test_exporter_buildHeader(t *testing.T) {
overview := Overview{
StaffId: "22050626",
StaffName: "王文杰",
ClassNo: "22184111",
Score: 100,
Tags: []string{"Tag1", "Tag2", "Tag3"},
Achievements: []Achievements{
{
Name: "第一届钱潮杯普法短视频创意大赛",
Score: 0,
ArchiveScore: 10,
Achieved: false,
},
{
Name: "卡尔·马克思杯2",
Score: 100,
ArchiveScore: 10,
Achieved: true,
},
var overview = Overview{
StaffId: "22050626",
StaffName: "王文杰",
ClassNo: "22184111",
Score: 100,
Tags: []string{"Tag1", "Tag2", "Tag3"},
Achievements: []Achievements{
{
Name: "第一届钱潮杯普法短视频创意大赛",
Score: 0,
ArchiveScore: 10,
Achieved: false,
},
Pins: []Pins{
{
Name: "学生工作奖",
Score: 10,
}, {
Name: "学习进步奖",
Score: 10,
},
{
Name: "优秀学生干部",
Score: 10,
},
{
Name: "卡尔·马克思杯2",
Score: 100,
ArchiveScore: 10,
Achieved: true,
},
}
},
Pins: []Pins{
{
Name: "学生工作奖",
Score: 10,
}, {
Name: "学习进步奖",
Score: 10,
},
{
Name: "优秀学生干部",
Score: 10,
},
},
}
var achievementOverview = AchievementOverview{
StaffId: "22050626",
StaffName: "王文杰",
ClassNo: "22184111",
Achievements: map[string]bool{
"第一届钱潮杯普法短视频创意大赛": true,
"卡尔·马克思杯2": false,
},
}

func Test_exporter_buildHeader(t *testing.T) {

wb := NewWorkBook()
sheet, err := wb.AddSheet("总览", Overview{})
_ = overview
sheetOverview, err := wb.AddSheet("总览", Overview{})
if err != nil {
t.Fatal(err)
}
err = sheetOverview.AddRecord(overview)
err = sheetOverview.AddRecord(overview)
err = sheetOverview.AddRecord(overview)
if err != nil {
t.Fatal(err)
}
err = sheet.AddRecord(overview)
sheetAchievementOverview, err := wb.AddSheet("达标情况", AchievementOverview{
Achievements: map[string]bool{
"第一届钱潮杯普法短视频创意大赛": false,
"卡尔·马克思杯2": false,
},
})
if err != nil {
t.Fatal(err)
}
err = sheetAchievementOverview.AddRecord(achievementOverview)
err = sheetAchievementOverview.AddRecord(achievementOverview)
err = sheetAchievementOverview.AddRecord(achievementOverview)
file := wb.Export()
if err := file.SaveAs("test.xlsx"); err != nil {
t.Fatal(err)
Expand Down
97 changes: 39 additions & 58 deletions sheet.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,15 @@ type sheet struct {
name string
template template
file *excelize.File
offset int
index map[string]int
length int
colIndex map[string]int
}

func newSheet(name string, model any, file *excelize.File) (s Sheet, err error) {
var e = &sheet{
name: name,
index: make(map[string]int),
file: file,
name: name,
colIndex: make(map[string]int),
file: file,
}
e.template, err = newTemplate(model)
if err != nil {
Expand All @@ -40,7 +40,7 @@ func newSheet(name string, model any, file *excelize.File) (s Sheet, err error)
}

func (s *sheet) buildHeader() (err error) {
s.offset = s.template.depth()
s.length = s.template.depth()

currentColumn := 1
var mergeRanges [][2]string // 用于记录需要合并的单元格范围
Expand All @@ -59,11 +59,11 @@ func (s *sheet) buildHeader() (err error) {
if err != nil {
return err
}
s.index[node.fieldPath] = currentColumn
s.colIndex[node.fieldPath] = currentColumn

if len(node.subItems) == 0 { // 如果是叶子节点
if s.offset > row { // 需要跨行合并
cellEnd, _ := excelize.CoordinatesToCellName(currentColumn, s.offset)
if s.length > row { // 需要跨行合并
cellEnd, _ := excelize.CoordinatesToCellName(currentColumn, s.length)
mergeRanges = append(mergeRanges, [2]string{cell, cellEnd})
}
currentColumn++ // 移动到下一个列
Expand Down Expand Up @@ -112,63 +112,44 @@ func (s *sheet) AddRecords(records []any) error {
return nil
}

func (s *sheet) AddRecord(record any) error {
func (s *sheet) AddRecord(record any) (err error) {
if reflect.TypeOf(record) != s.template.t {
return fmt.Errorf("record type mismatch")
}

v := reflect.ValueOf(record)
startIdx := s.offset + 1

var insert func(v reflect.Value, node *itemNode) error
insert = func(v reflect.Value, node *itemNode) error {
for _, item := range node.subItems {
val := v.FieldByName(item.name)
switch val.Kind() {
case reflect.Struct:
err := insert(val, item)
if err != nil {
return err
}

case reflect.Slice:
currentIdx := s.offset + 1
for i := 0; i < val.Len(); i++ {
val := val.Index(i)
if val.Kind() == reflect.Struct {
tmpIdx := startIdx
startIdx = currentIdx
err := insert(val, item)
if err != nil {
return err
}
startIdx = tmpIdx
} else {
cell, _ := excelize.CoordinatesToCellName(s.index[item.fieldPath], currentIdx)
err := s.file.SetCellValue(s.name, cell, val.Interface())
if err != nil {
return err
}
}
currentIdx++
}
startIdx = max(startIdx, currentIdx)

default:
cell, _ := excelize.CoordinatesToCellName(s.index[item.fieldPath], startIdx)
err := s.file.SetCellValue(s.name, cell, val.Interface())
if err != nil {
return err
}
var insert func(v reflect.Value, node *itemNode, currIdx int) (err error, maxIdx int)
insert = func(v reflect.Value, node *itemNode, currIdx int) (err error, maxIdx int) {
maxIdx = max(currIdx, maxIdx)
switch v.Kind() {
case reflect.Struct:
for _, child := range node.subItems {
_, childMaxIdx := insert(v.FieldByName(child.name), child, currIdx)
maxIdx = max(maxIdx, childMaxIdx)
}
case reflect.Map:
for i, key := range v.MapKeys() {
_, childMaxIdx := insert(v.MapIndex(key), node.subItems[i], currIdx)
maxIdx = max(maxIdx, childMaxIdx)
}
case reflect.Slice:
for i := 0; i < v.Len(); i++ {
_, childMaxIdx := insert(v.Index(i), node, currIdx+i)
maxIdx = max(maxIdx, childMaxIdx)
}
default:
s.writeCell(node.fieldPath, currIdx, v.Interface())
}
return nil
return
}

err := insert(v, s.template.items)
if err != nil {
return err
}
s.offset = startIdx - 1
return nil
err, s.length = insert(v, s.template.items, s.length+1)
return
}

func (s *sheet) writeCell(colName string, row int, value any) error {
cell, _ := excelize.CoordinatesToCellName(s.colIndex[colName], row)
fmt.Println(colName, "->", s.colIndex[colName], row, cell, value)
return s.file.SetCellValue(s.name, cell, value)
}
37 changes: 33 additions & 4 deletions template.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,11 @@ func (n itemNode) depth() int {
func newTemplate(model any) (template, error) {
return template{
t: reflect.TypeOf(model),
items: buildItemTree(reflect.TypeOf(model), ""),
items: buildItemTree(model, reflect.TypeOf(model), ""),
}, nil
}

func buildItemTree(t reflect.Type, parentPath string) *itemNode {
func buildItemTree(model any, t reflect.Type, parentPath string) *itemNode {
// 处理指针类型,我们需要其指向的元素类型
if t.Kind() == reflect.Ptr {
t = t.Elem()
Expand All @@ -59,7 +59,6 @@ func buildItemTree(t reflect.Type, parentPath string) *itemNode {
t = t.Elem()
}

// 只有结构体类型才有子字段需要遍历
if t.Kind() == reflect.Struct {
for i := 0; i < t.NumField(); i++ {
field := t.Field(i)
Expand All @@ -70,7 +69,7 @@ func buildItemTree(t reflect.Type, parentPath string) *itemNode {
}

// 为当前字段创建 itemNode,并递归处理嵌套结构体字段
childNode := buildItemTree(field.Type, fieldPath)
childNode := buildItemTree(model, field.Type, fieldPath)
childNode.name = field.Name // 更新为实际的字段名
childNode.tagName = field.Name
if name, ok := field.Tag.Lookup("excel"); ok {
Expand All @@ -79,6 +78,36 @@ func buildItemTree(t reflect.Type, parentPath string) *itemNode {
childNode.fieldPath = fieldPath
node.subItems = append(node.subItems, childNode)
}
} else if t.Kind() == reflect.Map {
val, err := getValueByPath(model, parentPath)
if err != nil {
panic(err)
}

valMap := val.(map[string]bool)
for key := range valMap {
childNode := &itemNode{
name: key,
tagName: key,
fieldPath: parentPath + "." + key,
subItems: []*itemNode{},
}
node.subItems = append(node.subItems, childNode)
}

//switch t.Elem().Kind() {
//case reflect.Bool:
// for key := range val.(map[string]bool) {
// childNode := &itemNode{
// name: key,
// tagName: key,
// fieldPath: parentPath + "." + t.Name(),
// subItems: []*itemNode{},
// }
// node.subItems = append(node.subItems, childNode)
// }
//
//}
}
return node
}
Expand Down

0 comments on commit 7992ec2

Please sign in to comment.