375 lines
8.1 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package utils
import (
"bytes"
"errors"
"fmt"
"github.com/xuri/excelize/v2"
"reflect"
)
// HeaderCellData 表头内容
type HeaderCellData struct {
Value string // 值
CellType string // 类型
NumberFmt string // 格式化方式
ColWidth int // 列宽
Colour string // 颜色
}
// GenerateSheet 生成表格
func GenerateSheet(header []HeaderCellData, sheetName string) (f *excelize.File, err error) {
// 创建工作表
err, f = createSheet(sheetName)
if err != nil {
return nil, err
}
// 设置表头
if err := setHeader(f, sheetName, header); err != nil {
return nil, err
}
return f, nil
}
// 创建工作表
func createSheet(sheetName string) (err error, f *excelize.File) {
f = excelize.NewFile()
defer func() {
_ = f.Close()
}()
// 创建一个工作表
index, err := f.NewSheet(sheetName)
if err != nil {
return err, nil
}
// 设置工作簿的默认工作表
f.SetActiveSheet(index)
// 设置工作表默认字体
err = f.SetDefaultFont("宋体")
if err != nil {
return err, nil
}
// 设置行高 35-第一行
err = f.SetRowHeight(sheetName, 1, 35)
if err != nil {
return err, nil
}
return nil, f
}
// 设置表头
func setHeader(f *excelize.File, sheetName string, header []HeaderCellData) error {
for c, cell := range header {
// 获取列名
colName, err := excelize.ColumnNumberToName(c + 1)
if err != nil {
return err
}
// 添加单元格的值
if err := f.SetCellValue(sheetName, colName+"1", cell.Value); err != nil {
return err
}
// 单元格颜色填充样式
var fill excelize.Fill
if cell.Colour != "" {
fill = excelize.Fill{
Type: "pattern",
Pattern: 1,
Color: []string{cell.Colour},
Shading: 0,
}
}
// 第一行的左、右、下边框
border := []excelize.Border{
{Type: "left", Color: "#000000", Style: 1},
{Type: "right", Color: "#000000", Style: 1},
{Type: "bottom", Color: "#000000", Style: 1},
}
// 设置单元格值类型和格式
style, err := f.NewStyle(&excelize.Style{
Alignment: &excelize.Alignment{
Horizontal: "center",
Vertical: "center",
},
Fill: fill,
Border: border,
})
if err != nil {
return err
}
if err := f.SetCellStyle(sheetName, colName+"1", colName+"1", style); err != nil {
return err
}
if err := f.SetColWidth(sheetName, colName, colName, float64(cell.ColWidth)); err != nil {
return err
}
}
return nil
}
// Export 导出
func Export(header []HeaderCellData, data []interface{}) (b *bytes.Buffer, err error) {
sheetName := "Sheet1"
// 生成表格
f, err := GenerateSheet(header, sheetName)
if err != nil {
return nil, err
}
alignment := &excelize.Alignment{
Horizontal: "center",
Vertical: "center",
}
row := 1
for _, item := range data {
var err error
row, err = fillDataWithMerge(f, sheetName, header, item, row, alignment)
if err != nil {
return nil, err
}
}
//buffer, err := f.WriteToBuffer()
//if err != nil {
// return nil, err
//}
//return buffer, nil
// 保存文件
if err := f.SaveAs("output.xlsx"); err != nil {
return nil, err
}
return nil, errors.New("已导出文件")
}
// 填充数据
func fillDataWithMerge(f *excelize.File, sheetName string, header []HeaderCellData, data interface{}, row int, alignment *excelize.Alignment) (int, error) {
v := reflect.ValueOf(data)
// 判断是否是结构体或指向结构体的指针
if v.Kind() == reflect.Ptr {
v = v.Elem()
}
if v.Kind() != reflect.Struct {
panic("data must be a struct or a pointer to struct")
}
var axis string // 坐标值
colOffset := 0 // 列偏移量
mergeNum := 0 // 合并单元格数量。默认为0每次+1当存在切片时启用
var mergeNumSlice []string // 需合并单元格列名。默认为空,当存在切片时启用["A","B"]
filedNumSlice := 1 // 切片数量默认为1,此数量为其余需合并的数量
for i := 0; i < v.NumField(); i++ {
field := v.Field(i)
// 获取字段类型
valueType := field.Kind()
if valueType != reflect.Slice {
// 获取字段值
cellValue := field.Interface()
// 获取列名
colName, err := excelize.ColumnNumberToName(colOffset + 1)
if err != nil {
return 0, err
}
// 获取坐标,+1表示去除header行
axis = colName + fmt.Sprintf("%d", row+1)
// 填入值
err = f.SetCellValue(sheetName, axis, cellValue)
if err != nil {
return 0, err
}
// 设置单元格样式
err = setCellStyle(f, sheetName, axis, header[i], alignment)
if err != nil {
return 0, err
}
// 设置行高 35-第一行
err = f.SetRowHeight(sheetName, row+1, 35)
if err != nil {
return 0, err
}
// 合并单元格数量
mergeNum = mergeNum + 1
mergeNumSlice = append(mergeNumSlice, colName)
} else {
var sliceFieldNum int // 切片字段数量
// 最大切片数量,此数量为其余需合并的数量
if filedNumSlice < field.Len() {
filedNumSlice = field.Len()
}
for j := 0; j < field.Len(); j++ {
item := field.Index(j).Interface()
v1 := reflect.ValueOf(item)
for i2 := 0; i2 < field.Index(j).NumField(); i2++ {
// 获取字段值
cellValue := v1.Field(i2).Interface()
// 获取列名
colName, err := excelize.ColumnNumberToName(i + i2 + 1)
if err != nil {
return 0, err
}
// 获取坐标,+1表示去除header行
axis = colName + fmt.Sprintf("%d", row+j+1)
// 填入值
err = f.SetCellValue(sheetName, axis, cellValue)
if err != nil {
return 0, err
}
// 设置单元格样式
err = setCellStyle(f, sheetName, axis, header[i+i2], alignment)
if err != nil {
return row, err
}
// 设置行高 35-第一行
err = f.SetRowHeight(sheetName, row+j+1, 35)
if err != nil {
return row, err
}
sliceFieldNum = i2
}
}
// 列偏移量需增加上切片字段数量
colOffset = colOffset + sliceFieldNum
}
colOffset++
}
// 合并单元格
if filedNumSlice > 1 {
for _, s := range mergeNumSlice {
// 设置单元格样式
for i := 0; i < filedNumSlice; i++ {
axis = s + fmt.Sprintf("%d", row+i+1)
err := setCellStyle(f, sheetName, axis, header[row+i], alignment)
if err != nil {
return row, err
}
}
startCell := s + fmt.Sprintf("%d", row+1)
endCell := s + fmt.Sprintf("%d", row+filedNumSlice)
err := f.MergeCell(sheetName, startCell, endCell)
if err != nil {
return 0, err
}
}
}
// 行数 = 切片数量如不存在切片filedNumSlice默认为1
row = row + filedNumSlice
// 返回当前处理到的行数
return row, nil
}
// 设置单元格样式
func setCellStyle(f *excelize.File, sheetName, axis string, header HeaderCellData, alignment *excelize.Alignment) error {
// 获取现有样式id
styleID, err := f.GetCellStyle(sheetName, axis)
if err != nil {
return err
}
// 处理样式问题
var style *excelize.Style
if styleID == 0 {
// 新样式
style = &excelize.Style{
Alignment: alignment,
}
} else {
// 存在现有样式,直接获取
style, err = f.GetStyle(styleID)
if err != nil {
return err
}
}
if header.CellType == "float" {
style.NumFmt = 2
customNumFmt := "0.000"
style.CustomNumFmt = &customNumFmt
}
if header.CellType == "date" {
style.NumFmt = 22
customNumFmt := "yyyy-mm-dd hh:mm:ss"
style.CustomNumFmt = &customNumFmt
}
// 颜色-可用,但是目前无需使用
if header.Colour != "" {
// 单元格颜色
//fill := excelize.Fill{
// Type: "pattern",
// Pattern: 1,
// Color: []string{header.Colour},
// Shading: 0,
//}
//
//style.Fill = fill
//
//// 字体颜色
//font := &excelize.Font{
// Color: header.Colour,
//}
//
//style.Font = font
}
// 边框
border := []excelize.Border{
{Type: "left", Color: "#000000", Style: 1},
{Type: "right", Color: "#000000", Style: 1},
{Type: "bottom", Color: "#000000", Style: 1},
}
style.Border = border
newStyle, err := f.NewStyle(style)
if err != nil {
return err
}
if err := f.SetCellStyle(sheetName, axis, axis, newStyle); err != nil {
return err
}
return nil
}