[add] widget table bind model support

This commit is contained in:
Max 2022-10-06 19:58:23 +08:00
parent f57cb9f260
commit 5d32073ffc
26 changed files with 2256 additions and 259 deletions

View file

@ -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{

View file

@ -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 {

View file

@ -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
}

View file

@ -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
}

135
widgets/component/props.go Normal file
View file

@ -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
}

View file

@ -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
}

View file

@ -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()
}

View file

@ -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)
}

114
widgets/field/column.go Normal file
View file

@ -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
}
}

View file

@ -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}",
},
},
}
}

View file

@ -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")
}

View file

@ -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()
}

60
widgets/field/filter.go Normal file
View file

@ -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
}

View file

@ -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}",
},
},
}
}

107
widgets/field/transform.go Normal file
View file

@ -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)
}

View file

@ -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"]
}

View file

@ -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"`
}

View file

@ -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
}

View file

@ -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
}

View file

@ -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))
}

View file

@ -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

View file

@ -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
}

View file

@ -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)
}
}

View file

@ -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

View file

@ -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
}

558
yao/fields/model.trans.json Normal file
View file

@ -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 }
}
}
}
}
}
}