254 lines
6.4 KiB
Go
254 lines
6.4 KiB
Go
// Package excel -----------------------------
|
||
// @file : templateInter.go
|
||
// @author : JJXu
|
||
// @contact : wavingbear@163.com
|
||
// @time : 2022/7/23 15:34
|
||
// -------------------------------------------
|
||
package excel
|
||
|
||
import (
|
||
"errors"
|
||
"fmt"
|
||
"github.com/xuri/excelize/v2"
|
||
"io"
|
||
"log"
|
||
"path/filepath"
|
||
"reflect"
|
||
"sync"
|
||
)
|
||
|
||
var (
|
||
ErrSheetNotExist = errors.New("sheet does not exist")
|
||
ErrSheetDataFrormat = errors.New("sheet data format must be slice or struct")
|
||
//ErrSheetNameNotInTemplate = errors.New("sheet name not in emailTemplate")
|
||
)
|
||
|
||
//======================================================================================================================
|
||
// Sheet define
|
||
|
||
// 抽象工作簿
|
||
type Sheet interface {
|
||
GetData() any
|
||
SheetName() string
|
||
}
|
||
|
||
// 实例化工作簿
|
||
func NewSheet(sheetName string, datas any) Sheet {
|
||
return &newSheetDefine{
|
||
Datas: datas,
|
||
Name: sheetName,
|
||
}
|
||
}
|
||
|
||
// 定义一个能够通用的工作簿结构,此结构必须遵循Sheet接口规范
|
||
type newSheetDefine struct {
|
||
Datas any
|
||
Name string
|
||
}
|
||
|
||
func (s *newSheetDefine) GetData() any {
|
||
return s.Datas
|
||
}
|
||
func (s *newSheetDefine) SheetName() string {
|
||
return s.Name
|
||
}
|
||
|
||
//======================================================================================================================
|
||
// Sheet define
|
||
|
||
// WriteToExcel 通过模板文件写入数据并另存为
|
||
// param fileName : 文件名
|
||
// param filesSuffix : 文件后缀名生成函数
|
||
// param fileRoot : 导出目录
|
||
// param templatePath : 模板文件路径
|
||
// param sheets : Sheet类型的数据,类型为[]Sheet
|
||
// return path : 文件路径
|
||
// return exfileName : 导出后的文件名
|
||
// return err
|
||
func WriteToExcel(fileName string, fileRoot string, templatePath string, sheets ...Sheet) (path string, exFileName string, err error) {
|
||
var exc *Excel
|
||
exc, err = NewExcelTemplate(fileName, fileRoot, templatePath, sheets...)
|
||
if err != nil {
|
||
return
|
||
}
|
||
return exc.WriteToFile()
|
||
}
|
||
|
||
// ReadDataFromExcel 从excel文件读取数据
|
||
func ReadDataFromExcel(filepath string, sheetName string, handler func(rowIndex int, rows []string)) error {
|
||
ex := Excel{OriginFilePath: filepath}
|
||
return ex.ReadSheetData(sheetName, handler)
|
||
}
|
||
|
||
// ReadDataFromBytes 从io口读取数据,用户http上传的附件
|
||
func ReadDataFromBytes(file io.Reader, sheetName string, handler func(rowIndex int, rows []string)) error {
|
||
exce, err := excelize.OpenReader(file)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
var ex = Excel{ex: exce}
|
||
return ex.ReadSheetData(sheetName, handler)
|
||
}
|
||
|
||
// 新建工作表实例
|
||
func NewExcelTemplate(fileName string, fileRoot string, templatePath string, sheets ...Sheet) (exc *Excel, err error) {
|
||
exc = &Excel{
|
||
SaveRoot: fileRoot,
|
||
SaveName: fileName,
|
||
OriginFilePath: templatePath,
|
||
rwLock: sync.RWMutex{},
|
||
}
|
||
if sheets != nil {
|
||
err = exc.AddSheets(sheets...)
|
||
if err != nil {
|
||
return
|
||
}
|
||
}
|
||
return exc, nil
|
||
}
|
||
|
||
type Excel struct {
|
||
ex *excelize.File
|
||
SaveRoot string
|
||
SaveName string
|
||
OriginFilePath string
|
||
Sheets map[string]Sheet
|
||
rwLock sync.RWMutex
|
||
Opts []Option
|
||
}
|
||
|
||
// UseOption 使用可选项
|
||
func (s *Excel) UseOption(opts ...Option) {
|
||
if opts != nil {
|
||
s.Opts = append(s.Opts, opts...)
|
||
}
|
||
}
|
||
|
||
// 添加工作簿
|
||
// 注意如果添加相同的工作簿,之前的会被覆盖
|
||
func (s *Excel) AddSheets(sheets ...Sheet) (err error) {
|
||
if s.Sheets == nil {
|
||
s.Sheets = make(map[string]Sheet, 0)
|
||
}
|
||
for _, sheet := range sheets {
|
||
var sheetName = sheet.SheetName()
|
||
s.Sheets[sheetName] = sheet
|
||
}
|
||
return
|
||
}
|
||
|
||
// 删除工作簿
|
||
func (s *Excel) DeleteSheets(sheetName string) error {
|
||
if s.Sheets == nil {
|
||
return nil
|
||
} else if s.Sheets[sheetName] != nil {
|
||
delete(s.Sheets, sheetName)
|
||
} else {
|
||
return ErrSheetNotExist
|
||
}
|
||
return nil
|
||
}
|
||
|
||
// 读取工作簿
|
||
func (s *Excel) ReadSheetData(sheetName string, handler func(rowIndex int, rows []string)) (err error) {
|
||
if s.ex == nil {
|
||
s.ex, err = excelize.OpenFile(s.OriginFilePath)
|
||
if err != nil {
|
||
return
|
||
}
|
||
}
|
||
datas, err := s.ex.GetRows(sheetName)
|
||
for i, rows := range datas {
|
||
handler(i, rows)
|
||
}
|
||
return nil
|
||
}
|
||
|
||
// 写入到文件
|
||
func (s *Excel) WriteToFile() (path string, fileName string, err error) {
|
||
if s.ex == nil {
|
||
s.ex, err = excelize.OpenFile(s.OriginFilePath)
|
||
if err != nil {
|
||
return
|
||
}
|
||
}
|
||
if s.Opts != nil {
|
||
for _, opt := range s.Opts {
|
||
opt(s)
|
||
}
|
||
}
|
||
//插入数据
|
||
var cellNameList []string
|
||
for sheetName, st := range s.Sheets {
|
||
var firstRow = s.getFirstEmptyRowIndex(s.ex, sheetName)
|
||
var SheetData = reflect.ValueOf(st.GetData())
|
||
var SheetType = reflect.TypeOf(st.GetData())
|
||
switch SheetData.Kind() {
|
||
case reflect.Slice:
|
||
cellNameList = s.getJsonFieldList(SheetType)
|
||
var rowLen = SheetData.Len()
|
||
for i := 0; i < rowLen; i++ {
|
||
var dataMap = s.dataToMap(SheetData.Index(i), SheetType)
|
||
for column, v := range cellNameList {
|
||
var axis = GetCellIndex(i+firstRow+1, column+1)
|
||
err = s.ex.SetCellValue(sheetName, axis, dataMap[v])
|
||
if err != nil {
|
||
fmt.Println(err.Error())
|
||
}
|
||
}
|
||
}
|
||
case reflect.Struct:
|
||
cellNameList = s.getJsonFieldList(SheetType)
|
||
var dataMap = s.dataToMap(SheetData, SheetType)
|
||
for column, v := range cellNameList {
|
||
var axis = GetCellIndex(firstRow+1, column+1)
|
||
err = s.ex.SetCellValue(sheetName, axis, dataMap[v])
|
||
if err != nil {
|
||
fmt.Println(err.Error())
|
||
}
|
||
}
|
||
default:
|
||
return "", "", ErrSheetDataFrormat
|
||
}
|
||
|
||
}
|
||
//保存
|
||
path = filepath.ToSlash(filepath.Join(s.SaveRoot, s.SaveName))
|
||
s.rwLock.Lock()
|
||
if err = s.ex.SaveAs(path); err != nil {
|
||
log.Println(fmt.Sprintf("save file error :%v", err))
|
||
s.rwLock.Unlock()
|
||
return
|
||
}
|
||
s.rwLock.Unlock()
|
||
return
|
||
}
|
||
|
||
// getJsonFieldList 获取json字段列表
|
||
func (s *Excel) getJsonFieldList(sheetType reflect.Type) (tagList []string) {
|
||
t := sheetType.Elem()
|
||
for i := 0; i < t.NumField(); i++ {
|
||
tagList = append(tagList, t.Field(i).Tag.Get("json"))
|
||
}
|
||
return
|
||
}
|
||
|
||
// dataToMap 数据转字典
|
||
func (s *Excel) dataToMap(sheet reflect.Value, sheetType reflect.Type) (dataMap map[string]any) {
|
||
dataMap = make(map[string]any)
|
||
t := sheetType.Elem()
|
||
for i := 0; i < t.NumField(); i++ {
|
||
dataMap[t.Field(i).Tag.Get("json")] = sheet.Field(i).Interface()
|
||
}
|
||
return dataMap
|
||
}
|
||
|
||
// getFirstEmptyRowIndex 获取首个空行的索引位置
|
||
func (s *Excel) getFirstEmptyRowIndex(ex *excelize.File, sheetName string) (index int) {
|
||
rows, err := ex.GetRows(sheetName)
|
||
if err != nil {
|
||
return 1
|
||
}
|
||
return len(rows)
|
||
}
|