[add] table.Export process

This commit is contained in:
Max 2022-11-16 17:25:25 +08:00
parent 3439a25d3e
commit e157945bd1
3 changed files with 199 additions and 0 deletions

116
widgets/table/excel.go Normal file
View file

@ -0,0 +1,116 @@
package table
import (
"errors"
"fmt"
"os"
"path/filepath"
"github.com/xuri/excelize/v2"
"github.com/yaoapp/kun/any"
"github.com/yaoapp/kun/log"
"github.com/yaoapp/kun/maps"
"github.com/yaoapp/yao/xfs"
)
// Export Export query result to Excel
func (dsl *DSL) Export(filename string, data interface{}, page int, chunkSize int) error {
log.Trace("[Export] %s %d %d Before: %#v", filename, page, chunkSize, data)
rows := []maps.MapStr{}
if values, ok := data.([]maps.MapStrAny); ok {
for _, row := range values {
rows = append(rows, row.Dot())
}
} else if values, ok := data.([]map[string]interface{}); ok {
for _, row := range values {
rows = append(rows, maps.Of(row).Dot())
}
} else if values, ok := data.([]interface{}); ok {
for _, row := range values {
rows = append(rows, any.Of(row).MapStr().Dot())
}
}
log.Trace("[Export] %s %d %d After: %#v", filename, page, chunkSize, data)
columns, err := dsl.exportSetting()
if err != nil {
return err
}
if len(columns) == 0 {
return fmt.Errorf("the table does not support export")
}
filename = filepath.Join(xfs.Stor.Root, filename)
if _, err := os.Stat(filename); errors.Is(err, os.ErrNotExist) {
f := excelize.NewFile()
index := f.GetActiveSheetIndex()
name := f.GetSheetName(index)
f.SetSheetName(name, dsl.Name)
for i, column := range columns {
axis, err := excelize.CoordinatesToCellName(i+1, 1)
if err != nil {
return err
}
f.SetCellValue(dsl.Name, axis, column["name"])
}
if err := f.SaveAs(filename); err != nil {
fmt.Println(err)
return err
}
}
f, err := excelize.OpenFile(filename)
if err != nil {
return err
}
defer f.Close()
offset := (page-1)*chunkSize + 2
for line, row := range rows {
for i, column := range columns {
v := row.Get(column["field"])
if v != nil {
axis, err := excelize.CoordinatesToCellName(i+1, line+offset)
if err != nil {
return err
}
f.SetCellValue(dsl.Name, axis, v)
}
}
// fmt.Println("--", line, page, offset, filename, chunkSize, "--")
}
return f.Save()
}
func (dsl *DSL) exportSetting() ([]map[string]string, error) {
// Validate params
if dsl.Layout == nil {
return nil, fmt.Errorf("the table layout does not found")
}
if dsl.Fields == nil || dsl.Fields.Table == nil {
return nil, fmt.Errorf("the table fields does not found")
}
if dsl.Layout.Table == nil || dsl.Layout.Table.Columns == nil {
return nil, fmt.Errorf("the columns table layout does not found")
}
setting := []map[string]string{}
for _, column := range dsl.Layout.Table.Columns {
if field, has := dsl.Fields.Table[column.Name]; has {
bind := field.Bind
if field.View != nil && field.View.Bind != "" {
bind = field.View.Bind
}
setting = append(setting, map[string]string{"name": column.Name, "field": bind})
}
}
return setting, nil
}

View file

@ -1,13 +1,20 @@
package table
import (
"crypto/md5"
"encoding/hex"
"fmt"
"os"
"path/filepath"
"strings"
"time"
"github.com/yaoapp/gou"
"github.com/yaoapp/gou/fs"
"github.com/yaoapp/kun/any"
"github.com/yaoapp/kun/exception"
"github.com/yaoapp/kun/log"
"github.com/yaoapp/kun/maps"
"github.com/yaoapp/yao/helper"
)
@ -31,6 +38,7 @@ func exportProcess() {
gou.RegisterProcessHandler("yao.table.delete", processDelete)
gou.RegisterProcessHandler("yao.table.deletewhere", processDeleteWhere)
gou.RegisterProcessHandler("yao.table.deletein", processDeleteIn)
gou.RegisterProcessHandler("yao.table.export", processExport)
}
func processXgen(process *gou.Process) interface{} {
@ -225,3 +233,66 @@ func processDeleteIn(process *gou.Process) interface{} {
}
return tab.Action.DeleteIn.MustExec(process)
}
// processExport yao.table.Export (:table, :queryParam, :chunkSize)
func processExport(process *gou.Process) interface{} {
process.ValidateArgNums(1)
tab := MustGet(process) // 0
params := process.ArgsQueryParams(1, gou.QueryParam{})
pagesize := process.ArgsInt(2, 50)
// Filename
hash := md5.Sum([]byte(time.Now().Format("20060102-15:04:05")))
fingerprint := string(hex.EncodeToString(hash[:]))
fingerprint = strings.ToUpper(fingerprint)
dir := time.Now().Format("20060102")
filename := filepath.Join(string(os.PathSeparator), dir, fmt.Sprintf("%s.xlsx", fingerprint))
// Create Data Path
fs := fs.MustGet("system")
if has, _ := fs.Exists(dir); !has {
fs.MkdirAll(dir, uint32(os.ModePerm))
}
// Query
page := 1
for page > 0 {
process.Args = []interface{}{tab.ID, params, page, pagesize}
data, err := tab.Action.Search.Exec(process)
if err != nil {
log.Error("[table] export error %s", err.Error())
page = -1
continue
}
res, ok := data.(map[string]interface{})
if !ok {
res, ok = data.(maps.MapStrAny)
if !ok {
log.Error("[table] export Search Action response data error %#v", data)
page = -1
continue
}
}
if _, ok := res["next"]; !ok {
page = -1
continue
}
size := pagesize
if _, ok := res["pagesize"]; ok {
size = any.Of(res["pagesize"]).CInt()
}
// Export
err = tab.Export(filename, res["data"], page, size)
if err != nil {
log.Error("Export %s %s", tab.ID, err.Error())
}
page = any.Of(res["next"]).CInt()
}
return filename
}

View file

@ -432,6 +432,18 @@ func TestProcessXgen(t *testing.T) {
}
func TestProcessExport(t *testing.T) {
load(t)
clear(t)
testData(t)
args := []interface{}{"pet", gou.QueryParam{Wheres: []gou.QueryWhere{{Column: "mode", Value: "enabled"}}}, 2}
response := gou.NewProcess("yao.table.Export", args...).Run()
assert.NotNil(t, response)
fs := fs.MustGet("system")
size, _ := fs.Size(response.(string))
assert.Greater(t, size, 1000)
}
func load(t *testing.T) {
prepare(t)
err := Load(config.Conf)