From 5d32073ffcedafcca4750fe772982d7feb617d6e Mon Sep 17 00:00:00 2001 From: Max Date: Thu, 6 Oct 2022 19:58:23 +0800 Subject: [PATCH] [add] widget table bind model support --- data/bindata.go | 110 ++--- data/data.go | 5 + widgets/component/component.go | 143 +------ widgets/component/process.go | 34 ++ widgets/component/props.go | 135 +++++++ widgets/expression/expression.go | 224 +++++++++++ widgets/expression/expression_test.go | 391 ++++++++++++++++++ widgets/expression/process.go | 76 ++++ widgets/field/column.go | 114 ++++++ widgets/field/column_test.go | 47 +++ widgets/field/field.go | 94 ++--- widgets/field/field_test.go | 59 +++ widgets/field/filter.go | 60 +++ widgets/field/filter_test.go | 41 ++ widgets/field/transform.go | 107 +++++ widgets/field/transform_test.go | 96 +++++ widgets/field/types.go | 20 +- widgets/table/bind.go | 8 +- widgets/table/fields.go | 67 +++- widgets/table/fields_test.go | 23 ++ widgets/table/layout.go | 44 +- widgets/table/table.go | 10 +- widgets/table/table_test.go | 22 + widgets/table/types.go | 4 +- widgets/widgets.go | 23 +- yao/fields/model.trans.json | 558 ++++++++++++++++++++++++++ 26 files changed, 2256 insertions(+), 259 deletions(-) create mode 100644 widgets/component/process.go create mode 100644 widgets/component/props.go create mode 100644 widgets/expression/expression.go create mode 100644 widgets/expression/expression_test.go create mode 100644 widgets/expression/process.go create mode 100644 widgets/field/column.go create mode 100644 widgets/field/column_test.go create mode 100644 widgets/field/field_test.go create mode 100644 widgets/field/filter.go create mode 100644 widgets/field/filter_test.go create mode 100644 widgets/field/transform.go create mode 100644 widgets/field/transform_test.go create mode 100644 widgets/table/fields_test.go create mode 100644 yao/fields/model.trans.json diff --git a/data/bindata.go b/data/bindata.go index 651693f9..d5ccee3f 100644 --- a/data/bindata.go +++ b/data/bindata.go @@ -1,7 +1,6 @@ // Code generated for package data by go-bindata DO NOT EDIT. (@generated) // sources: // .tmp/data/ui/index.html -// .tmp/data/xgen/.DS_Store // .tmp/data/xgen/v0.9/index.html // .tmp/data/xgen/v1.0/index.html // .tmp/data/xgen/v1.0/layouts__index.async.js @@ -20,6 +19,7 @@ // .tmp/data/yao/data/icons/icon.ico // .tmp/data/yao/data/icons/icon.png // .tmp/data/yao/data/index.html +// .tmp/data/yao/fields/model.trans.json // .tmp/data/yao/flows/README.md // .tmp/data/yao/flows/admin/guard.flow.json // .tmp/data/yao/flows/menu.flow.json @@ -210,27 +210,7 @@ func uiIndexHtml() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "ui/index.html", size: 11, mode: os.FileMode(420), modTime: time.Unix(1664456219, 0)} - a := &asset{bytes: bytes, info: info} - return a, nil -} - -var _xgenDs_store = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xec\x98\xdf\x4a\xf3\x30\x14\xc0\x4f\xba\x7e\x5f\x5b\xc6\x07\xbd\xf8\x84\x5d\x06\xbc\x1d\xda\x0d\xff\x30\xbc\x29\x75\xbe\x80\x15\xbd\x98\x32\x1b\x3a\xb4\x58\x93\xd1\x76\xeb\xc5\x18\xec\x5a\x5f\xc8\x27\xf1\x79\xa4\xcb\x19\x16\x9d\xde\x89\x53\xce\x0f\xca\x2f\x25\x4d\x7a\xd2\x42\x72\x12\x00\x60\xc1\x24\xee\x00\xb8\x00\x60\x83\x36\xab\x0a\x6b\xb0\xf1\x7a\x87\x51\x33\xab\xfa\x10\x65\x3e\x16\xa9\x12\x00\xf0\xb4\xbe\xaf\x8d\xa3\x8a\xdd\x84\x29\x74\x60\x07\xbc\x7a\xfc\x62\x9c\x26\x79\xe1\x79\xcf\xcc\x68\x98\x7f\xfe\x5a\x96\x63\x35\xad\xab\xf0\x56\x95\x61\x11\x15\x93\x3c\x88\xb2\x41\x75\x77\xa6\x54\x2a\x56\xe5\x48\x9c\x27\xa3\x72\xe8\xfe\x3f\x56\xb2\x88\x12\x39\xca\x96\x0d\x92\x78\x24\xa2\xec\xf2\x22\x91\xb1\x2a\x03\x35\x91\x71\x3e\xa8\x55\x38\x8e\xed\x0c\xdd\xd6\x6c\xb6\xef\x1d\xb6\x79\xb7\x77\x30\x6f\xf3\x59\xaf\xeb\xb5\xf9\x5e\xaf\x3b\x9f\x3b\xf6\xd6\xf6\xee\xd1\xe9\xf0\x2e\xbd\x97\xea\x41\x87\xcd\x18\xc6\xff\xef\xcd\x78\x1e\xeb\xe3\x99\x86\x99\x4c\x95\xbc\xd1\xff\x87\x20\x08\x82\x20\x96\xe0\x9a\x60\x37\xbf\x3b\x10\x82\x20\x36\x8e\x6a\x7e\xe0\x68\x1f\xbd\xd0\x66\x58\x6f\xa0\xcd\x5a\x1b\x17\xcd\xd1\x3e\x7a\xa1\xcd\xf0\x39\x03\x6d\xa2\x6d\xb4\x8b\xe6\x68\x1f\xbd\xd0\xc6\x49\x8b\xe1\xe6\x83\xe1\x9b\x57\x9b\x17\xe6\xa2\x39\xda\xff\x9a\x6f\x43\x10\x3f\x9d\x86\x96\x5b\xad\xff\x27\x1f\xef\xff\x09\x82\xf8\xc5\x30\xb3\x1f\xf6\x83\x4f\x0e\x89\x0c\x4c\x04\xae\x57\x0d\x30\x11\x80\x35\x49\x80\xa1\x0f\x0b\x5b\xf0\x5a\x4f\x89\x00\x41\x6c\x18\x2f\x01\x00\x00\xff\xff\x4b\x83\x9d\x9e\x04\x18\x00\x00") - -func xgenDs_storeBytes() ([]byte, error) { - return bindataRead( - _xgenDs_store, - "xgen/.DS_Store", - ) -} - -func xgenDs_store() (*asset, error) { - bytes, err := xgenDs_storeBytes() - if err != nil { - return nil, err - } - - info := bindataFileInfo{name: "xgen/.DS_Store", size: 6148, mode: os.FileMode(420), modTime: time.Unix(1664456219, 0)} + info := bindataFileInfo{name: "ui/index.html", size: 11, mode: os.FileMode(420), modTime: time.Unix(1665057464, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -250,7 +230,7 @@ func xgenV09IndexHtml() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "xgen/v0.9/index.html", size: 14, mode: os.FileMode(420), modTime: time.Unix(1664456219, 0)} + info := bindataFileInfo{name: "xgen/v0.9/index.html", size: 14, mode: os.FileMode(420), modTime: time.Unix(1665057464, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -270,7 +250,7 @@ func xgenV10IndexHtml() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "xgen/v1.0/index.html", size: 50, mode: os.FileMode(420), modTime: time.Unix(1664456219, 0)} + info := bindataFileInfo{name: "xgen/v1.0/index.html", size: 50, mode: os.FileMode(420), modTime: time.Unix(1665057464, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -290,7 +270,7 @@ func xgenV10Layouts__indexAsyncJs() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "xgen/v1.0/layouts__index.async.js", size: 71, mode: os.FileMode(420), modTime: time.Unix(1664456219, 0)} + info := bindataFileInfo{name: "xgen/v1.0/layouts__index.async.js", size: 71, mode: os.FileMode(420), modTime: time.Unix(1665057464, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -310,7 +290,7 @@ func xgenV10UmiJs() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "xgen/v1.0/umi.js", size: 71, mode: os.FileMode(420), modTime: time.Unix(1664456219, 0)} + info := bindataFileInfo{name: "xgen/v1.0/umi.js", size: 71, mode: os.FileMode(420), modTime: time.Unix(1665057464, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -330,7 +310,7 @@ func yaoApisReadmeMd() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/apis/README.md", size: 13, mode: os.FileMode(420), modTime: time.Unix(1664456219, 0)} + info := bindataFileInfo{name: "yao/apis/README.md", size: 13, mode: os.FileMode(420), modTime: time.Unix(1665057464, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -350,7 +330,7 @@ func yaoApisChartHttpJson() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/apis/chart.http.json", size: 624, mode: os.FileMode(420), modTime: time.Unix(1664456219, 0)} + info := bindataFileInfo{name: "yao/apis/chart.http.json", size: 624, mode: os.FileMode(420), modTime: time.Unix(1665057464, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -370,7 +350,7 @@ func yaoApisImportHttpJson() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/apis/import.http.json", size: 1676, mode: os.FileMode(420), modTime: time.Unix(1664456219, 0)} + info := bindataFileInfo{name: "yao/apis/import.http.json", size: 1676, mode: os.FileMode(420), modTime: time.Unix(1665057464, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -390,7 +370,7 @@ func yaoApisPageHttpJson() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/apis/page.http.json", size: 609, mode: os.FileMode(420), modTime: time.Unix(1664456219, 0)} + info := bindataFileInfo{name: "yao/apis/page.http.json", size: 609, mode: os.FileMode(420), modTime: time.Unix(1665057464, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -410,7 +390,7 @@ func yaoApisStorageHttpJson() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/apis/storage.http.json", size: 1026, mode: os.FileMode(420), modTime: time.Unix(1664456219, 0)} + info := bindataFileInfo{name: "yao/apis/storage.http.json", size: 1026, mode: os.FileMode(420), modTime: time.Unix(1665057464, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -430,7 +410,7 @@ func yaoApisTableHttpJson() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/apis/table.http.json", size: 3213, mode: os.FileMode(420), modTime: time.Unix(1664456219, 0)} + info := bindataFileInfo{name: "yao/apis/table.http.json", size: 3213, mode: os.FileMode(420), modTime: time.Unix(1665057464, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -450,7 +430,7 @@ func yaoApisUserHttpJson() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/apis/user.http.json", size: 1453, mode: os.FileMode(420), modTime: time.Unix(1664456219, 0)} + info := bindataFileInfo{name: "yao/apis/user.http.json", size: 1453, mode: os.FileMode(420), modTime: time.Unix(1665057464, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -470,7 +450,7 @@ func yaoApisXiangHttpJson() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/apis/xiang.http.json", size: 1239, mode: os.FileMode(420), modTime: time.Unix(1664456219, 0)} + info := bindataFileInfo{name: "yao/apis/xiang.http.json", size: 1239, mode: os.FileMode(420), modTime: time.Unix(1665057464, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -490,7 +470,7 @@ func yaoDataAppJson() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/data/app.json", size: 277, mode: os.FileMode(420), modTime: time.Unix(1664456219, 0)} + info := bindataFileInfo{name: "yao/data/app.json", size: 277, mode: os.FileMode(420), modTime: time.Unix(1665057464, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -510,7 +490,7 @@ func yaoDataIcons404Png() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/data/icons/404.png", size: 9342, mode: os.FileMode(420), modTime: time.Unix(1664456219, 0)} + info := bindataFileInfo{name: "yao/data/icons/404.png", size: 9342, mode: os.FileMode(420), modTime: time.Unix(1665057464, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -530,7 +510,7 @@ func yaoDataIconsIconIcns() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/data/icons/icon.icns", size: 67465, mode: os.FileMode(420), modTime: time.Unix(1664456219, 0)} + info := bindataFileInfo{name: "yao/data/icons/icon.icns", size: 67465, mode: os.FileMode(420), modTime: time.Unix(1665057464, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -550,7 +530,7 @@ func yaoDataIconsIconIco() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/data/icons/icon.ico", size: 54993, mode: os.FileMode(420), modTime: time.Unix(1664456219, 0)} + info := bindataFileInfo{name: "yao/data/icons/icon.ico", size: 54993, mode: os.FileMode(420), modTime: time.Unix(1665057464, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -570,7 +550,7 @@ func yaoDataIconsIconPng() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/data/icons/icon.png", size: 34558, mode: os.FileMode(420), modTime: time.Unix(1664456219, 0)} + info := bindataFileInfo{name: "yao/data/icons/icon.png", size: 34558, mode: os.FileMode(420), modTime: time.Unix(1665057464, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -590,7 +570,27 @@ func yaoDataIndexHtml() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/data/index.html", size: 282, mode: os.FileMode(420), modTime: time.Unix(1664456219, 0)} + info := bindataFileInfo{name: "yao/data/index.html", size: 282, mode: os.FileMode(420), modTime: time.Unix(1665057464, 0)} + a := &asset{bytes: bytes, info: info} + return a, nil +} + +var _yaoFieldsModelTransJson = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xec\x9b\xcd\x72\xdb\x36\x10\xc7\xef\x7a\x0a\x0c\xeb\xa3\xc6\xe3\xf6\xd0\x83\x6e\x49\x6c\x37\xee\x21\xe9\x44\x9a\x5c\x3a\x3d\x40\xc4\x5a\x42\x8d\x0f\x96\x00\xad\x28\x0a\xdf\xbd\x03\x80\x10\x09\x9a\x74\xd8\x56\x96\x68\x15\x39\x24\xe4\xee\xe2\xeb\x0f\xfc\x00\x50\x40\x76\x13\x84\x92\x47\x9c\x53\xbc\x64\xa0\x92\x19\x32\x06\x84\x92\x54\x32\x99\x9b\x57\x94\x64\x39\xe5\x38\xdf\x26\x33\x94\xfc\x70\x7b\x7b\x73\x73\x75\x95\xa0\x72\x82\x50\x39\x9d\x98\xc4\x98\x51\xac\x9a\x49\x95\xce\xa9\x58\x99\x70\x02\xf7\xb8\x60\x3a\x99\x56\x79\xae\x71\xde\x61\xe6\x40\x68\xc1\x17\xf0\x45\x1b\xa7\x36\xff\x56\x1e\x26\xc5\xaa\xcb\xbe\xa4\xa2\xaa\x50\xd3\x4a\xb0\x06\x4d\x39\x2c\xbe\xda\x42\xaa\x37\xef\xad\x3d\x6d\xab\xd2\x98\x67\xb5\xcb\xbe\xd6\x7e\xb1\xbd\x13\x1a\x56\x60\x6b\x4e\xab\xc7\xc0\x9b\xe6\xc0\x41\x68\xd5\x11\x50\x08\x45\x57\x02\xc8\xe2\xd9\x6c\x14\xc7\x8c\x7d\xd7\xfd\x4c\x31\x4b\xba\xea\x4f\x6e\x9d\x03\xea\xf8\x36\xc8\xc4\x5b\xef\xba\x83\xe7\xad\x2a\xf7\x84\xdf\x5d\xdb\x02\x89\x7f\xa7\x61\x45\xf6\x76\x02\x29\xe5\x98\x19\xe3\x3d\x93\xb8\xee\x52\x59\x2c\x19\x3c\x31\xfb\xd2\xae\xeb\x64\xde\x74\xdb\x19\xb7\xcf\xa6\x33\xec\xd7\xf9\xc7\x0f\xc6\xf9\xa7\x92\xc2\xdb\xcc\xf3\xb2\x6d\x34\x81\x6f\xdb\xc6\xa2\xa0\xa4\x63\x54\xd3\xec\x0d\x21\x39\x28\xd5\x35\xe2\x71\xda\xe1\xac\x91\xba\xa7\xc0\x48\x83\x28\x1f\xe1\x0d\x36\x84\x69\x2b\xbd\xb7\x20\x94\x3c\x80\x65\xe2\x62\xc7\xf0\x12\x18\xfa\xf6\x0d\xa5\x92\x1b\xb5\xcd\xa3\xc0\x1c\xca\xaa\x02\x1e\x22\x5b\xef\xcd\x1a\x72\xb8\xbc\xd8\xd9\x80\x4b\x8e\x75\xba\x6e\x86\x01\xa1\x3a\x28\xc6\x8c\xfb\x6d\x66\xd5\xbc\x13\x59\xa1\x1b\xc1\xc8\x4c\x16\x32\x53\xd5\xbc\xc1\x70\x0a\x6b\xc9\x88\x1b\x22\xb3\x59\xc6\x00\x2b\x40\xd4\xa4\x42\x4f\x6b\x59\xba\x69\xc5\xfd\xf1\x4f\xe5\x74\xdf\x62\x99\xf3\x43\xb4\xb7\x6a\xe9\x28\xdb\xa8\xb1\x1b\xa7\x2f\xd1\xc8\x47\x0a\x9b\xbe\x46\x2e\xea\x69\xb4\xdd\xc6\x46\x75\x47\xa4\xd8\xa4\xaa\x90\xe3\x03\x44\xc1\x8f\x01\x07\xfc\x35\x74\xd4\xcc\x81\x41\xda\x27\x42\xc3\x88\xfa\x05\x51\x36\x8b\x4e\x45\xa6\x61\x0e\x32\xd3\x54\x0a\x3b\x97\x5c\x5c\xba\x92\x3f\x5a\xd3\xce\x79\xca\xa4\x11\x3f\x42\xc4\x5e\xa7\x58\x2f\xc9\x2a\x15\x96\x4a\xbc\xba\x31\xb2\x35\x1c\xb2\xd0\x95\xe7\xb3\xc1\xb9\x03\xef\x06\xd4\x78\x95\x4c\x43\xf6\x8a\xdc\x78\x74\x5e\x00\x2a\x07\xf3\xfc\xaa\xba\x27\x9c\x18\xb4\xdb\x3c\xee\x4e\x3c\xc0\xcd\xf4\xfa\x26\x07\xfc\x9f\x34\xec\x9f\x20\x5b\x12\xe2\x42\xcb\x39\xfd\x0a\xae\xcf\x39\x15\x9f\xe4\xc6\x94\xf3\xd3\xd4\xec\x3c\xbe\x54\x6f\x3f\x37\xd4\x3b\xdd\x40\x7f\x76\x51\x92\x92\x69\x9a\xf5\xae\x24\x9a\x6a\xb7\xa9\xfb\xde\xa2\x71\xce\xbd\xd4\x1a\xef\xe6\xd3\x66\x54\x0b\xe1\x35\xd6\xf0\x1b\x4d\x1f\xf6\xdf\x04\xff\x4e\xd6\xe1\x13\x48\xe6\x0a\xf3\xdf\x79\xe3\x5e\xf9\x5e\x89\x3a\xc7\x98\x01\xda\x9b\xd1\xe6\x16\x74\x30\xc2\xe3\xd6\x73\xd2\x50\x35\xd9\x02\xce\x23\xaa\x95\x78\x56\x8c\x88\xea\x01\xd4\x89\xa8\x1e\x44\xcf\x00\x55\x82\x75\x5c\x55\xbd\x78\x56\x8c\x88\xea\x01\xd4\x89\xa8\x1e\x44\xcf\x27\xa8\xc6\x4d\x70\xa2\xd6\x72\xb3\xa8\x64\x70\x18\x62\xfb\xbb\xc1\xfb\xf7\x33\xce\x67\x4a\x25\x83\x3f\xfe\xfe\x2f\x08\x1f\x4e\xb1\x88\xf5\x8b\x69\x1c\xa0\x5e\x9f\xd5\x45\xd6\x23\xeb\x91\xf5\x33\xd3\xd8\xb3\xee\x60\xa7\xfb\xa3\xe6\x11\x9e\x80\x7e\x28\xf8\xb2\x57\xd8\xb3\x3a\x07\x3d\x6e\x4b\xcf\xe7\x34\xf4\x28\xba\x05\xab\x63\xfb\x42\x46\x04\x27\x82\x13\xc1\x19\x72\x99\xc0\xde\xe6\x89\xb0\x8c\x15\x96\x61\x1b\x95\xc1\xc7\x70\x84\x2a\x03\x0b\xa9\x8e\xc5\x47\xb4\xef\x1b\x01\x60\xa7\xd4\x3a\x84\xd2\x5d\xff\x8b\x5c\x9e\x2f\x97\x91\xbc\xe3\xa9\x19\xb2\x15\xde\x89\x8d\x8c\x45\xc6\x22\x63\x87\x66\x6c\x29\x25\x03\x2c\x46\x47\xd7\x27\x4c\xa8\xfc\x25\x97\x45\xdf\x35\xab\x50\xa4\xfa\x76\xde\xef\x81\x03\x19\x0a\x6d\x9d\x9d\x76\x37\xc2\x0e\xa1\x29\x4a\x1e\x31\x2b\x4c\x49\x3f\x36\x3b\xae\x2b\xc9\xb5\xdb\x1c\x34\xd3\x5c\x05\xbf\x11\x21\xf4\xc7\xc0\xf1\x7b\x22\x7e\xcf\x52\xcb\x53\xcd\x05\xf3\x0d\x0d\x87\x71\xbf\x90\xe9\x1a\xd2\x07\x20\x9f\xbd\x3a\xad\x5d\x67\x21\xde\x85\x01\x57\xd3\xce\xf4\xef\xd6\x94\x91\x1c\x44\xa8\x7a\x4f\x5e\x61\xb0\xd7\xfb\x9f\xcc\x08\xf6\xbf\x90\xc4\xab\xa9\xf1\x6a\xea\xd9\xf6\xd2\xc4\xff\x5d\x4e\xca\xc9\xdf\x01\x00\x00\xff\xff\x70\x0e\x1a\xc6\x68\x38\x00\x00") + +func yaoFieldsModelTransJsonBytes() ([]byte, error) { + return bindataRead( + _yaoFieldsModelTransJson, + "yao/fields/model.trans.json", + ) +} + +func yaoFieldsModelTransJson() (*asset, error) { + bytes, err := yaoFieldsModelTransJsonBytes() + if err != nil { + return nil, err + } + + info := bindataFileInfo{name: "yao/fields/model.trans.json", size: 14440, mode: os.FileMode(420), modTime: time.Unix(1665057464, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -610,7 +610,7 @@ func yaoFlowsReadmeMd() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/flows/README.md", size: 12, mode: os.FileMode(420), modTime: time.Unix(1664456219, 0)} + info := bindataFileInfo{name: "yao/flows/README.md", size: 12, mode: os.FileMode(420), modTime: time.Unix(1665057464, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -630,7 +630,7 @@ func yaoFlowsAdminGuardFlowJson() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/flows/admin/guard.flow.json", size: 559, mode: os.FileMode(420), modTime: time.Unix(1664456219, 0)} + info := bindataFileInfo{name: "yao/flows/admin/guard.flow.json", size: 559, mode: os.FileMode(420), modTime: time.Unix(1665057464, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -650,7 +650,7 @@ func yaoFlowsMenuFlowJson() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/flows/menu.flow.json", size: 1045, mode: os.FileMode(420), modTime: time.Unix(1664456219, 0)} + info := bindataFileInfo{name: "yao/flows/menu.flow.json", size: 1045, mode: os.FileMode(420), modTime: time.Unix(1665057464, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -670,7 +670,7 @@ func yaoFlowsUserFindFlowJson() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/flows/user/find.flow.json", size: 328, mode: os.FileMode(420), modTime: time.Unix(1664456219, 0)} + info := bindataFileInfo{name: "yao/flows/user/find.flow.json", size: 328, mode: os.FileMode(420), modTime: time.Unix(1665057464, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -690,7 +690,7 @@ func yaoLangsEnUsJson() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/langs/en-US.json", size: 66, mode: os.FileMode(420), modTime: time.Unix(1664456219, 0)} + info := bindataFileInfo{name: "yao/langs/en-US.json", size: 66, mode: os.FileMode(420), modTime: time.Unix(1665057464, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -710,7 +710,7 @@ func yaoModelsReadmeMd() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/models/README.md", size: 15, mode: os.FileMode(420), modTime: time.Unix(1664456219, 0)} + info := bindataFileInfo{name: "yao/models/README.md", size: 15, mode: os.FileMode(420), modTime: time.Unix(1665057464, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -730,7 +730,7 @@ func yaoModelsMenuModJson() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/models/menu.mod.json", size: 2378, mode: os.FileMode(420), modTime: time.Unix(1664456219, 0)} + info := bindataFileInfo{name: "yao/models/menu.mod.json", size: 2378, mode: os.FileMode(420), modTime: time.Unix(1665057464, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -750,7 +750,7 @@ func yaoModelsUserModJson() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/models/user.mod.json", size: 8826, mode: os.FileMode(420), modTime: time.Unix(1664456219, 0)} + info := bindataFileInfo{name: "yao/models/user.mod.json", size: 8826, mode: os.FileMode(420), modTime: time.Unix(1665057464, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -770,7 +770,7 @@ func yaoModelsWorkflowModJson() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/models/workflow.mod.json", size: 3333, mode: os.FileMode(420), modTime: time.Unix(1664456219, 0)} + info := bindataFileInfo{name: "yao/models/workflow.mod.json", size: 3333, mode: os.FileMode(420), modTime: time.Unix(1665057464, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -790,7 +790,7 @@ func yaoTablesReadmeMd() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/tables/README.md", size: 15, mode: os.FileMode(420), modTime: time.Unix(1664456219, 0)} + info := bindataFileInfo{name: "yao/tables/README.md", size: 15, mode: os.FileMode(420), modTime: time.Unix(1665057464, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -810,7 +810,7 @@ func yaoTablesMenuTabJson() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/tables/menu.tab.json", size: 10471, mode: os.FileMode(420), modTime: time.Unix(1664456219, 0)} + info := bindataFileInfo{name: "yao/tables/menu.tab.json", size: 10471, mode: os.FileMode(420), modTime: time.Unix(1665057464, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -830,7 +830,7 @@ func yaoTablesUserTabJson() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/tables/user.tab.json", size: 2357, mode: os.FileMode(420), modTime: time.Unix(1664456219, 0)} + info := bindataFileInfo{name: "yao/tables/user.tab.json", size: 2357, mode: os.FileMode(420), modTime: time.Unix(1665057464, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -888,7 +888,6 @@ func AssetNames() []string { // _bindata is a table, holding each asset generator, mapped to its name. var _bindata = map[string]func() (*asset, error){ "ui/index.html": uiIndexHtml, - "xgen/.DS_Store": xgenDs_store, "xgen/v0.9/index.html": xgenV09IndexHtml, "xgen/v1.0/index.html": xgenV10IndexHtml, "xgen/v1.0/layouts__index.async.js": xgenV10Layouts__indexAsyncJs, @@ -907,6 +906,7 @@ var _bindata = map[string]func() (*asset, error){ "yao/data/icons/icon.ico": yaoDataIconsIconIco, "yao/data/icons/icon.png": yaoDataIconsIconPng, "yao/data/index.html": yaoDataIndexHtml, + "yao/fields/model.trans.json": yaoFieldsModelTransJson, "yao/flows/README.md": yaoFlowsReadmeMd, "yao/flows/admin/guard.flow.json": yaoFlowsAdminGuardFlowJson, "yao/flows/menu.flow.json": yaoFlowsMenuFlowJson, @@ -966,7 +966,6 @@ var _bintree = &bintree{nil, map[string]*bintree{ "index.html": {uiIndexHtml, map[string]*bintree{}}, }}, "xgen": {nil, map[string]*bintree{ - ".DS_Store": {xgenDs_store, map[string]*bintree{}}, "v0.9": {nil, map[string]*bintree{ "index.html": {xgenV09IndexHtml, map[string]*bintree{}}, }}, @@ -997,6 +996,9 @@ var _bintree = &bintree{nil, map[string]*bintree{ }}, "index.html": {yaoDataIndexHtml, map[string]*bintree{}}, }}, + "fields": {nil, map[string]*bintree{ + "model.trans.json": {yaoFieldsModelTransJson, map[string]*bintree{}}, + }}, "flows": {nil, map[string]*bintree{ "README.md": {yaoFlowsReadmeMd, map[string]*bintree{}}, "admin": {nil, map[string]*bintree{ diff --git a/data/data.go b/data/data.go index a8450faa..d49c82ce 100644 --- a/data/data.go +++ b/data/data.go @@ -48,6 +48,11 @@ func ReplaceXGen(search, replace string) error { return replaceXGenLayouts(search, replace) } +// Read file from bin +func Read(name string) ([]byte, error) { + return Asset(name) +} + // ReplaceXGenIndex bindata file func replaceXGenIndex(search, replace string) error { diff --git a/widgets/component/component.go b/widgets/component/component.go index a7261b97..486f9c07 100644 --- a/widgets/component/component.go +++ b/widgets/component/component.go @@ -1,136 +1,23 @@ package component -import ( - "fmt" - "strings" +// process +// yao.component.TagView +// yao.component.TagEdit +// yao.component.ImageView +// yao.component.UploadEdit - jsoniter "github.com/json-iterator/go" - "github.com/yaoapp/gou" - "github.com/yaoapp/kun/log" -) - -// CloudProps parse CloudProps -func (p PropsDSL) CloudProps(xpath string) (map[string]CloudPropsDSL, error) { - return p.parseCloudProps(xpath, p) -} - -// ExecQuery execute query -func (cProp CloudPropsDSL) ExecQuery(process *gou.Process, query map[string]interface{}) (interface{}, error) { - - if query == nil { - query = map[string]interface{}{} - } - - // Process - name := cProp.Process - if name == "" { - log.Error("[component] %s.$%s process is required", cProp.Xpath, cProp.Name) - return nil, fmt.Errorf("[component] %s.$%s process is required", cProp.Xpath, cProp.Name) - } - - // Create process - p, err := gou.ProcessOf(name, query) - if err != nil { - log.Error("[component] %s.$%s %s", cProp.Xpath, cProp.Name, err.Error()) - return nil, fmt.Errorf("[component] %s.$%s %s", cProp.Xpath, cProp.Name, err.Error()) - } - - // Excute process - res, err := p.WithGlobal(process.Global).WithSID(process.Sid).Exec() - if err != nil { - log.Error("[component] %s.$%s %s", cProp.Xpath, cProp.Name, err.Error()) - return nil, fmt.Errorf("[component] %s.$%s %s", cProp.Xpath, cProp.Name, err.Error()) - } - - return res, nil -} - -// Replace xpath -func (cProp CloudPropsDSL) Replace(data interface{}, replace func(cProp CloudPropsDSL) interface{}) error { - return cProp.replaceAny(data, "", replace) -} - -func (cProp CloudPropsDSL) replaceAny(data interface{}, root string, replace func(cProp CloudPropsDSL) interface{}) error { - switch data.(type) { - case map[string]interface{}: - return cProp.replaceMap(data.(map[string]interface{}), root, replace) - // case []interface{}: - } +// Export processes +func Export() error { + exportProcess() return nil } -func (cProp CloudPropsDSL) replaceMap(data map[string]interface{}, root string, replace func(cProp CloudPropsDSL) interface{}) error { - xpath := fmt.Sprintf(".%s.$%s", cProp.Xpath, cProp.Name) - for key := range data { - path := fmt.Sprintf("%s.%s", root, key) - if !strings.HasPrefix(xpath, path) { - continue - } - - // Replace field - if path == xpath { - data[cProp.Name] = replace(cProp) - delete(data, fmt.Sprintf("$%s", cProp.Name)) - continue - } - - err := cProp.replaceAny(data[key], path, replace) - if err != nil { - return err - } +// Map cast to map[string]interface{} +func (dsl DSL) Map() map[string]interface{} { + return map[string]interface{}{ + "type": dsl.Type, + "in": dsl.In, + "out": dsl.Out, + "props": map[string]interface{}(dsl.Props), } - return nil -} - -func (p PropsDSL) parseCloudProps(xpath string, props map[string]interface{}) (map[string]CloudPropsDSL, error) { - - res := map[string]CloudPropsDSL{} - - for name, prop := range props { - - fullname := fmt.Sprintf("%s.%s", xpath, name) - if sub, ok := prop.(map[string]interface{}); ok { - cProps, err := p.parseCloudProps(fullname, sub) - if err != nil { - return nil, err - } - for k, v := range cProps { - res[k] = v - } - } - - if !strings.HasPrefix(name, "$") { - continue - } - - cProp := &CloudPropsDSL{ - Name: strings.TrimPrefix(name, "$"), - Xpath: xpath, - } - - err := cProp.Parse(prop) - if err != nil { - return nil, fmt.Errorf("%s %s", fullname, err.Error()) - } - - cProp.Xpath = xpath - res[fullname] = *cProp - } - - return res, nil -} - -// Parse parse cloud props -func (cProp *CloudPropsDSL) Parse(v interface{}) error { - - bytes, err := jsoniter.Marshal(v) - if err != nil { - return err - } - - err = jsoniter.Unmarshal(bytes, cProp) - if err != nil { - return err - } - return nil } diff --git a/widgets/component/process.go b/widgets/component/process.go new file mode 100644 index 00000000..3616b12e --- /dev/null +++ b/widgets/component/process.go @@ -0,0 +1,34 @@ +package component + +import ( + "github.com/yaoapp/gou" +) + +// Export process +func exportProcess() { + gou.RegisterProcessHandler("yao.component.tagview", processTagView) + gou.RegisterProcessHandler("yao.component.tagedit", processTagEdit) +} + +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"} + } + 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 + } + return nil + } + return value +} diff --git a/widgets/component/props.go b/widgets/component/props.go new file mode 100644 index 00000000..016d12e9 --- /dev/null +++ b/widgets/component/props.go @@ -0,0 +1,135 @@ +package component + +import ( + "fmt" + "strings" + + jsoniter "github.com/json-iterator/go" + "github.com/yaoapp/gou" + "github.com/yaoapp/kun/log" +) + +// CloudProps parse CloudProps +func (p PropsDSL) CloudProps(xpath string) (map[string]CloudPropsDSL, error) { + return p.parseCloudProps(xpath, p) +} + +// ExecQuery execute query +func (cProp CloudPropsDSL) ExecQuery(process *gou.Process, query map[string]interface{}) (interface{}, error) { + + if query == nil { + query = map[string]interface{}{} + } + + // Process + name := cProp.Process + if name == "" { + log.Error("[component] %s.$%s process is required", cProp.Xpath, cProp.Name) + return nil, fmt.Errorf("[component] %s.$%s process is required", cProp.Xpath, cProp.Name) + } + + // Create process + p, err := gou.ProcessOf(name, query) + if err != nil { + log.Error("[component] %s.$%s %s", cProp.Xpath, cProp.Name, err.Error()) + return nil, fmt.Errorf("[component] %s.$%s %s", cProp.Xpath, cProp.Name, err.Error()) + } + + // Excute process + res, err := p.WithGlobal(process.Global).WithSID(process.Sid).Exec() + if err != nil { + log.Error("[component] %s.$%s %s", cProp.Xpath, cProp.Name, err.Error()) + return nil, fmt.Errorf("[component] %s.$%s %s", cProp.Xpath, cProp.Name, err.Error()) + } + + return res, nil +} + +// Replace xpath +func (cProp CloudPropsDSL) Replace(data interface{}, replace func(cProp CloudPropsDSL) interface{}) error { + return cProp.replaceAny(data, "", replace) +} + +func (cProp CloudPropsDSL) replaceAny(data interface{}, root string, replace func(cProp CloudPropsDSL) interface{}) error { + switch data.(type) { + case map[string]interface{}: + return cProp.replaceMap(data.(map[string]interface{}), root, replace) + } + return nil +} + +func (cProp CloudPropsDSL) replaceMap(data map[string]interface{}, root string, replace func(cProp CloudPropsDSL) interface{}) error { + xpath := fmt.Sprintf(".%s.$%s", cProp.Xpath, cProp.Name) + for key := range data { + path := fmt.Sprintf("%s.%s", root, key) + if !strings.HasPrefix(xpath, path) { + continue + } + + // Replace field + if path == xpath { + data[cProp.Name] = replace(cProp) + delete(data, fmt.Sprintf("$%s", cProp.Name)) + continue + } + + err := cProp.replaceAny(data[key], path, replace) + if err != nil { + return err + } + } + return nil +} + +func (p PropsDSL) parseCloudProps(xpath string, props map[string]interface{}) (map[string]CloudPropsDSL, error) { + + res := map[string]CloudPropsDSL{} + + for name, prop := range props { + + fullname := fmt.Sprintf("%s.%s", xpath, name) + if sub, ok := prop.(map[string]interface{}); ok { + cProps, err := p.parseCloudProps(fullname, sub) + if err != nil { + return nil, err + } + for k, v := range cProps { + res[k] = v + } + } + + if !strings.HasPrefix(name, "$") { + continue + } + + cProp := &CloudPropsDSL{ + Name: strings.TrimPrefix(name, "$"), + Xpath: xpath, + } + + err := cProp.Parse(prop) + if err != nil { + return nil, fmt.Errorf("%s %s", fullname, err.Error()) + } + + cProp.Xpath = xpath + res[fullname] = *cProp + } + + return res, nil +} + +// Parse parse cloud props +func (cProp *CloudPropsDSL) Parse(v interface{}) error { + + bytes, err := jsoniter.Marshal(v) + if err != nil { + return err + } + + err = jsoniter.Unmarshal(bytes, cProp) + if err != nil { + return err + } + return nil +} diff --git a/widgets/expression/expression.go b/widgets/expression/expression.go new file mode 100644 index 00000000..91c3f59a --- /dev/null +++ b/widgets/expression/expression.go @@ -0,0 +1,224 @@ +package expression + +import ( + "fmt" + "reflect" + "regexp" + "strings" + + "github.com/yaoapp/gou" + "github.com/yaoapp/kun/any" + "github.com/yaoapp/kun/maps" +) + +// [ *]([0-9a-zA-Z_\-\.])[ *] +var regVar, _ = regexp.Compile(`([\\]*)\$[\.]*([\.0-9a-zA-Z_\-]*)\{[ ]*([0-9a-zA-Z_,\-\.\|\', ]+)[ ]*\}`) +var regNum, _ = regexp.Compile(`[0-9\.]+`) + +// Export processes +func Export() error { + exportProcess() + return nil +} + +// Replace with the given data +// ${label || comment} +// please input ${label || comment} +// where.${name}.eq +// ${name} +// $.SelectOption{option} +func Replace(ptr interface{}, data map[string]interface{}) error { + if data == nil { + return nil + } + + ptrRef := reflect.ValueOf(ptr) + if ptrRef.Kind() != reflect.Pointer { + return fmt.Errorf("the value is %s, should be a pointer", ptrRef.Kind().String()) + } + + ref := ptrRef.Elem() + kind := ref.Kind() + data = any.Of(data).MapStr() + + switch kind { + case reflect.String: + new, replaced := replace(ref.String(), data) + if _, ok := new.(string); replaced && ok { + ptrRef.Elem().Set(reflect.ValueOf(new)) + } + break + + case reflect.Map: + keys := ref.MapKeys() + for _, key := range keys { + val := ref.MapIndex(key).Interface() + Replace(&val, data) + + ref.SetMapIndex(key, reflect.ValueOf(val)) + } + ptrRef.Elem().Set(ref) + break + + case reflect.Slice: + values := []interface{}{} + for i := 0; i < ref.Len(); i++ { + val := ref.Index(i).Interface() + Replace(&val, data) + values = append(values, val) + } + ptrRef.Elem().Set(reflect.ValueOf(values)) + break + + case reflect.Struct: + for i := 0; i < ref.NumField(); i++ { + if ref.Field(i).CanSet() { + val := ref.Field(i).Interface() + Replace(&val, data) + ref.Field(i).Set(reflect.ValueOf(val).Convert(ref.Field(i).Type())) + } + } + ptrRef.Elem().Set(ref) + break + + case reflect.Interface: + elmRef := ref.Elem() + elmKind := elmRef.Kind() + switch elmKind { + case reflect.String: + new, replaced := replace(ref.Elem().String(), data) + if replaced { + ptrRef.Elem().Set(reflect.ValueOf(new)) + } + break + + case reflect.Map: + keys := elmRef.MapKeys() + for _, key := range keys { + val := elmRef.MapIndex(key).Interface() + Replace(&val, data) + elmRef.SetMapIndex(key, reflect.ValueOf(val)) + } + ptrRef.Elem().Set(elmRef) + break + + case reflect.Slice: + values := []interface{}{} + for i := 0; i < elmRef.Len(); i++ { + val := elmRef.Index(i).Interface() + Replace(&val, data) + values = append(values, val) + } + ptrRef.Elem().Set(reflect.ValueOf(values)) + break + } + + break + + } + + return nil +} + +func replace(value string, data maps.MapStrAny) (interface{}, bool) { + matches := regVar.FindAllStringSubmatch(value, -1) + length := len(matches) + if length == 0 { + return value, false + } + + // "${ name }" + if length == 1 && strings.TrimSpace(value) == strings.TrimSpace(matches[0][0]) { + + if matches[0][1] != "" { + return value, false + } + + if matches[0][2] != "" { + // computeOf( "SelectOption", []string{"option"}, data ) + return computeOf(matches[0][2], strings.Split(matches[0][3], ","), data) + } + + return valueOf(strings.TrimSpace(matches[0][3]), data) // valueOf( "name", data ) + } + + replaced := false + // "${ name } ${ label || comment || 'value' || 0.618 } and ${label} \${name}" + for _, match := range matches { + if match[1] != "" { + continue + } + + if match[2] != "" { + // computeOf( "SelectOption", []string{"option"}, data ) + if v, ok := computeOf(match[2], strings.Split(match[3], ","), data); ok { + value = strings.ReplaceAll(value, strings.TrimSpace(match[0]), fmt.Sprintf("%v", v)) + replaced = true + } + } + + // valueOf( "name", data ) + if v, ok := valueOf(strings.TrimSpace(match[3]), data); ok { + value = strings.ReplaceAll(value, strings.TrimSpace(match[0]), fmt.Sprintf("%v", v)) + replaced = true + } + } + + return value, replaced +} + +func computeOf(process string, argsvars []string, data maps.MapStrAny) (interface{}, bool) { + args := []interface{}{} + for _, name := range argsvars { + arg, _ := valueOf(strings.TrimSpace(name), data) + args = append(args, arg) + } + + if !strings.Contains(process, ".") { + process = fmt.Sprintf("yao.expression.%s", process) + } + + p, err := gou.ProcessOf(process, args...) + if err != nil { + return err.Error(), true + } + + res, err := p.Exec() + if err != nil { + return err.Error(), true + } + + return res, true +} + +func valueOf(name string, data maps.MapStrAny) (interface{}, bool) { + + // label || comment || 'value' || 0.618 || 1 + if strings.Contains(name, "||") { + names := strings.Split(name, "||") + for _, name := range names { + name := strings.TrimSpace(name) + value, replaced := valueOf(name, data) + if replaced { + return value, true + } + } + } + + // 'value' + if strings.HasPrefix(name, "'") && strings.HasSuffix(name, "'") { + return strings.Trim(name, "'"), true + } + + // 0.618 / 1 + if regNum.MatchString(name) { + return name, true + } + + // label / comment + if data.Has(name) { + return data.Get(name), true + } + + return nil, false +} diff --git a/widgets/expression/expression_test.go b/widgets/expression/expression_test.go new file mode 100644 index 00000000..6fc7e5de --- /dev/null +++ b/widgets/expression/expression_test.go @@ -0,0 +1,391 @@ +package expression + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/yaoapp/kun/maps" +) + +type TestMap map[string]interface{} +type TestSlice []interface{} +type TestStruct struct { + Name string + Map TestMap + Slice TestSlice + Option TestOption +} + +type TestOption struct { + Int int + Float float32 + Bool bool + Map TestMap + Slice TestSlice + Nest TestNest +} + +type TestNest struct { + Int int + Float float32 + Bool bool + Map TestMap + Slice TestSlice +} + +func TestReplaceString(t *testing.T) { + + prepare(t) + + data := testData() + err := Replace(nil, data) + assert.NotNil(t, err) + + err = Replace(0.618, data) + assert.NotNil(t, err) + + err = Replace(1, data) + assert.NotNil(t, err) + + err = Replace("${label}", data) + assert.NotNil(t, err) + + strv := "" + err = Replace(&strv, data) + assert.Equal(t, "", strv) + + strv = "hello world" + err = Replace(&strv, data) + assert.Equal(t, "hello world", strv) + + strv = "hello world \\${name}" + err = Replace(&strv, data) + assert.Equal(t, "hello world \\${name}", strv) + + strv = "\\$.SelectOption{option}" + err = Replace(&strv, data) + assert.Equal(t, "\\$.SelectOption{option}", strv) + + strv = "${name}" + err = Replace(&strv, data) + assert.Equal(t, "Foo", strv) + + strv = "${ name }" + err = Replace(&strv, data) + assert.Equal(t, "Foo", strv) + + strv = "${ name } and ${label}" + err = Replace(&strv, data) + assert.Equal(t, "Foo and Bar", strv) + + strv = "please select ${ name }" + err = Replace(&strv, data) + assert.Equal(t, "please select Foo", strv) + + strv = "${label || comment}" + err = Replace(&strv, data) + assert.Equal(t, "Bar", strv) + + strv = "${ comment || label }" + err = Replace(&strv, data) + assert.Equal(t, "Hi", strv) + + strv = "${name || 'value' || 0.618 || 1}" + err = Replace(&strv, data) + assert.Equal(t, "Foo", strv) + + strv = "${ 'value' || name || 0.618 || 1}" + err = Replace(&strv, data) + assert.Equal(t, "value", strv) + + strv = "${ 0.618 || 'value' || name || 1}" + err = Replace(&strv, data) + assert.Equal(t, "0.618", strv) + + strv = "${ 1 || 0.618 || 'value' || name }" + err = Replace(&strv, data) + assert.Equal(t, "1", strv) + + strv = "please select ${ label || comment }" + err = Replace(&strv, data) + assert.Equal(t, "please select Bar", strv) + + strv = "please select ${ comment || label }" + err = Replace(&strv, data) + assert.Equal(t, "please select Hi", strv) + + strv = "$.TrimSpace{ space }" + err = Replace(&strv, data) + assert.Equal(t, "Hello World", strv) + + intv := 1024 + err = Replace(&intv, data) + assert.Equal(t, 1024, intv) + + floatv := 0.168 + err = Replace(&floatv, data) + assert.Equal(t, 0.168, floatv) + +} + +func TestReplaceMap(t *testing.T) { + prepare(t) + data := testData() + mapv := testMap() + err := Replace(&mapv, data) + assert.Nil(t, err) + assert.Equal(t, "::please select Bar", mapv["placeholder"]) + assert.Equal(t, "Hello", mapv["options"].([]map[string]interface{})[0]["label"]) + assert.Equal(t, "Hello", mapv["options"].([]map[string]interface{})[0]["value"]) + assert.Equal(t, "World", mapv["options"].([]map[string]interface{})[1]["label"]) + assert.Equal(t, "World", mapv["options"].([]map[string]interface{})[1]["value"]) +} + +func TestReplaceSlice(t *testing.T) { + prepare(t) + data := testData() + arrv := testSlice() + err := Replace(&arrv, data) + assert.Nil(t, err) + assert.Equal(t, 2, len(arrv)) + assert.Equal(t, "::please select Bar", arrv[0]) + assert.Equal(t, "Hello", arrv[1].([]map[string]interface{})[0]["label"]) + assert.Equal(t, "Hello", arrv[1].([]map[string]interface{})[0]["value"]) + assert.Equal(t, "World", arrv[1].([]map[string]interface{})[1]["label"]) + assert.Equal(t, "World", arrv[1].([]map[string]interface{})[1]["value"]) +} + +func TestReplaceNest(t *testing.T) { + prepare(t) + data := testData() + nestv := testNest() + err := Replace(&nestv, data) + assert.Nil(t, err) + assert.Equal(t, "::please select Bar", nestv["placeholder"]) + assert.Equal(t, "Hello", nestv["options"].([]map[string]interface{})[0]["label"]) + assert.Equal(t, "Hello", nestv["options"].([]map[string]interface{})[0]["value"]) + assert.Equal(t, "World", nestv["options"].([]map[string]interface{})[1]["label"]) + assert.Equal(t, "World", nestv["options"].([]map[string]interface{})[1]["value"]) + + arrv := nestv["data"].([]interface{}) + assert.Equal(t, 2, len(arrv)) + assert.Equal(t, "::please select Bar", arrv[0]) + assert.Equal(t, "Hello", arrv[1].([]map[string]interface{})[0]["label"]) + assert.Equal(t, "Hello", arrv[1].([]map[string]interface{})[0]["value"]) + assert.Equal(t, "World", arrv[1].([]map[string]interface{})[1]["label"]) + assert.Equal(t, "World", arrv[1].([]map[string]interface{})[1]["value"]) +} + +func TestReplaceStruct(t *testing.T) { + prepare(t) + data := testData() + structv := testStruct() + err := Replace(&structv, data) + assert.Nil(t, err) + + assert.Equal(t, "Bar", structv.Name) + assert.Equal(t, "Hello", structv.Map["options"].([]map[string]interface{})[0]["label"]) + assert.Equal(t, "Hello", structv.Map["options"].([]map[string]interface{})[0]["value"]) + assert.Equal(t, "World", structv.Map["options"].([]map[string]interface{})[1]["label"]) + assert.Equal(t, "World", structv.Map["options"].([]map[string]interface{})[1]["value"]) + + arrv := structv.Slice + assert.Equal(t, 2, len(arrv)) + assert.Equal(t, "::please select Bar", arrv[0]) + assert.Equal(t, "Hello", arrv[1].([]map[string]interface{})[0]["label"]) + assert.Equal(t, "Hello", arrv[1].([]map[string]interface{})[0]["value"]) + assert.Equal(t, "World", arrv[1].([]map[string]interface{})[1]["label"]) + assert.Equal(t, "World", arrv[1].([]map[string]interface{})[1]["value"]) +} + +func TestReplaceAny(t *testing.T) { + prepare(t) + + data := testData() + var anyv interface{} = "" + err := Replace(&anyv, data) + assert.Nil(t, err) + assert.Equal(t, "", anyv) + + anyv = "hello world" + err = Replace(&anyv, data) + assert.Equal(t, "hello world", anyv) + + anyv = "hello world \\${name}" + err = Replace(&anyv, data) + assert.Equal(t, "hello world \\${name}", anyv) + + anyv = "\\$.SelectOption{option}" + err = Replace(&anyv, data) + assert.Equal(t, "\\$.SelectOption{option}", anyv) + + anyv = "${name}" + err = Replace(&anyv, data) + assert.Equal(t, "Foo", anyv) + + anyv = "${ name }" + err = Replace(&anyv, data) + assert.Equal(t, "Foo", anyv) + + anyv = "${ name } and ${label}" + err = Replace(&anyv, data) + assert.Equal(t, "Foo and Bar", anyv) + + anyv = "please select ${ name }" + err = Replace(&anyv, data) + assert.Equal(t, "please select Foo", anyv) + + anyv = "${label || comment}" + err = Replace(&anyv, data) + assert.Equal(t, "Bar", anyv) + + anyv = "${ comment || label }" + err = Replace(&anyv, data) + assert.Equal(t, "Hi", anyv) + + anyv = "${name || 'value' || 0.618 || 1}" + err = Replace(&anyv, data) + assert.Equal(t, "Foo", anyv) + + anyv = "${ 'value' || name || 0.618 || 1}" + err = Replace(&anyv, data) + assert.Equal(t, "value", anyv) + + anyv = "${ 0.618 || 'value' || name || 1}" + err = Replace(&anyv, data) + assert.Equal(t, "0.618", anyv) + + anyv = "${ 1 || 0.618 || 'value' || name }" + err = Replace(&anyv, data) + assert.Equal(t, "1", anyv) + + anyv = "please select ${ label || comment }" + err = Replace(&anyv, data) + assert.Equal(t, "please select Bar", anyv) + + anyv = "please select ${ comment || label }" + err = Replace(&anyv, data) + assert.Equal(t, "please select Hi", anyv) + + anyv = "$.TrimSpace{ space }" + err = Replace(&anyv, data) + assert.Equal(t, "Hello World", anyv) + + anyv = "$.SelectOption{ option }" + err = Replace(&anyv, data) + res, ok := anyv.([]map[string]interface{}) + assert.True(t, ok) + assert.Equal(t, 2, len(res)) + assert.Equal(t, "Hello", res[0]["label"]) + assert.Equal(t, "Hello", res[0]["value"]) + assert.Equal(t, "World", res[1]["label"]) + assert.Equal(t, "World", res[1]["value"]) + + anyv = 1024 + err = Replace(&anyv, data) + assert.Equal(t, 1024, anyv) + + anyv = 0.168 + err = Replace(&anyv, data) + assert.Equal(t, 0.168, anyv) + + anyv = testMap() + err = Replace(&anyv, data) + assert.Nil(t, err) + assert.Equal(t, "::please select Bar", anyv.(TestMap)["placeholder"]) + assert.Equal(t, "Hello", anyv.(TestMap)["options"].([]map[string]interface{})[0]["label"]) + assert.Equal(t, "Hello", anyv.(TestMap)["options"].([]map[string]interface{})[0]["value"]) + assert.Equal(t, "World", anyv.(TestMap)["options"].([]map[string]interface{})[1]["label"]) + assert.Equal(t, "World", anyv.(TestMap)["options"].([]map[string]interface{})[1]["value"]) + + anyv = testSlice() + err = Replace(&anyv, data) + assert.Nil(t, err) + assert.Equal(t, 2, len(anyv.([]interface{}))) + assert.Equal(t, "::please select Bar", anyv.([]interface{})[0]) + assert.Equal(t, "Hello", anyv.([]interface{})[1].([]map[string]interface{})[0]["label"]) + assert.Equal(t, "Hello", anyv.([]interface{})[1].([]map[string]interface{})[0]["value"]) + assert.Equal(t, "World", anyv.([]interface{})[1].([]map[string]interface{})[1]["label"]) + assert.Equal(t, "World", anyv.([]interface{})[1].([]map[string]interface{})[1]["value"]) + + anyv = testNest() + err = Replace(&anyv, data) + assert.Nil(t, err) + assert.Equal(t, "::please select Bar", anyv.(TestMap)["placeholder"]) + assert.Equal(t, "Hello", anyv.(TestMap)["options"].([]map[string]interface{})[0]["label"]) + assert.Equal(t, "Hello", anyv.(TestMap)["options"].([]map[string]interface{})[0]["value"]) + assert.Equal(t, "World", anyv.(TestMap)["options"].([]map[string]interface{})[1]["label"]) + assert.Equal(t, "World", anyv.(TestMap)["options"].([]map[string]interface{})[1]["value"]) + + arrv := anyv.(TestMap)["data"].([]interface{}) + assert.Equal(t, 2, len(arrv)) + assert.Equal(t, "::please select Bar", arrv[0]) + assert.Equal(t, "Hello", arrv[1].([]map[string]interface{})[0]["label"]) + assert.Equal(t, "Hello", arrv[1].([]map[string]interface{})[0]["value"]) + assert.Equal(t, "World", arrv[1].([]map[string]interface{})[1]["label"]) + assert.Equal(t, "World", arrv[1].([]map[string]interface{})[1]["value"]) + +} + +func prepare(t *testing.T) { + Export() +} + +func testMap() TestMap { + return TestMap{ + "placeholder": "::please select ${label || comment}", + "options": "$.SelectOption{option}", + } +} + +func testSlice() TestSlice { + return []interface{}{ + "::please select ${label || comment}", + "$.SelectOption{option}", + } +} + +func testStruct() TestStruct { + return TestStruct{ + Name: "${label || comment}", + Map: testMap(), + Slice: testSlice(), + Option: TestOption{ + Int: 1, + Float: 0.618, + Bool: true, + Map: testMap(), + Slice: testSlice(), + Nest: TestNest{ + Int: 1, + Float: 0.618, + Bool: true, + Map: testMap(), + Slice: testSlice(), + }, + }, + } +} + +func testNest() TestMap { + return TestMap{ + "placeholder": "::please select ${label || comment}", + "options": "$.SelectOption{option}", + "data": testSlice(), + } +} + +func testData() map[string]interface{} { + return maps.MapStr{ + "name": "Foo", + "label": "Bar", + "comment": "Hi", + "space": " Hello World ", + "variables": map[string]interface{}{ + "color": TestMap{ + "primary": "#FF0000", + }, + }, + "option": []interface{}{"Hello", "World"}, + }.Dot() +} diff --git a/widgets/expression/process.go b/widgets/expression/process.go new file mode 100644 index 00000000..f4f87511 --- /dev/null +++ b/widgets/expression/process.go @@ -0,0 +1,76 @@ +package expression + +import ( + "fmt" + "strings" + + "github.com/yaoapp/gou" + "github.com/yaoapp/kun/any" + "github.com/yaoapp/kun/maps" +) + +// Export process +func exportProcess() { + gou.RegisterProcessHandler("yao.expression.selectoption", processSelectOption) + gou.RegisterProcessHandler("yao.expression.trimspace", processTrimSpace) +} + +func processSelectOption(process *gou.Process) interface{} { + process.ValidateArgNums(1) + input := process.Args[0] + switch input.(type) { + + case string: + options := []map[string]interface{}{} + opts := strings.Split(input.(string), ",") + for _, opt := range opts { + options = append(options, map[string]interface{}{ + "label": strings.TrimSpace(opt), + "value": strings.TrimSpace(opt), + }) + } + return options + + case []interface{}: + options := []map[string]interface{}{} + opts := input.([]interface{}) + for _, opt := range opts { + switch opt.(type) { + case string, int, int64, int32, int8, float32, float64: + options = append(options, map[string]interface{}{ + "label": strings.TrimSpace(fmt.Sprintf("%v", opt)), + "value": strings.TrimSpace(fmt.Sprintf("%v", opt)), + }) + break + + case map[string]interface{}, maps.MapStr: + key := "name" + value := "id" + + if process.NumOfArgs() > 1 { + key = process.ArgsString(1) + } + + if process.NumOfArgs() > 2 { + value = process.ArgsString(2) + } + + row := any.Of(opt).MapStr() + options = append(options, map[string]interface{}{ + "label": row.Get(key), + "value": row.Get(value), + }) + break + } + } + return options + } + + return []map[string]interface{}{} +} + +func processTrimSpace(process *gou.Process) interface{} { + process.ValidateArgNums(1) + input := process.ArgsString(0) + return strings.TrimSpace(input) +} diff --git a/widgets/field/column.go b/widgets/field/column.go new file mode 100644 index 00000000..8a37601c --- /dev/null +++ b/widgets/field/column.go @@ -0,0 +1,114 @@ +package field + +import ( + "fmt" + "strings" + + "github.com/yaoapp/yao/widgets/component" + "github.com/yaoapp/yao/widgets/expression" +) + +// Replace replace with data +func (column ColumnDSL) Replace(data map[string]interface{}) (*ColumnDSL, error) { + new := column + err := expression.Replace(&new.Key, data) + if err != nil { + return nil, err + } + + err = expression.Replace(&new.Bind, data) + if err != nil { + return nil, err + } + + if new.Edit != nil { + err = expression.Replace(&new.Edit.Props, data) + if err != nil { + return nil, err + } + } + + if new.View != nil { + err = expression.Replace(&new.View.Props, data) + if err != nil { + return nil, err + } + } + + return &new, nil +} + +// Map cast to map[string]inteface{} +func (column ColumnDSL) Map() map[string]interface{} { + res := map[string]interface{}{ + "key": column.Key, + "link": column.Link, + "bind": column.Bind, + } + + if column.View != nil { + res["view"] = column.View.Map() + } + + if column.Edit != nil { + res["edit"] = column.Edit.Map() + } + return res +} + +// CPropsMerge merge the Columns cloud props +func (columns Columns) CPropsMerge(cloudProps map[string]component.CloudPropsDSL, getXpath func(name string, kind string, column ColumnDSL) (xpath string)) error { + + for name, column := range columns { + + if column.Edit != nil && column.Edit.Props != nil { + xpath := getXpath(name, "edit", column) + cProps, err := column.Edit.Props.CloudProps(xpath) + if err != nil { + return err + } + mergeCProps(cloudProps, cProps) + } + + if column.View != nil && column.View.Props != nil { + xpath := getXpath(name, "view", column) + cProps, err := column.View.Props.CloudProps(xpath) + if err != nil { + return err + } + mergeCProps(cloudProps, cProps) + } + } + + return nil +} + +// ComputeFieldsMerge merge the compute fields +func (columns Columns) ComputeFieldsMerge(computeInFields map[string]string, computeOutFields map[string]string) { + for name, column := range columns { + + // Compute In + if column.In != "" { + if !strings.Contains(column.In, ".") { + column.In = fmt.Sprintf("yao.component.%s", column.In) + } + computeInFields[column.Bind] = column.In + computeInFields[name] = column.In + } + + // Compute Out + if column.Out != "" { + if !strings.Contains(column.Out, ".") { + column.In = fmt.Sprintf("yao.component.%s", column.Out) + } + computeOutFields[column.Bind] = column.Out + computeOutFields[name] = column.Out + } + } +} + +func mergeCProps(cloudProps map[string]component.CloudPropsDSL, cProps map[string]component.CloudPropsDSL) { + for k, v := range cProps { + cloudProps[k] = v + } +} diff --git a/widgets/field/column_test.go b/widgets/field/column_test.go new file mode 100644 index 00000000..c57e2643 --- /dev/null +++ b/widgets/field/column_test.go @@ -0,0 +1,47 @@ +package field + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/yaoapp/yao/widgets/component" +) + +func TestColumnReplace(t *testing.T) { + prepare(t) + + col := testColumn() + data := testData() + new, err := col.Replace(data) + if err != nil { + t.Fatal(err) + } + + assert.Equal(t, "Bar", new.Key) + assert.Equal(t, "Foo", new.Bind) + assert.Equal(t, "::please select Bar", new.Edit.Props["placeholder"]) + assert.Equal(t, "Hello", new.Edit.Props["options"].([]map[string]interface{})[0]["label"]) + assert.Equal(t, "Hello", new.Edit.Props["options"].([]map[string]interface{})[0]["value"]) + assert.Equal(t, "World", new.Edit.Props["options"].([]map[string]interface{})[1]["label"]) + assert.Equal(t, "World", new.Edit.Props["options"].([]map[string]interface{})[1]["value"]) +} + +func testColumn() ColumnDSL { + return ColumnDSL{ + Key: "${label || comment}", + Bind: "${name}", + In: "TagEdit", + Out: "TagView", + View: &component.DSL{ + Type: "Tag", + Props: component.PropsDSL{"pure": true}, + }, + Edit: &component.DSL{ + Type: "Select", + Props: component.PropsDSL{ + "placeholder": "::please select ${label || comment}", + "options": "$.SelectOption{option}", + }, + }, + } +} diff --git a/widgets/field/field.go b/widgets/field/field.go index bed7e637..20868de0 100644 --- a/widgets/field/field.go +++ b/widgets/field/field.go @@ -1,73 +1,53 @@ package field import ( - "github.com/yaoapp/yao/widgets/component" + "fmt" + "io/ioutil" + "os" + "path/filepath" + + "github.com/yaoapp/yao/config" + "github.com/yaoapp/yao/data" ) -// CPropsMerge merge the Filters cloud props -func (filters Filters) CPropsMerge(cloudProps map[string]component.CloudPropsDSL, getXpath func(name string, filter FilterDSL) (xpath string)) error { +// LoadAndExport load table +func LoadAndExport(cfg config.Config) error { - for name, filter := range filters { - if filter.Edit != nil && filter.Edit.Props != nil { - xpath := getXpath(name, filter) - cProps, err := filter.Edit.Props.CloudProps(xpath) - if err != nil { - return err - } - mergeCProps(cloudProps, cProps) + if os.Getenv("YAO_DEV") != "" { + file := filepath.Join(os.Getenv("YAO_DEV"), "yao", "fields", "model.trans.json") + source, err := ioutil.ReadFile(file) + if err != nil { + return err } + _, err = OpenTransform(source, "model") + if err != nil { + return err + } + } + + source, err := data.Read(filepath.Join("yao", "fields", "model.trans.json")) + if err != nil { + return err + } + + _, err = OpenTransform(source, "model") + if err != nil { + return err } return nil } -// CPropsMerge merge the Columns cloud props -func (columns Columns) CPropsMerge(cloudProps map[string]component.CloudPropsDSL, getXpath func(name string, kind string, column ColumnDSL) (xpath string)) error { - - for name, column := range columns { - - if column.Edit != nil && column.Edit.Props != nil { - xpath := getXpath(name, "edit", column) - cProps, err := column.Edit.Props.CloudProps(xpath) - if err != nil { - return err - } - mergeCProps(cloudProps, cProps) - } - - if column.View != nil && column.View.Props != nil { - xpath := getXpath(name, "view", column) - cProps, err := column.View.Props.CloudProps(xpath) - if err != nil { - return err - } - mergeCProps(cloudProps, cProps) - } +// SelectTransform select a transform via name +func SelectTransform(name string) (*Transform, error) { + trans, has := Transforms[name] + if !has { + return nil, fmt.Errorf("%s does not found", name) } - - return nil + return trans, nil } -// ComputeFieldsMerge merge the compute fields -func (columns Columns) ComputeFieldsMerge(computeInFields map[string]string, computeOutFields map[string]string) { - for name, column := range columns { - - // Compute In - if column.In != "" { - computeInFields[column.Bind] = column.In - computeInFields[name] = column.In - } - - // Compute Out - if column.Out != "" { - computeOutFields[column.Bind] = column.Out - computeOutFields[name] = column.Out - } - } -} - -func mergeCProps(cloudProps map[string]component.CloudPropsDSL, cProps map[string]component.CloudPropsDSL) { - for k, v := range cProps { - cloudProps[k] = v - } +// ModelTransform select model transform via name +func ModelTransform() (*Transform, error) { + return SelectTransform("model") } diff --git a/widgets/field/field_test.go b/widgets/field/field_test.go new file mode 100644 index 00000000..2e1cf615 --- /dev/null +++ b/widgets/field/field_test.go @@ -0,0 +1,59 @@ +package field + +import ( + "fmt" + "os" + "testing" + + "github.com/yaoapp/kun/maps" + "github.com/yaoapp/yao/config" + "github.com/yaoapp/yao/widgets/expression" +) + +func TestLoadAndExport(t *testing.T) { + + // backup YAO_DEV + dev := os.Getenv("YAO_DEV") + + // From Bindata + os.Unsetenv("YAO_DEV") + err := LoadAndExport(config.Conf) + if err != nil { + t.Fatal(err) + } + if _, has := Transforms["model"]; !has { + t.Fatal(fmt.Errorf("create model transform error")) + } + + // clear + delete(Transforms, "model") + + // From local path + os.Setenv("YAO_DEV", dev) + err = LoadAndExport(config.Conf) + if err != nil { + t.Fatal(err) + } + if _, has := Transforms["model"]; !has { + t.Fatal(fmt.Errorf("create model transform error")) + } +} + +func testData() map[string]interface{} { + return maps.MapStr{ + "name": "Foo", + "label": "Bar", + "comment": "Hi", + "space": " Hello World ", + "variables": map[string]interface{}{ + "color": map[string]interface{}{ + "primary": "#FF0000", + }, + }, + "option": []interface{}{"Hello", "World"}, + }.Dot() +} + +func prepare(t *testing.T) { + expression.Export() +} diff --git a/widgets/field/filter.go b/widgets/field/filter.go new file mode 100644 index 00000000..2402d83a --- /dev/null +++ b/widgets/field/filter.go @@ -0,0 +1,60 @@ +package field + +import ( + "github.com/yaoapp/yao/widgets/component" + "github.com/yaoapp/yao/widgets/expression" +) + +// Replace replace with data +func (filter FilterDSL) Replace(data map[string]interface{}) (*FilterDSL, error) { + new := filter + err := expression.Replace(&new.Key, data) + if err != nil { + return nil, err + } + + err = expression.Replace(&new.Bind, data) + if err != nil { + return nil, err + } + + if new.Edit != nil { + err = expression.Replace(&new.Edit.Props, data) + if err != nil { + return nil, err + } + } + + return &new, nil +} + +// Map cast to map[string]inteface{} +func (filter FilterDSL) Map() map[string]interface{} { + res := map[string]interface{}{ + "key": filter.Key, + "bind": filter.Bind, + } + + if filter.Edit != nil { + res["edit"] = filter.Edit.Map() + } + + return res +} + +// CPropsMerge merge the Filters cloud props +func (filters Filters) CPropsMerge(cloudProps map[string]component.CloudPropsDSL, getXpath func(name string, filter FilterDSL) (xpath string)) error { + + for name, filter := range filters { + if filter.Edit != nil && filter.Edit.Props != nil { + xpath := getXpath(name, filter) + cProps, err := filter.Edit.Props.CloudProps(xpath) + if err != nil { + return err + } + mergeCProps(cloudProps, cProps) + } + } + + return nil +} diff --git a/widgets/field/filter_test.go b/widgets/field/filter_test.go new file mode 100644 index 00000000..8d31bbac --- /dev/null +++ b/widgets/field/filter_test.go @@ -0,0 +1,41 @@ +package field + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/yaoapp/yao/widgets/component" +) + +func TestFilterReplace(t *testing.T) { + prepare(t) + + filter := testFilter() + data := testData() + new, err := filter.Replace(data) + if err != nil { + t.Fatal(err) + } + + assert.Equal(t, "Bar", new.Key) + assert.Equal(t, "Foo", new.Bind) + assert.Equal(t, "::please select Bar", new.Edit.Props["placeholder"]) + assert.Equal(t, "Hello", new.Edit.Props["options"].([]map[string]interface{})[0]["label"]) + assert.Equal(t, "Hello", new.Edit.Props["options"].([]map[string]interface{})[0]["value"]) + assert.Equal(t, "World", new.Edit.Props["options"].([]map[string]interface{})[1]["label"]) + assert.Equal(t, "World", new.Edit.Props["options"].([]map[string]interface{})[1]["value"]) +} + +func testFilter() FilterDSL { + return FilterDSL{ + Key: "${label || comment}", + Bind: "${name}", + Edit: &component.DSL{ + Type: "Select", + Props: component.PropsDSL{ + "placeholder": "::please select ${label || comment}", + "options": "$.SelectOption{option}", + }, + }, + } +} diff --git a/widgets/field/transform.go b/widgets/field/transform.go new file mode 100644 index 00000000..d34fc32d --- /dev/null +++ b/widgets/field/transform.go @@ -0,0 +1,107 @@ +package field + +import ( + "fmt" + "strings" + + jsoniter "github.com/json-iterator/go" + "github.com/yaoapp/kun/any" +) + +// Transforms opend transform +var Transforms = map[string]*Transform{} + +// OpenTransform open the transform from source +func OpenTransform(data []byte, name string) (*Transform, error) { + trans := Transform{} + err := jsoniter.Unmarshal(data, &trans) + if err != nil { + return nil, err + } + Transforms[name] = &trans + return Transforms[name], nil +} + +// IsNotFound check if the given error is not found +func IsNotFound(err error) bool { + return strings.Contains(err.Error(), "does not found") +} + +// Filter transform to filter +func (t *Transform) Filter(typeName string, data map[string]interface{}) (*FilterDSL, error) { + + if alias, has := t.Aliases[typeName]; has { + typeName = alias + } + + field, has := t.Fields[typeName] + if !has { + return nil, fmt.Errorf("%s does not found", typeName) + } + + if field.Filter == nil { + return nil, fmt.Errorf("%s.filter does not found", typeName) + } + + return t.filter(field.Filter, data) +} + +// Table transform to table +func (t *Transform) Table(typeName string, data map[string]interface{}) (*ColumnDSL, error) { + + if alias, has := t.Aliases[typeName]; has { + typeName = alias + } + + field, has := t.Fields[typeName] + if !has { + return nil, fmt.Errorf("%s does not found", typeName) + } + + if field.Table == nil { + return nil, fmt.Errorf("%s.table does not found", typeName) + } + + return t.column(field.Table, data) +} + +// Form transform to form +func (t *Transform) Form(typeName string, data map[string]interface{}) (*ColumnDSL, error) { + + if alias, has := t.Aliases[typeName]; has { + typeName = alias + } + + field, has := t.Fields[typeName] + if !has { + return nil, fmt.Errorf("%s does not found", typeName) + } + + if field.Form == nil { + return nil, fmt.Errorf("%s.form does not found", typeName) + } + + return t.column(field.Form, data) +} + +// trans transform to form/table +func (t *Transform) column(column *ColumnDSL, data map[string]interface{}) (*ColumnDSL, error) { + if _, has := data["variables"]; !has { + variables := any.Of(t.Variables).Map().MapStrAny.Dot() + for k, v := range variables { + data[k] = v + } + } + return column.Replace(data) +} + +// trans transform to filter +func (t *Transform) filter(filter *FilterDSL, data map[string]interface{}) (*FilterDSL, error) { + if _, has := data["variables"]; !has { + variables := any.Of(t.Variables).Map().MapStrAny.Dot() + for k, v := range variables { + data[k] = v + } + } + return filter.Replace(data) +} diff --git a/widgets/field/transform_test.go b/widgets/field/transform_test.go new file mode 100644 index 00000000..bc74e563 --- /dev/null +++ b/widgets/field/transform_test.go @@ -0,0 +1,96 @@ +package field + +import ( + "fmt" + "io/ioutil" + "os" + "path/filepath" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestOpenTransformReplace(t *testing.T) { + + data := testTransformData(t) + tr, err := OpenTransform(data, "unit-test") + if err != nil { + t.Fatal(err) + } + + assert.NotNil(t, tr.Aliases) + assert.NotNil(t, tr.Fields) + assert.NotNil(t, tr.Variables) + _, has := Transforms["unit-test"] + assert.True(t, has) +} + +func TestTransformFilter(t *testing.T) { + tr := testDefaultTransform(t) + data := testData() + fi, err := tr.Filter("string", data) + if err != nil { + t.Fatal(err) + } + assert.Equal(t, "Bar", fi.Key) + assert.Equal(t, "where.Foo.match", fi.Bind) + assert.Equal(t, "::please input Bar", fi.Edit.Props["placeholder"]) + + fi, err = tr.Filter("not-found", data) + assert.True(t, IsNotFound(err)) + + fi, err = tr.Filter("text", data) + assert.True(t, IsNotFound(err)) +} + +func TestTransformTable(t *testing.T) { + tr := testDefaultTransform(t) + data := testData() + tab, err := tr.Table("string", data) + if err != nil { + t.Fatal(err) + } + assert.Equal(t, "Bar", tab.Key) + assert.Equal(t, "Foo", tab.Bind) + assert.Equal(t, "::please input Bar", tab.Edit.Props["placeholder"]) + + tab, err = tr.Table("not-found", data) + assert.True(t, IsNotFound(err)) +} + +func TestTransformForm(t *testing.T) { + tr := testDefaultTransform(t) + data := testData() + form, err := tr.Form("string", data) + if err != nil { + t.Fatal(err) + } + assert.Equal(t, "Bar", form.Key) + assert.Equal(t, "Foo", form.Bind) + assert.Equal(t, "::please input Bar", form.Edit.Props["placeholder"]) + + form, err = tr.Form("not-found", data) + assert.True(t, IsNotFound(err)) +} + +func testTransformData(t *testing.T) []byte { + file := filepath.Join(os.Getenv("YAO_DEV"), "yao", "fields", "model.trans.json") + data, err := ioutil.ReadFile(file) + if err != nil { + t.Fatal(err) + } + return data +} + +func testDefaultTransform(t *testing.T) *Transform { + data := testTransformData(t) + _, err := OpenTransform(data, "unit-test") + if err != nil { + t.Fatal(err) + } + + if _, has := Transforms["unit-test"]; !has { + t.Fatal(fmt.Errorf("create unit-test transform error")) + } + return Transforms["unit-test"] +} diff --git a/widgets/field/types.go b/widgets/field/types.go index 95016708..d80e9c8a 100644 --- a/widgets/field/types.go +++ b/widgets/field/types.go @@ -1,6 +1,8 @@ package field -import "github.com/yaoapp/yao/widgets/component" +import ( + "github.com/yaoapp/yao/widgets/component" +) // Filters the filters DSL type Filters map[string]FilterDSL @@ -16,6 +18,7 @@ type CloudProps map[string]component.CloudPropsDSL // ColumnDSL the field column dsl type ColumnDSL struct { + Key string `json:"key,omitempty"` Bind string `json:"bind,omitempty"` Link string `json:"link,omitempty"` In string `json:"in,omitempty"` @@ -26,6 +29,21 @@ type ColumnDSL struct { // FilterDSL the field filter dsl type FilterDSL struct { + Key string `json:"key,omitempty"` Bind string `json:"bind,omitempty"` Edit *component.DSL `json:"edit,omitempty"` } + +// Transform the field transform +type Transform struct { + Variables map[string]interface{} `json:"variables,omitempty"` + Aliases map[string]string `json:"aliases,omitempty"` + Fields map[string]TransformField `json:"fields,omitempty"` +} + +// TransformField the transform.types[*] +type TransformField struct { + Filter *FilterDSL `json:"filter,omitempty"` + Form *ColumnDSL `json:"form,omitempty"` + Table *ColumnDSL `json:"table,omitempty"` +} diff --git a/widgets/table/bind.go b/widgets/table/bind.go index 27c7ab26..af4f644d 100644 --- a/widgets/table/bind.go +++ b/widgets/table/bind.go @@ -36,9 +36,13 @@ func (dsl *DSL) bindModel() error { return fmt.Errorf("%s does not exist", id) } + err := dsl.Fields.BindModel(m) + if err != nil { + return err + } + dsl.Action.BindModel(m) - dsl.Fields.BindModel(m) - dsl.Layout.BindModel(m) + dsl.Layout.BindModel(m, dsl.Fields) return nil } diff --git a/widgets/table/fields.go b/widgets/table/fields.go index 38230a28..17433123 100644 --- a/widgets/table/fields.go +++ b/widgets/table/fields.go @@ -1,26 +1,73 @@ package table import ( - jsoniter "github.com/json-iterator/go" + "fmt" + "github.com/yaoapp/gou" + "github.com/yaoapp/yao/widgets/field" ) -// BindModel bind model -func (fields *FieldsDSL) BindModel(m *gou.Model) { +// BindModel cast model to fields +func (fields *FieldsDSL) BindModel(m *gou.Model) error { + + trans, err := field.ModelTransform() + if err != nil { + return err + } + + for _, col := range m.Columns { + data := col.Map() + tableField, err := trans.Table(col.Type, data) + if err != nil { + return err + } + // append columns + if _, has := fields.Table[tableField.Key]; !has { + fields.Table[tableField.Key] = *tableField + } + + // Index as filter + if col.Index || col.Unique || col.Primary { + filterField, err := trans.Filter(col.Type, data) + if err != nil && !field.IsNotFound(err) { + return err + } + if _, has := fields.Filter[filterField.Key]; !has { + fields.Filter[tableField.Key] = *filterField + } + } + } + + return nil } // Xgen trans to xgen setting -func (fields *FieldsDSL) Xgen() (map[string]interface{}, error) { +func (fields *FieldsDSL) Xgen(layout *LayoutDSL) (map[string]interface{}, error) { res := map[string]interface{}{} - data, err := jsoniter.Marshal(fields) - if err != nil { - return nil, err + + filters := map[string]interface{}{} + tables := map[string]interface{}{} + if layout.Filter != nil { + for i, f := range layout.Filter.Columns { + field, has := fields.Filter[f.Name] + if !has { + return nil, fmt.Errorf("fields.filter.%s not found, checking layout.filter.columns.%d.name", f.Name, i) + } + filters[f.Name] = field.Map() + } } - err = jsoniter.Unmarshal(data, &res) - if err != nil { - return nil, err + if layout.Table != nil { + for i, f := range layout.Table.Columns { + field, has := fields.Table[f.Name] + if !has { + return nil, fmt.Errorf("fields.table.%s not found, checking layout.table.columns.%d.name", f.Name, i) + } + tables[f.Name] = field.Map() + } } + res["filter"] = filters + res["table"] = tables return res, nil } diff --git a/widgets/table/fields_test.go b/widgets/table/fields_test.go new file mode 100644 index 00000000..f0d5ca10 --- /dev/null +++ b/widgets/table/fields_test.go @@ -0,0 +1,23 @@ +package table + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/yaoapp/gou" +) + +func TestFiledsBindModel(t *testing.T) { + load(t) + clear(t) + testData(t) + m := gou.Select("pet") + tab := New("unit-test") + err := tab.Fields.BindModel(m) + if err != nil { + t.Fatal(err) + } + assert.Equal(t, "id", tab.Fields.Table["ID"].Bind) + assert.Equal(t, 15, len(tab.Fields.Table)) + assert.Equal(t, 4, len(tab.Fields.Filter)) +} diff --git a/widgets/table/layout.go b/widgets/table/layout.go index c5f52b71..8bcdb9a4 100644 --- a/widgets/table/layout.go +++ b/widgets/table/layout.go @@ -5,11 +5,51 @@ import ( jsoniter "github.com/json-iterator/go" "github.com/yaoapp/gou" + "github.com/yaoapp/yao/widgets/component" ) // BindModel bind model -func (layout *LayoutDSL) BindModel(m *gou.Model) { - layout.Primary = m.PrimaryKey +func (layout *LayoutDSL) BindModel(m *gou.Model, fields *FieldsDSL) { + + if layout.Primary == "" { + layout.Primary = m.PrimaryKey + } + + if layout.Filter == nil && len(fields.Filter) > 0 { + + layout.Filter = &FilterLayoutDSL{ + BtnAddText: "::Create", + Columns: component.Instances{}, + } + max := 3 + curr := 0 + for name := range fields.Filter { + curr++ + if curr >= max { + break + } + layout.Filter.Columns = append(layout.Filter.Columns, component.InstanceDSL{ + Name: name, + }) + } + } + + if layout.Table == nil && len(fields.Table) > 0 { + layout.Table = &ViewLayoutDSL{ + Props: component.PropsDSL{}, + Columns: component.Instances{}, + Operation: OperationTableDSL{ + Fold: false, + Actions: component.Actions{}, + }, + } + for name := range fields.Table { + layout.Table.Columns = append(layout.Table.Columns, component.InstanceDSL{ + Name: name, + }) + } + } + } // Xgen trans to Xgen setting diff --git a/widgets/table/table.go b/widgets/table/table.go index 6513bca2..5bf9d88d 100644 --- a/widgets/table/table.go +++ b/widgets/table/table.go @@ -128,7 +128,12 @@ func LoadFrom(dir string, prefix string) error { dsl.Action.SetDefaultProcess() if dsl.Layout == nil { - dsl.Layout = &LayoutDSL{} + dsl.Layout = &LayoutDSL{ + Header: &HeaderLayoutDSL{ + Preset: &PresetHeaderDSL{}, + Actions: []component.ActionDSL{}, + }, + } } if dsl.Fields == nil { @@ -228,7 +233,7 @@ func (dsl *DSL) Xgen() (map[string]interface{}, error) { return nil, err } - fields, err := dsl.Fields.Xgen() + fields, err := dsl.Fields.Xgen(dsl.Layout) if err != nil { return nil, err } @@ -247,5 +252,6 @@ func (dsl *DSL) Xgen() (map[string]interface{}, error) { } } + setting["name"] = dsl.Name return setting, nil } diff --git a/widgets/table/table_test.go b/widgets/table/table_test.go index 6b6e57db..793b7a1f 100644 --- a/widgets/table/table_test.go +++ b/widgets/table/table_test.go @@ -9,6 +9,9 @@ import ( "github.com/yaoapp/yao/model" "github.com/yaoapp/yao/script" "github.com/yaoapp/yao/widgets/app" + "github.com/yaoapp/yao/widgets/component" + "github.com/yaoapp/yao/widgets/expression" + "github.com/yaoapp/yao/widgets/field" "github.com/yaoapp/yao/widgets/test" ) @@ -53,9 +56,28 @@ func prepare(t *testing.T, language ...string) { t.Fatal(err) } + // load field transform + err = field.LoadAndExport(config.Conf) + if err != nil { + t.Fatal(err) + } + + // load expression + err = expression.Export() + if err != nil { + t.Fatal(err) + } + + // load component + err = component.Export() + if err != nil { + t.Fatal(err) + } + // export err = Export() if err != nil { t.Fatal(err) } + } diff --git a/widgets/table/types.go b/widgets/table/types.go index 91d4a9d7..678c2caa 100644 --- a/widgets/table/types.go +++ b/widgets/table/types.go @@ -81,7 +81,7 @@ type LayoutDSL struct { // HeaderLayoutDSL layout.header type HeaderLayoutDSL struct { Preset *PresetHeaderDSL `json:"preset,omitempty"` - Actions []component.ActionDSL `json:"actions,omitempty"` + Actions []component.ActionDSL `json:"actions"` } // PresetHeaderDSL layout.header.preset @@ -124,7 +124,7 @@ type ViewLayoutDSL struct { // OperationTableDSL layout.table.operation type OperationTableDSL struct { Fold bool `json:"fold,omitempty"` - Actions component.Actions `json:"actions,omitempty"` + Actions component.Actions `json:"actions"` } // FieldsDSL the table fields DSL diff --git a/widgets/widgets.go b/widgets/widgets.go index e528cc49..0164beea 100644 --- a/widgets/widgets.go +++ b/widgets/widgets.go @@ -4,6 +4,9 @@ import ( "github.com/yaoapp/yao/config" "github.com/yaoapp/yao/widgets/app" "github.com/yaoapp/yao/widgets/chart" + "github.com/yaoapp/yao/widgets/component" + "github.com/yaoapp/yao/widgets/expression" + "github.com/yaoapp/yao/widgets/field" "github.com/yaoapp/yao/widgets/form" "github.com/yaoapp/yao/widgets/login" "github.com/yaoapp/yao/widgets/table" @@ -12,8 +15,26 @@ import ( // Load the widgets func Load(cfg config.Config) error { + // load expression + err := expression.Export() + if err != nil { + return err + } + + // load component + err = component.Export() + if err != nil { + return err + } + + // load field transform + err = field.LoadAndExport(config.Conf) + if err != nil { + return err + } + // login widget - err := login.LoadAndExport(cfg) + err = login.LoadAndExport(cfg) if err != nil { return err } diff --git a/yao/fields/model.trans.json b/yao/fields/model.trans.json new file mode 100644 index 00000000..a18779bf --- /dev/null +++ b/yao/fields/model.trans.json @@ -0,0 +1,558 @@ +{ + "variables": { + "color": { "primary": "#FFEE00" } + }, + + "aliases": { + "string": "default", + "char": "default", + "mediumText": "text", + "longText": "text", + "binary": "text", + "datetimeTz": "datetime", + "timeTz": "time", + "timestampTz": "timestamp", + "tinyInteger": "integer", + "tinyIncrements": "integer", + "unsignedTinyInteger": "integer", + "smallInteger": "integer", + "smallIncrements": "integer", + "bigInteger": "integer", + "bigIncrements": "integer", + "unsignedBigInteger": "unsignedInteger", + "unsignedSmallInteger": "unsignedInteger", + "ID": "id", + "increments": "id", + "decimal": "float", + "double": "float", + "unsignedDecimal": "unsignedFloat", + "unsignedDouble": "unsignedFloat", + "JSON": "json", + "jsonb": "json", + "JSONB": "json", + "uuid": "default", + "ipAddress": "default", + "macAddress": "default" + }, + + "fields": { + "default": { + "filter": { + "key": "${label || comment || name}", + "bind": "where.${name}.match", + "edit": { + "type": "Input", + "props": { "placeholder": "::please input ${label || comment}" } + } + }, + "form": { + "key": "${label || comment || name}", + "bind": "${name}", + "edit": { + "type": "Input", + "props": { "placeholder": "::please input ${label || comment}" } + } + }, + "table": { + "key": "${label || comment || name}", + "bind": "${name}", + "view": { + "type": "Text", + "props": {} + }, + "edit": { + "type": "Input", + "props": { "placeholder": "::please input ${label || comment}" } + } + } + }, + + "enum": { + "filter": { + "key": "${label || comment || name}", + "bind": "where.${name}.eq", + "edit": { + "type": "Select", + "props": { + "placeholder": "::please select ${label || comment}", + "options": "$.SelectOption{option}" + } + } + }, + "form": { + "key": "${label || comment || name}", + "bind": "${name}", + "edit": { + "type": "Select", + "props": { + "placeholder": "::please select ${label || comment}", + "options": "$.SelectOption{option}" + } + } + }, + "table": { + "key": "${label || comment || name}", + "bind": "${name}", + "in": "TagEdit", + "out": "TagView", + "view": { "type": "Tag", "props": { "pure": true } }, + "edit": { + "type": "Select", + "props": { + "placeholder": "::please select ${label || comment}", + "options": "$.SelectOption{option}" + } + } + } + }, + + "text": { + "form": { + "key": "${label || comment || name}", + "bind": "${name}", + "edit": { + "type": "TextArea", + "props": { + "placeholder": "::please input ${label || comment}", + "autoSize": { "minRows": 2, "maxRows": 6 } + } + } + }, + "table": { + "key": "${label || comment || name}", + "bind": "${name}", + "view": { + "type": "Tooltip", + "props": { "title": "${label || comment}" } + }, + "edit": { + "type": "TextArea", + "props": { + "placeholder": "::please input ${label || comment}", + "autoSize": { "minRows": 2, "maxRows": 6 } + } + } + } + }, + + "time": { + "filter": { + "key": "${label || comment || name}", + "bind": "where.${name}.eq", + "edit": { + "type": "DatePicker", + "props": { + "placeholder": "::please select ${label || comment}", + "picker": "time" + } + } + }, + "form": { + "key": "${label || comment || name}", + "bind": "${name}", + "edit": { + "type": "DatePicker", + "props": { + "placeholder": "::please select ${label || comment}", + "picker": "time" + } + } + }, + "table": { + "key": "${label || comment || name}", + "bind": "${name}", + "view": { "type": "Text", "props": {} }, + "edit": { + "type": "DatePicker", + "props": { + "placeholder": "::please select ${label || comment}", + "picker": "time" + } + } + } + }, + "year": { + "filter": { + "key": "${label || comment || name}", + "bind": "where.${name}.eq", + "edit": { + "type": "DatePicker", + "props": { + "placeholder": "::please select ${label || comment}", + "picker": "year" + } + } + }, + "form": { + "key": "${label || comment || name}", + "bind": "${name}", + "edit": { + "type": "DatePicker", + "props": { + "placeholder": "::please select ${label || comment}", + "picker": "year" + } + } + }, + "table": { + "key": "${label || comment || name}", + "bind": "${name}", + "view": { "type": "Text", "props": {} }, + "edit": { + "type": "DatePicker", + "props": { + "placeholder": "::please select ${label || comment}", + "picker": "year" + } + } + } + }, + "date": { + "filter": { + "key": "${label || comment || name}", + "bind": "where.${name}.eq", + "edit": { + "type": "DatePicker", + "props": { + "placeholder": "::please select ${label || comment}", + "picker": "date" + } + } + }, + "form": { + "key": "${label || comment || name}", + "bind": "${name}", + "edit": { + "type": "DatePicker", + "props": { + "placeholder": "::please select ${label || comment}", + "picker": "date" + } + } + }, + "table": { + "key": "${label || comment || name}", + "bind": "${name}", + "view": { "type": "Text", "props": {} }, + "edit": { + "type": "DatePicker", + "props": { + "placeholder": "::please select ${label || comment}", + "picker": "date" + } + } + } + }, + "datetime": { + "filter": { + "key": "${label || comment || name}", + "bind": "where.${name}.eq", + "edit": { + "type": "DatePicker", + "props": { + "placeholder": "::please select ${label || comment}", + "showTime": { "format": "HH:mm:ss" } + } + } + }, + "form": { + "key": "${label || comment || name}", + "bind": "${name}", + "edit": { + "type": "DatePicker", + "props": { + "placeholder": "::please select ${label || comment}", + "showTime": { "format": "HH:mm:ss" } + } + } + }, + "table": { + "key": "${label || comment || name}", + "bind": "${name}", + "view": { "type": "Text", "props": {} }, + "edit": { + "type": "DatePicker", + "props": { + "placeholder": "::please select ${label || comment}", + "showTime": { "format": "HH:mm:ss" } + } + } + } + }, + "timestamp": { + "filter": { + "key": "${label || comment || name}", + "bind": "where.${name}.eq", + "edit": { + "type": "DatePicker", + "props": { + "placeholder": "::please select ${label || comment}", + "showTime": { "format": "HH:mm:ss" } + } + } + }, + "form": { + "key": "${label || comment || name}", + "bind": "${name}", + "edit": { + "type": "DatePicker", + "props": { + "placeholder": "::please select ${label || comment}", + "showTime": { "format": "HH:mm:ss" } + } + } + }, + "table": { + "key": "${label || comment || name}", + "bind": "${name}", + "view": { "type": "Text", "props": {} }, + "edit": { + "type": "DatePicker", + "props": { + "placeholder": "::please select ${label || comment}", + "showTime": { "format": "HH:mm:ss" } + } + } + } + }, + + "integer": { + "filter": { + "key": "${label || comment || name}", + "bind": "where.${name}.match", + "edit": { + "type": "InputNumber", + "props": { "placeholder": "::please input ${label || comment}" } + } + }, + "form": { + "key": "${label || comment || name}", + "bind": "${name}", + "edit": { + "type": "InputNumber", + "props": { "placeholder": "::please input ${label || comment}" } + } + }, + "table": { + "key": "${label || comment || name}", + "bind": "${name}", + "view": { + "type": "Text", + "props": {} + }, + "edit": { + "type": "InputNumber", + "props": { "placeholder": "::please input ${label || comment}" } + } + } + }, + "unsignedInteger": { + "filter": { + "key": "${label || comment || name}", + "bind": "where.${name}.match", + "edit": { + "type": "InputNumber", + "props": { "placeholder": "::please input ${label || comment}" } + } + }, + "form": { + "key": "${label || comment || name}", + "bind": "${name}", + "edit": { + "type": "InputNumber", + "props": { "placeholder": "::please input ${label || comment}" } + } + }, + "table": { + "key": "${label || comment || name}", + "bind": "${name}", + "view": { + "type": "Text", + "props": {} + }, + "edit": { + "type": "InputNumber", + "props": { "placeholder": "::please input ${label || comment}" } + } + } + }, + + "id": { + "filter": { + "key": "${label || comment || name}", + "bind": "where.${name}.match", + "edit": { + "type": "InputNumber", + "props": { "placeholder": "::please input ${label || comment}" } + } + }, + "form": { + "key": "${label || comment || name}", + "bind": "${name}", + "edit": { + "type": "InputNumber", + "props": { + "placeholder": "::please input ${label || comment}", + "disabled": true + } + } + }, + "table": { + "key": "${label || comment || name}", + "bind": "${name}", + "view": { + "type": "Text", + "props": {} + }, + "edit": { + "type": "InputNumber", + "props": { + "placeholder": "::please input ${label || comment}", + "disabled": true + } + } + } + }, + + "float": { + "filter": { + "key": "${label || comment || name}", + "bind": "where.${name}.match", + "edit": { + "type": "InputNumber", + "props": { "placeholder": "::please input ${label || comment}" } + } + }, + "form": { + "key": "${label || comment || name}", + "bind": "${name}", + "edit": { + "type": "InputNumber", + "props": { + "placeholder": "::please input ${label || comment}" + } + } + }, + "table": { + "key": "${label || comment || name}", + "bind": "${name}", + "view": { + "type": "Text", + "props": {} + }, + "edit": { + "type": "InputNumber", + "props": { + "placeholder": "::please input ${label || comment}" + } + } + } + }, + + "unsignedFloat": { + "filter": { + "key": "${label || comment || name}", + "bind": "where.${name}.match", + "edit": { + "type": "InputNumber", + "props": { "placeholder": "::please input ${label || comment}" } + } + }, + "form": { + "key": "${label || comment || name}", + "bind": "${name}", + "edit": { + "type": "InputNumber", + "props": { + "placeholder": "::please input ${label || comment}" + } + } + }, + "table": { + "key": "${label || comment || name}", + "bind": "${name}", + "view": { + "type": "Text", + "props": {} + }, + "edit": { + "type": "InputNumber", + "props": { + "placeholder": "::please input ${label || comment}" + } + } + } + }, + + "boolean": { + "filter": { + "key": "${label || comment || name}", + "bind": "where.${name}.match", + "edit": { + "type": "RadioGroup", + "props": { + "options": [ + { "label": "::Enable", "value": 1 }, + { "label": "::Disable", "value": 0 } + ] + } + } + }, + "form": { + "key": "${label || comment || name}", + "bind": "${name}", + "edit": { + "type": "RadioGroup", + "props": { + "options": [ + { "label": "::Enable", "value": 1 }, + { "label": "::Disable", "value": 0 } + ] + } + } + }, + "table": { + "key": "${label || comment || name}", + "bind": "${name}", + "view": { + "type": "Switch", + "props": { + "checkedValue": 1, + "unCheckedValue": 0, + "checkedChildren": "::Enable", + "unCheckedChildren": "::Disable" + } + } + } + }, + + "json": { + "form": { + "key": "${label || comment || name}", + "bind": "${name}", + "edit": { + "type": "TextArea", + "props": { + "placeholder": "::please input ${label || comment}", + "autoSize": { "minRows": 2, "maxRows": 6 } + } + } + }, + "table": { + "key": "${label || comment || name}", + "bind": "${name}", + "view": { + "type": "Tooltip", + "props": { "title": "${label || comment}" } + }, + "edit": { + "type": "TextArea", + "props": { + "placeholder": "::please input ${label || comment}", + "autoSize": { "minRows": 2, "maxRows": 6 } + } + } + } + } + } +}