+ 共享库支持
This commit is contained in:
parent
30e57d6580
commit
42262ce799
17 changed files with 689 additions and 9 deletions
|
|
@ -38,6 +38,7 @@ type XiangConfig struct {
|
||||||
RootModel string `json:"root_model,omitempty" env:"XIANG_ROOT_MODEL"` // 应用模型文件目录
|
RootModel string `json:"root_model,omitempty" env:"XIANG_ROOT_MODEL"` // 应用模型文件目录
|
||||||
RootFLow string `json:"root_flow,omitempty" env:"XIANG_ROOT_FLOW"` // 应用业务逻辑文件目录
|
RootFLow string `json:"root_flow,omitempty" env:"XIANG_ROOT_FLOW"` // 应用业务逻辑文件目录
|
||||||
RootPlugin string `json:"root_plugin,omitempty" env:"XIANG_ROOT_PLUGIN"` // 应用业务插件文件目录
|
RootPlugin string `json:"root_plugin,omitempty" env:"XIANG_ROOT_PLUGIN"` // 应用业务插件文件目录
|
||||||
|
RootLib string `json:"root_lib,omitempty" env:"XIANG_ROOT_LIB"` // 应用资料库文件目录
|
||||||
RootTable string `json:"root_table,omitempty" env:"XIANG_ROOT_TABLE"` // 应用数据表格文件目录
|
RootTable string `json:"root_table,omitempty" env:"XIANG_ROOT_TABLE"` // 应用数据表格文件目录
|
||||||
RootChart string `json:"root_chart,omitempty" env:"XIANG_ROOT_CHART"` // 应用分析图表文件目录
|
RootChart string `json:"root_chart,omitempty" env:"XIANG_ROOT_CHART"` // 应用分析图表文件目录
|
||||||
RootPage string `json:"root_page,omitempty" env:"XIANG_ROOT_PAGE"` // 应用通用页面文件目录
|
RootPage string `json:"root_page,omitempty" env:"XIANG_ROOT_PAGE"` // 应用通用页面文件目录
|
||||||
|
|
@ -148,6 +149,11 @@ func (cfg *Config) SetDefaults() {
|
||||||
if cfg.RootTable == "" {
|
if cfg.RootTable == "" {
|
||||||
cfg.RootTable = cfg.Root + "/tables"
|
cfg.RootTable = cfg.Root + "/tables"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if cfg.RootLib == "" {
|
||||||
|
cfg.RootLib = cfg.Root + "/libs"
|
||||||
|
}
|
||||||
|
|
||||||
if cfg.RootChart == "" {
|
if cfg.RootChart == "" {
|
||||||
cfg.RootChart = cfg.Root + "/charts"
|
cfg.RootChart = cfg.Root + "/charts"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@ func Load(cfg config.Config) {
|
||||||
LoadEngine(cfg.Path)
|
LoadEngine(cfg.Path)
|
||||||
query.Load(cfg) // 加载数据分析引擎
|
query.Load(cfg) // 加载数据分析引擎
|
||||||
|
|
||||||
|
share.Load(cfg) // 加载共享库 lib
|
||||||
model.Load(cfg) // 加载数据模型 model
|
model.Load(cfg) // 加载数据模型 model
|
||||||
api.Load(cfg) // 加载业务接口 API
|
api.Load(cfg) // 加载业务接口 API
|
||||||
flow.Load(cfg) // 加载业务逻辑 Flow
|
flow.Load(cfg) // 加载业务逻辑 Flow
|
||||||
|
|
|
||||||
|
|
@ -23,5 +23,5 @@ func check(t *testing.T) {
|
||||||
for key := range gou.Models {
|
for key := range gou.Models {
|
||||||
keys = append(keys, key)
|
keys = append(keys, key)
|
||||||
}
|
}
|
||||||
assert.Equal(t, 9, len(keys))
|
assert.Equal(t, 10, len(keys))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
158
share/importable.go
Normal file
158
share/importable.go
Normal file
|
|
@ -0,0 +1,158 @@
|
||||||
|
package share
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
jsoniter "github.com/json-iterator/go"
|
||||||
|
"github.com/yaoapp/gou/query/share"
|
||||||
|
"github.com/yaoapp/kun/exception"
|
||||||
|
"github.com/yaoapp/kun/maps"
|
||||||
|
"github.com/yaoapp/xiang/config"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Libs 共享库
|
||||||
|
var Libs = map[string]map[string]interface{}{}
|
||||||
|
|
||||||
|
// Load 加载共享库
|
||||||
|
func Load(cfg config.Config) {
|
||||||
|
LoadFrom(cfg.RootLib)
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoadFrom 从特定目录加载共享库
|
||||||
|
func LoadFrom(dir string) {
|
||||||
|
|
||||||
|
if DirNotExists(dir) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
Walk(dir, ".json", func(root, filename string) {
|
||||||
|
name := SpecName(root, filename)
|
||||||
|
content := ReadFile(filename)
|
||||||
|
libs := map[string]map[string]interface{}{}
|
||||||
|
err := jsoniter.Unmarshal(content, &libs)
|
||||||
|
if err != nil {
|
||||||
|
exception.New("共享数据结构异常 %s", 400, err).Throw()
|
||||||
|
}
|
||||||
|
for key, lib := range libs {
|
||||||
|
key := fmt.Sprintf("%s.%s", name, key)
|
||||||
|
Libs[key] = lib
|
||||||
|
// 删除注释
|
||||||
|
if _, has := lib["__comment"]; has {
|
||||||
|
delete(lib, "__comment")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSON Column 字段JSON解析
|
||||||
|
func (col *Column) UnmarshalJSON(data []byte) error {
|
||||||
|
new := ColumnImp{}
|
||||||
|
err := jsoniter.Unmarshal(data, &new)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 导入
|
||||||
|
err = ImportJSON(new.Import, new.In, &new)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
*col = Column(new)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSON Filter 字段JSON解析
|
||||||
|
func (filter *Filter) UnmarshalJSON(data []byte) error {
|
||||||
|
new := FilterImp{}
|
||||||
|
err := jsoniter.Unmarshal(data, &new)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 导入
|
||||||
|
err = ImportJSON(new.Import, new.In, &new)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
*filter = Filter(new)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSON Render 字段JSON解析
|
||||||
|
func (render *Render) UnmarshalJSON(data []byte) error {
|
||||||
|
new := RenderImp{}
|
||||||
|
err := jsoniter.Unmarshal(data, &new)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 导入
|
||||||
|
err = ImportJSON(new.Import, new.In, &new)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
*render = Render(new)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSON Page 字段JSON解析
|
||||||
|
func (page *Page) UnmarshalJSON(data []byte) error {
|
||||||
|
new := PageImp{}
|
||||||
|
err := jsoniter.Unmarshal(data, &new)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 导入
|
||||||
|
err = ImportJSON(new.Import, new.In, &new)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
*page = Page(new)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSON API 字段JSON解析
|
||||||
|
func (api *API) UnmarshalJSON(data []byte) error {
|
||||||
|
new := APIImp{}
|
||||||
|
err := jsoniter.Unmarshal(data, &new)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 导入
|
||||||
|
err = ImportJSON(new.Import, new.In, &new)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
*api = API(new)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ImportJSON 导入
|
||||||
|
func ImportJSON(name string, in []interface{}, v interface{}) error {
|
||||||
|
if name == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
lib, has := Libs[name]
|
||||||
|
if !has {
|
||||||
|
return fmt.Errorf("共享库 %s 不存在", name)
|
||||||
|
}
|
||||||
|
|
||||||
|
data := maps.MapStrAny{"$in": in}.Dot()
|
||||||
|
content, err := jsoniter.Marshal(share.Bind(lib, data))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = jsoniter.Unmarshal(content, v)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
57
share/importable_test.go
Normal file
57
share/importable_test.go
Normal file
|
|
@ -0,0 +1,57 @@
|
||||||
|
package share
|
||||||
|
|
||||||
|
import (
|
||||||
|
"path"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
jsoniter "github.com/json-iterator/go"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/yaoapp/xiang/config"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
rootLib := path.Join(config.Conf.Source, "/tests/libs")
|
||||||
|
LoadFrom(rootLib)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestColumn(t *testing.T) {
|
||||||
|
content := `{ "@": "column.Image", "in": ["LOGO", ":logo", 40] }`
|
||||||
|
column := Column{}
|
||||||
|
jsoniter.Unmarshal([]byte(content), &column)
|
||||||
|
assert.Equal(t, "upload", column.Edit.Type)
|
||||||
|
assert.Equal(t, ":logo", column.Edit.Props["value"])
|
||||||
|
assert.Equal(t, "image", column.View.Type)
|
||||||
|
assert.Equal(t, float64(40), column.View.Props["height"])
|
||||||
|
assert.Equal(t, float64(40), column.View.Props["width"])
|
||||||
|
assert.Equal(t, ":logo", column.View.Props["value"])
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFilter(t *testing.T) {
|
||||||
|
content := `{ "@": "filter.关键词", "in": ["where.name.match"] }`
|
||||||
|
filter := Filter{}
|
||||||
|
jsoniter.Unmarshal([]byte(content), &filter)
|
||||||
|
assert.Equal(t, "where.name.match", filter.Bind)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRender(t *testing.T) {
|
||||||
|
content := `{ "@": "render.Image", "in": [":image", 40, 60] }`
|
||||||
|
render := Render{}
|
||||||
|
jsoniter.Unmarshal([]byte(content), &render)
|
||||||
|
assert.Equal(t, ":image", render.Props["value"])
|
||||||
|
assert.Equal(t, float64(40), render.Props["width"])
|
||||||
|
assert.Equal(t, float64(60), render.Props["height"])
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPage(t *testing.T) {
|
||||||
|
content := `{ "@": "pages.static.Page", "in": ["id"] }`
|
||||||
|
page := Page{}
|
||||||
|
jsoniter.Unmarshal([]byte(content), &page)
|
||||||
|
assert.Equal(t, "id", page.Primary)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAPI(t *testing.T) {
|
||||||
|
content := `{ "@": "apis.table.Search", "in": [10] }`
|
||||||
|
api := API{}
|
||||||
|
jsoniter.Unmarshal([]byte(content), &api)
|
||||||
|
assert.Equal(t, []interface{}{nil, nil, float64(10)}, api.Default)
|
||||||
|
}
|
||||||
|
|
@ -2,6 +2,15 @@ package share
|
||||||
|
|
||||||
import "github.com/yaoapp/kun/maps"
|
import "github.com/yaoapp/kun/maps"
|
||||||
|
|
||||||
|
// Importable 可导入JSON
|
||||||
|
type Importable struct {
|
||||||
|
Import string `json:"@,omitempty"` // 从 Global 或 Vendor 载入
|
||||||
|
In []interface{} `json:"in,omitempty"` // 从 Global 或 Vendor 载入, 解析参数
|
||||||
|
}
|
||||||
|
|
||||||
|
// APIImp 导入配置数据结构
|
||||||
|
type APIImp API
|
||||||
|
|
||||||
// API API 配置数据结构
|
// API API 配置数据结构
|
||||||
type API struct {
|
type API struct {
|
||||||
Name string `json:"-"`
|
Name string `json:"-"`
|
||||||
|
|
@ -10,36 +19,53 @@ type API struct {
|
||||||
Process string `json:"process,omitempty"`
|
Process string `json:"process,omitempty"`
|
||||||
Guard string `json:"guard,omitempty"`
|
Guard string `json:"guard,omitempty"`
|
||||||
Default []interface{} `json:"default,omitempty"`
|
Default []interface{} `json:"default,omitempty"`
|
||||||
|
Importable
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ColumnImp 导入模式查询过滤器
|
||||||
|
type ColumnImp Column
|
||||||
|
|
||||||
// Column 字段呈现方式
|
// Column 字段呈现方式
|
||||||
type Column struct {
|
type Column struct {
|
||||||
Label string `json:"label"`
|
Label string `json:"label"`
|
||||||
View Render `json:"view,omitempty"`
|
View Render `json:"view,omitempty"`
|
||||||
Edit Render `json:"edit,omitempty"`
|
Edit Render `json:"edit,omitempty"`
|
||||||
Form Render `json:"form,omitempty"`
|
Form Render `json:"form,omitempty"`
|
||||||
|
Importable
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FilterImp 导入模式查询过滤器
|
||||||
|
type FilterImp Filter
|
||||||
|
|
||||||
// Filter 查询过滤器
|
// Filter 查询过滤器
|
||||||
type Filter struct {
|
type Filter struct {
|
||||||
Label string `json:"label"`
|
Label string `json:"label"`
|
||||||
Bind string `json:"bind,omitempty"`
|
Bind string `json:"bind,omitempty"`
|
||||||
Input Render `json:"input,omitempty"`
|
Input Render `json:"input,omitempty"`
|
||||||
|
Importable
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RenderImp 导入模式组件渲染方式
|
||||||
|
type RenderImp Render
|
||||||
|
|
||||||
|
// Render 组件渲染方式
|
||||||
|
type Render struct {
|
||||||
|
Type string `json:"type,omitempty"`
|
||||||
|
Props map[string]interface{} `json:"props,omitempty"`
|
||||||
|
Components map[string]interface{} `json:"components,omitempty"`
|
||||||
|
Importable
|
||||||
|
}
|
||||||
|
|
||||||
|
// PageImp 导入模式页面
|
||||||
|
type PageImp Page
|
||||||
|
|
||||||
// Page 页面
|
// Page 页面
|
||||||
type Page struct {
|
type Page struct {
|
||||||
Primary string `json:"primary"`
|
Primary string `json:"primary"`
|
||||||
Layout map[string]interface{} `json:"layout"`
|
Layout map[string]interface{} `json:"layout"`
|
||||||
Actions map[string]Render `json:"actions,omitempty"`
|
Actions map[string]Render `json:"actions,omitempty"`
|
||||||
Option map[string]interface{} `json:"option,omitempty"`
|
Option map[string]interface{} `json:"option,omitempty"`
|
||||||
}
|
Importable
|
||||||
|
|
||||||
// Render 组件渲染方式
|
|
||||||
type Render struct {
|
|
||||||
Type string `json:"type,omitempty"`
|
|
||||||
Props map[string]interface{} `json:"props,omitempty"`
|
|
||||||
Components map[string]interface{} `json:"components,omitempty"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// AppInfo 应用信息
|
// AppInfo 应用信息
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ import (
|
||||||
func init() {
|
func init() {
|
||||||
share.DBConnect(config.Conf.Database)
|
share.DBConnect(config.Conf.Database)
|
||||||
model.Load(config.Conf)
|
model.Load(config.Conf)
|
||||||
|
share.Load(config.Conf)
|
||||||
Load(config.Conf)
|
Load(config.Conf)
|
||||||
}
|
}
|
||||||
func TestTableProcessSearch(t *testing.T) {
|
func TestTableProcessSearch(t *testing.T) {
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ import (
|
||||||
|
|
||||||
func TestLoad(t *testing.T) {
|
func TestLoad(t *testing.T) {
|
||||||
share.DBConnect(config.Conf.Database)
|
share.DBConnect(config.Conf.Database)
|
||||||
|
share.Load(config.Conf)
|
||||||
model.Load(config.Conf)
|
model.Load(config.Conf)
|
||||||
|
|
||||||
Tables = make(map[string]*Table)
|
Tables = make(map[string]*Table)
|
||||||
|
|
@ -47,5 +48,5 @@ func check(t *testing.T) {
|
||||||
for key := range Tables {
|
for key := range Tables {
|
||||||
keys = append(keys, key)
|
keys = append(keys, key)
|
||||||
}
|
}
|
||||||
assert.Equal(t, 2, len(keys))
|
assert.Equal(t, 3, len(keys))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
14
tests/libs/action.json
Normal file
14
tests/libs/action.json
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
{
|
||||||
|
"保存": {
|
||||||
|
"type": "button",
|
||||||
|
"props": {
|
||||||
|
"label": "保存"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"删除": {
|
||||||
|
"type": "button",
|
||||||
|
"props": {
|
||||||
|
"label": "保存"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
5
tests/libs/apis/table.json
Normal file
5
tests/libs/apis/table.json
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
{
|
||||||
|
"Search": {
|
||||||
|
"default": [null, null, "{{$in.0}}"]
|
||||||
|
}
|
||||||
|
}
|
||||||
32
tests/libs/column.json
Normal file
32
tests/libs/column.json
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
{
|
||||||
|
"Label": {
|
||||||
|
"__comment": "column.Lable('ID',':id')",
|
||||||
|
"label": "{{$in.0}}",
|
||||||
|
"view": {
|
||||||
|
"props": {
|
||||||
|
"value": "{{$in.1}}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Image": {
|
||||||
|
"__comment": "column.Image('图标',':icon', 40)",
|
||||||
|
"label": "{{$in.0}}",
|
||||||
|
"view": {
|
||||||
|
"type": "image",
|
||||||
|
"props": {
|
||||||
|
"value": "{{$in.1}}",
|
||||||
|
"width": "{{$in.2}}",
|
||||||
|
"height": "{{$in.2}}"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"edit": {
|
||||||
|
"type": "upload",
|
||||||
|
"props": {
|
||||||
|
"value": "{{$in.1}}",
|
||||||
|
"filetype": "image",
|
||||||
|
"multiple": false,
|
||||||
|
"maxCount": 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
47
tests/libs/filter.json
Normal file
47
tests/libs/filter.json
Normal file
|
|
@ -0,0 +1,47 @@
|
||||||
|
{
|
||||||
|
"关键词": {
|
||||||
|
"__comment": "filter.关键词('where.name.match')",
|
||||||
|
"label": "关键词",
|
||||||
|
"bind": "{{$in.0}}",
|
||||||
|
"input": {
|
||||||
|
"type": "input",
|
||||||
|
"props": {
|
||||||
|
"placeholder": "请输入关键词"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"排序": {
|
||||||
|
"__comment": "filter.排序()",
|
||||||
|
"label": "排序方式",
|
||||||
|
"bind": "order",
|
||||||
|
"input": {
|
||||||
|
"type": "select",
|
||||||
|
"props": {
|
||||||
|
"placeholder": "排序方式",
|
||||||
|
"options": [
|
||||||
|
{ "label": "手动顺序", "value": "rank" },
|
||||||
|
{ "label": "最近更新", "value": "updated_at.desc" },
|
||||||
|
{ "label": "最近创建", "value": "created_at.desc" }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"客户状态": {
|
||||||
|
"__comment": "filter.客户状态()",
|
||||||
|
"label": "状态",
|
||||||
|
"bind": "where.status.in",
|
||||||
|
"input": {
|
||||||
|
"type": "select",
|
||||||
|
"props": {
|
||||||
|
"placeholder": "选择厂商状态",
|
||||||
|
"mode": "multiple",
|
||||||
|
"options": [
|
||||||
|
{ "value": "已注册" },
|
||||||
|
{ "value": "待收录" },
|
||||||
|
{ "value": "待审核" },
|
||||||
|
{ "value": "已禁用" }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
16
tests/libs/pages/static.json
Normal file
16
tests/libs/pages/static.json
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
{
|
||||||
|
"Page": {
|
||||||
|
"primary": "{{$in.0}}",
|
||||||
|
"layout": {
|
||||||
|
"filters": [
|
||||||
|
{ "name": "开始时间", "width": 6 },
|
||||||
|
{ "name": "结束时间", "width": 6 }
|
||||||
|
],
|
||||||
|
"charts": [
|
||||||
|
{ "type": "line", "props": {} },
|
||||||
|
{ "type": "bar", "props": {} }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"actions": {}
|
||||||
|
}
|
||||||
|
}
|
||||||
10
tests/libs/render.json
Normal file
10
tests/libs/render.json
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
{
|
||||||
|
"Image": {
|
||||||
|
"type": "image",
|
||||||
|
"props": {
|
||||||
|
"value": "{{$in.0}}",
|
||||||
|
"width": "{{$in.1}}",
|
||||||
|
"height": "{{$in.2}}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
230
tests/models/customer.mod.json
Normal file
230
tests/models/customer.mod.json
Normal file
|
|
@ -0,0 +1,230 @@
|
||||||
|
{
|
||||||
|
"name": "客户",
|
||||||
|
"table": {
|
||||||
|
"name": "customer",
|
||||||
|
"comment": "客户表",
|
||||||
|
"engine": "InnoDB"
|
||||||
|
},
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"label": "ID",
|
||||||
|
"name": "id",
|
||||||
|
"type": "ID",
|
||||||
|
"comment": "ID"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "来源",
|
||||||
|
"name": "channel_id",
|
||||||
|
"type": "bigInteger",
|
||||||
|
"comment": "客户来源"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "公司名称",
|
||||||
|
"name": "name",
|
||||||
|
"type": "string",
|
||||||
|
"length": 200,
|
||||||
|
"comment": "公司全称",
|
||||||
|
"unique": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "公司简称",
|
||||||
|
"name": "short_name",
|
||||||
|
"type": "string",
|
||||||
|
"length": 200,
|
||||||
|
"comment": "简称",
|
||||||
|
"nullable": true,
|
||||||
|
"index": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "credit_no",
|
||||||
|
"type": "string",
|
||||||
|
"length": 200,
|
||||||
|
"comment": "统一社会信用代码",
|
||||||
|
"nullable": true,
|
||||||
|
"unique": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "oper_name",
|
||||||
|
"type": "string",
|
||||||
|
"length": 50,
|
||||||
|
"comment": "法人代表",
|
||||||
|
"nullable": true,
|
||||||
|
"index": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "注册资本(万元)",
|
||||||
|
"name": "reg_capi",
|
||||||
|
"type": "integer",
|
||||||
|
"comment": "注册资本(万元)",
|
||||||
|
"nullable": true,
|
||||||
|
"index": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "注册时间",
|
||||||
|
"name": "opened_at",
|
||||||
|
"type": "date",
|
||||||
|
"comment": "注册时间",
|
||||||
|
"nullable": true,
|
||||||
|
"index": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "省份",
|
||||||
|
"name": "province",
|
||||||
|
"type": "string",
|
||||||
|
"length": 50,
|
||||||
|
"comment": "总部所在省份",
|
||||||
|
"nullable": true,
|
||||||
|
"index": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "城市",
|
||||||
|
"name": "city",
|
||||||
|
"type": "string",
|
||||||
|
"length": 100,
|
||||||
|
"comment": "总部所在城市",
|
||||||
|
"nullable": true,
|
||||||
|
"index": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "地址",
|
||||||
|
"name": "address",
|
||||||
|
"type": "string",
|
||||||
|
"length": 255,
|
||||||
|
"comment": "总部地址",
|
||||||
|
"nullable": true,
|
||||||
|
"index": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "经度",
|
||||||
|
"name": "lng",
|
||||||
|
"type": "unsignedDecimal",
|
||||||
|
"precision": 12,
|
||||||
|
"scale": 6,
|
||||||
|
"comment": "经度",
|
||||||
|
"nullable": true,
|
||||||
|
"index": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "纬度",
|
||||||
|
"name": "lat",
|
||||||
|
"type": "unsignedDecimal",
|
||||||
|
"comment": "纬度",
|
||||||
|
"precision": 12,
|
||||||
|
"scale": 6,
|
||||||
|
"nullable": true,
|
||||||
|
"index": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "经营状态",
|
||||||
|
"name": "company_status",
|
||||||
|
"type": "string",
|
||||||
|
"length": 255,
|
||||||
|
"comment": "经营状态",
|
||||||
|
"nullable": true,
|
||||||
|
"index": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "LOGO",
|
||||||
|
"name": "logo",
|
||||||
|
"type": "json",
|
||||||
|
"comment": "LOGO",
|
||||||
|
"nullable": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "简介",
|
||||||
|
"name": "summary",
|
||||||
|
"type": "string",
|
||||||
|
"length": 600,
|
||||||
|
"comment": "简介",
|
||||||
|
"nullable": true,
|
||||||
|
"index": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "官网",
|
||||||
|
"name": "link",
|
||||||
|
"type": "string",
|
||||||
|
"length": 200,
|
||||||
|
"comment": "官网地址",
|
||||||
|
"nullable": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "联系人",
|
||||||
|
"name": "contact_name",
|
||||||
|
"type": "string",
|
||||||
|
"length": 200,
|
||||||
|
"comment": "联系人姓名",
|
||||||
|
"nullable": true,
|
||||||
|
"index": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "联系人职务",
|
||||||
|
"name": "contact_title",
|
||||||
|
"type": "string",
|
||||||
|
"length": 200,
|
||||||
|
"comment": "联系人职务",
|
||||||
|
"nullable": true,
|
||||||
|
"index": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "联系电话",
|
||||||
|
"name": "contact_mobile",
|
||||||
|
"type": "string",
|
||||||
|
"comment": "联系电话",
|
||||||
|
"nullable": true,
|
||||||
|
"index": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "联系微信",
|
||||||
|
"name": "contact_wechat",
|
||||||
|
"type": "string",
|
||||||
|
"comment": "联系微信",
|
||||||
|
"nullable": true,
|
||||||
|
"index": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "介绍",
|
||||||
|
"name": "desc",
|
||||||
|
"type": "text",
|
||||||
|
"comment": "介绍",
|
||||||
|
"nullable": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "领域",
|
||||||
|
"name": "fields",
|
||||||
|
"type": "json",
|
||||||
|
"comment": "服务领域(多选)",
|
||||||
|
"nullable": true,
|
||||||
|
"index": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "行业",
|
||||||
|
"name": "industries",
|
||||||
|
"type": "json",
|
||||||
|
"comment": "行业覆盖(多选)",
|
||||||
|
"nullable": true,
|
||||||
|
"index": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "标签",
|
||||||
|
"name": "tags",
|
||||||
|
"type": "json",
|
||||||
|
"comment": "标签(多选)",
|
||||||
|
"nullable": true,
|
||||||
|
"index": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "关系",
|
||||||
|
"name": "relation",
|
||||||
|
"type": "enum",
|
||||||
|
"default": "正在接洽",
|
||||||
|
"option": ["已有合作", "正在接洽", "潜在客户"],
|
||||||
|
"comment": "合作关系: 已有合作, 正在接洽, 潜在客户",
|
||||||
|
"index": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"relations": {},
|
||||||
|
"option": {
|
||||||
|
"timestamps": true,
|
||||||
|
"soft_deletes": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -27,6 +27,7 @@
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"length": 50,
|
"length": 50,
|
||||||
"comment": "城市",
|
"comment": "城市",
|
||||||
|
"nullable": true,
|
||||||
"index": true
|
"index": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
||||||
75
tests/tables/importable/table.tab.json
Normal file
75
tests/tables/importable/table.tab.json
Normal file
|
|
@ -0,0 +1,75 @@
|
||||||
|
{
|
||||||
|
"name": "客户",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"decription": "客户管理数据表格",
|
||||||
|
"bind": {
|
||||||
|
"model": "user"
|
||||||
|
},
|
||||||
|
"apis": {
|
||||||
|
"search": {}
|
||||||
|
},
|
||||||
|
"columns": {
|
||||||
|
"LOGO": { "@": "column.Image", "in": ["LOGO", ":logo", 40] },
|
||||||
|
"ID": { "@": "column.Label", "in": ["ID", ":id"] }
|
||||||
|
},
|
||||||
|
"filters": {
|
||||||
|
"关键词": { "@": "filter.关键词", "in": ["where.name.match"] },
|
||||||
|
"排序": { "@": "filter.排序" },
|
||||||
|
"状态": { "@": "filter.客户状态" }
|
||||||
|
},
|
||||||
|
"list": {
|
||||||
|
"primary": "id",
|
||||||
|
"layout": {
|
||||||
|
"columns": [
|
||||||
|
{ "name": "ID", "width": 80 },
|
||||||
|
{ "name": "LOGO", "width": 300 }
|
||||||
|
],
|
||||||
|
"filters": [
|
||||||
|
{ "name": "关键词" },
|
||||||
|
{ "name": "状态", "width": 3 },
|
||||||
|
{ "name": "排序", "width": 3 }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"actions": {
|
||||||
|
"create": {
|
||||||
|
"type": "button",
|
||||||
|
"props": {
|
||||||
|
"label": "添加厂商",
|
||||||
|
"icon": "fas fa-plus"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"pagination": {
|
||||||
|
"props": { "showTotal": true }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"option": {
|
||||||
|
"batch": {
|
||||||
|
"delete": {},
|
||||||
|
"columns": [
|
||||||
|
{ "name": "状态", "width": 12 },
|
||||||
|
{ "name": "类型", "width": 12 },
|
||||||
|
{ "name": "排序", "width": 12 }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"edit": {
|
||||||
|
"primary": "id",
|
||||||
|
"layout": {
|
||||||
|
"fieldset": [
|
||||||
|
{
|
||||||
|
"title": "客户资料",
|
||||||
|
"description": "",
|
||||||
|
"columns": [{ "name": "LOGO", "width": 12 }]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"actions": {
|
||||||
|
"cancel": {},
|
||||||
|
"save": { "@": "action.保存" },
|
||||||
|
"delete": { "@": "action.删除" }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"insert": {},
|
||||||
|
"view": {}
|
||||||
|
}
|
||||||
Loading…
Add table
Reference in a new issue