commit
c15b12760b
3 changed files with 199 additions and 0 deletions
116
widgets/table/excel.go
Normal file
116
widgets/table/excel.go
Normal 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
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue