mirror of
https://gitee.com/xiangheng/x_admin.git
synced 2025-10-05 08:07:06 +08:00
完善excel2导出
This commit is contained in:
@@ -1,13 +1,8 @@
|
||||
package excel2
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"x_admin/util"
|
||||
|
||||
"github.com/xuri/excelize/v2"
|
||||
)
|
||||
@@ -24,16 +19,16 @@ func GetExcelColumnName(columnNumber int) string {
|
||||
}
|
||||
|
||||
// NormalDynamicExport 导出excel
|
||||
//
|
||||
// 需要在传入的结构体中的字段加上tag:excel:"title:列头名称;index:列下标(从0开始);"
|
||||
//
|
||||
// list 要导出的数据
|
||||
// sheet 文档
|
||||
// title 标题
|
||||
// changeHead map[string]string{"Id": "账号", "Name": "真实姓名"}
|
||||
func NormalDynamicExport(list interface{}, sheet string, title string, changeHead map[string]string) (file *excelize.File, err error) {
|
||||
// lists 要导出的数据
|
||||
// cols 列
|
||||
// sheet 文档
|
||||
// title 标题
|
||||
func NormalDynamicExport(lists any, cols []Col, sheet string, title string) (file *excelize.File, err error) {
|
||||
e := ExcelInit()
|
||||
err = ExportExcel(sheet, title, list, changeHead, e)
|
||||
|
||||
listsAny := util.ConvertUtil.StructsToMaps(lists)
|
||||
|
||||
err = ExportExcel(sheet, title, listsAny, cols, e)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@@ -41,57 +36,25 @@ func NormalDynamicExport(list interface{}, sheet string, title string, changeHea
|
||||
}
|
||||
|
||||
// ExportExcel excel导出
|
||||
func ExportExcel(sheet, title string, list interface{}, changeHead map[string]string, e *Excel) (err error) {
|
||||
func ExportExcel(sheet, title string, lists []map[string]interface{}, cols []Col, e *Excel) (err error) {
|
||||
index, _ := e.F.GetSheetIndex(sheet)
|
||||
if index < 0 { // 如果sheet名称不存在
|
||||
e.F.NewSheet(sheet)
|
||||
}
|
||||
// 构造excel表格
|
||||
// 取目标对象的元素类型、字段类型和 tag
|
||||
dataValue := reflect.ValueOf(list)
|
||||
// 判断数据的类型
|
||||
if dataValue.Kind() != reflect.Slice {
|
||||
err = errors.New("invalid data type")
|
||||
return
|
||||
}
|
||||
// 构造表头
|
||||
endColName, dataRow, err := normalBuildTitle(e, sheet, title, changeHead, dataValue)
|
||||
endColName, startDataRow, err := normalBuildTitle(e, sheet, title, cols)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
// 构造数据行
|
||||
err = normalBuildDataRow(e, sheet, endColName, dataRow, dataValue)
|
||||
err = normalBuildDataRow(e, sheet, endColName, startDataRow, lists, cols)
|
||||
return
|
||||
}
|
||||
|
||||
// 构造表头(endColName 最后一列的列名 dataRow 数据行开始的行号)
|
||||
func normalBuildTitle(e *Excel, sheet, title string, changeHead map[string]string, dataValue reflect.Value) (endColName string, dataRow int, err error) {
|
||||
dataType := dataValue.Type().Elem() // 获取导入目标对象的类型信息
|
||||
var exportTitle []ExcelTag // 遍历目标对象的字段
|
||||
for i := 0; i < dataType.NumField(); i++ {
|
||||
var excelTag ExcelTag
|
||||
field := dataType.Field(i) // 获取字段信息和tag
|
||||
tag := field.Tag.Get(ExcelTagKey)
|
||||
if tag == "" { // 如果非导出则跳过
|
||||
continue
|
||||
}
|
||||
|
||||
err = excelTag.GetTag(tag)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
// 更改指定字段的表头标题
|
||||
if changeHead != nil && changeHead[field.Name] != "" {
|
||||
excelTag.Name = changeHead[field.Name]
|
||||
}
|
||||
exportTitle = append(exportTitle, excelTag)
|
||||
}
|
||||
// 排序
|
||||
sort.Slice(exportTitle, func(i, j int) bool {
|
||||
return exportTitle[i].Index < exportTitle[j].Index
|
||||
})
|
||||
// 构造表头(endColName 最后一列的列名 startDataRow 数据行开始的行号)
|
||||
func normalBuildTitle(e *Excel, sheet, title string, cols []Col) (endColName string, startDataRow int, err error) {
|
||||
var titleRowData []interface{} // 列头行
|
||||
for i, colTitle := range exportTitle {
|
||||
for i, colTitle := range cols {
|
||||
endColName := GetExcelColumnName(i + 1)
|
||||
if colTitle.Width > 0 { // 根据给定的宽度设置列宽
|
||||
_ = e.F.SetColWidth(sheet, endColName, endColName, float64(colTitle.Width))
|
||||
@@ -102,7 +65,7 @@ func normalBuildTitle(e *Excel, sheet, title string, changeHead map[string]strin
|
||||
}
|
||||
endColName = GetExcelColumnName(len(titleRowData)) // 根据列数生成 Excel 列名
|
||||
if title != "" {
|
||||
dataRow = 3 // 如果有title,那么从第3行开始就是数据行,第1行是title,第2行是表头
|
||||
startDataRow = 3 // 如果有title,那么从第3行开始就是数据行,第1行是title,第2行是表头
|
||||
e.F.SetCellValue(sheet, "A1", title)
|
||||
e.F.MergeCell(sheet, "A1", endColName+"1") // 合并标题单元格
|
||||
e.F.SetCellStyle(sheet, "A1", endColName+"1", e.TitleStyle)
|
||||
@@ -113,7 +76,7 @@ func normalBuildTitle(e *Excel, sheet, title string, changeHead map[string]strin
|
||||
return
|
||||
}
|
||||
} else {
|
||||
dataRow = 2 // 如果没有title,那么从第2行开始就是数据行,第1行是表头
|
||||
startDataRow = 2 // 如果没有title,那么从第2行开始就是数据行,第1行是表头
|
||||
e.F.SetRowHeight(sheet, 1, float64(30))
|
||||
e.F.SetCellStyle(sheet, "A1", endColName+"1", e.HeadStyle)
|
||||
if err = e.F.SetSheetRow(sheet, "A1", &titleRowData); err != nil {
|
||||
@@ -124,96 +87,44 @@ func normalBuildTitle(e *Excel, sheet, title string, changeHead map[string]strin
|
||||
}
|
||||
|
||||
// 构造数据行
|
||||
func normalBuildDataRow(e *Excel, sheet, endColName string, row int, dataValue reflect.Value) (err error) {
|
||||
func normalBuildDataRow(e *Excel, sheet, endColName string, startDataRow int, lists []map[string]interface{}, cols []Col) (err error) {
|
||||
//实时写入数据
|
||||
for i := 0; i < dataValue.Len(); i++ {
|
||||
startCol := fmt.Sprintf("A%d", row)
|
||||
endCol := fmt.Sprintf("%s%d", endColName, row)
|
||||
item := dataValue.Index(i)
|
||||
typ := item.Type()
|
||||
num := item.NumField()
|
||||
var exportRow []ExcelTag
|
||||
maxLen := 0 // 记录这一行中,数据最多的单元格的值的长度
|
||||
//遍历结构体的所有字段
|
||||
for j := 0; j < num; j++ {
|
||||
dataField := typ.Field(j) //获取到struct标签,需要通过reflect.Type来获取tag标签的值
|
||||
tagVal := dataField.Tag.Get(ExcelTagKey)
|
||||
if tagVal == "" { // 如果非导出则跳过
|
||||
continue
|
||||
for i := 0; i < len(lists); i++ {
|
||||
startCol := fmt.Sprintf("A%d", startDataRow)
|
||||
endCol := fmt.Sprintf("%s%d", endColName, startDataRow)
|
||||
|
||||
var rowData []interface{} // 数据列
|
||||
|
||||
list := lists[i]
|
||||
for j := 0; j < len(cols); j++ {
|
||||
col := cols[j]
|
||||
replace := col.Replace
|
||||
|
||||
val := list[col.Key]
|
||||
|
||||
for replaceKey, v := range replace {
|
||||
|
||||
if replaceKey == fmt.Sprintf("%v", val) {
|
||||
val = fmt.Sprintf("%v", v)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
var dataCol ExcelTag
|
||||
err = dataCol.GetTag(tagVal)
|
||||
fieldData := item.FieldByName(dataField.Name) // 取字段值
|
||||
if fieldData.Type().String() == "string" { // string类型的才计算长度
|
||||
rwsTemp := fieldData.Len() // 当前单元格内容的长度
|
||||
if rwsTemp > maxLen { //这里取每一行中的每一列字符长度最大的那一列的字符
|
||||
maxLen = rwsTemp
|
||||
}
|
||||
}
|
||||
// 替换
|
||||
if dataCol.Replace != "" {
|
||||
split := strings.Split(dataCol.Replace, ",")
|
||||
for j := range split {
|
||||
s := strings.Split(split[j], "_") // 根据下划线进行分割,格式:需要替换的内容_替换后的内容
|
||||
value := fieldData.String()
|
||||
if strings.Contains(fieldData.Type().String(), "int") {
|
||||
value = strconv.Itoa(int(fieldData.Int()))
|
||||
} else if fieldData.Type().String() == "bool" {
|
||||
value = strconv.FormatBool(fieldData.Bool())
|
||||
} else if strings.Contains(fieldData.Type().String(), "float") {
|
||||
value = strconv.FormatFloat(fieldData.Float(), 'f', -1, 64)
|
||||
}
|
||||
if s[0] == value {
|
||||
dataCol.Value = s[1]
|
||||
}
|
||||
}
|
||||
} else {
|
||||
dataCol.Value = fieldData
|
||||
}
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
exportRow = append(exportRow, dataCol)
|
||||
rowData = append(rowData, val)
|
||||
}
|
||||
// 排序
|
||||
sort.Slice(exportRow, func(i, j int) bool {
|
||||
return exportRow[i].Index < exportRow[j].Index
|
||||
})
|
||||
var rowData []interface{} // 数据列
|
||||
for _, colTitle := range exportRow {
|
||||
rowData = append(rowData, colTitle.Value)
|
||||
}
|
||||
if row%2 == 0 {
|
||||
|
||||
if startDataRow%2 == 0 {
|
||||
_ = e.F.SetCellStyle(sheet, startCol, endCol, e.ContentStyle2)
|
||||
} else {
|
||||
_ = e.F.SetCellStyle(sheet, startCol, endCol, e.ContentStyle1)
|
||||
}
|
||||
if maxLen > 25 { // 自适应行高
|
||||
d := maxLen / 25
|
||||
f := 25 * d
|
||||
_ = e.F.SetRowHeight(sheet, row, float64(f))
|
||||
} else {
|
||||
_ = e.F.SetRowHeight(sheet, row, float64(25)) // 默认行高25
|
||||
}
|
||||
|
||||
_ = e.F.SetRowHeight(sheet, startDataRow, float64(25)) // 默认行高25
|
||||
|
||||
if err = e.F.SetSheetRow(sheet, startCol, &rowData); err != nil {
|
||||
return
|
||||
}
|
||||
row++
|
||||
startDataRow++
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// 下载
|
||||
func DownLoadExcel(fileName string, res http.ResponseWriter, file *excelize.File) {
|
||||
// 设置响应头
|
||||
res.Header().Set("Content-Type", "text/html; charset=UTF-8")
|
||||
res.Header().Set("Content-Type", "application/octet-stream")
|
||||
res.Header().Set("Content-Disposition", "attachment; filename="+fileName+".xlsx")
|
||||
res.Header().Set("Access-Control-Expose-Headers", "Content-Disposition")
|
||||
err := file.Write(res) // 写入Excel文件内容到响应体
|
||||
if err != nil {
|
||||
http.Error(res, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user