From eadf112e1eddf5866d2e0559548d8eecb1137eaa Mon Sep 17 00:00:00 2001 From: Max Date: Fri, 11 Nov 2022 20:03:56 +0800 Subject: [PATCH] [add] yao.component.* & fix cloud props api bug --- widgets/chart/chart.go | 2 +- widgets/chart/process_test.go | 5 +- widgets/component/process.go | 113 ++++++++++++++++++++++++++++------ widgets/component/props.go | 14 +++++ widgets/form/form.go | 2 +- widgets/form/process_test.go | 5 +- widgets/table/api_test.go | 5 +- widgets/table/process_test.go | 28 +++++---- widgets/table/table.go | 2 +- 9 files changed, 137 insertions(+), 39 deletions(-) diff --git a/widgets/chart/chart.go b/widgets/chart/chart.go index efa90d79..fa6ffffc 100644 --- a/widgets/chart/chart.go +++ b/widgets/chart/chart.go @@ -185,7 +185,7 @@ func (dsl *DSL) Xgen() (map[string]interface{}, error) { for _, cProp := range dsl.CProps { err := cProp.Replace(setting, func(cProp component.CloudPropsDSL) interface{} { return map[string]interface{}{ - "api": fmt.Sprintf("/api/__yao/chart/%s/component/%s/%s", dsl.ID, cProp.Xpath, cProp.Name), + "api": fmt.Sprintf("/api/__yao/chart/%s%s", dsl.ID, cProp.Path()), "params": cProp.Query, } }) diff --git a/widgets/chart/process_test.go b/widgets/chart/process_test.go index 5e5597aa..3c3b563c 100644 --- a/widgets/chart/process_test.go +++ b/widgets/chart/process_test.go @@ -1,6 +1,7 @@ package chart import ( + "net/url" "testing" "github.com/stretchr/testify/assert" @@ -66,7 +67,7 @@ func TestProcessSetting(t *testing.T) { } data := any.Of(res).MapStr().Dot() - assert.Equal(t, "/api/__yao/chart/dashboard/component/fields.filter.状态.edit.props.xProps/remote", data.Get("fields.filter.状态.edit.props.xProps.remote.api")) + assert.Equal(t, "/api/__yao/chart/dashboard/component/fields.filter."+url.QueryEscape("状态")+".edit.props.xProps/remote", data.Get("fields.filter.状态.edit.props.xProps.remote.api")) } func TestProcessXgen(t *testing.T) { @@ -78,7 +79,7 @@ func TestProcessXgen(t *testing.T) { } data := any.Of(res).MapStr().Dot() - assert.Equal(t, "/api/__yao/chart/dashboard/component/fields.filter.状态.edit.props.xProps/remote", data.Get("fields.filter.状态.edit.props.xProps.remote.api")) + assert.Equal(t, "/api/__yao/chart/dashboard/component/fields.filter."+url.QueryEscape("状态")+".edit.props.xProps/remote", data.Get("fields.filter.状态.edit.props.xProps.remote.api")) } func load(t *testing.T) { diff --git a/widgets/component/process.go b/widgets/component/process.go index 3616b12e..940208da 100644 --- a/widgets/component/process.go +++ b/widgets/component/process.go @@ -1,34 +1,109 @@ package component import ( + "fmt" + "strings" + + jsoniter "github.com/json-iterator/go" "github.com/yaoapp/gou" + "github.com/yaoapp/kun/any" + "github.com/yaoapp/kun/exception" ) // Export process func exportProcess() { - gou.RegisterProcessHandler("yao.component.tagview", processTagView) - gou.RegisterProcessHandler("yao.component.tagedit", processTagEdit) + gou.RegisterProcessHandler("yao.component.selectoptions", processSelectOptions) } -func processTagView(process *gou.Process) interface{} { - process.ValidateArgNums(3) - value := process.Args[1] - switch value.(type) { - case string: - return map[string]interface{}{"label": value, "color": "#FF0000"} +func processSelectOptions(process *gou.Process) interface{} { + process.ValidateArgNums(1) + query := process.ArgsMap(0, map[string]interface{}{}) + if !query.Has("model") { + exception.New("query.model required", 400).Throw() } - return map[string]interface{}{} -} -func processTagEdit(process *gou.Process) interface{} { - process.ValidateArgNums(3) - value := process.Args[1] - switch value.(type) { - case map[string]interface{}: - if val, has := value.(map[string]interface{})["label"]; has { - return val + model, ok := query.Get("model").(string) + if !ok { + exception.New("query.model must be a string", 400).Throw() + } + + m := gou.Select(model) + + valueField := query.Get("value") + if valueField == nil { + valueField = "id" + } + value, ok := valueField.(string) + if !ok { + exception.New("query.value must be a string", 400).Throw() + } + + labelField := query.Get("label") + if labelField == nil { + labelField = "name" + } + label, ok := labelField.(string) + if !ok { + exception.New("query.label must be a string", 400).Throw() + } + + limit := 500 + if query.Get("limit") != nil { + v := any.Of(query.Get("limit")) + if v.IsInt() || v.IsString() { + limit = v.CInt() } - return nil } - return value + + wheres := []gou.QueryWhere{} + switch input := query.Get("wheres").(type) { + case string: + where := gou.QueryWhere{} + err := jsoniter.Unmarshal([]byte(input), &where) + if err != nil { + exception.New("query.wheres error %s", 400, err.Error()).Throw() + } + wheres = append(wheres, where) + break + + case []string: + for _, data := range input { + where := gou.QueryWhere{} + err := jsoniter.Unmarshal([]byte(data), &where) + if err != nil { + exception.New("query.wheres error %s", 400, err.Error()).Throw() + } + wheres = append(wheres, where) + } + break + } + + if data, ok := query.Get("wheres").(string); ok { + data = strings.TrimSpace(data) + if strings.HasPrefix(data, "{") && strings.HasSuffix(data, "}") { + data = fmt.Sprintf("[%s]", data) + } + err := jsoniter.Unmarshal([]byte(data), &wheres) + if err != nil { + exception.New("query.wheres error %s", 400, err.Error()).Throw() + } + } + + rows, err := m.Get(gou.QueryParam{ + Select: []interface{}{valueField, labelField}, + Wheres: wheres, + Limit: limit, + }) + if err != nil { + exception.New("%s", 500, err.Error()).Throw() + } + + res := []map[string]interface{}{} + for _, row := range rows { + res = append(res, map[string]interface{}{ + "label": row.Get(label), + "value": row.Get(value), + }) + } + return res } diff --git a/widgets/component/props.go b/widgets/component/props.go index 016d12e9..9db963e2 100644 --- a/widgets/component/props.go +++ b/widgets/component/props.go @@ -2,6 +2,7 @@ package component import ( "fmt" + "net/url" "strings" jsoniter "github.com/json-iterator/go" @@ -14,6 +15,11 @@ func (p PropsDSL) CloudProps(xpath string) (map[string]CloudPropsDSL, error) { return p.parseCloudProps(xpath, p) } +// Path api path +func (cProp CloudPropsDSL) Path() string { + return fmt.Sprintf("/component/%s/%s", url.QueryEscape(cProp.Xpath), url.QueryEscape(cProp.Name)) +} + // ExecQuery execute query func (cProp CloudPropsDSL) ExecQuery(process *gou.Process, query map[string]interface{}) (interface{}, error) { @@ -21,6 +27,14 @@ func (cProp CloudPropsDSL) ExecQuery(process *gou.Process, query map[string]inte query = map[string]interface{}{} } + // filter array + for key, value := range query { + if strings.HasSuffix(key, "[]") { + query[strings.TrimSuffix(key, "[]")] = value + delete(query, key) + } + } + // Process name := cProp.Process if name == "" { diff --git a/widgets/form/form.go b/widgets/form/form.go index e8c61978..bcc183ba 100644 --- a/widgets/form/form.go +++ b/widgets/form/form.go @@ -241,7 +241,7 @@ func (dsl *DSL) Xgen() (map[string]interface{}, error) { for _, cProp := range dsl.CProps { err := cProp.Replace(setting, func(cProp component.CloudPropsDSL) interface{} { return map[string]interface{}{ - "api": fmt.Sprintf("/api/__yao/form/%s/component/%s/%s", dsl.ID, cProp.Xpath, cProp.Name), + "api": fmt.Sprintf("/api/__yao/form/%s%s", dsl.ID, cProp.Path()), "params": cProp.Query, } }) diff --git a/widgets/form/process_test.go b/widgets/form/process_test.go index f0769e2c..7bc61052 100644 --- a/widgets/form/process_test.go +++ b/widgets/form/process_test.go @@ -2,6 +2,7 @@ package form import ( "fmt" + "net/url" "testing" "github.com/stretchr/testify/assert" @@ -180,7 +181,7 @@ func TestProcessSetting(t *testing.T) { } data := any.Of(res).MapStr().Dot() - assert.Equal(t, "/api/__yao/form/pet/component/fields.form.状态.edit.props.xProps/remote", data.Get("fields.form.状态.edit.props.xProps.remote.api")) + assert.Equal(t, "/api/__yao/form/pet/component/fields.form."+url.QueryEscape("状态")+".edit.props.xProps/remote", data.Get("fields.form.状态.edit.props.xProps.remote.api")) } func TestProcessXgen(t *testing.T) { @@ -194,7 +195,7 @@ func TestProcessXgen(t *testing.T) { } data := any.Of(res).MapStr().Dot() - assert.Equal(t, "/api/__yao/form/pet/component/fields.form.状态.edit.props.xProps/remote", data.Get("fields.form.状态.edit.props.xProps.remote.api")) + assert.Equal(t, "/api/__yao/form/pet/component/fields.form."+url.QueryEscape("状态")+".edit.props.xProps/remote", data.Get("fields.form.状态.edit.props.xProps.remote.api")) } func load(t *testing.T) { diff --git a/widgets/table/api_test.go b/widgets/table/api_test.go index 5422579b..06b34d34 100644 --- a/widgets/table/api_test.go +++ b/widgets/table/api_test.go @@ -2,6 +2,7 @@ package table import ( "fmt" + "net/url" "testing" "time" @@ -42,8 +43,8 @@ func TestAPISetting(t *testing.T) { data := any.Of(v).MapStr().Dot() assert.Equal(t, "/api/xiang/import/pet", data.Get("header.preset.import.api.import")) // assert.Equal(t, "跳转", data.Get("header.preset.import.operation.0.title")) - assert.Equal(t, "/api/__yao/table/pet/component/fields.table.入院状态.view.props.xProps/remote", data.Get("fields.table.入院状态.view.props.xProps.remote.api")) - assert.Equal(t, "/api/__yao/table/pet/component/fields.table.入院状态.edit.props.xProps/remote", data.Get("fields.table.入院状态.edit.props.xProps.remote.api")) + assert.Equal(t, "/api/__yao/table/pet/component/fields.table."+url.QueryEscape("入院状态")+".view.props.xProps/remote", data.Get("fields.table.入院状态.view.props.xProps.remote.api")) + assert.Equal(t, "/api/__yao/table/pet/component/fields.table."+url.QueryEscape("入院状态")+".edit.props.xProps/remote", data.Get("fields.table.入院状态.edit.props.xProps.remote.api")) } func TestAPISearch(t *testing.T) { diff --git a/widgets/table/process_test.go b/widgets/table/process_test.go index 7174aaac..f0d38b43 100644 --- a/widgets/table/process_test.go +++ b/widgets/table/process_test.go @@ -2,12 +2,12 @@ package table import ( "fmt" + "net/url" "testing" "github.com/stretchr/testify/assert" "github.com/yaoapp/gou" "github.com/yaoapp/kun/any" - "github.com/yaoapp/kun/maps" "github.com/yaoapp/yao/config" q "github.com/yaoapp/yao/query" ) @@ -308,7 +308,13 @@ func TestProcessComponent(t *testing.T) { "pet", "fields.filter.状态.edit.props.xProps", "remote", - map[string]interface{}{"select": []string{"name", "status"}, "limit": 2}, + map[string]interface{}{ + "model": "pet", + "label": "name", + "value": "status", + "wheres[]": `{"column":"id","op":"ge","value":0}`, + "limit": "2", + }, } res, err := gou.NewProcess("yao.table.Component", args...).Exec() @@ -316,13 +322,13 @@ func TestProcessComponent(t *testing.T) { t.Fatal(err) } - pets, ok := res.([]maps.MapStr) + pets, ok := res.([]map[string]interface{}) assert.True(t, ok) assert.Equal(t, 2, len(pets)) - assert.Equal(t, "Cookie", pets[0]["name"]) - assert.Equal(t, "checked", pets[0]["status"]) - assert.Equal(t, "Baby", pets[1]["name"]) - assert.Equal(t, "checked", pets[1]["status"]) + assert.Equal(t, "Cookie", pets[0]["label"]) + assert.Equal(t, "checked", pets[0]["value"]) + assert.Equal(t, "Baby", pets[1]["label"]) + assert.Equal(t, "checked", pets[1]["value"]) } func TestProcessComponentError(t *testing.T) { @@ -353,8 +359,8 @@ func TestProcessSetting(t *testing.T) { assert.Equal(t, "/api/xiang/import/pet", data.Get("header.preset.import.api.import")) assert.Equal(t, "查看详情1", data.Get("header.preset.import.actions[0].title")) assert.Equal(t, "查看详情2", data.Get("header.preset.import.actions[1].title")) - assert.Equal(t, "/api/__yao/table/pet/component/fields.table.入院状态.view.props.xProps/remote", data.Get("fields.table.入院状态.view.props.xProps.remote.api")) - assert.Equal(t, "/api/__yao/table/pet/component/fields.table.入院状态.edit.props.xProps/remote", data.Get("fields.table.入院状态.edit.props.xProps.remote.api")) + assert.Equal(t, "/api/__yao/table/pet/component/fields.table."+url.QueryEscape("入院状态")+".view.props.xProps/remote", data.Get("fields.table.入院状态.view.props.xProps.remote.api")) + assert.Equal(t, "/api/__yao/table/pet/component/fields.table."+url.QueryEscape("入院状态")+".edit.props.xProps/remote", data.Get("fields.table.入院状态.edit.props.xProps.remote.api")) } func TestProcessXgen(t *testing.T) { @@ -371,8 +377,8 @@ func TestProcessXgen(t *testing.T) { assert.Equal(t, "/api/xiang/import/pet", data.Get("header.preset.import.api.import")) assert.Equal(t, "查看详情1", data.Get("header.preset.import.actions[0].title")) assert.Equal(t, "查看详情2", data.Get("header.preset.import.actions[1].title")) - assert.Equal(t, "/api/__yao/table/pet/component/fields.table.入院状态.view.props.xProps/remote", data.Get("fields.table.入院状态.view.props.xProps.remote.api")) - assert.Equal(t, "/api/__yao/table/pet/component/fields.table.入院状态.edit.props.xProps/remote", data.Get("fields.table.入院状态.edit.props.xProps.remote.api")) + assert.Equal(t, "/api/__yao/table/pet/component/fields.table."+url.QueryEscape("入院状态")+".view.props.xProps/remote", data.Get("fields.table.入院状态.view.props.xProps.remote.api")) + assert.Equal(t, "/api/__yao/table/pet/component/fields.table."+url.QueryEscape("入院状态")+".edit.props.xProps/remote", data.Get("fields.table.入院状态.edit.props.xProps.remote.api")) } func load(t *testing.T) { diff --git a/widgets/table/table.go b/widgets/table/table.go index e011c8aa..21f1508b 100644 --- a/widgets/table/table.go +++ b/widgets/table/table.go @@ -272,7 +272,7 @@ func (dsl *DSL) Xgen() (map[string]interface{}, error) { for _, cProp := range dsl.CProps { err := cProp.Replace(setting, func(cProp component.CloudPropsDSL) interface{} { return map[string]interface{}{ - "api": fmt.Sprintf("/api/__yao/table/%s/component/%s/%s", dsl.ID, cProp.Xpath, cProp.Name), + "api": fmt.Sprintf("/api/__yao/table/%s%s", dsl.ID, cProp.Path()), "params": cProp.Query, } })