重要:更新TsTime,excel2导出不使用tag

This commit is contained in:
xiangheng
2024-08-12 02:25:15 +08:00
parent 2b1a8ce035
commit fcaf6de2e3
25 changed files with 1102 additions and 171 deletions

View File

@@ -11,7 +11,7 @@
>
<el-form ref="formRef" :model="formData" label-width="84px" :rules="formRules">
<el-form-item label="场景编号" prop="scene">
<el-input v-model="formData.scene" type="number" placeholder="请输入场景编号" />
<el-input v-model="formData.scene" placeholder="请输入场景编号" />
</el-form-item>
<el-form-item label="手机号码" prop="mobile">
<el-input v-model="formData.mobile" placeholder="请输入手机号码" />
@@ -84,12 +84,12 @@ const popupTitle = computed(() => {
const formData = reactive({
id: '',
scene: 0,
scene: '',
mobile: '',
content: '',
status: '',
results: '',
send_time: 0
send_time: ''
})
const formRules = {

View File

@@ -18,7 +18,7 @@ type IAlbumService interface {
AlbumMove(ids []uint, cid int) (e error)
AlbumAdd(addReq CommonAlbumAddReq) (res uint, e error)
AlbumDel(ids []uint) (e error)
CateList(listReq CommonCateListReq) (mapList []interface{}, e error)
CateList(listReq CommonCateListReq) (mapList []CommonCateListResp, e error)
CateAdd(addReq CommonCateAddReq) (e error)
CateRename(id uint, name string) (e error)
CateDel(id uint) (e error)
@@ -159,7 +159,7 @@ func (albSrv albumService) AlbumDel(ids []uint) (e error) {
}
// CateList 相册分类列表
func (albSrv albumService) CateList(listReq CommonCateListReq) (mapList []interface{}, e error) {
func (albSrv albumService) CateList(listReq CommonCateListReq) (mapList []CommonCateListResp, e error) {
var cates []common_model.AlbumCate
cateModel := albSrv.db.Where("is_delete = ?", 0).Order("id desc")
if listReq.Type > 0 {
@@ -174,8 +174,7 @@ func (albSrv albumService) CateList(listReq CommonCateListReq) (mapList []interf
}
cateResps := []CommonCateListResp{}
response.Copy(&cateResps, cates)
return util.ArrayUtil.ListToTree(
util.ConvertUtil.StructsToMaps(cateResps), "id", "pid", "children"), nil
return cateResps, nil
}
// CateAdd 分类新增

View File

@@ -9,14 +9,14 @@ type {{{ toUpperCamelCase .EntityName }}} struct {
{{{- range .Columns }}}
{{{- if not (contains $.SubTableFields .ColumnName) }}}
{{{- if eq .GoField "is_delete" }}}
IsDelete soft_delete.DeletedAt `mapstructure:"{{{ .GoField }}}" gorm:"not null;default:0;softDelete:flag,DeletedAtField:DeleteTime;comment:'是否删除: 0=否, 1=是'"`
IsDelete soft_delete.DeletedAt `mapstructure:"{{{ .GoField }}}" json:"{{{ .GoField }}}" gorm:"not null;default:0;softDelete:flag,DeletedAtField:DeleteTime;comment:'是否删除: 0=否, 1=是'"`
{{{- else }}}
{{{- if eq .GoType "core.TsTime" }}}
{{{ toUpperCamelCase .GoField }}} core.TsTime `mapstructure:"{{{ .GoField }}}" gorm:"{{{ if eq .GoField "create_time" }}}autoCreateTime;{{{ else }}}{{{if eq .GoField "update_time"}}}autoUpdateTime;{{{ end }}}{{{ end }}}comment:'{{{ .ColumnComment }}}'" excel:"name:{{{ .ColumnComment }}};"` // {{{ .ColumnComment }}}
{{{ toUpperCamelCase .GoField }}} core.TsTime `mapstructure:"{{{ .GoField }}}" json:"{{{ .GoField }}}" gorm:"{{{ if eq .GoField "create_time" }}}autoCreateTime;{{{ else }}}{{{if eq .GoField "update_time"}}}autoUpdateTime;{{{ end }}}{{{ end }}}comment:'{{{ .ColumnComment }}}'"` // {{{ .ColumnComment }}}
{{{- else if .IsPk }}}
{{{ toUpperCamelCase .GoField }}} {{{ .GoType }}} `mapstructure:"{{{ .GoField }}}" gorm:"primarykey;" excel:"name:{{{ .ColumnComment }}};"` // {{{ .ColumnComment }}}
{{{ toUpperCamelCase .GoField }}} {{{ .GoType }}} `mapstructure:"{{{ .GoField }}}" json:"{{{ .GoField }}}" gorm:"primarykey;"` // {{{ .ColumnComment }}}
{{{- else }}}
{{{ toUpperCamelCase .GoField }}} {{{ .GoType }}} `mapstructure:"{{{ .GoField }}}" excel:"name:{{{ .ColumnComment }}};"` // {{{ .ColumnComment }}}
{{{ toUpperCamelCase .GoField }}} {{{ .GoType }}} `mapstructure:"{{{ .GoField }}}" json:"{{{ .GoField }}}"` // {{{ .ColumnComment }}}
{{{- end }}}
{{{- end }}}

View File

@@ -52,5 +52,5 @@ func {{{ toUpperCamelCase .ModuleName }}}Route(rg *gin.RouterGroup) {
r.POST("/{{{ .ModuleName }}}/edit",middleware.RecordLog("{{{ .FunctionName }}}编辑"), handle.Edit)
r.POST("/{{{ .ModuleName }}}/del", middleware.RecordLog("{{{ .FunctionName }}}删除"), handle.Del)
r.GET("/{{{ .ModuleName }}}/ExportFile", middleware.RecordLog("{{{ .FunctionName }}}导出"), handle.ExportFile)
r.POST("/{{{ .ModuleName }}}/ImportFile",middleware.RecordLog("{{{ .FunctionName }}}导入"), handle.ImportFile)
r.POST("/{{{ .ModuleName }}}/ImportFile", handle.ImportFile)
}

View File

@@ -38,5 +38,5 @@ func MonitorProjectRoute(rg *gin.RouterGroup) {
r.POST("/monitor_project/edit", middleware.RecordLog("错误项目编辑"), handle.Edit)
r.POST("/monitor_project/del", middleware.RecordLog("错误项目删除"), handle.Del)
r.GET("/monitor_project/ExportFile", middleware.RecordLog("错误项目导出"), handle.ExportFile)
r.POST("/monitor_project/ImportFile", middleware.RecordLog("错误项目导入"), handle.ImportFile)
r.POST("/monitor_project/ImportFile", handle.ImportFile)
}

View File

@@ -51,13 +51,13 @@ type MonitorWebDelReq struct {
//MonitorWebResp 错误收集error返回信息
type MonitorWebResp struct {
Id int `json:"id" structs:"id"` // uuid
ProjectKey string `json:"projectKey" structs:"projectKey" excel:"name:项目key;"` // 项目key
ClientId string `json:"clientId" structs:"clientId" excel:"name:sdk生成的客户端id;"` // sdk生成的客户端id
EventType string `json:"eventType" structs:"eventType" excel:"name:事件类型;"` // 事件类型
Page string `json:"page" structs:"page" excel:"name:URL地址;"` // URL地址
Message string `json:"message" structs:"message" excel:"name:错误消息;"` // 错误消息
Stack string `json:"stack" structs:"stack" excel:"name:错误堆栈;"` // 错误堆栈
ClientTime core.TsTime `json:"clientTime" structs:"clientTime" excel:"name:客户端时间;"` // 客户端时间
CreateTime core.TsTime `json:"createTime" structs:"createTime" excel:"name:创建时间;"` // 创建时间
Id int `json:"id"` // uuid
ProjectKey string `json:"projectKey" excel:"name:项目key;"` // 项目key
ClientId string `json:"clientId" excel:"name:sdk生成的客户端id;"` // sdk生成的客户端id
EventType string `json:"eventType" excel:"name:事件类型;"` // 事件类型
Page string `json:"page" excel:"name:URL地址;"` // URL地址
Message string `json:"message" excel:"name:错误消息;"` // 错误消息
Stack string `json:"stack" excel:"name:错误堆栈;"` // 错误堆栈
ClientTime core.TsTime `json:"clientTime" excel:"name:客户端时间;"` // 客户端时间
CreateTime core.TsTime `json:"createTime" excel:"name:创建时间;"` // 创建时间
}

View File

@@ -466,7 +466,7 @@ func (adminSrv systemAuthAdminService) Del(c *gin.Context, id uint) (e error) {
if id == config.AdminConfig.GetAdminId(c) {
return response.AssertArgumentError.SetMessage("不能删除自己!")
}
err = adminSrv.db.Model(&admin).Updates(system_model.SystemAuthAdmin{IsDelete: 1, DeleteTime: core.TsTime(time.Now())}).Error
err = adminSrv.db.Model(&admin).Updates(system_model.SystemAuthAdmin{IsDelete: 1, DeleteTime: core.NowTime()}).Error
e = response.CheckErr(err, "Del Updates err")
return
}

View File

@@ -4,15 +4,16 @@ import (
"net/http"
"strconv"
"time"
"github.com/gin-gonic/gin"
"x_admin/core/request"
"x_admin/core/response"
"x_admin/util"
"x_admin/util/excel"
"x_admin/util/excel2"
"github.com/gin-gonic/gin"
"golang.org/x/sync/singleflight"
)
type SystemLogSmsHandler struct {
requestGroup singleflight.Group
}
@@ -33,6 +34,7 @@ type SystemLogSmsHandler struct {
// @Param create_timeEnd query core.TsTime false "创建时间"
// @Param update_timeStart query core.TsTime false "更新时间"
// @Param update_timeEnd query core.TsTime false "更新时间"
//
// @Success 200 {object} response.Response{ data=response.PageResp{ lists= []SystemLogSmsResp}} "成功"
// @Router /api/admin/system_log_sms/list [get]
func (hd *SystemLogSmsHandler) List(c *gin.Context) {
@@ -92,7 +94,6 @@ func (hd *SystemLogSmsHandler) Detail(c *gin.Context) {
response.CheckAndRespWithData(c, res, err)
}
// @Summary 系统短信日志新增
// @Tags system_log_sms-系统短信日志
// @Produce json
@@ -113,6 +114,7 @@ func (hd *SystemLogSmsHandler) Add(c *gin.Context) {
createId, e := SystemLogSmsService.Add(addReq)
response.CheckAndRespWithData(c, createId, e)
}
// @Summary 系统短信日志编辑
// @Tags system_log_sms-系统短信日志
// @Produce json
@@ -133,6 +135,7 @@ func (hd *SystemLogSmsHandler) Edit(c *gin.Context) {
}
response.CheckAndRespWithData(c, editReq.Id, SystemLogSmsService.Edit(editReq))
}
// @Summary 系统短信日志删除
// @Tags system_log_sms-系统短信日志
// @Produce json
@@ -148,8 +151,6 @@ func (hd *SystemLogSmsHandler) Del(c *gin.Context) {
response.CheckAndResp(c, SystemLogSmsService.Del(delReq.Id))
}
// @Summary 系统短信日志导出
// @Tags system_log_sms-系统短信日志
// @Produce json
@@ -175,11 +176,29 @@ func (hd *SystemLogSmsHandler) ExportFile(c *gin.Context) {
response.FailWithMsg(c, response.SystemError, "查询信息失败")
return
}
f, err := excel.NormalDynamicExport(res, "Sheet1", "系统短信日志", nil)
// f, err := excel.NormalDynamicExport(res, "Sheet1", "系统短信日志", nil)
// if err != nil {
// response.FailWithMsg(c, response.SystemError, "导出失败")
// 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: "send_time", Width: 20},
{Name: "创建时间", Key: "create_time", Width: 25},
{Name: "更新时间", Key: "update_time", Width: 30},
}
list := util.ConvertUtil.StructsToMaps(res)
f, err := excel2.NormalDynamicExport2(list, cols, "Sheet1", "系统短信日志")
if err != nil {
response.FailWithMsg(c, response.SystemError, "导出失败")
return
}
excel.DownLoadExcel("系统短信日志"+time.Now().Format("20060102-150405"), c.Writer, f)
}

View File

@@ -55,7 +55,7 @@ type SystemLogSmsResp struct {
Scene int `mapstructure:"scene" json:"scene" excel:"name:场景编号;"` // 场景编号
Mobile string `mapstructure:"mobile" json:"mobile" excel:"name:手机号码;"` // 手机号码
Content string `mapstructure:"content" json:"content" excel:"name:发送内容;"` // 发送内容
Status int `mapstructure:"status" json:"status" excel:"name:发送状态"` // 发送状态:[0=发送中, 1=发送成功, 2=发送失败]
Status int `mapstructure:"status" json:"status" excel:"name:发送状态;"` // 发送状态:[0=发送中, 1=发送成功, 2=发送失败]
Results string `mapstructure:"results" json:"results" excel:"name:短信结果;"` // 短信结果
SendTime int `mapstructure:"send_time" json:"send_time" excel:"name:发送时间;"` // 发送时间
CreateTime core.TsTime `mapstructure:"create_time" json:"create_time" excel:"name:创建时间;"` // 创建时间

View File

@@ -1,9 +1,10 @@
package admin
import (
"github.com/gin-gonic/gin"
"x_admin/middleware"
"x_admin/admin/system_log_sms"
"x_admin/middleware"
"github.com/gin-gonic/gin"
)
/**
@@ -39,7 +40,6 @@ INSERT INTO x_system_auth_menu (pid, menu_type, menu_name, perms,is_cache, is_sh
INSERT INTO x_system_auth_menu (pid, menu_type, menu_name, perms,is_cache, is_show, is_disable, create_time, update_time) VALUES (0, 'A', '系统短信日志导入excel','admin:system_log_sms:ImportFile', 0, 1, 0, now(), now());
*/
// SystemLogSmsRoute(rg)
func SystemLogSmsRoute(rg *gin.RouterGroup) {
handle := system_log_sms.SystemLogSmsHandler{}
@@ -52,5 +52,5 @@ func SystemLogSmsRoute(rg *gin.RouterGroup) {
r.POST("/system_log_sms/edit", middleware.RecordLog("系统短信日志编辑"), handle.Edit)
r.POST("/system_log_sms/del", middleware.RecordLog("系统短信日志删除"), handle.Del)
r.GET("/system_log_sms/ExportFile", middleware.RecordLog("系统短信日志导出"), handle.ExportFile)
r.POST("/system_log_sms/ImportFile",middleware.RecordLog("系统短信日志导入"), handle.ImportFile)
r.POST("/system_log_sms/ImportFile", handle.ImportFile)
}

97
server/core/time2.go Normal file
View File

@@ -0,0 +1,97 @@
package core
import (
"database/sql/driver"
"encoding/json"
"time"
"gorm.io/gorm"
"gorm.io/gorm/schema"
)
const DateFormat = "2006-01-02"
const TimeFormat = "2006-01-02 15:04:05"
// 注解:时间类型从time.Time改为string的原因是struct转interface{}时丢弃了部分信息导致导出excel时时间格式不对
// TsTime 自定义时间格式
type TsTime string
// 通过时间字符串生成时间戳
//
// func ToUnix(date string) int64 {
// if date == "" {
// return 0
// }
// tt, _ := time.ParseInLocation(TimeFormat, date, time.Local)
// return tt.Unix()
// }
func ParseTimeToTsTime(date time.Time) TsTime {
return TsTime(date.Format(TimeFormat))
}
func NowTime() TsTime {
return TsTime(time.Now().Format(TimeFormat))
}
func (tst *TsTime) UnmarshalJSON(bs []byte) error {
var date string
err := json.Unmarshal(bs, &date)
if err != nil {
return err
}
tt, _ := time.ParseInLocation(TimeFormat, date, time.Local)
*tst = TsTime(
tt.Format(TimeFormat),
)
return nil
}
// MarshalJSON 将TsTime类型的时间转化为JSON字符串格式
// 返回转化后的JSON字符串和错误信息
func (tst TsTime) MarshalJSON() ([]byte, error) {
// tt := time.Time(tst.Str).Format(TimeFormat)
tt, _ := time.Parse(TimeFormat, tst.String())
str := tt.Format(TimeFormat)
return json.Marshal(str)
}
// 写入数据库gorm调用
func (t TsTime) Value() (driver.Value, error) {
// timeStr := t.String()
// if timeStr == "0001-01-01 00:00:00" {
// return nil, nil
// }
return t.String(), nil
}
// 读取数据gorm调用
func (t *TsTime) Scan(v any) error {
// pt, err := time.ParseInLocation("2006-01-02 15:04:05", v.(time.Time).String(), time.Local)
if _, ok := v.(time.Time); ok {
*t = TsTime(v.(time.Time).Format(TimeFormat))
return nil
} else {
*t = "0001-01-01 00:00:00"
return nil
}
}
func (t TsTime) String() string {
return string(t)
}
func (TsTime) GormDBDataType(db *gorm.DB, field *schema.Field) string {
// 使用 field.Tag、field.TagSettings 获取字段的 tag
// 查看 https://github.com/go-gorm/gorm/blob/master/schema/field.go 获取全部的选项
// 根据不同的数据库驱动返回不同的数据类型
// switch db.Dialector.Name() {
// case "mysql", "sqlite":
// return "JSON"
// case "postgres":
// return "JSONB"
// }
// return ""
return "DATETIME"
}

View File

@@ -96,8 +96,8 @@ func RecordLog(title string, reqTypes ...requestType) gin.HandlerFunc {
err := core.GetDB().Create(&system_model.SystemLogOperate{
AdminId: adminId, Type: reqMethod, Title: title, Ip: ip,
Url: urlPath, Method: method, Args: args, Error: errStr, Status: status,
StartTime: core.TsTime(startTime),
EndTime: core.TsTime(endTime),
StartTime: core.ParseTimeToTsTime(startTime),
EndTime: core.ParseTimeToTsTime(endTime),
TaskTime: taskTime,
}).Error
response.CheckErr(err, "RecordLog recover Create err")
@@ -124,8 +124,8 @@ func RecordLog(title string, reqTypes ...requestType) gin.HandlerFunc {
err := core.GetDB().Create(&system_model.SystemLogOperate{
AdminId: adminId, Type: reqMethod, Title: title, Ip: ip,
Url: urlPath, Method: method, Args: args, Error: errStr, Status: status,
StartTime: core.TsTime(startTime),
EndTime: core.TsTime(endTime),
StartTime: core.ParseTimeToTsTime(startTime),
EndTime: core.ParseTimeToTsTime(endTime),
TaskTime: taskTime,
}).Error
response.CheckErr(err, "RecordLog Create err")

View File

@@ -8,20 +8,20 @@ import (
// MonitorProject 错误项目实体
type MonitorProject struct {
Id int `gorm:"primarykey;comment:'项目id'" excel:"name:项目id;"` // 项目id
Id int `gorm:"primarykey;comment:'项目id'"` // 项目id
ProjectKey string `gorm:"comment:'项目uuid'" excel:"name:项目uuid;"` // 项目uuid
ProjectKey string `gorm:"comment:'项目uuid'"` // 项目uuid
ProjectName string `gorm:"comment:'项目名称'" excel:"name:项目名称;"` // 项目名称
ProjectName string `gorm:"comment:'项目名称'"` // 项目名称
ProjectType string `gorm:"comment:'项目类型go java web node php 等'" excel:"name:项目类型"` // 项目类型go java web node php 等
ProjectType string `gorm:"comment:'项目类型go java web node php 等'"` // 项目类型go java web node php 等
IsDelete soft_delete.DeletedAt `gorm:"not null;default:0;softDelete:flag,DeletedAtField:DeleteTime;comment:'是否删除: 0=否, 1=是'"`
UpdateTime core.TsTime `gorm:"autoUpdateTime;comment:'更新时间'" excel:"name:更新时间;"` // 更新时间
UpdateTime core.TsTime `gorm:"autoUpdateTime;comment:'更新时间'"` // 更新时间
CreateTime core.TsTime `gorm:"autoCreateTime;comment:'创建时间'" excel:"name:创建时间;"` // 创建时间
CreateTime core.TsTime `gorm:"autoCreateTime;comment:'创建时间'"` // 创建时间
DeleteTime core.TsTime `gorm:"default:null;comment:'删除时间'" excel:"name:删除时间;"` // 删除时间
DeleteTime core.TsTime `gorm:"default:null;comment:'删除时间'"` // 删除时间
}

View File

@@ -4,22 +4,22 @@ import "x_admin/core"
//MonitorWeb 错误收集error实体
type MonitorWeb struct {
Id int `gorm:"primarykey;comment:'uuid'" excel:"name:uuid;"` // uuid
Id int `gorm:"primarykey;comment:'uuid'"` // uuid
ProjectKey string `gorm:"comment:'项目key'" excel:"name:项目key;"` // 项目key
ProjectKey string `gorm:"comment:'项目key'"` // 项目key
ClientId string `gorm:"comment:'sdk生成的客户端id'" excel:"name:sdk生成的客户端id;"` // sdk生成的客户端id
ClientId string `gorm:"comment:'sdk生成的客户端id'"` // sdk生成的客户端id
EventType string `gorm:"comment:'事件类型'" excel:"name:事件类型;"` // 事件类型
EventType string `gorm:"comment:'事件类型'"` // 事件类型
Page string `gorm:"comment:'URL地址'" excel:"name:URL地址;"` // URL地址
Page string `gorm:"comment:'URL地址'"` // URL地址
Message string `gorm:"comment:'错误消息'" excel:"name:错误消息;"` // 错误消息
Message string `gorm:"comment:'错误消息'"` // 错误消息
Stack string `gorm:"comment:'错误堆栈'" excel:"name:错误堆栈;"` // 错误堆栈
Stack string `gorm:"comment:'错误堆栈'"` // 错误堆栈
ClientTime core.TsTime `gorm:"comment:'客户端时间'" excel:"name:客户端时间;"` // 客户端时间
ClientTime core.TsTime `gorm:"comment:'客户端时间'"` // 客户端时间
CreateTime core.TsTime `gorm:"autoCreateTime;comment:'创建时间'" excel:"name:创建时间;"` // 创建时间
CreateTime core.TsTime `gorm:"autoCreateTime;comment:'创建时间'"` // 创建时间
}

View File

@@ -6,13 +6,13 @@ import (
// SystemLogSms 系统短信日志实体
type SystemLogSms struct {
Id int `json:"id" gorm:"primarykey;" excel:"name:id;"` // id
Scene int `json:"scene" excel:"name:场景编号;"` // 场景编号
Mobile string `json:"mobile" excel:"name:手机号码;"` // 手机号码
Content string `json:"content" excel:"name:发送内容;"` // 发送内容
Status int `json:"status" excel:"name:发送状态:[0=发送中, 1=发送成功, 2=发送失败];"` // 发送状态:[0=发送中, 1=发送成功, 2=发送失败]
Results string `json:"results" excel:"name:短信结果;"` // 短信结果
SendTime int `mapstructure:"send_time" excel:"name:发送时间;"` // 发送时间
CreateTime core.TsTime `json:"CreateTime" gorm:"autoCreateTime;comment:'创建时间'" excel:"name:创建时间;"` // 创建时间
UpdateTime core.TsTime `json:"UpdateTime" gorm:"autoUpdateTime;comment:'更新时间'" excel:"name:更新时间;"` // 更新时间
Id int `json:"id" gorm:"primarykey;"` // id
Scene int `json:"scene"` // 场景编号
Mobile string `json:"mobile"` // 手机号码
Content string `json:"content"` // 发送内容
Status int `json:"status"` // 发送状态:[0=发送中, 1=发送成功, 2=发送失败]
Results string `json:"results"` // 短信结果
SendTime int `json:"send_time" mapstructure:"send_time"` // 发送时间
CreateTime core.TsTime `json:"CreateTime" gorm:"autoCreateTime;comment:'创建时间'"` // 创建时间
UpdateTime core.TsTime `json:"UpdateTime" gorm:"autoUpdateTime;comment:'更新时间'"` // 更新时间
}

View File

@@ -29,6 +29,10 @@ func (au arrayUtil) ListToTree(arr []map[string]interface{}, id string, pid stri
}
pNode.(map[string]interface{})[child] = cVal
continue
} else {
cVal := []interface{}{m}
pNode.(map[string]interface{})[child] = cVal
continue
}
}
}

View File

@@ -14,14 +14,15 @@ var ConvertUtil = convertUtil{}
type convertUtil struct{}
// StructsToMaps 将结构体转换成Map列表
func (c convertUtil) StructsToMaps(obj interface{}) (data []map[string]interface{}) {
func (c convertUtil) StructsToMaps(from interface{}) (data []map[string]interface{}) {
var objList []interface{}
err := copier.Copy(&objList, obj)
err := copier.Copy(&objList, from)
if err != nil {
core.Logger.Errorf("convertUtil.StructsToMaps err: err=[%+v]", err)
return nil
}
for _, v := range objList {
// data = append(data, structs.Map(v))
data = append(data, c.StructToMap(v))
}
return data
@@ -29,15 +30,11 @@ func (c convertUtil) StructsToMaps(obj interface{}) (data []map[string]interface
// StructToMap 结构体转换成map
func (c convertUtil) StructToMap(from interface{}) map[string]interface{} {
// var y = map[string]interface{}{}
// mapstructure.Decode(from, &y) //mapstructure
// var m = map[string]interface{}{}
// mapstructure.Decode(from, &m) //mapstructure
// copier.Copy(&m, from)
m, _ := convertor.StructToMap(from) // 需要tag:json
// if e != nil {
// return nil, err
// }
return m
}

View File

@@ -0,0 +1,97 @@
package excel2
import (
"github.com/xuri/excelize/v2"
)
type Excel struct {
F *excelize.File // excel 对象
TitleStyle int // 表头样式
HeadStyle int // 表头样式
ContentStyle1 int // 主体样式1无背景色
ContentStyle2 int // 主体样式2有背景色
}
// 初始化
func ExcelInit() (e *Excel) {
e = &Excel{}
// excel构建
e.F = excelize.NewFile()
// 初始化样式
e.getTitleRowStyle()
e.getHeadRowStyle()
e.getDataRowStyle()
return e
}
// 获取边框样式
func getBorder() []excelize.Border {
return []excelize.Border{ // 边框
{Type: "top", Color: "000000", Style: 1},
{Type: "bottom", Color: "000000", Style: 1},
{Type: "left", Color: "000000", Style: 1},
{Type: "right", Color: "000000", Style: 1},
}
}
// 标题样式
func (e *Excel) getTitleRowStyle() {
e.TitleStyle, _ = e.F.NewStyle(&excelize.Style{
Alignment: &excelize.Alignment{ // 对齐方式
Horizontal: "center", // 水平对齐居中
Vertical: "center", // 垂直对齐居中
},
Fill: excelize.Fill{ // 背景颜色
Type: "pattern",
Color: []string{"#fff2cc"},
Pattern: 1,
},
Font: &excelize.Font{ // 字体
Bold: true,
Size: 16,
},
Border: getBorder(),
})
}
// 列头行样式
func (e *Excel) getHeadRowStyle() {
e.HeadStyle, _ = e.F.NewStyle(&excelize.Style{
Alignment: &excelize.Alignment{ // 对齐方式
Horizontal: "center", // 水平对齐居中
Vertical: "center", // 垂直对齐居中
WrapText: true, // 自动换行
},
Fill: excelize.Fill{ // 背景颜色
Type: "pattern",
Color: []string{"#FDE9D8"},
Pattern: 1,
},
Font: &excelize.Font{ // 字体
Bold: true,
Size: 14,
},
Border: getBorder(),
})
}
// 数据行样式
func (e *Excel) getDataRowStyle() {
style := excelize.Style{}
style.Border = getBorder()
style.Alignment = &excelize.Alignment{
Horizontal: "center", // 水平对齐居中
Vertical: "center", // 垂直对齐居中
WrapText: true, // 自动换行
}
style.Font = &excelize.Font{
Size: 12,
}
e.ContentStyle1, _ = e.F.NewStyle(&style)
style.Fill = excelize.Fill{ // 背景颜色
Type: "pattern",
Color: []string{"#cce7f5"},
Pattern: 1,
}
e.ContentStyle2, _ = e.F.NewStyle(&style)
}

View File

@@ -0,0 +1,219 @@
package excel2
import (
"errors"
"fmt"
"net/http"
"reflect"
"sort"
"strconv"
"strings"
"github.com/xuri/excelize/v2"
)
// GetExcelColumnName 根据列数生成 Excel 列名
func GetExcelColumnName(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
//
// 需要在传入的结构体中的字段加上tagexcel:"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) {
e := ExcelInit()
err = ExportExcel(sheet, title, list, changeHead, e)
if err != nil {
return
}
return e.F, err
}
// ExportExcel excel导出
func ExportExcel(sheet, title string, list interface{}, changeHead map[string]string, 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)
if err != nil {
return
}
// 构造数据行
err = normalBuildDataRow(e, sheet, endColName, dataRow, dataValue)
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
})
var titleRowData []interface{} // 列头行
for i, colTitle := range exportTitle {
endColName := GetExcelColumnName(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 = GetExcelColumnName(len(titleRowData)) // 根据列数生成 Excel 列名
if title != "" {
dataRow = 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 {
dataRow = 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 normalBuildDataRow(e *Excel, sheet, endColName string, row int, dataValue reflect.Value) (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
}
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)
}
// 排序
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 {
_ = 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
}
if err = e.F.SetSheetRow(sheet, startCol, &rowData); err != nil {
return
}
row++
}
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
}
}

View File

@@ -0,0 +1,173 @@
package excel2
import (
"fmt"
"net/http"
"github.com/xuri/excelize/v2"
)
type Col struct {
Name string
Key string
Width int
}
// 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
//
// 需要在传入的结构体中的字段加上tagexcel:"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]
val := list[col.Key]
// switch val.(type) {
// default:
// v, _ := json.Marshal(list[col.Key])
// rowData = append(rowData, v)
// }
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 {
_ = 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
}
}

View File

@@ -0,0 +1,176 @@
package excel2
import (
"bytes"
"errors"
"fmt"
"io"
"mime/multipart"
"reflect"
"strconv"
"github.com/xuri/excelize/v2"
)
func GetExcelData(file multipart.File, dst interface{}) (err error) {
// 创建缓冲区
buf := new(bytes.Buffer)
// 将文件内容复制到缓冲区
_, err = io.Copy(buf, file)
if err != nil {
fmt.Println(err)
// c.String(http.StatusInternalServerError, "读取失败")
err = errors.New("读取失败")
return
}
// 创建Excel文件对象
f, err := excelize.OpenReader(bytes.NewReader(buf.Bytes()))
if err != nil {
fmt.Println(err)
// c.String(http.StatusInternalServerError, "Excel读取失败")
err = errors.New("Excel读取失败")
return
}
err = ImportExcel(f, dst, 1, 2)
// if err != nil {
// fmt.Println(err)
// }
return err
}
// ImportExcel 导入数据单个sheet
// 需要在传入的结构体中的字段加上tagexcel:"title:列头名称;"
// f 获取到的excel对象、dst 导入目标对象【传指针】
// headIndex 表头的索引从0开始用于获取表头名字
// startRow 头行行数从第startRow+1行开始扫
func ImportExcel(f *excelize.File, dst interface{}, headIndex, startRow int) (err error) {
sheetName := f.GetSheetName(0) // 单个sheet时默认读取第一个sheet
err = importData(f, dst, sheetName, headIndex, startRow)
return
}
// ImportBySheet 导入数据读取指定sheetsheetName Sheet名称
func ImportBySheet(f *excelize.File, dst interface{}, sheetName string, headIndex, startRow int) (err error) {
// 当需要读取多个sheet时可以通过下面的方式来调用 ImportBySheet 这个函数
//sheetList := f.GetSheetList()
//for _, sheetName := range sheetList {
// ImportBySheet(f,dst,sheetName,headIndex,startRow)
//}
err = importData(f, dst, sheetName, headIndex, startRow)
return
}
// 获取在数组中得下标
func GetIndex(items []string, item string) int {
for i, v := range items {
if v == item {
return i
}
}
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) {
rows, err := f.GetRows(sheetName) // 获取所有行
if err != nil {
err = errors.New(sheetName + "工作表不存在")
return
}
dataValue := reflect.ValueOf(dst) // 取目标对象的元素类型、字段类型和 tag
// 判断数据的类型
if dataValue.Kind() != reflect.Ptr || dataValue.Elem().Kind() != reflect.Slice {
err = errors.New("invalid data type")
}
heads := []string{} // 表头
dataType := dataValue.Elem().Type().Elem() // 获取导入目标对象的类型信息
// 遍历行,解析数据并填充到目标对象中
for rowIndex, row := range rows {
if rowIndex == headIndex {
heads = row
}
if rowIndex < startRow { // 跳过头行
continue
}
newData := reflect.New(dataType).Elem() // 创建新的目标对象
// 遍历目标对象的字段
for i := 0; i < dataType.NumField(); i++ {
// 这里要用构造函数构造函数里指定了Index默认值为-1当目标结构体的tag没有指定index的话那么 excelTag.Index 就一直为0
// 那么 row[excelizeIndex] 就始终是 row[0],始终拿的是第一列的数据
var excelTag = NewExcelTag()
field := dataType.Field(i) // 获取字段信息和tag
tag := field.Tag.Get(ExcelTagKey)
if tag == "" { // 如果tag不存在则跳过
continue
}
err = excelTag.GetTag(tag)
if err != nil {
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())
// 根据字段类型设置值
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)
}
}
// 将新的目标对象添加到导入目标对象的slice中
dataValue.Elem().Set(reflect.Append(dataValue.Elem(), newData))
}
return
}

View File

@@ -0,0 +1,74 @@
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
}
}

View File

@@ -0,0 +1,76 @@
// 测试源码的文件名以 _test.go 结尾。
// 测试函数的函数名以 Test 开头。
// 函数签名为 func (t *testing.T)。
// https://blog.csdn.net/weixin_43165220/article/details/132939884
package excel2
import (
"fmt"
"testing"
"github.com/xuri/excelize/v2"
)
type Test struct {
Id string `excel:"name:用户账号;"`
Name string `excel:"name:用户姓名;index:1;"`
Email string `excel:"name:用户邮箱;width:25;"`
Com string `excel:"name:所属公司;"`
Dept string `excel:"name:所在部门;"`
RoleKey string `excel:"name:角色代码;"`
RoleName string `excel:"name:角色名称;replace:1_超级管理员,2_普通用户;"`
Remark string `excel:"name:备注;width:40;"`
}
// 导出
func TestExport(t *testing.T) {
var testList = []Test{
{"fuhua", "符华", "fuhua@123.com", "太虚剑派", "开发部", "CJGLY", "1", "备注备注"},
{"baiye", "白夜", "baiye@123.com", "天命科技有限公司", "执行部", "PTYG", "2", ""},
{"chiling", "炽翎", "chiling@123.com", "太虚剑派", "行政部", "PTYG", "2", "备注备注备注备注"},
{"yunmo", "云墨", "yunmo@123.com", "太虚剑派", "财务部", "CJGLY", "1", ""},
{"yuelun", "月轮", "yuelun@123.com", "天命科技有限公司", "执行部", "CJGLY", "1", ""},
{"xunyu", "迅羽",
"xunyu@123.com哈哈哈哈哈哈哈哈这里是最大行高测试哈哈哈哈哈哈哈哈这11111111111里是最大行高测试哈哈哈哈哈哈哈哈这里是最大行高测试",
"天命科技有限公司", "开发部", "PTYG", "2",
"备注备注备注备注com哈哈哈哈哈哈哈哈这里是最大行高测试哈哈哈哈哈哈哈哈这里是最大行高测试哈哈哈哈哈哈哈哈这里是最大行高测里是最大行高测试哈哈哈哈哈哈哈哈这里是最大行高测试"},
}
changeHead := map[string]string{"Id": "账号", "Name": "真实姓名"}
//f, err := excel.NormalExport(testList, "Sheet1", "用户信息", "Id,Email,", true, true, changeHead)
f, err := NormalDynamicExport(testList, "Sheet1", "用户信息", changeHead)
if err != nil {
fmt.Println(err)
return
}
f.Path = "./测试.xlsx"
if err := f.Save(); err != nil {
fmt.Println(err)
return
}
}
// 导入
func TestImports(t *testing.T) {
f, err := excelize.OpenFile("./测试.xlsx")
if err != nil {
fmt.Println("文件打开失败")
}
importList := []Test{}
err = ImportExcel(f, &importList, 1, 2)
if err != nil {
fmt.Println(err)
}
for _, t := range importList {
fmt.Println(t)
}
}
func TestGetExcelColumnName(t *testing.T) {
for i := 0; i < 100; i++ {
var col = GetExcelColumnName(i)
fmt.Println("col:", col)
}
}

Binary file not shown.