代码拉取完成,页面将自动刷新
package main
import (
"fmt"
"github.com/xuri/excelize/v2"
"reflect"
"regexp"
"strconv"
"strings"
"testing"
)
// 定义一些常量
const (
letterNum = 26 // 字母的个数
headerRow = 1 // 表头的行数
detailRow = 2 // 明细的行数
dataRow = 3 // 数据的起始行数
fieldNameReg = `^[^:|/!@#$^*-]+` // 用于提取字段名的正则表达式
)
// 定义结构体
type ZpRecord struct {
Amount int `json:"amount" excelTag:"薪资金额$0.00:red!14^solid"`
CreateAt string `json:"createAt" excelTag:"薪资日期$yyyy-mm-dd:blue!14^solid"`
Creator string `json:"creator" excelTag:"薪资发放人:green!14^solid"`
Remark string `json:"remark" excelTag:"薪资备注:yellow!14^solid"`
}
type WxRecord struct {
Amount int `json:"amount" excelTag:"微信金额$0.00:red!14^solid"`
CreateAt string `json:"createAt" excelTag:"微信日期$yyyy-mm-dd:blue!14^solid"`
Creator string `json:"creator" excelTag:"微信发放人:green!14^solid"`
Remark string `json:"remark" excelTag:"微信备注:yellow!14^solid"`
}
type QqRecord struct {
Amount int `json:"amount" excelTag:"QQ金额$0.00:red!14^solid"`
CreateAt string `json:"createAt" excelTag:"QQ日期$yyyy-mm-dd:blue!14^solid"`
Creator string `json:"creator" excelTag:"QQ发放人:green!14^solid"`
Remark string `json:"remark" excelTag:"QQ备注:yellow!14^solid"`
}
type Result struct {
Name string `json:"name" excelTag:"姓名:center*!16@微软雅黑#gray" ` // 姓名
Id int `json:"id" excelTag:"编号:center*!16@微软雅黑#gray" ` // 编号
Title string `json:"title" excelTag:"职位:center*!16@微软雅黑#gray" ` // 职位
ZpRecord []ZpRecord `json:"zpRecord" excelTag:"薪资:center*!16@微软雅黑#gray" ` // 薪资
WxRecord []WxRecord `json:"wxRecord" excelTag:"微信:center*!16@微软雅黑#gray" ` // 微信
QqRecord []QqRecord `json:"qqRecord" excelTag:"QQ:center*!16@微软雅黑#gray"` // QQ
}
func TestName(t *testing.T) {
// Define some sample data
results := []Result{
{
Name: "张三",
Id: 1,
Title: "项目经理",
ZpRecord: []ZpRecord{
{Amount: 1000, CreateAt: "2023-01-01", Creator: "李四", Remark: "奖金"},
{Amount: 500, CreateAt: "2023-01-02", Creator: "王五", Remark: "补贴"},
},
WxRecord: []WxRecord{
{Amount: 200, CreateAt: "2023-01-03", Creator: "赵六", Remark: "红包"},
{Amount: 300, CreateAt: "2023-01-04", Creator: "孙七", Remark: "转账"},
{Amount: 300, CreateAt: "2023-01-04", Creator: "孙七", Remark: "转账"},
{Amount: 300, CreateAt: "2023-01-04", Creator: "孙七", Remark: "转账"},
{Amount: 300, CreateAt: "2023-01-04", Creator: "孙七", Remark: "转账"},
},
QqRecord: []QqRecord{
{Amount: 200, CreateAt: "2023-01-03", Creator: "赵六", Remark: "红包"},
{Amount: 300, CreateAt: "2023-01-04", Creator: "孙七", Remark: "转账"},
},
},
{
Name: "李四",
Id: 2,
Title: "开发工程师",
ZpRecord: []ZpRecord{
{Amount: 800, CreateAt: "2023-01-01", Creator: "张三", Remark: "奖金"},
{Amount: 400, CreateAt: "2023-01-02", Creator: "王五", Remark: "补贴"},
},
WxRecord: []WxRecord{
{Amount: 100, CreateAt: "2023-01-03", Creator: "赵六", Remark: "红包"},
{Amount: 200, CreateAt: "2023-01-04", Creator: "孙七", Remark: "转账"},
},
QqRecord: []QqRecord{
{Amount: 200, CreateAt: "2023-01-03", Creator: "赵六", Remark: "红包"},
},
},
}
exportExcel(results, "test.xlsx")
}
// 导出excel文件
func exportExcel(results []Result, filename string) error {
// 创建excel文件
f := excelize.NewFile()
// 创建工作表
index, _ := f.NewSheet("Sheet1")
// 获取结果的类型
resultType := reflect.TypeOf(Result{})
// 获取结果的字段数
fieldNum := resultType.NumField()
// 设置表头
row := headerRow // 当前行数
col := 1 // 当前列数
for i := 0; i < fieldNum; i++ {
// 获取字段
field := resultType.Field(i)
// 获取字段的excelTag
tag := field.Tag.Get("excelTag")
// 解析excelTag标签,提取字段名
// 解析excelTag标签,提取字段名
fieldName := regexp.MustCompile(fieldNameReg).FindString(tag)
// 写入表头
f.SetCellValue("Sheet1", getCellName(col, row), fieldName)
// 如果字段是切片类型,获取切片的元素类型
if field.Type.Kind() == reflect.Slice {
elemType := field.Type.Elem()
// 如果元素类型是结构体类型,获取结构体的字段数
if elemType.Kind() == reflect.Struct {
elemFieldNum := elemType.NumField()
// 合并表头
f.MergeCell("Sheet1", getCellName(col, row), getCellName(col+elemFieldNum-1, row))
// 设置明细的表头
row++ // 下一行
for j := 0; j < elemFieldNum; j++ {
// 获取结构体的字段
elemField := elemType.Field(j)
// 获取字段的excelTag
elemTag := elemField.Tag.Get("excelTag")
// 解析excelTag标签,提取字段名
fieldName := regexp.MustCompile(fieldNameReg).FindString(elemTag)
// 写入明细的表头
f.SetCellValue("Sheet1", getCellName(col, row), fieldName)
col++ // 下一列
}
row-- // 回到上一行
}
} else {
col++ // 下一列
}
}
// 遍历结果
row = dataRow // 当前行数
for _, result := range results {
// 获取结果的值
resultValue := reflect.ValueOf(result)
// 获取最大的记录数
maxLen := 0
for i := 0; i < fieldNum; i++ {
// 获取字段的值
fieldValue := resultValue.Field(i)
// 如果字段是切片类型,获取切片的长度
if fieldValue.Kind() == reflect.Slice {
sliceLen := fieldValue.Len()
// 如果切片的长度大于最大的记录数,更新最大的记录数
if sliceLen > maxLen {
maxLen = sliceLen
}
}
}
// 写入数据
col = 1 // 当前列数
for i := 0; i < fieldNum; i++ {
// 获取字段的值
fieldValue := resultValue.Field(i)
// 判断字段的类型
switch fieldValue.Kind() {
case reflect.Slice: // 如果字段是切片类型
// 获取切片的元素类型
elemType := fieldValue.Type().Elem()
// 判断元素类型
switch elemType.Kind() {
case reflect.Struct: // 如果元素类型是结构体类型
// 获取结构体的字段数
elemFieldNum := elemType.NumField()
// 写入切片的数据
for j := 0; j < maxLen; j++ {
// 如果有切片的元素,获取元素的值
if j < fieldValue.Len() {
elemValue := fieldValue.Index(j)
// 遍历结构体的字段
for k := 0; k < elemFieldNum; k++ {
// 获取结构体的字段的值
elemFieldValue := elemValue.Field(k)
// 写入数据
f.SetCellValue("Sheet1", getCellName(col, row+j), elemFieldValue.Interface())
col++ // 下一列
}
col -= elemFieldNum // 回到切片的第一列
}
}
col += elemFieldNum // 跳到下一个切片的第一列
}
default: // 如果字段是非切片类型
// 写入非切片的数据
f.SetCellValue("Sheet1", getCellName(col, row), fieldValue.Interface())
// 如果记录数大于1,合并单元格
if maxLen > 1 {
f.MergeCell("Sheet1", getCellName(col, row), getCellName(col, row+maxLen-1))
}
col++ // 下一列
}
}
// 更新行数
row += maxLen
}
// 设置工作表为活动表
f.SetActiveSheet(index)
// 保存文件
err := f.SaveAs(filename)
if err != nil {
return err
}
return nil
}
// 定义一个函数,根据列数和行数返回单元格的名称
func getCellName(col, row int) string {
return fmt.Sprintf("%s%d", getColName(col), row)
}
// 获取列名
func getColName(col int) string {
// 定义列名的字母
letters := []string{"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"}
// 定义列名的切片
colNames := []string{}
// 判断列的序号是否大于26
if col > 26 {
// 使用双字母的列名
// 获取第一个字母的位置
first := (col - 1) / 26
// 获取第二个字母的位置
second := col % 26
// 如果第二个字母的位置为0,说明是Z
if second == 0 {
// 添加第一个字母到列名的切片
colNames = append(colNames, letters[first-2])
// 添加Z到列名的切片
colNames = append(colNames, "Z")
} else {
// 添加第一个字母到列名的切片
colNames = append(colNames, letters[first-1])
// 添加第二个字母到列名的切片
colNames = append(colNames, letters[second-1])
}
} else {
// 使用单字母的列名
// 获取余数
rem := col % 26
// 如果余数为0,说明是Z
if rem == 0 {
// 添加Z到列名的切片
colNames = append(colNames, "Z")
} else {
// 添加对应的字母到列名的切片
colNames = append(colNames, letters[rem-1])
}
}
// 拼接列名的切片
colName := strings.Join(colNames, "")
// 返回列名
return colName
}
// 定义一个函数,根据excelTag标签生成一个样式的字符串
func getCellStyle(tag string) *excelize.Style {
// 定义一个样式的结构体
// 定义一个样式的变量
var s excelize.Style
// 定义一个颜色的映射
colors := map[string]string{
"black": "FF000000",
"white": "FFFFFFFF",
"red": "FFFF0000",
"green": "FF00FF00",
"blue": "FF0000FF",
"yellow": "FFFFFF00",
"gray": "FF808080",
}
// 定义一个数字格式的映射
numFmts := map[string]int{
"0.00": 2,
"yyyy-mm-dd": 14,
"0.00%": 10,
}
// 定义一个边框样式的映射
borderStyles := map[string]int{
"solid": 1,
"dashed": 3,
"dotted": 4,
}
// 遍历excelTag标签中的符号
for _, symbol := range strings.Split(tag, "") {
// 根据符号的类型,设置样式的属性
switch symbol {
case "|":
// 水平合并单元格,设置水平居中
s.Alignment.Horizontal = "center"
case "-":
// 垂直合并单元格,设置垂直居中
s.Alignment.Vertical = "center"
case ":":
// 指定对齐方式,获取冒号后面的字符串
align := strings.Split(tag, ":")[1]
// 根据对齐方式,设置水平或垂直对齐
if align == "left" || align == "right" || align == "center" {
s.Alignment.Horizontal = align
} else if align == "top" || align == "bottom" || align == "middle" {
s.Alignment.Vertical = align
}
case "*":
// 指定字体加粗,设置字体的粗细
s.Font.Bold = true
case "/":
// 指定字体颜色,获取斜杠后面的字符串
color := strings.Split(tag, "/")[1]
// 根据颜色名称,设置字体的颜色
if hex, ok := colors[color]; ok {
s.Font.Color = hex
}
case "#":
// 指定背景颜色,获取井号后面的字符串
color := strings.Split(tag, "#")[1]
// 根据颜色名称,设置背景的颜色
if hex, ok := colors[color]; ok {
s.Fill.Type = "pattern"
s.Fill.Pattern = 1
s.Fill.Color = []string{hex}
}
case "^":
// 指定边框样式,获取插入符号后面的字符串
style := strings.Split(tag, "^")[1]
// 根据边框样式,设置边框的样式
if num, ok := borderStyles[style]; ok {
s.Border = []excelize.Border{
{Type: "left", Color: "FF000000", Style: num},
{Type: "right", Color: "FF000000", Style: num},
{Type: "top", Color: "FF000000", Style: num},
{Type: "bottom", Color: "FF000000", Style: num},
}
}
case "!":
// 指定字体大小,获取感叹号后面的字符串
size, err := strconv.Atoi(strings.Split(tag, "!")[1])
// 如果没有错误,设置字体的大小
if err == nil {
s.Font.Size = float64(size)
}
case "@":
// 指定字体名称,获取@后面的字符串
name := strings.Split(tag, "@")[1]
// 设置字体的名称
s.Font.Family = name
case "$":
// 指定数字格式,获取$后面的字符串
format := strings.Split(tag, "$")[1]
// 根据数字格式,设置数字的格式
if num, ok := numFmts[format]; ok {
s.NumFmt = num
}
}
}
return &s
}
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。