+ 共享库支持
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"` // 应用模型文件目录
|
||||
RootFLow string `json:"root_flow,omitempty" env:"XIANG_ROOT_FLOW"` // 应用业务逻辑文件目录
|
||||
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"` // 应用数据表格文件目录
|
||||
RootChart string `json:"root_chart,omitempty" env:"XIANG_ROOT_CHART"` // 应用分析图表文件目录
|
||||
RootPage string `json:"root_page,omitempty" env:"XIANG_ROOT_PAGE"` // 应用通用页面文件目录
|
||||
|
|
@ -148,6 +149,11 @@ func (cfg *Config) SetDefaults() {
|
|||
if cfg.RootTable == "" {
|
||||
cfg.RootTable = cfg.Root + "/tables"
|
||||
}
|
||||
|
||||
if cfg.RootLib == "" {
|
||||
cfg.RootLib = cfg.Root + "/libs"
|
||||
}
|
||||
|
||||
if cfg.RootChart == "" {
|
||||
cfg.RootChart = cfg.Root + "/charts"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ func Load(cfg config.Config) {
|
|||
LoadEngine(cfg.Path)
|
||||
query.Load(cfg) // 加载数据分析引擎
|
||||
|
||||
share.Load(cfg) // 加载共享库 lib
|
||||
model.Load(cfg) // 加载数据模型 model
|
||||
api.Load(cfg) // 加载业务接口 API
|
||||
flow.Load(cfg) // 加载业务逻辑 Flow
|
||||
|
|
|
|||
|
|
@ -23,5 +23,5 @@ func check(t *testing.T) {
|
|||
for key := range gou.Models {
|
||||
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"
|
||||
|
||||
// Importable 可导入JSON
|
||||
type Importable struct {
|
||||
Import string `json:"@,omitempty"` // 从 Global 或 Vendor 载入
|
||||
In []interface{} `json:"in,omitempty"` // 从 Global 或 Vendor 载入, 解析参数
|
||||
}
|
||||
|
||||
// APIImp 导入配置数据结构
|
||||
type APIImp API
|
||||
|
||||
// API API 配置数据结构
|
||||
type API struct {
|
||||
Name string `json:"-"`
|
||||
|
|
@ -10,36 +19,53 @@ type API struct {
|
|||
Process string `json:"process,omitempty"`
|
||||
Guard string `json:"guard,omitempty"`
|
||||
Default []interface{} `json:"default,omitempty"`
|
||||
Importable
|
||||
}
|
||||
|
||||
// ColumnImp 导入模式查询过滤器
|
||||
type ColumnImp Column
|
||||
|
||||
// Column 字段呈现方式
|
||||
type Column struct {
|
||||
Label string `json:"label"`
|
||||
View Render `json:"view,omitempty"`
|
||||
Edit Render `json:"edit,omitempty"`
|
||||
Form Render `json:"form,omitempty"`
|
||||
Importable
|
||||
}
|
||||
|
||||
// FilterImp 导入模式查询过滤器
|
||||
type FilterImp Filter
|
||||
|
||||
// Filter 查询过滤器
|
||||
type Filter struct {
|
||||
Label string `json:"label"`
|
||||
Bind string `json:"bind,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 页面
|
||||
type Page struct {
|
||||
Primary string `json:"primary"`
|
||||
Layout map[string]interface{} `json:"layout"`
|
||||
Actions map[string]Render `json:"actions,omitempty"`
|
||||
Option map[string]interface{} `json:"option,omitempty"`
|
||||
}
|
||||
|
||||
// Render 组件渲染方式
|
||||
type Render struct {
|
||||
Type string `json:"type,omitempty"`
|
||||
Props map[string]interface{} `json:"props,omitempty"`
|
||||
Components map[string]interface{} `json:"components,omitempty"`
|
||||
Importable
|
||||
}
|
||||
|
||||
// AppInfo 应用信息
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ import (
|
|||
func init() {
|
||||
share.DBConnect(config.Conf.Database)
|
||||
model.Load(config.Conf)
|
||||
share.Load(config.Conf)
|
||||
Load(config.Conf)
|
||||
}
|
||||
func TestTableProcessSearch(t *testing.T) {
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import (
|
|||
|
||||
func TestLoad(t *testing.T) {
|
||||
share.DBConnect(config.Conf.Database)
|
||||
share.Load(config.Conf)
|
||||
model.Load(config.Conf)
|
||||
|
||||
Tables = make(map[string]*Table)
|
||||
|
|
@ -47,5 +48,5 @@ func check(t *testing.T) {
|
|||
for key := range Tables {
|
||||
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",
|
||||
"length": 50,
|
||||
"comment": "城市",
|
||||
"nullable": 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