package xlsx import ( "context" "joybuywork/internal/plugins/export" "fmt" "github.com/xuri/excelize/v2" ) const ( DefaultSheetName = "Sheet1" //默认拥有的工作簿名称 ) const ( defaultSfIdx = 2 //每个sheet写入行从第二行开始 ) var columnField = []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"} type Export struct { export.BaseExport file *excelize.File //xlsx的文件对象 //多sheet模式需要的参数 ss []Sheet //要导出的sheet ssIdx int //当前导出的sheet索引 swIdx int //当前切换的idx sfv map[string]int //切换字段值对应的sheet的索引(PS:当设定了sheet对应字段是对应的值得时候,就会写到那个sheet上) //下面都是不用多sheet模式需要的参数 sfIdx int64 //当前sheet写到第几行 sheet string //当前写入的sheet fields map[string]string //头部对应的表格列和字段名 map[string]string{"A":"字段key1", "B": "字段key2"} header map[string]string //表头数据map[string]string{"A1":"表头1", "B1": "表头2"} } // NewExport 实例化一个xlsx导出对象 func NewExport(ctx context.Context, header export.HeaderField, total int64, opts ...export.Option) *Export { ept := new(Export) { ept.Init(ctx, header, total, opts...) } //初始化表头信息 ept.header, ept.fields = ept.getHeaderAndFields() //设置文件名称 ept.SetFilename(ept) //初始化文件 ept.initFile() //默认的工作簿名称 ept.sheet, ept.sfIdx = DefaultSheetName, defaultSfIdx return ept } // Use 使用一些参数 func (e *Export) Use(opts ...Option) *Export { for _, opt := range opts { opt(e) } return e } // 初始化文件 func (e *Export) initFile() { if e.IsCustomerFile() { e.file, _ = excelize.OpenFile(e.GetFullPath()) } else { e.file = excelize.NewFile() } } // 自动切换sheet func (e *Export) checkSwitchSheet() (ok bool) { if !e.HasManySheet() || e.sfv != nil { return } //当前sheet是不是写满了,写满了就切 if e.ss[e.ssIdx].writeNum == e.ss[e.ssIdx].Total { ok = e.switchSheet() } else { //当前sheet写入多一条数据 e.ss[e.ssIdx].writeNum++ } return } // 切换sheet func (e *Export) switchSheet() (sws bool) { l := len(e.ss) //已经是最后一页 if l-1 == e.swIdx { return } //剩余的sheet remain := e.ss[e.swIdx+1:] for _, v := range remain { //下一个sheet e.swIdx++ if v.Total > 0 { sws = true break } } //有下一个sheet if sws { //把切换成功的idx换成导出的idx e.ssIdx, e.sfIdx = e.swIdx, defaultSfIdx //初始化sheet e.initSheet(&e.ss[e.ssIdx], false) } return } // 初始化sheet func (e *Export) initSheet(sheet *Sheet, setHead bool) { e.sheet, e.Header = sheet.Name, sheet.Header if sheet.header == nil || sheet.fields == nil { //初始化字段数据 sheet.header, sheet.fields = e.getHeaderAndFields() sheet.sfIdx = defaultSfIdx } //设置当前导出sheet e.header, e.fields = sheet.header, sheet.fields //设置sheet的表头 if setHead { _ = e.setHeader() } } // 获取字段信息字段信息 func (e *Export) getHeaderAndFields() (map[string]string, map[string]string) { //初始化导出表头和字段数据 header, fields := make(map[string]string), make(map[string]string) for k, v := range e.Header { cf := e.getColumnField(k) fields[cf] = v.Field header[fmt.Sprintf("%s1", cf)] = v.Title //第一行写表头 } return header, fields } // 获取column func (e *Export) getColumnField(idx int) string { if idx < len(columnField) { return columnField[idx] } f := (idx / len(columnField)) - 1 i := idx % len(columnField) return fmt.Sprintf("%s%s", columnField[f], columnField[i]) } type SheetRowData struct { sheet string //当前行写入的sheet row map[string]any //当前行的数据 } // 获取写入xlsx的数据行 func (e *Export) getList(data []export.RowData) (list []SheetRowData) { list = make([]SheetRowData, 0, len(data)) for _, row := range data { list = append(list, e.getSheetData(row)) } return } // 获取sheet的数据 func (e *Export) getSheetData(row export.RowData) SheetRowData { fields, idx, sfIdx := e.fields, e.ssIdx, e.sfIdx var one = SheetRowData{ sheet: e.sheet, row: map[string]any{}, } //如果事标识的分sheet if e.sfv != nil { for field, value := range row { if i, ok := e.sfv[e.getSfvKey(field, value)]; ok { idx = i fields, one.sheet, sfIdx = e.ss[idx].fields, e.ss[idx].Name, e.ss[idx].sfIdx } } e.ss[idx].sfIdx++ } //当前行的数据 for column, field := range fields { columnKey := fmt.Sprintf("%s%d", column, sfIdx) one.row[columnKey] = "" if v, ok := row[field]; ok { one.row[columnKey] = v } } e.sfIdx++ return one } // 初始化xlsx文件对象 func (e *Export) setHeader() (err error) { for axis, value := range e.header { err = e.file.SetCellValue(e.sheet, axis, value) if err != nil { return } } return } // DeleteSheet 删除工作表 func (e *Export) DeleteSheet(name string) { e.file.DeleteSheet(name) } // CreateSheet 创建sheet func (e *Export) CreateSheet(name string) (idx int) { idx, _ = e.file.GetSheetIndex(name) if idx == -1 { idx, _ = e.file.NewSheet(name) } return } // ExportFile 直接导出数据 func (e *Export) ExportFile(data []export.RowData) (err error) { err = e.setHeader() if err != nil { return } //工作簿写入 e.sheetWrite(data) return e.file.SaveAs(e.GetFullPath()) } // 工作簿写入 func (e *Export) sheetWrite(data []export.RowData) { list := e.getList(data) for key, row := range list { //有标识切sheet的时候 ok := e.checkSwitchSheet() if ok { //切换成功,要重新计算行数 e.sheetWrite(data[key:]) return } //写入文件 for axis, value := range row.row { switch value.(type) { case int: _ = e.file.SetCellInt(row.sheet, axis, value.(int)) case int32: _ = e.file.SetCellInt(row.sheet, axis, int(value.(int32))) case uint32: _ = e.file.SetCellInt(row.sheet, axis, int(value.(uint32))) case int64: _ = e.file.SetCellInt(row.sheet, axis, int(value.(int64))) case uint64: _ = e.file.SetCellInt(row.sheet, axis, int(value.(uint64))) default: _ = e.file.SetCellValue(row.sheet, axis, fmt.Sprintf("%v\t", value)) } } } } // FileSuffix 文件后缀 func (e *Export) FileSuffix() string { return "xlsx" } // HasManySheet 是否有多sheet导出 func (e *Export) HasManySheet() bool { return len(e.ss) > 0 } // 获取sfv的key func (e *Export) getSfvKey(key string, val interface{}) string { return fmt.Sprintf("%s_%v", key, val) }