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
|
package table
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto/md5"
|
||||||
|
"encoding/hex"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/yaoapp/gou"
|
"github.com/yaoapp/gou"
|
||||||
"github.com/yaoapp/gou/fs"
|
"github.com/yaoapp/gou/fs"
|
||||||
|
"github.com/yaoapp/kun/any"
|
||||||
"github.com/yaoapp/kun/exception"
|
"github.com/yaoapp/kun/exception"
|
||||||
"github.com/yaoapp/kun/log"
|
"github.com/yaoapp/kun/log"
|
||||||
|
"github.com/yaoapp/kun/maps"
|
||||||
"github.com/yaoapp/yao/helper"
|
"github.com/yaoapp/yao/helper"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -31,6 +38,7 @@ func exportProcess() {
|
||||||
gou.RegisterProcessHandler("yao.table.delete", processDelete)
|
gou.RegisterProcessHandler("yao.table.delete", processDelete)
|
||||||
gou.RegisterProcessHandler("yao.table.deletewhere", processDeleteWhere)
|
gou.RegisterProcessHandler("yao.table.deletewhere", processDeleteWhere)
|
||||||
gou.RegisterProcessHandler("yao.table.deletein", processDeleteIn)
|
gou.RegisterProcessHandler("yao.table.deletein", processDeleteIn)
|
||||||
|
gou.RegisterProcessHandler("yao.table.export", processExport)
|
||||||
}
|
}
|
||||||
|
|
||||||
func processXgen(process *gou.Process) interface{} {
|
func processXgen(process *gou.Process) interface{} {
|
||||||
|
|
@ -225,3 +233,66 @@ func processDeleteIn(process *gou.Process) interface{} {
|
||||||
}
|
}
|
||||||
return tab.Action.DeleteIn.MustExec(process)
|
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) {
|
func load(t *testing.T) {
|
||||||
prepare(t)
|
prepare(t)
|
||||||
err := Load(config.Conf)
|
err := Load(config.Conf)
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue