mirror of
https://gitee.com/xiangheng/x_admin.git
synced 2025-10-05 08:07:06 +08:00
编程式导入导出
This commit is contained in:
@@ -4,6 +4,7 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
"x_admin/core"
|
||||||
"x_admin/core/request"
|
"x_admin/core/request"
|
||||||
"x_admin/core/response"
|
"x_admin/core/response"
|
||||||
"x_admin/util"
|
"x_admin/util"
|
||||||
@@ -151,6 +152,29 @@ func (hd *SystemLogSmsHandler) Del(c *gin.Context) {
|
|||||||
response.CheckAndResp(c, SystemLogSmsService.Del(delReq.Id))
|
response.CheckAndResp(c, SystemLogSmsService.Del(delReq.Id))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func DecodeTime(value any) any {
|
||||||
|
t, e := core.ParseStringToTsTime(value.(string))
|
||||||
|
if e != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
|
||||||
|
var cols = []excel2.Col{
|
||||||
|
{Name: "场景编号", Key: "Scene", Width: 15, Replace: map[string]interface{}{
|
||||||
|
"1": "a",
|
||||||
|
"2": "b",
|
||||||
|
}},
|
||||||
|
{Name: "手机号码", Key: "Mobile", Width: 15},
|
||||||
|
{Name: "发送内容", Key: "Content", Width: 15},
|
||||||
|
{Name: "发送状态", Key: "Status", Width: 20},
|
||||||
|
{Name: "短信结果", Key: "Results", Width: 21},
|
||||||
|
{Name: "发送时间", Key: "SendTime", Width: 20},
|
||||||
|
{Name: "创建时间", Key: "CreateTime", Width: 25},
|
||||||
|
{Name: "更新时间", Key: "UpdateTime", Width: 30, Decode: DecodeTime},
|
||||||
|
}
|
||||||
|
|
||||||
// @Summary 系统短信日志导出
|
// @Summary 系统短信日志导出
|
||||||
// @Tags system_log_sms-系统短信日志
|
// @Tags system_log_sms-系统短信日志
|
||||||
// @Produce json
|
// @Produce json
|
||||||
@@ -182,16 +206,6 @@ func (hd *SystemLogSmsHandler) ExportFile(c *gin.Context) {
|
|||||||
// return
|
// return
|
||||||
// }
|
// }
|
||||||
|
|
||||||
var cols = []excel2.Col{
|
|
||||||
{Name: "场景编号", Key: "Scene", Width: 15},
|
|
||||||
{Name: "手机号码", Key: "Mobile", Width: 15},
|
|
||||||
{Name: "发送内容", Key: "Content", Width: 15},
|
|
||||||
{Name: "发送状态", Key: "Status", Width: 20},
|
|
||||||
{Name: "短信结果", Key: "Results", Width: 21},
|
|
||||||
{Name: "发送时间", Key: "SendTime", Width: 20},
|
|
||||||
{Name: "创建时间", Key: "CreateTime", Width: 25},
|
|
||||||
{Name: "更新时间", Key: "UpdateTime", Width: 30},
|
|
||||||
}
|
|
||||||
list := util.ConvertUtil.StructsToMaps(res)
|
list := util.ConvertUtil.StructsToMaps(res)
|
||||||
f, err := excel2.NormalDynamicExport2(list, cols, "Sheet1", "系统短信日志")
|
f, err := excel2.NormalDynamicExport2(list, cols, "Sheet1", "系统短信日志")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -214,7 +228,7 @@ func (hd *SystemLogSmsHandler) ImportFile(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
defer file.Close()
|
defer file.Close()
|
||||||
importList := []SystemLogSmsResp{}
|
importList := []SystemLogSmsResp{}
|
||||||
err = excel.GetExcelData(file, &importList)
|
err = excel2.GetExcelData(file, &importList, cols)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.String(http.StatusInternalServerError, err.Error())
|
c.String(http.StatusInternalServerError, err.Error())
|
||||||
return
|
return
|
||||||
|
@@ -8,9 +8,11 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Col struct {
|
type Col struct {
|
||||||
Name string
|
Name string
|
||||||
Key string
|
Key string
|
||||||
Width int
|
Width int
|
||||||
|
Replace map[string]any
|
||||||
|
Decode func(value any) any
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetExcelColumnName2 根据列数生成 Excel 列名
|
// GetExcelColumnName2 根据列数生成 Excel 列名
|
||||||
@@ -106,42 +108,21 @@ func normalBuildDataRow2(e *Excel, sheet, endColName string, startDataRow int, l
|
|||||||
list := lists[i]
|
list := lists[i]
|
||||||
for j := 0; j < len(cols); j++ {
|
for j := 0; j < len(cols); j++ {
|
||||||
col := cols[j]
|
col := cols[j]
|
||||||
val := list[col.Key]
|
replace := col.Replace
|
||||||
// switch val.(type) {
|
|
||||||
|
val := list[col.Key]
|
||||||
|
|
||||||
|
for replaceKey, v := range replace {
|
||||||
|
|
||||||
|
if replaceKey == fmt.Sprintf("%v", val) {
|
||||||
|
val = fmt.Sprintf("%v", v)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// default:
|
|
||||||
// v, _ := json.Marshal(list[col.Key])
|
|
||||||
// rowData = append(rowData, v)
|
|
||||||
// }
|
|
||||||
rowData = append(rowData, val)
|
rowData = append(rowData, val)
|
||||||
}
|
}
|
||||||
|
|
||||||
// // 替换
|
|
||||||
// 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)
|
|
||||||
// }
|
|
||||||
|
|
||||||
if startDataRow%2 == 0 {
|
if startDataRow%2 == 0 {
|
||||||
_ = e.F.SetCellStyle(sheet, startCol, endCol, e.ContentStyle2)
|
_ = e.F.SetCellStyle(sheet, startCol, endCol, e.ContentStyle2)
|
||||||
} else {
|
} else {
|
||||||
|
@@ -6,13 +6,12 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"mime/multipart"
|
"mime/multipart"
|
||||||
"reflect"
|
"x_admin/util"
|
||||||
"strconv"
|
|
||||||
|
|
||||||
"github.com/xuri/excelize/v2"
|
"github.com/xuri/excelize/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
func GetExcelData(file multipart.File, dst interface{}) (err error) {
|
func GetExcelData(file multipart.File, dst interface{}, cols []Col) (err error) {
|
||||||
// 创建缓冲区
|
// 创建缓冲区
|
||||||
buf := new(bytes.Buffer)
|
buf := new(bytes.Buffer)
|
||||||
|
|
||||||
@@ -32,7 +31,7 @@ func GetExcelData(file multipart.File, dst interface{}) (err error) {
|
|||||||
err = errors.New("Excel读取失败")
|
err = errors.New("Excel读取失败")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
err = ImportExcel(f, dst, 1, 2)
|
err = ImportExcel(f, dst, 2, cols)
|
||||||
// if err != nil {
|
// if err != nil {
|
||||||
// fmt.Println(err)
|
// fmt.Println(err)
|
||||||
// }
|
// }
|
||||||
@@ -44,20 +43,20 @@ func GetExcelData(file multipart.File, dst interface{}) (err error) {
|
|||||||
// f 获取到的excel对象、dst 导入目标对象【传指针】
|
// f 获取到的excel对象、dst 导入目标对象【传指针】
|
||||||
// headIndex 表头的索引,从0开始(用于获取表头名字)
|
// headIndex 表头的索引,从0开始(用于获取表头名字)
|
||||||
// startRow 头行行数(从第startRow+1行开始扫)
|
// startRow 头行行数(从第startRow+1行开始扫)
|
||||||
func ImportExcel(f *excelize.File, dst interface{}, headIndex, startRow int) (err error) {
|
func ImportExcel(f *excelize.File, dst interface{}, startRow int, cols []Col) (err error) {
|
||||||
sheetName := f.GetSheetName(0) // 单个sheet时,默认读取第一个sheet
|
sheetName := f.GetSheetName(0) // 单个sheet时,默认读取第一个sheet
|
||||||
err = importData(f, dst, sheetName, headIndex, startRow)
|
err = importData(f, dst, sheetName, startRow, cols)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// ImportBySheet 导入数据(读取指定sheet)sheetName Sheet名称
|
// ImportBySheet 导入数据(读取指定sheet)sheetName Sheet名称
|
||||||
func ImportBySheet(f *excelize.File, dst interface{}, sheetName string, headIndex, startRow int) (err error) {
|
func ImportBySheet(f *excelize.File, dst interface{}, sheetName string, startRow int, cols []Col) (err error) {
|
||||||
// 当需要读取多个sheet时,可以通过下面的方式,来调用 ImportBySheet 这个函数
|
// 当需要读取多个sheet时,可以通过下面的方式,来调用 ImportBySheet 这个函数
|
||||||
//sheetList := f.GetSheetList()
|
//sheetList := f.GetSheetList()
|
||||||
//for _, sheetName := range sheetList {
|
//for _, sheetName := range sheetList {
|
||||||
// ImportBySheet(f,dst,sheetName,headIndex,startRow)
|
// ImportBySheet(f,dst,sheetName,startRow)
|
||||||
//}
|
//}
|
||||||
err = importData(f, dst, sheetName, headIndex, startRow)
|
err = importData(f, dst, sheetName, startRow, cols)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -71,106 +70,69 @@ func GetIndex(items []string, item string) int {
|
|||||||
return -1
|
return -1
|
||||||
}
|
}
|
||||||
|
|
||||||
// 判断数组中是否包含指定元素
|
|
||||||
// func IsContain(items interface{}, item interface{}) bool {
|
|
||||||
// switch items.(type) {
|
|
||||||
// case []int:
|
|
||||||
// intArr := items.([]int)
|
|
||||||
// for _, value := range intArr {
|
|
||||||
// if value == item.(int) {
|
|
||||||
// return true
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// case []string:
|
|
||||||
// strArr := items.([]string)
|
|
||||||
// for _, value := range strArr {
|
|
||||||
// if value == item.(string) {
|
|
||||||
// return true
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// default:
|
|
||||||
// return false
|
|
||||||
// }
|
|
||||||
// return false
|
|
||||||
// }
|
|
||||||
|
|
||||||
// 解析数据
|
// 解析数据
|
||||||
func importData(f *excelize.File, dst interface{}, sheetName string, headIndex, startRow int) (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) // 获取所有行
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = errors.New(sheetName + "工作表不存在")
|
err = errors.New(sheetName + "工作表不存在")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
dataValue := reflect.ValueOf(dst) // 取目标对象的元素类型、字段类型和 tag
|
// heads := []string{}
|
||||||
// 判断数据的类型
|
|
||||||
if dataValue.Kind() != reflect.Ptr || dataValue.Elem().Kind() != reflect.Slice {
|
var data = []map[string]interface{}{}
|
||||||
err = errors.New("invalid data type")
|
for i := 0; i < len(rows); i++ {
|
||||||
}
|
|
||||||
heads := []string{} // 表头
|
if i < startRow { // 跳过头行
|
||||||
dataType := dataValue.Elem().Type().Elem() // 获取导入目标对象的类型信息
|
|
||||||
// 遍历行,解析数据并填充到目标对象中
|
|
||||||
for rowIndex, row := range rows {
|
|
||||||
if rowIndex == headIndex {
|
|
||||||
heads = row
|
|
||||||
}
|
|
||||||
if rowIndex < startRow { // 跳过头行
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
newData := reflect.New(dataType).Elem() // 创建新的目标对象
|
var rowMap = map[string]interface{}{}
|
||||||
// 遍历目标对象的字段
|
|
||||||
for i := 0; i < dataType.NumField(); i++ {
|
for j := 0; j < len(cols); j++ {
|
||||||
// 这里要用构造函数,构造函数里指定了Index默认值为-1,当目标结构体的tag没有指定index的话,那么 excelTag.Index 就一直为0
|
col := cols[j]
|
||||||
// 那么 row[excelizeIndex] 就始终是 row[0],始终拿的是第一列的数据
|
key := col.Key
|
||||||
var excelTag = NewExcelTag()
|
replace := col.Replace
|
||||||
field := dataType.Field(i) // 获取字段信息和tag
|
|
||||||
tag := field.Tag.Get(ExcelTagKey)
|
val := rows[i][j]
|
||||||
if tag == "" { // 如果tag不存在,则跳过
|
// 将val替换为key
|
||||||
continue
|
for replaceKey, v := range replace {
|
||||||
}
|
if fmt.Sprintf("%v", v) == fmt.Sprintf("%v", val) {
|
||||||
err = excelTag.GetTag(tag)
|
val = fmt.Sprintf("%v", replaceKey)
|
||||||
if err != nil {
|
break
|
||||||
return
|
|
||||||
}
|
|
||||||
cellValue := ""
|
|
||||||
if excelTag.Index >= 0 { // 当tag里指定了index时,根据这个index来拿数据
|
|
||||||
excelizeIndex := excelTag.Index // 解析tag的值
|
|
||||||
if excelizeIndex >= len(row) { // 防止下标越界
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
cellValue = row[excelizeIndex] // 获取单元格的值
|
|
||||||
} else { // 否则根据表头名称来拿数据
|
|
||||||
var index = GetIndex(heads, excelTag.Name)
|
|
||||||
if index != -1 {
|
|
||||||
if index >= len(row) { // 防止下标越界
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
cellValue = row[index] // 获取单元格的值
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fmt.Println("Type.Name:", field.Type.Name(), field.Type.Kind())
|
|
||||||
// 根据字段类型设置值
|
if col.Decode != nil {
|
||||||
switch field.Type.Kind() {
|
rowMap[key] = col.Decode(val)
|
||||||
case reflect.Int:
|
} else {
|
||||||
v, _ := strconv.ParseInt(cellValue, 10, 64)
|
rowMap[key] = val
|
||||||
newData.Field(i).SetInt(v)
|
|
||||||
case reflect.Int64:
|
|
||||||
v, _ := strconv.ParseInt(cellValue, 10, 64)
|
|
||||||
newData.Field(i).SetInt(v)
|
|
||||||
case reflect.Uint:
|
|
||||||
v, _ := strconv.ParseUint(cellValue, 10, 64)
|
|
||||||
newData.Field(i).SetUint(v)
|
|
||||||
case reflect.Uint8:
|
|
||||||
v, _ := strconv.ParseUint(cellValue, 10, 64)
|
|
||||||
newData.Field(i).SetUint(v)
|
|
||||||
case reflect.Uint16:
|
|
||||||
v, _ := strconv.ParseUint(cellValue, 10, 64)
|
|
||||||
newData.Field(i).SetUint(v)
|
|
||||||
case reflect.String:
|
|
||||||
newData.Field(i).SetString(cellValue)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 将新的目标对象添加到导入目标对象的slice中
|
data = append(data, rowMap)
|
||||||
dataValue.Elem().Set(reflect.Append(dataValue.Elem(), newData))
|
|
||||||
}
|
}
|
||||||
|
util.ConvertUtil.MapToStruct(data, dst)
|
||||||
|
|
||||||
|
// fmt.Println("Type.Name:", field.Type.Name(), field.Type.Kind())
|
||||||
|
// // 根据字段类型设置值
|
||||||
|
// switch field.Type.Kind() {
|
||||||
|
// case reflect.Int:
|
||||||
|
// v, _ := strconv.ParseInt(cellValue, 10, 64)
|
||||||
|
// newData.Field(i).SetInt(v)
|
||||||
|
// case reflect.Int64:
|
||||||
|
// v, _ := strconv.ParseInt(cellValue, 10, 64)
|
||||||
|
// newData.Field(i).SetInt(v)
|
||||||
|
// case reflect.Uint:
|
||||||
|
// v, _ := strconv.ParseUint(cellValue, 10, 64)
|
||||||
|
// newData.Field(i).SetUint(v)
|
||||||
|
// case reflect.Uint8:
|
||||||
|
// v, _ := strconv.ParseUint(cellValue, 10, 64)
|
||||||
|
// newData.Field(i).SetUint(v)
|
||||||
|
// case reflect.Uint16:
|
||||||
|
// v, _ := strconv.ParseUint(cellValue, 10, 64)
|
||||||
|
// newData.Field(i).SetUint(v)
|
||||||
|
// case reflect.String:
|
||||||
|
// newData.Field(i).SetString(cellValue)
|
||||||
|
// }
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@@ -58,7 +58,7 @@ func TestImports(t *testing.T) {
|
|||||||
fmt.Println("文件打开失败")
|
fmt.Println("文件打开失败")
|
||||||
}
|
}
|
||||||
importList := []Test{}
|
importList := []Test{}
|
||||||
err = ImportExcel(f, &importList, 1, 2)
|
err = ImportExcel(f, &importList, 2, []Col{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user