From 30346b1f7b0cad0338ec04be52b0be87b9dcc0c5 Mon Sep 17 00:00:00 2001 From: Max Date: Fri, 19 Aug 2022 23:46:57 +0800 Subject: [PATCH] [add] language pack support for table --- table/lang.go | 98 +++++++++++++++++++++ table/table.go | 14 +++ table/table_test.go | 17 +++- table/validate.go | 132 ++++++++++++++++++++++++++++- tests/langs/zh-cn/tables/demo.yml | 10 +++ tests/langs/zh-hk/tables/demo.yml | 10 +++ tests/tables/assign.tab.json | 2 +- tests/tables/demo.tab.json | 85 +++++++++++++++++++ tests/tables/foo/bar.tab.json | 66 ++------------- tests/tables/hooks/find.tab.json | 4 +- tests/tables/hooks/save.tab.json | 4 +- tests/tables/hooks/search.tab.json | 2 +- tests/tables/hooks/select.tab.json | 4 +- 13 files changed, 375 insertions(+), 73 deletions(-) create mode 100644 table/lang.go create mode 100644 tests/langs/zh-cn/tables/demo.yml create mode 100644 tests/langs/zh-hk/tables/demo.yml create mode 100644 tests/tables/demo.tab.json diff --git a/table/lang.go b/table/lang.go new file mode 100644 index 00000000..4b23f071 --- /dev/null +++ b/table/lang.go @@ -0,0 +1,98 @@ +package table + +// Lang for applying a language pack +func (table *Table) Lang(trans func(widget string, inst string, value *string) bool) { + inst := table.Table + widget := "table" + + trans(widget, inst, &table.Name) + trans(widget, inst, &table.Decription) + + // Columns + for name, column := range table.Columns { + new := name + trans(widget, inst, &new) + trans(widget, inst, &column.Label) + + // Props + transMap(widget, inst, column.Edit.Props, trans) + transMap(widget, inst, column.View.Props, trans) + transMap(widget, inst, column.Form.Props, trans) + + delete(table.Columns, name) + table.Columns[new] = column + } + + // Filters + for name, filter := range table.Filters { + new := name + trans(widget, inst, &new) + trans(widget, inst, &filter.Label) + delete(table.Filters, name) + + // Props + transMap(widget, inst, filter.Input.Props, trans) + table.Filters[new] = filter + } + + // List + transMap(widget, inst, table.List.Layout, trans) + transMap(widget, inst, table.List.Option, trans) + + // Edit + transMap(widget, inst, table.Edit.Layout, trans) + transMap(widget, inst, table.Edit.Option, trans) +} + +func transMap(widget string, inst string, values map[string]interface{}, trans func(widget string, inst string, value *string) bool) { + for key, value := range values { + + switch value.(type) { + + case string: + v := value.(string) + trans(widget, inst, &v) + values[key] = v + break + + case []interface{}: + v := value.([]interface{}) + transArr(widget, inst, v, trans) + values[key] = v + break + + case map[string]interface{}: + v := value.(map[string]interface{}) + transMap(widget, inst, v, trans) + values[key] = v + break + } + + } +} + +func transArr(widget string, inst string, values []interface{}, trans func(widget string, inst string, value *string) bool) { + for key, value := range values { + switch value.(type) { + + case string: + v := value.(string) + trans(widget, inst, &v) + values[key] = v + break + + case []interface{}: + v := value.([]interface{}) + transArr(widget, inst, v, trans) + values[key] = v + break + + case map[string]interface{}: + v := value.(map[string]interface{}) + transMap(widget, inst, v, trans) + values[key] = v + break + } + + } +} diff --git a/table/table.go b/table/table.go index 5cf15a80..765bbdd7 100644 --- a/table/table.go +++ b/table/table.go @@ -12,6 +12,7 @@ import ( "github.com/xuri/excelize/v2" "github.com/yaoapp/gou" "github.com/yaoapp/gou/helper" + "github.com/yaoapp/gou/lang" "github.com/yaoapp/kun/any" "github.com/yaoapp/kun/exception" "github.com/yaoapp/kun/log" @@ -172,7 +173,20 @@ func LoadTable(source string, name string) (*Table, error) { table.loadColumns() table.loadFilters() table.loadAPIs() + + err = table.Validate() + if err != nil { + log.Error("[Table] %s is not valid: %s ", table.Table, err.Error()) + return nil, err + } + Tables[name] = &table + + // Apply a language pack + if lang.Default != nil { + lang.Default.Apply(Tables[name]) + } + return Tables[name], nil } diff --git a/table/table_test.go b/table/table_test.go index c3e5f245..c57ec2a9 100644 --- a/table/table_test.go +++ b/table/table_test.go @@ -1,16 +1,22 @@ package table import ( + "os" "testing" "github.com/stretchr/testify/assert" "github.com/yaoapp/yao/config" + "github.com/yaoapp/yao/lang" "github.com/yaoapp/yao/model" "github.com/yaoapp/yao/share" ) func TestLoad(t *testing.T) { + + os.Setenv("YAO_LANG", "zh-hk") + lang.Load(config.Conf) + share.DBConnect(config.Conf.DB) share.Load(config.Conf) model.Load(config.Conf) @@ -48,5 +54,14 @@ func check(t *testing.T) { for key := range Tables { keys = append(keys, key) } - assert.Equal(t, 9, len(keys)) + assert.Equal(t, 10, len(keys)) + + demo := Select("demo") + assert.NotNil(t, demo.Columns["類型"]) + assert.NotNil(t, demo.Columns["類型"].Edit.Props["options"]) + + options := demo.Columns["類型"].Edit.Props["options"].([]interface{}) + opt1 := options[0].(map[string]interface{}) + assert.Equal(t, "貓", opt1["label"]) + } diff --git a/table/validate.go b/table/validate.go index 01df3881..52db7db6 100644 --- a/table/validate.go +++ b/table/validate.go @@ -1,6 +1,132 @@ package table +import "fmt" + // Validate 校验表格格式 -// func (table Table) Validate() error { -// return nil -// } +func (table Table) Validate() error { + err := table.validateList() + if err != nil { + return err + } + + err = table.validateEdit() + if err != nil { + return err + } + + return nil +} + +func (table Table) validateEdit() error { + if table.Edit.Layout == nil { + return nil + } + + fieldset, ok := table.Edit.Layout["fieldset"].([]interface{}) + if !ok { + return fmt.Errorf("edit.layout.columns is required") + } + + for idx, set := range fieldset { + set, ok := set.(map[string]interface{}) + if !ok { + return fmt.Errorf("edit.layout.fieldset.%d format error", idx) + } + + columns, ok := set["columns"].([]interface{}) + if !ok { + return fmt.Errorf("edit.layout.fieldset.%d.columns format error", idx) + } + + for cidx, column := range columns { + col, ismap := column.(map[string]interface{}) + name, isstr := column.(string) + if !ismap && !isstr { + return fmt.Errorf("edit.layout.fieldset.%d.columns.%d format error", idx, cidx) + } + + if ismap { + v, has := col["name"] + if !has { + return fmt.Errorf("edit.layout.fieldset.%d.columns.%d.name is required", idx, cidx) + } + namestr, ok := v.(string) + if !ok { + return fmt.Errorf("edit.layout.fieldset.%d.columns.%d.name format error", idx, cidx) + } + name = namestr + } + + if _, has := table.Columns[name]; !has { + return fmt.Errorf("edit.layout.fieldset.%d.columns.%d.name %s is not found in columns", idx, cidx, name) + } + } + + } + + return nil +} + +func (table Table) validateList() error { + + if table.List.Layout == nil { + return nil + } + + // table.List.Layout + columns, ok := table.List.Layout["columns"].([]interface{}) + if !ok { + return fmt.Errorf("list.layout.columns is required") + } + + for idx, column := range columns { + + col, ismap := column.(map[string]interface{}) + name, isstr := column.(string) + if !ismap && !isstr { + return fmt.Errorf("list.layout.columns.%d format error", idx) + } + + if ismap { + v, has := col["name"] + if !has { + return fmt.Errorf("list.layout.columns.%d.name is required", idx) + } + namestr, ok := v.(string) + if !ok { + return fmt.Errorf("list.layout.columns.%d.name format error", idx) + } + name = namestr + } + + if _, has := table.Columns[name]; !has { + return fmt.Errorf("list.layout.columns.%d.name %s is not found in columns", idx, name) + } + } + + filters, ok := table.List.Layout["filters"].([]interface{}) + if ok { + for idx, fliter := range filters { + fli, ok := fliter.(map[string]interface{}) + if !ok { + return fmt.Errorf("list.layout.fliters.%d format error", idx) + } + + name, has := fli["name"] + if !has { + return fmt.Errorf("list.layout.fliters.%d.name is required", idx) + } + + namestr, ok := name.(string) + if !ok { + return fmt.Errorf("list.layout.fliters.%d.name format error", idx) + } + + if _, has := table.Filters[namestr]; !has { + return fmt.Errorf("list.layout.fliters.%d.name %s is not found in fliters", idx, namestr) + } + } + } + + return nil +} diff --git a/tests/langs/zh-cn/tables/demo.yml b/tests/langs/zh-cn/tables/demo.yml new file mode 100644 index 00000000..17bcd44b --- /dev/null +++ b/tests/langs/zh-cn/tables/demo.yml @@ -0,0 +1,10 @@ +ID: 序号 +SN: 编码 +Name: 姓名 +Kind: 类型 +Cat: 猫 +Dog: 狗 +Description: 介绍 +Keywords: 关键词 +Please input the SN: 请输入编码 +type the keywords...: 关键词... diff --git a/tests/langs/zh-hk/tables/demo.yml b/tests/langs/zh-hk/tables/demo.yml new file mode 100644 index 00000000..42d52b75 --- /dev/null +++ b/tests/langs/zh-hk/tables/demo.yml @@ -0,0 +1,10 @@ +ID: 序號 +SN: 編碼 +Name: 姓名 +Kind: 類型 +Description: 介绍 +Keywords: 關鍵詞 +Cat: 貓 +Dog: 狗 +Please input the SN: 請輸入編碼 +type the keywords...: 關鍵詞... diff --git a/tests/tables/assign.tab.json b/tests/tables/assign.tab.json index db60aa71..093e85f6 100644 --- a/tests/tables/assign.tab.json +++ b/tests/tables/assign.tab.json @@ -8,7 +8,7 @@ "layout": { "fieldset": [ { - "columns": [{ "name": "商务负责人", "width": 24 }] + "columns": [{ "name": "name", "width": 24 }] } ] } diff --git a/tests/tables/demo.tab.json b/tests/tables/demo.tab.json new file mode 100644 index 00000000..11d38e06 --- /dev/null +++ b/tests/tables/demo.tab.json @@ -0,0 +1,85 @@ +{ + "name": "::Demo", + "version": "1.0.0", + "decription": "::Demo", + "bind": { "model": "demo" }, + "apis": {}, + "columns": { + "::ID": { + "label": "::ID", + "view": { "type": "label", "props": { "value": ":id" } } + }, + "::SN": { + "label": "::SN", + "view": { "type": "label", "props": { "value": ":sn" } }, + "edit": { + "type": "input", + "props": { "value": ":sn", "placeholder": "::Please input the SN" } + } + }, + "::Name": { + "label": "::Name", + "view": { "type": "label", "props": { "value": ":name" } }, + "edit": { "type": "input", "props": { "value": ":name" } } + }, + "::Kind": { + "label": "::Kind", + "view": { "type": "label", "props": { "value": ":kind" } }, + "edit": { + "type": "select", + "props": { + "value": ":kind", + "options": [ + { "label": "::Cat", "value": "cat" }, + { "label": "::Dog", "value": "dog" } + ] + } + } + }, + "::Description": { + "label": "::Description", + "view": { "type": "label", "props": { "value": ":desc" } }, + "edit": { "type": "textArea", "props": { "value": ":desc", "rows": 4 } } + } + }, + "filters": { + "::Keywords": { + "label": "::Keywords", + "bind": "where.name.match", + "input": { + "type": "input", + "props": { "placeholder": "::type the keywords..." } + } + } + }, + "list": { + "primary": "id", + "layout": { + "columns": [ + { "name": "::ID", "width": 80 }, + { "name": "::SN", "width": 100 }, + { "name": "::Name", "width": 200 }, + { "name": "::Kind" } + ], + "filters": [{ "name": "::Keywords" }] + }, + "actions": { "pagination": { "props": { "showTotal": true } } }, + "option": { "operation": { "unfold": true } } + }, + "edit": { + "primary": "id", + "layout": { + "fieldset": [ + { + "columns": [ + { "name": "::SN", "width": 8 }, + { "name": "::Name", "width": 8 }, + { "name": "::Kind", "width": 8 }, + { "name": "::Description", "width": 24 } + ] + } + ] + }, + "actions": { "cancel": {}, "save": {}, "delete": {} } + } +} diff --git a/tests/tables/foo/bar.tab.json b/tests/tables/foo/bar.tab.json index 0cd473dc..fb76ec06 100644 --- a/tests/tables/foo/bar.tab.json +++ b/tests/tables/foo/bar.tab.json @@ -11,47 +11,13 @@ "list": { "primary": "id", "layout": { - "columns": [ - { - "name": "名称", - "width": "240px" - }, - { - "name": "父节点" - }, - { - "name": "图标" - }, - { - "name": "状态" - }, - { - "name": "路由" - }, - { - "name": "排列" - }, - { - "name": "block布局" - }, - { - "name": "显示" - } - ], - "filters": [ - { - "name": "名称", - "width": 6 - } - ] + "columns": [{ "name": "name", "width": "240px" }], + "filters": [{ "name": "name", "width": 6 }] }, "actions": { "create": { "type": "button", - "props": { - "label": "新建菜单", - "icon": "fas fa-plus" - } + "props": { "label": "新建菜单", "icon": "fas fa-plus" } }, "view": {}, "edit": {}, @@ -75,30 +41,8 @@ "title": "基础信息", "description": "", "columns": [ - { - "name": "名称", - "width": 6 - }, - { - "name": "图标", - "width": 6 - }, - { - "name": "路由", - "width": 6 - }, - { - "name": "排列", - "width": 6 - }, - { - "name": "block布局", - "width": 6 - }, - { - "name": "显示", - "width": 6 - } + { "name": "name", "width": 6 }, + { "name": "id", "width": 6 } ] } ] diff --git a/tests/tables/hooks/find.tab.json b/tests/tables/hooks/find.tab.json index b0016c95..eb5c1233 100644 --- a/tests/tables/hooks/find.tab.json +++ b/tests/tables/hooks/find.tab.json @@ -13,7 +13,7 @@ "list": { "primary": "id", "layout": { - "columns": [{ "name": "ID", "width": 6 }], + "columns": [{ "name": "id", "width": 6 }], "filters": [] }, "actions": {} @@ -21,7 +21,7 @@ "edit": { "primary": "id", "layout": { - "fieldset": [{ "columns": [{ "name": "ID", "width": 6 }] }] + "fieldset": [{ "columns": [{ "name": "id", "width": 6 }] }] }, "actions": { "cancel": {} } }, diff --git a/tests/tables/hooks/save.tab.json b/tests/tables/hooks/save.tab.json index d0e1c859..455093aa 100644 --- a/tests/tables/hooks/save.tab.json +++ b/tests/tables/hooks/save.tab.json @@ -13,7 +13,7 @@ "list": { "primary": "id", "layout": { - "columns": [{ "name": "ID", "width": 6 }], + "columns": [{ "name": "id", "width": 6 }], "filters": [] }, "actions": {} @@ -21,7 +21,7 @@ "edit": { "primary": "id", "layout": { - "fieldset": [{ "columns": [{ "name": "ID", "width": 6 }] }] + "fieldset": [{ "columns": [{ "name": "id", "width": 6 }] }] }, "actions": { "cancel": {} } }, diff --git a/tests/tables/hooks/search.tab.json b/tests/tables/hooks/search.tab.json index 46f6a8b8..926d544f 100644 --- a/tests/tables/hooks/search.tab.json +++ b/tests/tables/hooks/search.tab.json @@ -30,7 +30,7 @@ "edit": { "primary": "id", "layout": { - "fieldset": [{ "columns": [{ "name": "ID", "width": 6 }] }] + "fieldset": [{ "columns": [{ "name": "id", "width": 6 }] }] }, "actions": { "cancel": {} } }, diff --git a/tests/tables/hooks/select.tab.json b/tests/tables/hooks/select.tab.json index ea4e031f..1c9f6dc1 100644 --- a/tests/tables/hooks/select.tab.json +++ b/tests/tables/hooks/select.tab.json @@ -13,7 +13,7 @@ "list": { "primary": "id", "layout": { - "columns": [{ "name": "ID", "width": 6 }], + "columns": [{ "name": "id", "width": 6 }], "filters": [] }, "actions": {} @@ -21,7 +21,7 @@ "edit": { "primary": "id", "layout": { - "fieldset": [{ "columns": [{ "name": "ID", "width": 6 }] }] + "fieldset": [{ "columns": [{ "name": "id", "width": 6 }] }] }, "actions": { "cancel": {} } },