mirror of
https://gitee.com/xiangheng/x_admin.git
synced 2025-10-05 16:17:00 +08:00
完善excel2导出
This commit is contained in:
@@ -8,7 +8,6 @@ import (
|
|||||||
"x_admin/core/request"
|
"x_admin/core/request"
|
||||||
"x_admin/core/response"
|
"x_admin/core/response"
|
||||||
"x_admin/util"
|
"x_admin/util"
|
||||||
"x_admin/util/excel"
|
|
||||||
"x_admin/util/excel2"
|
"x_admin/util/excel2"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
@@ -200,20 +199,14 @@ func (hd *SystemLogSmsHandler) ExportFile(c *gin.Context) {
|
|||||||
response.FailWithMsg(c, response.SystemError, "查询信息失败")
|
response.FailWithMsg(c, response.SystemError, "查询信息失败")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// f, err := excel.NormalDynamicExport(res, "Sheet1", "系统短信日志", nil)
|
|
||||||
// if err != nil {
|
|
||||||
// response.FailWithMsg(c, response.SystemError, "导出失败")
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
|
|
||||||
list := util.ConvertUtil.StructsToMaps(res)
|
f, err := excel2.NormalDynamicExport(res, cols, "Sheet1", "系统短信日志")
|
||||||
f, err := excel2.NormalDynamicExport2(list, cols, "Sheet1", "系统短信日志")
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
response.FailWithMsg(c, response.SystemError, "导出失败")
|
response.FailWithMsg(c, response.SystemError, "导出失败")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
excel.DownLoadExcel("系统短信日志"+time.Now().Format("20060102-150405"), c.Writer, f)
|
excel2.DownLoadExcel("系统短信日志"+time.Now().Format("20060102-150405"), c.Writer, f)
|
||||||
}
|
}
|
||||||
|
|
||||||
// @Summary 系统短信日志导入
|
// @Summary 系统短信日志导入
|
||||||
|
@@ -1,6 +1,8 @@
|
|||||||
package excel2
|
package excel2
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
"github.com/xuri/excelize/v2"
|
"github.com/xuri/excelize/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -11,6 +13,28 @@ type Excel struct {
|
|||||||
ContentStyle1 int // 主体样式1,无背景色
|
ContentStyle1 int // 主体样式1,无背景色
|
||||||
ContentStyle2 int // 主体样式2,有背景色
|
ContentStyle2 int // 主体样式2,有背景色
|
||||||
}
|
}
|
||||||
|
type Col struct {
|
||||||
|
Name string
|
||||||
|
Key string
|
||||||
|
Width int
|
||||||
|
Replace map[string]any
|
||||||
|
Encode func(value any) any //暂未使用
|
||||||
|
Decode func(value any) any
|
||||||
|
}
|
||||||
|
|
||||||
|
// 下载
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 初始化
|
// 初始化
|
||||||
func ExcelInit() (e *Excel) {
|
func ExcelInit() (e *Excel) {
|
||||||
|
@@ -1,13 +1,8 @@
|
|||||||
package excel2
|
package excel2
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"x_admin/util"
|
||||||
"reflect"
|
|
||||||
"sort"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/xuri/excelize/v2"
|
"github.com/xuri/excelize/v2"
|
||||||
)
|
)
|
||||||
@@ -24,16 +19,16 @@ func GetExcelColumnName(columnNumber int) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// NormalDynamicExport 导出excel
|
// NormalDynamicExport 导出excel
|
||||||
//
|
// lists 要导出的数据
|
||||||
// 需要在传入的结构体中的字段加上tag:excel:"title:列头名称;index:列下标(从0开始);"
|
// cols 列
|
||||||
//
|
// sheet 文档
|
||||||
// list 要导出的数据
|
// title 标题
|
||||||
// sheet 文档
|
func NormalDynamicExport(lists any, cols []Col, sheet string, title string) (file *excelize.File, err error) {
|
||||||
// title 标题
|
|
||||||
// changeHead map[string]string{"Id": "账号", "Name": "真实姓名"}
|
|
||||||
func NormalDynamicExport(list interface{}, sheet string, title string, changeHead map[string]string) (file *excelize.File, err error) {
|
|
||||||
e := ExcelInit()
|
e := ExcelInit()
|
||||||
err = ExportExcel(sheet, title, list, changeHead, e)
|
|
||||||
|
listsAny := util.ConvertUtil.StructsToMaps(lists)
|
||||||
|
|
||||||
|
err = ExportExcel(sheet, title, listsAny, cols, e)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -41,57 +36,25 @@ func NormalDynamicExport(list interface{}, sheet string, title string, changeHea
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ExportExcel excel导出
|
// 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)
|
index, _ := e.F.GetSheetIndex(sheet)
|
||||||
if index < 0 { // 如果sheet名称不存在
|
if index < 0 { // 如果sheet名称不存在
|
||||||
e.F.NewSheet(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 {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// 构造数据行
|
// 构造数据行
|
||||||
err = normalBuildDataRow(e, sheet, endColName, dataRow, dataValue)
|
err = normalBuildDataRow(e, sheet, endColName, startDataRow, lists, cols)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 构造表头(endColName 最后一列的列名 dataRow 数据行开始的行号)
|
// 构造表头(endColName 最后一列的列名 startDataRow 数据行开始的行号)
|
||||||
func normalBuildTitle(e *Excel, sheet, title string, changeHead map[string]string, dataValue reflect.Value) (endColName string, dataRow int, err error) {
|
func normalBuildTitle(e *Excel, sheet, title string, cols []Col) (endColName string, startDataRow 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
|
|
||||||
})
|
|
||||||
var titleRowData []interface{} // 列头行
|
var titleRowData []interface{} // 列头行
|
||||||
for i, colTitle := range exportTitle {
|
for i, colTitle := range cols {
|
||||||
endColName := GetExcelColumnName(i + 1)
|
endColName := GetExcelColumnName(i + 1)
|
||||||
if colTitle.Width > 0 { // 根据给定的宽度设置列宽
|
if colTitle.Width > 0 { // 根据给定的宽度设置列宽
|
||||||
_ = e.F.SetColWidth(sheet, endColName, endColName, float64(colTitle.Width))
|
_ = 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 列名
|
endColName = GetExcelColumnName(len(titleRowData)) // 根据列数生成 Excel 列名
|
||||||
if title != "" {
|
if title != "" {
|
||||||
dataRow = 3 // 如果有title,那么从第3行开始就是数据行,第1行是title,第2行是表头
|
startDataRow = 3 // 如果有title,那么从第3行开始就是数据行,第1行是title,第2行是表头
|
||||||
e.F.SetCellValue(sheet, "A1", title)
|
e.F.SetCellValue(sheet, "A1", title)
|
||||||
e.F.MergeCell(sheet, "A1", endColName+"1") // 合并标题单元格
|
e.F.MergeCell(sheet, "A1", endColName+"1") // 合并标题单元格
|
||||||
e.F.SetCellStyle(sheet, "A1", endColName+"1", e.TitleStyle)
|
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
|
return
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
dataRow = 2 // 如果没有title,那么从第2行开始就是数据行,第1行是表头
|
startDataRow = 2 // 如果没有title,那么从第2行开始就是数据行,第1行是表头
|
||||||
e.F.SetRowHeight(sheet, 1, float64(30))
|
e.F.SetRowHeight(sheet, 1, float64(30))
|
||||||
e.F.SetCellStyle(sheet, "A1", endColName+"1", e.HeadStyle)
|
e.F.SetCellStyle(sheet, "A1", endColName+"1", e.HeadStyle)
|
||||||
if err = e.F.SetSheetRow(sheet, "A1", &titleRowData); err != nil {
|
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++ {
|
for i := 0; i < len(lists); i++ {
|
||||||
startCol := fmt.Sprintf("A%d", row)
|
startCol := fmt.Sprintf("A%d", startDataRow)
|
||||||
endCol := fmt.Sprintf("%s%d", endColName, row)
|
endCol := fmt.Sprintf("%s%d", endColName, startDataRow)
|
||||||
item := dataValue.Index(i)
|
|
||||||
typ := item.Type()
|
var rowData []interface{} // 数据列
|
||||||
num := item.NumField()
|
|
||||||
var exportRow []ExcelTag
|
list := lists[i]
|
||||||
maxLen := 0 // 记录这一行中,数据最多的单元格的值的长度
|
for j := 0; j < len(cols); j++ {
|
||||||
//遍历结构体的所有字段
|
col := cols[j]
|
||||||
for j := 0; j < num; j++ {
|
replace := col.Replace
|
||||||
dataField := typ.Field(j) //获取到struct标签,需要通过reflect.Type来获取tag标签的值
|
|
||||||
tagVal := dataField.Tag.Get(ExcelTagKey)
|
val := list[col.Key]
|
||||||
if tagVal == "" { // 如果非导出则跳过
|
|
||||||
continue
|
for replaceKey, v := range replace {
|
||||||
|
|
||||||
|
if replaceKey == fmt.Sprintf("%v", val) {
|
||||||
|
val = fmt.Sprintf("%v", v)
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var dataCol ExcelTag
|
rowData = append(rowData, val)
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
// 排序
|
|
||||||
sort.Slice(exportRow, func(i, j int) bool {
|
if startDataRow%2 == 0 {
|
||||||
return exportRow[i].Index < exportRow[j].Index
|
|
||||||
})
|
|
||||||
var rowData []interface{} // 数据列
|
|
||||||
for _, colTitle := range exportRow {
|
|
||||||
rowData = append(rowData, colTitle.Value)
|
|
||||||
}
|
|
||||||
if row%2 == 0 {
|
|
||||||
_ = e.F.SetCellStyle(sheet, startCol, endCol, e.ContentStyle2)
|
_ = e.F.SetCellStyle(sheet, startCol, endCol, e.ContentStyle2)
|
||||||
} else {
|
} else {
|
||||||
_ = e.F.SetCellStyle(sheet, startCol, endCol, e.ContentStyle1)
|
_ = e.F.SetCellStyle(sheet, startCol, endCol, e.ContentStyle1)
|
||||||
}
|
}
|
||||||
if maxLen > 25 { // 自适应行高
|
|
||||||
d := maxLen / 25
|
_ = e.F.SetRowHeight(sheet, startDataRow, float64(25)) // 默认行高25
|
||||||
f := 25 * d
|
|
||||||
_ = e.F.SetRowHeight(sheet, row, float64(f))
|
|
||||||
} else {
|
|
||||||
_ = e.F.SetRowHeight(sheet, row, float64(25)) // 默认行高25
|
|
||||||
}
|
|
||||||
if err = e.F.SetSheetRow(sheet, startCol, &rowData); err != nil {
|
if err = e.F.SetSheetRow(sheet, startCol, &rowData); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
row++
|
startDataRow++
|
||||||
}
|
}
|
||||||
return
|
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@@ -1,154 +0,0 @@
|
|||||||
package excel2
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"net/http"
|
|
||||||
|
|
||||||
"github.com/xuri/excelize/v2"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Col struct {
|
|
||||||
Name string
|
|
||||||
Key string
|
|
||||||
Width int
|
|
||||||
Replace map[string]any
|
|
||||||
Decode func(value any) any
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetExcelColumnName2 根据列数生成 Excel 列名
|
|
||||||
func GetExcelColumnName2(columnNumber int) string {
|
|
||||||
columnName := ""
|
|
||||||
for columnNumber > 0 {
|
|
||||||
remainder := (columnNumber - 1) % 26
|
|
||||||
columnName = string(rune('A'+remainder)) + columnName
|
|
||||||
columnNumber = (columnNumber - 1) / 26
|
|
||||||
}
|
|
||||||
return columnName
|
|
||||||
}
|
|
||||||
|
|
||||||
// NormalDynamicExport 导出excel
|
|
||||||
//
|
|
||||||
// 需要在传入的结构体中的字段加上tag:excel:"title:列头名称;index:列下标(从0开始);"
|
|
||||||
//
|
|
||||||
// list 要导出的数据
|
|
||||||
//
|
|
||||||
// cols 列
|
|
||||||
//
|
|
||||||
// sheet 文档
|
|
||||||
// title 标题
|
|
||||||
func NormalDynamicExport2(lists []map[string]interface{}, cols []Col, sheet string, title string) (file *excelize.File, err error) {
|
|
||||||
e := ExcelInit()
|
|
||||||
err = ExportExcel2(sheet, title, lists, cols, e)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
return e.F, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExportExcel2 excel导出
|
|
||||||
func ExportExcel2(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)
|
|
||||||
}
|
|
||||||
// 构造表头
|
|
||||||
endColName, startDataRow, err := normalBuildTitle2(e, sheet, title, cols)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// 构造数据行
|
|
||||||
err = normalBuildDataRow2(e, sheet, endColName, startDataRow, lists, cols)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// 构造表头(endColName 最后一列的列名 startDataRow 数据行开始的行号)
|
|
||||||
func normalBuildTitle2(e *Excel, sheet, title string, cols []Col) (endColName string, startDataRow int, err error) {
|
|
||||||
var titleRowData []interface{} // 列头行
|
|
||||||
for i, colTitle := range cols {
|
|
||||||
endColName := GetExcelColumnName2(i + 1)
|
|
||||||
if colTitle.Width > 0 { // 根据给定的宽度设置列宽
|
|
||||||
_ = e.F.SetColWidth(sheet, endColName, endColName, float64(colTitle.Width))
|
|
||||||
} else {
|
|
||||||
_ = e.F.SetColWidth(sheet, endColName, endColName, float64(20)) // 默认宽度为20
|
|
||||||
}
|
|
||||||
titleRowData = append(titleRowData, colTitle.Name)
|
|
||||||
}
|
|
||||||
endColName = GetExcelColumnName2(len(titleRowData)) // 根据列数生成 Excel 列名
|
|
||||||
if title != "" {
|
|
||||||
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)
|
|
||||||
e.F.SetRowHeight(sheet, 1, float64(30)) // 第一行行高
|
|
||||||
e.F.SetRowHeight(sheet, 2, float64(30)) // 第二行行高
|
|
||||||
e.F.SetCellStyle(sheet, "A2", endColName+"2", e.HeadStyle)
|
|
||||||
if err = e.F.SetSheetRow(sheet, "A2", &titleRowData); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
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 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// 构造数据行
|
|
||||||
func normalBuildDataRow2(e *Excel, sheet, endColName string, startDataRow int, lists []map[string]interface{}, cols []Col) (err error) {
|
|
||||||
//实时写入数据
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
rowData = append(rowData, val)
|
|
||||||
}
|
|
||||||
|
|
||||||
if startDataRow%2 == 0 {
|
|
||||||
_ = e.F.SetCellStyle(sheet, startCol, endCol, e.ContentStyle2)
|
|
||||||
} else {
|
|
||||||
_ = e.F.SetCellStyle(sheet, startCol, endCol, e.ContentStyle1)
|
|
||||||
}
|
|
||||||
|
|
||||||
_ = e.F.SetRowHeight(sheet, startDataRow, float64(25)) // 默认行高25
|
|
||||||
|
|
||||||
if err = e.F.SetSheetRow(sheet, startCol, &rowData); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
startDataRow++
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// 下载
|
|
||||||
func DownLoadExcel2(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
|
|
||||||
}
|
|
||||||
}
|
|
@@ -26,15 +26,11 @@ func GetExcelData(file multipart.File, dst interface{}, cols []Col) (err error)
|
|||||||
// 创建Excel文件对象
|
// 创建Excel文件对象
|
||||||
f, err := excelize.OpenReader(bytes.NewReader(buf.Bytes()))
|
f, err := excelize.OpenReader(bytes.NewReader(buf.Bytes()))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err)
|
|
||||||
// c.String(http.StatusInternalServerError, "Excel读取失败")
|
|
||||||
err = errors.New("Excel读取失败")
|
err = errors.New("Excel读取失败")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
err = ImportExcel(f, dst, 2, cols)
|
err = ImportExcel(f, dst, 2, cols)
|
||||||
// if err != nil {
|
|
||||||
// fmt.Println(err)
|
|
||||||
// }
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -60,16 +56,6 @@ func ImportBySheet(f *excelize.File, dst interface{}, sheetName string, startRow
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取在数组中得下标
|
|
||||||
func GetIndex(items []string, item string) int {
|
|
||||||
for i, v := range items {
|
|
||||||
if v == item {
|
|
||||||
return i
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
|
|
||||||
// 解析数据
|
// 解析数据
|
||||||
func importData(f *excelize.File, dst interface{}, sheetName string, startRow int, cols []Col) (err error) {
|
func importData(f *excelize.File, dst interface{}, sheetName string, startRow int, cols []Col) (err error) {
|
||||||
rows, err := f.GetRows(sheetName) // 获取所有行
|
rows, err := f.GetRows(sheetName) // 获取所有行
|
||||||
|
@@ -1,74 +0,0 @@
|
|||||||
package excel2
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"regexp"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// 定义正则表达式模式
|
|
||||||
const (
|
|
||||||
ExcelTagKey = "excel"
|
|
||||||
Pattern = "name:(.*?);|index:(.*?);|width:(.*?);|needMerge:(.*?);|replace:(.*?);"
|
|
||||||
)
|
|
||||||
|
|
||||||
type ExcelTag struct {
|
|
||||||
Value interface{}
|
|
||||||
Name string // 表头标题
|
|
||||||
Index int // 列下标(从0开始)
|
|
||||||
Width int // 列宽
|
|
||||||
NeedMerge bool // 是否需要合并
|
|
||||||
Replace string // 替换(需要替换的内容_替换后的内容。比如:1_未开始 ==> 表示1替换为未开始)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 构造函数,返回一个带有默认值的 ExcelTag 实例
|
|
||||||
func NewExcelTag() ExcelTag {
|
|
||||||
return ExcelTag{
|
|
||||||
// 导入时会根据这个下标来拿单元格的值,当目标结构体字段没有设置index时,
|
|
||||||
// 解析字段tag值时Index没读到就一直默认为0,拿单元格的值时,就始终拿的是第一列的值
|
|
||||||
Index: -1, // 设置 Index 的默认值为 -1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 读取字段tag值
|
|
||||||
func (e *ExcelTag) GetTag(tag string) (err error) {
|
|
||||||
// 编译正则表达式
|
|
||||||
re := regexp.MustCompile(Pattern)
|
|
||||||
matches := re.FindAllStringSubmatch(tag, -1)
|
|
||||||
if len(matches) > 0 {
|
|
||||||
for _, match := range matches {
|
|
||||||
for i, val := range match {
|
|
||||||
if i != 0 && val != "" {
|
|
||||||
e.setValue(match, val)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
err = errors.New("未匹配到值")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// 设置ExcelTag 对应字段的值
|
|
||||||
func (e *ExcelTag) setValue(tag []string, value string) {
|
|
||||||
if strings.Contains(tag[0], "name") {
|
|
||||||
e.Name = value
|
|
||||||
}
|
|
||||||
if strings.Contains(tag[0], "index") {
|
|
||||||
v, _ := strconv.ParseInt(value, 10, 8)
|
|
||||||
e.Index = int(v)
|
|
||||||
}
|
|
||||||
if strings.Contains(tag[0], "width") {
|
|
||||||
v, _ := strconv.ParseInt(value, 10, 8)
|
|
||||||
e.Width = int(v)
|
|
||||||
}
|
|
||||||
if strings.Contains(tag[0], "needMerge") {
|
|
||||||
v, _ := strconv.ParseBool(value)
|
|
||||||
e.NeedMerge = v
|
|
||||||
}
|
|
||||||
if strings.Contains(tag[0], "replace") {
|
|
||||||
e.Replace = value
|
|
||||||
}
|
|
||||||
}
|
|
@@ -24,6 +24,26 @@ type Test struct {
|
|||||||
Remark string `excel:"name:备注;width:40;"`
|
Remark string `excel:"name:备注;width:40;"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// func decodeTime(value any) any {
|
||||||
|
// t, e := core.ParseStringToTsTime(value.(string))
|
||||||
|
// if e != nil {
|
||||||
|
// return nil
|
||||||
|
// }
|
||||||
|
|
||||||
|
// return t
|
||||||
|
// }
|
||||||
|
|
||||||
|
var cols = []Col{
|
||||||
|
{Name: "用户账号", Key: "Id", Width: 15},
|
||||||
|
{Name: "用户姓名", Key: "Name", Width: 15},
|
||||||
|
{Name: "用户邮箱", Key: "Email", Width: 15},
|
||||||
|
{Name: "所属公司", Key: "Com", Width: 20},
|
||||||
|
{Name: "所在部门", Key: "Dept", Width: 21},
|
||||||
|
{Name: "角色代码", Key: "RoleKey", Width: 20},
|
||||||
|
{Name: "角色名称", Key: "RoleName", Width: 25, Replace: map[string]interface{}{"1": "1_超级管理员", "2": "2_普通用户"}},
|
||||||
|
{Name: "备注", Key: "Remark", Width: 30},
|
||||||
|
}
|
||||||
|
|
||||||
// 导出
|
// 导出
|
||||||
func TestExport(t *testing.T) {
|
func TestExport(t *testing.T) {
|
||||||
var testList = []Test{
|
var testList = []Test{
|
||||||
@@ -32,14 +52,11 @@ func TestExport(t *testing.T) {
|
|||||||
{"chiling", "炽翎", "chiling@123.com", "太虚剑派", "行政部", "PTYG", "2", "备注备注备注备注"},
|
{"chiling", "炽翎", "chiling@123.com", "太虚剑派", "行政部", "PTYG", "2", "备注备注备注备注"},
|
||||||
{"yunmo", "云墨", "yunmo@123.com", "太虚剑派", "财务部", "CJGLY", "1", ""},
|
{"yunmo", "云墨", "yunmo@123.com", "太虚剑派", "财务部", "CJGLY", "1", ""},
|
||||||
{"yuelun", "月轮", "yuelun@123.com", "天命科技有限公司", "执行部", "CJGLY", "1", ""},
|
{"yuelun", "月轮", "yuelun@123.com", "天命科技有限公司", "执行部", "CJGLY", "1", ""},
|
||||||
{"xunyu", "迅羽",
|
{"xunyu", "迅羽", "xunyu@123.com哈哈哈哈哈哈哈哈这里是最大行高测试哈哈哈哈", "天命科技有限公司", "开发部", "PTYG", "2",
|
||||||
"xunyu@123.com哈哈哈哈哈哈哈哈这里是最大行高测试哈哈哈哈哈哈哈哈这11111111111里是最大行高测试哈哈哈哈哈哈哈哈这里是最大行高测试",
|
|
||||||
"天命科技有限公司", "开发部", "PTYG", "2",
|
|
||||||
"备注备注备注备注com哈哈哈哈哈哈哈哈这里是最大行高测试哈哈哈哈哈哈哈哈这里是最大行高测试哈哈哈哈哈哈哈哈这里是最大行高测里是最大行高测试哈哈哈哈哈哈哈哈这里是最大行高测试"},
|
"备注备注备注备注com哈哈哈哈哈哈哈哈这里是最大行高测试哈哈哈哈哈哈哈哈这里是最大行高测试哈哈哈哈哈哈哈哈这里是最大行高测里是最大行高测试哈哈哈哈哈哈哈哈这里是最大行高测试"},
|
||||||
}
|
}
|
||||||
changeHead := map[string]string{"Id": "账号", "Name": "真实姓名"}
|
|
||||||
//f, err := excel.NormalExport(testList, "Sheet1", "用户信息", "Id,Email,", true, true, changeHead)
|
f, err := NormalDynamicExport(testList, cols, "Sheet1", "用户信息")
|
||||||
f, err := NormalDynamicExport(testList, "Sheet1", "用户信息", changeHead)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
return
|
return
|
||||||
@@ -58,7 +75,7 @@ func TestImports(t *testing.T) {
|
|||||||
fmt.Println("文件打开失败")
|
fmt.Println("文件打开失败")
|
||||||
}
|
}
|
||||||
importList := []Test{}
|
importList := []Test{}
|
||||||
err = ImportExcel(f, &importList, 2, []Col{})
|
err = ImportExcel(f, &importList, 2, cols)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user