amis/docs/zh-CN/components/crud.md
吴多益 2da87439f2
feat: service 支持通过外部函数获取初始数据和更新数据 (#2768)
* feat: service 支持通过外部函数获取初始数据和更新数据

* 补点注释

* 避免 service unmount 的时候报错

* 改一下名字,并支持销毁
2021-10-28 18:14:44 +08:00

66 KiB
Executable File
Raw Blame History

title description type group menuName icon order
CRUD 增删改查 0 ⚙ 组件 CRUD 增删改查 25

CRUD即增删改查组件主要用来展现数据列表并支持各类【增】【删】【改】【查】等操作。

注意 CRUD 所需的数据必须放 items 中,因此如果只是想显示表格类型的数据没有分页,请使用 Table

基本用法

最基本的用法是配置 数据源接口(api) 以及 展示列(columns)

{
    "type": "crud",
    "api": "/api/sample",
    "syncLocation": false,
    "columns": [
        {
            "name": "id",
            "label": "ID"
        },
        {
            "name": "engine",
            "label": "Rendering engine"
        },
        {
            "name": "browser",
            "label": "Browser"
        },
        {
            "name": "platform",
            "label": "Platform(s)"
        },
        {
            "name": "version",
            "label": "Engine version"
        },
        {
            "name": "grade",
            "label": "CSS grade"
        }
    ]
}

数据源接口数据结构要求

  • itemsrows:用于返回数据源数据,格式是数组
  • total: 用于返回数据库中一共有多少条数据,用于生成分页
{
  "status": 0,
  "msg": "",
  "data": {
    "items": [
      {
        // 每一行的数据
        "id": 1,
        "xxx": "xxxx"
      }
    ],

    "total": 200 // 注意!!!这里不是当前请求返回的 items 的长度,而是数据库中一共有多少条数据,用于生成分页组件
    // 如果你不想要分页,把这个不返回就可以了。
  }
}

如果无法知道数据总数只能知道是否有下一页请返回如下格式amis 会简单生成一个简单版本的分页控件。

{
  "status": 0,
  "msg": "",
  "data": {
    "items": [
      {
        // 每个成员的数据。
        "id": 1,
        "xxx": "xxxx"
      }
    ],

    "hasNext": true // 是否有下一页。
  }
}

如果不需要分页,或者配置了 loadDataOnce 则可以忽略掉 totalhasNext 参数。

功能

既然这个渲染器叫增删改查,那接下来分开介绍这几个功能吧。

其实这个渲染器并没有包含新增功能,新增功能其实还是依靠其他位置放个弹框表单完成,弹框完事了会自动让页面里面的 CRUD 刷新如:

[
    {
        "label": "新增",
        "type": "button",
        "actionType": "dialog",
        "level": "primary",
        "className": "m-b-sm",
        "dialog": {
            "title": "新增表单",
            "body": {
                "type": "form",
                "api": "post:/api/sample",
                "body": [
                    {
                        "type": "input-text",
                        "name": "engine",
                        "label": "Engine"
                    },
                    {
                        "type": "input-text",
                        "name": "browser",
                        "label": "Browser"
                    }
                ]
            }
        }
    },
    {
        "type": "crud",
        "api": "/api/sample?orderBy=id&orderDir=desc",
        "syncLocation": false,
        "columns": [
            {
                "name": "id",
                "label": "ID"
            },
            {
                "name": "engine",
                "label": "Rendering engine"
            },
            {
                "name": "browser",
                "label": "Browser"
            },
            {
                "name": "platform",
                "label": "Platform(s)"
            },
            {
                "name": "version",
                "label": "Engine version"
            },
            {
                "name": "grade",
                "label": "CSS grade"
            }
        ]
    }
]

当然如果你不想要自动刷新,那么给按钮配置 reload: "none" 就行了。

删除功能主要有三种实现:单条操作批量操作或者直接添加一个操作栏,在里面放个类型为 ajax 类型的按钮即可。在这个按钮里面能获得对应的行数据,而且完成后也会自动刷新这个 CRUD 列表。

{
    "type": "crud",
    "api": "/api/sample?orderBy=id&orderDir=desc",
    "syncLocation": false,
    "columns": [
        {
            "name": "id",
            "label": "ID"
        },
        {
            "name": "engine",
            "label": "Rendering engine"
        },
        {
            "name": "browser",
            "label": "Browser"
        },
        {
            "type": "operation",
            "label": "操作",
            "buttons": [
                {
                    "label": "删除",
                    "type": "button",
                    "actionType": "ajax",
                    "level": "danger",
                    "confirmText": "确认要删除?",
                    "api": "delete:/api/sample/${id}"
                }
            ]
        }
    ]
}

改和删其实是差不多的,唯一的区别在于,配置不同的 api按钮类型改成弹框。

{
    "type": "crud",
    "api": "/api/sample?orderBy=id&orderDir=desc",
    "syncLocation": false,
    "columns": [
        {
            "name": "id",
            "label": "ID"
        },
        {
            "name": "engine",
            "label": "Rendering engine"
        },
        {
            "name": "browser",
            "label": "Browser"
        },
        {
            "type": "operation",
            "label": "操作",
            "buttons": [
                {
                    "label": "修改",
                    "type": "button",
                    "actionType": "drawer",
                    "drawer": {
                        "title": "新增表单",
                        "body": {
                            "type": "form",
                            "initApi": "/api/sample/${id}",
                            "api": "post:/api/sample/${id}",
                            "body": [
                                {
                                    "type": "input-text",
                                    "name": "engine",
                                    "label": "Engine"
                                },
                                {
                                    "type": "input-text",
                                    "name": "browser",
                                    "label": "Browser"
                                }
                            ]
                        }
                    }
                }
            ]
        }
    ]
}

弹框里面可用数据自动就是点击的那一行的行数据,如果列表没有返回,可以在 form 里面再配置个 initApi 初始化数据,如果行数据里面有倒是不需要再拉取了。表单项的 name 跟数据 key 对应上便自动回显了。默认发送给表单的保存接口只会包含配置了的表单项,如果不够,请在 api 上配置数据映射,或者直接添加 hidden 类型的表单项(即隐藏域 input[type=hidden])。

查,就不单独介绍了,这个文档绝大部分都是关于查的。

展示模式

CRUD 支持下面 3 种展示模式,默认为 Table 表格模式。

Table 表格模式

Table 模式支持 Table 中的所有功能。

{
    "type": "crud",
    "api": "/api/sample",
    "syncLocation": false,
    "columns": [
        {
            "name": "id",
            "label": "ID"
        },
        {
            "name": "engine",
            "label": "Rendering engine"
        },
        {
            "name": "browser",
            "label": "Browser"
        },
        {
            "name": "platform",
            "label": "Platform(s)"
        },
        {
            "name": "version",
            "label": "Engine version"
        }
    ]
}

这个模式下会默认开启固定表头功能,如果不需要可以使用 "affixHeader": false 关闭。

List 列表模式

List 模式支持 List 中的所有功能。

{
"type": "crud",
"api": "/api/mock2/crud/permissions",
"mode": "list",
"placeholder": "当前组内, 还没有配置任何权限.",
"syncLocation": false,
"title": null,
"listItem": {
  "title": "$name",
  "subTitle": "$description",
  "actions": [
    {
      "icon": "fa fa-edit",
      "tooltip": "编辑",
      "actionType": "dialog",
      "dialog": {
        "title": "编辑能力(权限)",
        "body": {
          "type": "form",
          "body": [
          {
            "type": "hidden",
            "name": "id"
          },
          {
            "name": "name",
            "label": "权限名称",
            "type": "input-text",
            "disabled": true
          },
          {
            "type": "divider"
          },
          {
            "name": "description",
            "label": "描述",
            "type": "textarea"
          }
        ]
        }
      }
    },
    {
      "tooltip": "删除",
      "disabledOn": "~[\"admin:permission\", \"admin:user\", \"admin:role\", \"admin:acl\", \"admin:page\", \"page:readAll\", \"admin:settings\"].indexOf(name)",
      "icon": "fa fa-times",
      "confirmText": "您确定要移除该权限?",
      "actionType": "ajax",
      "api": "delete:/api/mock2/notFound"
    }
  ]
}
}

Cards 卡片模式

Cards 模式支持 Cards 中的所有功能。

{
  "type": "crud",
  "api": "/api/mock2/crud/users",
  "syncLocation": false,
  "mode": "cards",
  "defaultParams": {
    "perPage": 6
  },
  "switchPerPage": false,
  "placeholder": "没有用户信息",
  "columnsCount": 2,
  "card": {
    "header": {
      "className": "bg-white",
      "title": "$name",
      "subTitle": "$realName",
      "description": "$email",
      "avatar": "${avatar | raw}",
      "highlight": "$isSuperAdmin",
      "avatarClassName": "pull-left thumb-md avatar b-3x m-r"
    },
    "bodyClassName": "padder",
    "body": "\n      <% if (this.roles && this.roles.length) { %>\n        <% this.roles.map(function(role) { %>\n          <span class=\"label label-default\"><%- role.name %></span>\n        <% }) %>\n      <% } else { %>\n        <p class=\"text-muted\">没有分配角色</p>\n      <% } %>\n      ",
    "actions": [
      {
        "label": "编辑",
        "actionType": "dialog",
        "dialog": {
          "title": null,
          "body": {
            "api": "",
            "type": "form",
            "tabs": [
              {
                "title": "基本信息",
                "body": [
                  {
                    "type": "hidden",
                    "name": "id"
                  },
                  {
                    "name": "name",
                    "label": "帐号",
                    "disabled": true,
                    "type": "input-text"
                  },
                  {
                    "type": "divider"
                  },
                  {
                    "name": "email",
                    "label": "邮箱",
                    "type": "input-text",
                    "disabled": true
                  },
                  {
                    "type": "divider"
                  },
                  {
                    "name": "isAmisOwner",
                    "label": "管理员",
                    "description": "设置是否为超级管理",
                    "type": "switch"
                  }
                ]
              },
              {
                "title": "角色信息",
                "body": []
              },
              {
                "title": "设置权限",
                "body": []
              }
            ]
          }
        }
      },
      {
        "label": "移除",
        "confirmText": "您确定要移除该用户?",
        "actionType": "ajax",
        "api": "delete:/api/mock2/notFound"
      }
    ]
  }
}

查询条件表单

大部分表格展示有对数据进行检索的需求CRUD 自身支持通过配置filter,实现查询条件过滤表单

filter 配置实际上同 Form 组件,因此支持绝大部分form的功能。

{
    "type": "crud",
    "syncLocation": false,
    "api": "/api/sample",
     "filter": {
        "title": "条件搜索",
        "body": [
            {
                "type": "input-text",
                "name": "keywords",
                "placeholder": "通过关键字搜索"
            }
        ]
    },
    "columns": [
        {
            "name": "id",
            "label": "ID"
        },
        {
            "name": "engine",
            "label": "Rendering engine"
        },
        {
            "name": "browser",
            "label": "Browser"
        },
        {
            "name": "platform",
            "label": "Platform(s)"
        },
        {
            "name": "version",
            "label": "Engine version"
        },
        {
            "name": "grade",
            "label": "CSS grade"
        }
    ]
}

请注意:在默认没有自定义配置 api 数据映射时,提交查询条件表单,会自动将表单中的表单项值,发送给crud所配置的接口,然后通过后端接口,实现对数据的过滤操作,前端默认是不会进行任何的数据过滤操作

如果想前端实现过滤功能,请看前端一次性加载部分。

配置默认请求参数

可以配置defaultParams,来指定拉取接口时的默认参数:

{
    "type": "crud",
    "syncLocation": false,
    "api": "/api/sample",
    "defaultParams": {
        "perPage": 50
    },
    "columns": [
        {
            "name": "id",
            "label": "ID"
        },
        {
            "name": "engine",
            "label": "Rendering engine"
        },
        {
            "name": "browser",
            "label": "Browser"
        },
        {
            "name": "platform",
            "label": "Platform(s)"
        },
        {
            "name": "version",
            "label": "Engine version"
        },
        {
            "name": "grade",
            "label": "CSS grade"
        }
    ]
}

例如上例中,配置{ perPage: 50 },指定分页的默认每页数据条数为 50 条。

数据源接口轮询

可以配置interval来实现数据接口轮询功能,最低为1000毫秒:

{
    "type": "crud",
    "syncLocation": false,
    "api": "/api/sample",
    "interval": 3000,
    "columns": [
        {
            "name": "id",
            "label": "ID"
        },
        {
            "name": "engine",
            "label": "Rendering engine"
        },
        {
            "name": "browser",
            "label": "Browser"
        },
        {
            "name": "platform",
            "label": "Platform(s)"
        },
        {
            "name": "version",
            "label": "Engine version"
        },
        {
            "name": "grade",
            "label": "CSS grade"
        }
    ]
}

配置stopAutoRefreshWhen表达式,来实现满足条件,停止轮询

列配置

除了支持 Table 中的列配置 以外crud 还支持下面这些配置,帮助更好的操作数据

排序检索

可以在列上配置"sortable": true,该列表头右侧会渲染一个可点击的排序图标,可以切换正序倒序

{
    "type": "crud",
    "syncLocation": false,
    "api": "/api/sample",
    "columns": [
        {
            "name": "id",
            "label": "ID"
        },
        {
            "name": "engine",
            "label": "Rendering engine",
            "sortable": true
        }
    ]
}

amis 只负责生成排序组件,并将排序参数传递给接口,而不会在前端对数据进行排序处理。参数格式如下:

{
  "orderBy": "engine", // 这里为所配置列的 name
  "orderDir": "asc" // asc 为升序desc 为降序
}

你可以通过数据映射,在api中获取这些参数。

快速搜索

可以在列上配置"searchable": true,该列表头右侧会渲染一个可点击的搜索图标,点击可以输入关键字进行该列的搜索:

{
    "type": "crud",
    "syncLocation": false,
    "api": "/api/sample",
    "columns": [
        {
            "name": "id",
            "label": "ID"
        },
        {
            "name": "engine",
            "label": "Rendering engine",
            "searchable": true
        }
    ]
}

amis 只负责生成搜索组件,并将搜索参数传递给接口,而不会在前端对数据进行搜索处理。参数格式如下:

{
  "engine": "xxx" // 这里的key是列的 namevalue是输入的关键字
}

你可以通过数据映射,在api中获取这些参数。

快速过滤

可以在列上配置filterable属性,该列表头右侧会渲染一个可点击的过滤图标,点击显示下拉框,选中进行过滤:

{
    "type": "crud",
    "syncLocation": false,
    "api": "/api/sample",
    "columns": [
        {
            "name": "id",
            "label": "ID"
        },
        {
            "name": "grade",
            "label": "CSS grade",
            "filterable": {
                "options": [
                    "A",
                    "B",
                    "C",
                    "D",
                    "X"
                ]
            }
        }
    ]
}

amis 只负责生成下拉选择器组件,并将搜索参数传递给接口,而不会在前端对数据进行搜索处理。参数格式如下:

{
  "grade": "xxx" // 这里的key是列的 namevalue是选中项的value值
}

你可以通过数据映射,在api中获取这些参数。

快速编辑

可以通过给列配置:"quickEdit":truequickSaveApi 可以实现表格内快速编辑并批量保存的功能。

如下Rendering engine列的每一行中,会生成可编辑图标,点击后会显示弹框,用于编辑该列的值,

{
    "type": "crud",
    "syncLocation": false,
    "api": "/api/sample",
    "quickSaveApi": "/api/sample/bulkUpdate",
    "columns": [
        {
            "name": "id",
            "label": "ID"
        },
        {
            "name": "engine",
            "label": "Rendering engine",
            "quickEdit":true
        }
    ]
}

指定编辑表单项类型

quickEdit也可以配置对象形式,可以指定编辑表单项的类型,例如"type": "select"

{
    "type": "crud",
    "syncLocation": false,
    "api": "/api/sample",
    "quickSaveApi": "/api/sample/bulkUpdate",
    "columns": [
        {
            "name": "id",
            "label": "ID"
        },
        {
            "name": "grade",
            "label": "CSS grade",
            "quickEdit": {
                "type": "select",
                "options": [
                    "A",
                    "B",
                    "C",
                    "D",
                    "X"
                ]
            }
        }
    ]
}

快速编辑多个表单项

{
    "type": "crud",
    "syncLocation": false,
    "api": "/api/sample",
    "quickSaveApi": "/api/sample/bulkUpdate",
    "columns": [
        {
            "name": "id",
            "label": "ID"
        },
        {
            "name": "grade",
            "label": "CSS grade",
            "quickEdit": {
                "body": [
                    {
                        "type": "select",
                        "name": "grade",
                        "options": [
                            "A",
                            "B",
                            "C",
                            "D",
                            "X"
                        ]
                    },

                    {
                        "label": "id",
                        "type": "input-text",
                        "name": "id"
                    }
                ]
            }
        }
    ]
}

内联模式

配置quickEditmodeinline。可以直接将编辑表单项渲染至表格内,可以直接操作编辑。

{
    "type": "crud",
    "syncLocation": false,
    "api": "/api/sample",
    "quickSaveApi": "/api/sample/bulkUpdate",
    "columns": [
        {
            "name": "id",
            "label": "ID"
        },
        {
            "name": "grade",
            "label": "CSS grade",
            "quickEdit": {
                "mode": "inline",
                "type": "select",
                "size": "xs",
                "options": [
                    "A",
                    "B",
                    "C",
                    "D",
                    "X"
                ]
            }
        },
        {
            "name": "switch",
            "label": "switch",
            "quickEdit": {
                "mode": "inline",
                "type": "switch",
                "onText": "开启",
                "offText": "关闭"
            }
        }
    ]
}

即时保存

如果想编辑完表单项之后,不想点击顶部确认按钮来进行保存,而是即时保存当前标记的数据,则需要配置 quickEdit 中的 "saveImmediately": true,然后配置接口quickSaveItemApi,可以直接将编辑表单项渲染至表格内操作。

{
    "type": "crud",
    "syncLocation": false,
    "api": "/api/sample",
    "quickSaveItemApi": "/api/sample/$id",
    "columns": [
        {
            "name": "id",
            "label": "ID"
        },
        {
            "name": "grade",
            "label": "CSS grade",
            "quickEdit": {
                "mode": "inline",
                "type": "select",
                "size": "xs",
                "options": [
                    "A",
                    "B",
                    "C",
                    "D",
                    "X"
                ],
                "saveImmediately": true
            }
        },
        {
            "name": "switch",
            "label": "switch",
            "quickEdit": {
                "mode": "inline",
                "type": "switch",
                "onText": "开启",
                "offText": "关闭",
                "saveImmediately": true
            }
        }
    ]
}

你也可以在saveImmediately中配置 api实现即时保存

{
    "type": "crud",
    "syncLocation": false,
    "api": "/api/sample",
    "columns": [
        {
            "name": "id",
            "label": "ID"
        },
        {
            "name": "grade",
            "label": "CSS grade",
            "quickEdit": {
                "mode": "inline",
                "type": "select",
                "size": "xs",
                "options": [
                    "A",
                    "B",
                    "C",
                    "D",
                    "X"
                ],
                "saveImmediately": {
                    "api": "/api/sample/$id"
                }
            }
        },
        {
            "name": "grade",
            "label": "CSS grade",
            "quickEdit": {
                "mode": "inline",
                "type": "switch",
                "onText": "开启",
                "offText": "关闭",
                "saveImmediately": true
            }
        }
    ]
}

配置快速编辑启动条件

通过 quickEditEnabledOn 配置表达式来实现,如下,只有 id 小于 5 的数据可以编辑 engine。

{
    "type": "crud",
    "syncLocation": false,
    "api": "/api/sample",
    "quickSaveApi": "/api/sample/bulkUpdate",
    "columns": [
        {
            "name": "id",
            "label": "ID"
        },
        {
            "name": "engine",
            "label": "Rendering engine",
            "quickEdit":true,
            "quickEditEnabledOn": "this.id < 5"
        }
    ]
}

顶部和底部工具栏

crud 组件支持通过配置headerToolbarfooterToolbar属性,实现在表格顶部和底部渲染组件,

{
    "type": "crud",
    "syncLocation": false,
    "api": "/api/sample",
    "headerToolbar": [
        {
            "type": "tpl",
            "tpl": "一共有${count}条数据"
        }
    ],
    "footerToolbar": [
        {
            "type": "action",
            "actionType": "dialog",
            "label": "底部工具栏按钮",
            "dialog": {
                "title": "一个弹框",
                "body": {
                    "type": "tpl",
                    "tpl": "一个简单的弹框"
                }
            }
        }
    ],
    "columns": [
        {
            "name": "id",
            "label": "ID"
        },
        {
            "name": "engine",
            "label": "Rendering engine"
        },
        {
            "name": "browser",
            "label": "Browser"
        },
        {
            "name": "platform",
            "label": "Platform(s)"
        },
        {
            "name": "version",
            "label": "Engine version"
        },
        {
            "name": "grade",
            "label": "CSS grade"
        }
    ]
}

上例中我们在顶部渲染了一段模板,通过${count}取到数据域中CRUD 返回的count变量值;然后我们在底部渲染了一个按钮。

从上面一些例子中你可能已经发现当我们不配置该属性时crud 默认会在顶部和底部渲染一些组件,实际上,headerToolbarfooterToolbar默认会有下面这些配置:

{
  "headerToolbar": ["bulkActions", "pagination"],
  "footerToolbar": ["statistics", "pagination"]
}
  • 在顶部工具栏中:渲染批量操作按钮(如果在 crud 中,配置了 bulkActions 的话)和 分页组件
  • 在底部工具栏中:渲染数据统计组件 和 分页组件

如果你不希望在顶部或者底部渲染默认组件,你可以设置headerToolbarfooterToolbar为空数组[]

这些组件还能设置 align 来控制位置,有 leftright 两种,比如

{
  "headerToolbar": [
    {
      "type": "bulkActions",
      "align": "right"
    }
  ]
}

其它 amis 组件

headerToolbarfooterToolbar 中可以配置各种 amis 其它组件,比如按钮和 tpl

{
    "type": "crud",
    "name": "myCRUD",
    "syncLocation": false,
    "api": "/api/sample",
    "headerToolbar": [
        {
            "label": "点击弹框",
            "type": "button",
            "actionType": "dialog",
            "icon": "fa fa-plus",
            "level": "primary",
            "dialog": {
                "title": "弹框标题",
                "body": "这是一个弹框"
            }
        },
        {
            "type": "tpl",
            "tpl": "自定义模板"
        },
        {
            "label": "",
            "icon": "fa fa-repeat",
            "type": "button",
            "actionType": "reload",
            "target": "myCRUD",
            "align": "right"
        }
    ],
    "footerToolbar": [],
    "columns": [
        {
            "name": "id",
            "label": "ID"
        }
    ]
}

分页

headerToolbar或者footerToolbar数组中添加pagination字符串,并且在数据源接口中返回了数据总数count,即可以渲染分页组件;添加switch-per-page字符串,可以渲染切换每页条数组件

{
    "type": "crud",
    "syncLocation": false,
    "api": "/api/sample",
    "headerToolbar": [],
    "footerToolbar": ["switch-per-page", "pagination"],
    "columns": [
        {
            "name": "id",
            "label": "ID"
        },
        {
            "name": "grade",
            "label": "CSS grade",
            "quickEdit": {
                "mode": "inline",
                "type": "select",
                "size": "xs",
                "options": [
                    "A",
                    "B",
                    "C",
                    "D",
                    "X"
                ],
                "saveImmediately": {
                    "api": "/api/sample/$id"
                }
            }
        }
    ]
}

crud默认不会处理数据分页,只是会把分页参数传给后端,由后端实现分页,并返回需要展示的数据 和 总数据数total变量:

默认传给后端的分页参数格式为:

{
  "page": 1,
  "perPage": 10
}

你可以通过配置pageFieldperPageField来修改传给后端的分页数据格式,如:

{
  "pageField": "pageNo",
  "perPageField": "pageSize"
}

这样传给后端的参数格式将为:

{
  "pageNo": 1,
  "pageSize": 10
}

你可以通过数据映射,在api中获取这些参数。

{
    "type": "crud",
    "api": {
        "method": "get",
        "url": "xxxxxx",
        "data": {
            "pageNo": "${page}",
            "pageSize": "${perPage}",
            ... // 一些其他参数
        }
    }
}

分页有两种模式:

1. 知道数据总数

如果后端可以知道数据总数时,接口返回格式如下:

{
  "status": 0,
  "msg": "",
  "data": {
    "items": [
      {
        // 每一行的数据。
        "id": 1,
        "xxx": "xxxx"
      }
    ],

    "total": 200 // 注意这里不是当前请求返回的 items 的长度,而是数据库一共有多少条数据,用于生成分页,
  }
}

该模式下,会自动计算总页码数,渲染出有页码的分页组件

2. 不知道数据总数

如果后端无法知道数据总数,那么可以返回hasNext字段,来标识是否有下一页。

{
  "status": 0,
  "msg": "",
  "data": {
    "items": [
      {
        // 每个成员的数据。
        "id": 1,
        "xxx": "xxxx"
      }
    ],

    "hasNext": true // 标识是否有下一页。
  }
}

这样 amis 会在配置分页组件的地方,渲染出一个简单的页面跳转控件。

如果总数据只够展示一页,则默认不显示该分页组件

批量操作

headerToolbar或者footerToolbar数组中添加bulkActions字符串,并且在 crud 上配置bulkActions行为按钮数组,可以实现选中表格项并批量操作的功能。

需要设置primaryField用于标识选中状态,配置当前行数据中的某一唯一标识字段,例如id,否则可能会出现无法选中的问题

{
    "type": "crud",
    "syncLocation": false,
    "api": "/api/sample",
    "headerToolbar": [
        "bulkActions"
    ],
    "bulkActions": [
        {
            "label": "批量删除",
            "actionType": "ajax",
            "api": "delete:/api/sample/${ids|raw}",
            "confirmText": "确定要批量删除?"
        },
        {
            "label": "批量修改",
            "actionType": "dialog",
            "dialog": {
                "title": "批量编辑",
                "body": {
                    "type": "form",
                    "api": "/api/sample/bulkUpdate2",
                    "body": [
                        {
                            "type": "hidden",
                            "name": "ids"
                        },
                        {
                            "type": "input-text",
                            "name": "engine",
                            "label": "Engine"
                        }
                    ]
                }
            }
        }
    ],
    "columns": [
        {
            "name": "id",
            "label": "ID"
        },
        {
            "name": "engine",
            "label": "Rendering engine"
        },
        {
            "name": "browser",
            "label": "Browser"
        },
        {
            "name": "platform",
            "label": "Platform(s)"
        },
        {
            "name": "version",
            "label": "Engine version"
        },
        {
            "name": "grade",
            "label": "CSS grade"
        }
    ]
}

批量操作会默认将下面数据添加到数据域中以供按钮行为使用

  • items Array<object> 选中的行数据。
  • rows items 的别名,推荐用 items。
  • selectedItems Array<object> 选中的行数据,建议直接用 items。
  • unselectedItems Array<object> 没选中的行数据也可获取。
  • ids string 多个 id 值用英文逗号隔开,前提是行数据中有 id 字段,或者有指定的 primaryField 字段。
  • 第一行所有行数据 还有第一行的所有行数据也会包含进去。

你可以通过数据映射,在api中获取这些参数。

约束批量操作

有时候并不是勾选了就能支持批量操作的,比如想约束如果勾选了某条数据 owner 值不是当前用户的就不可以操作。

有两种方式来约束。

  1. 批量操作按钮上配置 disabledOn 值为 this.selectedItems.some(item => item.owner === this.amisUser.name)
  2. 给表格加上 itemCheckableOn 值为 this.owner === this.amisUser.name 表示只有 owner 是自己的才可以打勾。

保留条目选择

默认分页、搜素后,用户选择条目会被清空,配置keepItemSelectionOnPageChange属性后会保留用户选择,可以实现跨页面批量操作。 同时可以通过配置maxKeepItemSelectionLength属性限制最大勾选数

{
    "type": "crud",
    "syncLocation": false,
    "api": "/api/sample",
    "headerToolbar": [
        "bulkActions"
    ],
    "keepItemSelectionOnPageChange": true,
    "maxKeepItemSelectionLength": 4,
    "bulkActions": [
        {
            "label": "批量删除",
            "actionType": "ajax",
            "api": "delete:/api/sample/${ids|raw}",
            "confirmText": "确定要批量删除?"
        },
        {
            "label": "批量修改",
            "actionType": "dialog",
            "dialog": {
                "title": "批量编辑",
                "body": {
                    "type": "form",
                    "api": "/api/sample/bulkUpdate2",
                    "body": [
                        {
                            "type": "hidden",
                            "name": "ids"
                        },
                        {
                            "type": "input-text",
                            "name": "engine",
                            "label": "Engine"
                        }
                    ]
                }
            }
        }
    ],
    "columns": [
        {
            "name": "id",
            "label": "ID"
        },
        {
            "name": "engine",
            "label": "Rendering engine"
        },
        {
            "name": "browser",
            "label": "Browser"
        },
        {
            "name": "platform",
            "label": "Platform(s)"
        },
        {
            "name": "version",
            "label": "Engine version"
        },
        {
            "name": "grade",
            "label": "CSS grade"
        }
    ]
}

数据统计

headerToolbar或者footerToolbar数组中添加statistics字符串,可以实现简单的数据统计功能

{
    "type": "crud",
    "syncLocation": false,
    "api": "/api/sample",
    "headerToolbar": ["statistics"],
    "columns": [
        {
            "name": "id",
            "label": "ID"
        },
        {
            "name": "engine",
            "label": "Rendering engine"
        },
        {
            "name": "browser",
            "label": "Browser"
        },
        {
            "name": "platform",
            "label": "Platform(s)"
        },
        {
            "name": "version",
            "label": "Engine version"
        },
        {
            "name": "grade",
            "label": "CSS grade"
        }
    ]
}

加载更多

headerToolbar或者footerToolbar数组中添加load-more字符串,可以实现点击加载更多功能。

{
    "type": "crud",
    "syncLocation": false,
    "api": "/api/sample",
    "headerToolbar": ["load-more"],
    "columns": [
        {
            "name": "id",
            "label": "ID"
        },
        {
            "name": "engine",
            "label": "Rendering engine"
        },
        {
            "name": "browser",
            "label": "Browser"
        },
        {
            "name": "platform",
            "label": "Platform(s)"
        },
        {
            "name": "version",
            "label": "Engine version"
        },
        {
            "name": "grade",
            "label": "CSS grade"
        }
    ]
}

导出 CSV

headerToolbar或者footerToolbar数组中添加export-csv字符串,可以实现点击下载 CSV 的功能,注意这里只包括当前分页的数据,要下载全部数据需要通过后端 API 实现。

{
    "type": "crud",
    "syncLocation": false,
    "api": "/api/sample",
    "headerToolbar": ["export-csv"],
    "columns": [
        {
            "name": "id",
            "label": "ID"
        },
        {
            "name": "engine",
            "label": "Rendering engine"
        },
        {
            "name": "browser",
            "label": "Browser"
        },
        {
            "name": "platform",
            "label": "Platform(s)"
        },
        {
            "name": "version",
            "label": "Engine version"
        },
        {
            "name": "grade",
            "label": "CSS grade"
        }
    ]
}

通过 api 导出 CSV

1.4.0 及以上版本

export-csv 可以单独配置 api 实现导出全量功能,这个 api 的返回结果和 CRUD 类似

{
    "type": "crud",
    "syncLocation": false,
    "api": "/api/sample",
    "headerToolbar": [{
        "type": "export-csv",
        "label": "全量导出 CSV",
        "api": "/api/sample"
    }],
    "columns": [
        {
            "name": "id",
            "label": "ID"
        },
        {
            "name": "engine",
            "label": "Rendering engine"
        },
        {
            "name": "browser",
            "label": "Browser"
        },
        {
            "name": "platform",
            "label": "Platform(s)"
        },
        {
            "name": "version",
            "label": "Engine version"
        },
        {
            "name": "grade",
            "label": "CSS grade"
        }
    ]
}

导出 Excel

headerToolbar或者footerToolbar数组中添加export-excel字符串,可以实现点击下载 Excel 的功能,和导出 CSV 一样只包括当前分页的数据,但它们有明显区别:

  1. 导出 CSV 是将 api 返回数据导出,表头是数据里的 key而 Excel 的表头使用的是 label。
  2. 导出 Excel 更重视展现一致支持合并单元格、链接、mapping 映射、图片(需要加跨域 Header)。
  3. 导出 Excel 只在 modetable 时能用。
{
    "type": "crud",
    "syncLocation": false,
    "api": "/api/sample",
    "headerToolbar": ["export-excel"],
    "columns": [
        {
            "name": "id",
            "label": "ID"
        },
        {
            "name": "engine",
            "label": "Rendering engine"
        },
        {
            "name": "browser",
            "label": "Browser"
        },
        {
            "name": "platform",
            "label": "Platform(s)"
        },
        {
            "name": "version",
            "label": "Engine version"
        },
        {
            "name": "grade",
            "label": "CSS grade"
        }
    ]
}

只导出部分列

1.4.0 及以上版本

通过配置 columns 来支持只导出部分列,其中是需要导出的列 name 数组

{
    "type": "crud",
    "syncLocation": false,
    "api": "/api/sample",
    "headerToolbar": [{
        "type": "export-excel",
        "label": "只导出 engine 和  browser 列",
        "columns": ["engine", "browser"]
    }],
    "columns": [
        {
            "name": "id",
            "label": "ID"
        },
        {
            "name": "engine",
            "label": "Rendering engine"
        },
        {
            "name": "browser",
            "label": "Browser"
        },
        {
            "name": "platform",
            "label": "Platform(s)"
        },
        {
            "name": "version",
            "label": "Engine version"
        },
        {
            "name": "grade",
            "label": "CSS grade"
        }
    ]
}

通过 api 导出 Excel

1.1.6 以上版本支持

除了前面的用法,还可以配置 api 来通过数据请求来导出 Excel实现类似全量导出的功能

{
    "type": "crud",
    "syncLocation": false,
    "headerToolbar": [{
        "type": "export-excel",
        "label": "全量导出 Excel",
        "api": "/api/sample"
    }],
    "columns": [
        {
            "name": "id",
            "label": "ID"
        },
        {
            "name": "engine",
            "label": "Rendering engine"
        },
        {
            "name": "browser",
            "label": "Browser"
        },
        {
            "name": "platform",
            "label": "Platform(s)"
        },
        {
            "name": "version",
            "label": "Engine version"
        },
        {
            "name": "grade",
            "label": "CSS grade"
        }
    ]
}

自定义导出 Excel 的文件名

1.1.7 以上版本支持

通过 filename 自定义导出文件名(支持模板变量)

{
    "type": "crud",
    "syncLocation": false,
    "headerToolbar": [{
        "type": "export-excel",
        "label": "自定义导出 Excel",
        "filename": "自定义文件名${test}",
        "api": "/api/sample"
    }],
    "columns": [
        {
            "name": "id",
            "label": "ID"
        },
        {
            "name": "engine",
            "label": "Rendering engine"
        },
        {
            "name": "browser",
            "label": "Browser"
        },
        {
            "name": "platform",
            "label": "Platform(s)"
        },
        {
            "name": "version",
            "label": "Engine version"
        },
        {
            "name": "grade",
            "label": "CSS grade"
        }
    ]
}

显隐显示查询条件表单

headerToolbar或者footerToolbar数组中添加filter-toggler字符串,并且在 crud 中配置"filterTogglable": true后,可以渲染一个可以切换显示查询表单的功能按钮

{
    "type": "crud",
    "syncLocation": false,
    "api": "/api/sample",
     "filter": {
        "title": "条件搜索",
        "body": [
            {
                "type": "input-text",
                "name": "keywords",
                "placeholder": "通过关键字搜索"
            }
        ]
    },
    "filterTogglable": true,
    "headerToolbar": [
        "filter-toggler"
    ],
    "columns": [
        {
            "name": "id",
            "label": "ID"
        },
        {
            "name": "engine",
            "label": "Rendering engine"
        },
        {
            "name": "browser",
            "label": "Browser"
        },
        {
            "name": "platform",
            "label": "Platform(s)"
        },
        {
            "name": "version",
            "label": "Engine version"
        },
        {
            "name": "grade",
            "label": "CSS grade"
        }
    ]
}

总结行

如果是默认的表格模式,还支持增加总结行,具体请参考 table 的文档。

弹框与数据链

一般 CRUD 中会有弹框,然后进行数据展示或进行二次编辑的需求,通过在列中配置按钮,然后配置弹框,弹框内配置相应的组件即可。

现在问题是,如何获取到当前操作行的数据呢?

实际上,你操作当前行数据,会成为弹框这层节点的父级节点,因此你可以通过 数据链,获取到上层,也就是点击的行的数据,具体获取方法和普通组件获取数据域中数据的方法相同,

{
  "type": "crud",
  "syncLocation": false,
  "api": "/api/sample",
  "draggable": true,
  "columns": [
    {
      "name": "id",
      "label": "ID"
    },
    {
      "name": "engine",
      "label": "Rendering engine"
    },
    {
      "name": "browser",
      "label": "Browser"
    },
    {
      "name": "platform",
      "label": "Platform(s)"
    },
    {
      "name": "version",
      "label": "Engine version"
    },
    {
      "name": "grade",
      "label": "CSS grade"
    },
    {
      "type": "button",
      "label": "一个弹框",
      "actionType": "dialog",
      "dialog": {
        "title": "一个弹框",
        "body": [
          {
            "type": "tpl",
            "tpl": "行数据中 Browser 值为:${browser}"
          },
          {
            "type": "divider"
          },
          {
            "type": "form",
            "api": "/api/sample/$id",
            "body": [
              {
                "type": "input-text",
                "name": "engine",
                "label": "Engine"
              }
            ]
          }
        ]
      }
    }
  ]
}

例如上例中 Tpl 用 ${browser} 获取 browser 变量Form 中配置"name": "engine" 映射 engine 变量。

遇到数据字段冲突时,可以在 弹框上通过配置数据映射 解决。

拖拽排序

通过配置"draggable": true和保存排序接口saveOrderApi,可以实现拖拽排序功能,

{
    "type": "crud",
    "syncLocation": false,
    "api": "/api/sample",
    "draggable": true,
    "columns": [
        {
            "name": "id",
            "label": "ID"
        },
        {
            "name": "engine",
            "label": "Rendering engine"
        },
        {
            "name": "browser",
            "label": "Browser"
        },
        {
            "name": "platform",
            "label": "Platform(s)"
        },
        {
            "name": "version",
            "label": "Engine version"
        },
        {
            "name": "grade",
            "label": "CSS grade"
        }
    ]
}

同样的,前端是不会处理排序结果,需要后端调用接口saveOrderApi来保存新的顺序

发送方式默认为POST,会包含以下信息。

  • ids 字符串如: 2,3,1,4,5,6 用 id 来记录新的顺序。 前提是你的列表接口返回了 id 字段。另外如果你的 primaryField 不是 id,则需要配置如: primaryField: "order_id"。注意:无论你配置成什么 primayField这个字段名始终是 ids。

  • rows Array<Item> 数组格式,新的顺序,数组里面包含所有原始信息。

  • insertAfter 或者 insertBefore 这是 amis 生成的 diff 信息对象格式key 为目标成员的 primaryField 值,即 idvalue 为数组,数组中存放成员 primaryField 值。如:

    {
      "insertAfter": {
        "2": ["1", "3"],
        "6": ["4", "5"]
      }
    }
    

    表示:成员 1 和成员 3 插入到了成员 2 的后面。成员 4 和 成员 5 插入到了 成员 6 的后面。

你可以通过数据映射,在api中获取这些参数。

如下:

{
  "saveOrderApi": {
    "url": "/api/xxxx",
    "data": {
      "ids": "${ids}"
    }
  }
}

这样就只会发送 ids 了。

单条操作

当操作对象是单条数据时这类操作叫单条操作比如编辑、删除、通过、拒绝等等。CRUD 的 table 模式可以在 column 通过放置按钮来完成(其他模式参考 table 模式)。比如编辑就是添加个按钮行为是弹框类型的按钮或者添加一个页面跳转类型的按钮把当前行数据的 id 放在 query 中传过去、删除操作就是配置一个按钮行为是 AJAX 类型的按钮,将数据通过 api 发送给后端完成。

CRUD 中不限制有多少个单条操作、添加一个操作对应的添加一个按钮就行了。CRUD 在处理按钮行为的时候会把当前行的完整数据传递过去,如果你的按钮行为是弹出时,还会包含一下信息:

  • hasNext boolean 当按钮行为是弹框时,还会携带这个数据可以用来判断当前页中是否有下一条数据。
  • hasPrev boolean 当按钮行为是弹框时,还会携带这个数据可以判断用来当前页中是否有上一条数据。
  • index number 当按钮行为是弹框时,还会携带这个数据可以用来获取当前行数据在这一页中的位置。
  • prevIndex number
  • nextIndex number

你可以通过数据映射,在api中获取这些参数。

如果你的按钮类型是 ajax你也可以限定只发送部分数据比如。

{
  "type": "button",
  "label": "删除",
  "actionType": "ajax",
  "api": "delete:/api/xxxx/$id",
  "confirmText": "确定要删除?"
}

上面这个例子就会发送 id 字段了,如果想要全部发送过去同时还想添加点别的字段就这样:

{
  "type": "button",
  "label": "删除",
  "actionType": "ajax",
  "api": {
    "method": "post",
    "url": "/api/xxxx/$id",
    "data": {
      "&": "$$",
      "op": "delete"
    }
  },
  "confirmText": "确定要删除?"
}

注意: 如果使用feedback弹窗,如果不想关闭弹窗时触发crud再次拉取数据,需要设置button"reload":"none"

过滤条件参数同步地址栏

默认 CRUD 会将过滤条件参数同步至浏览器地址栏中,比如搜索条件、当前页数,这也做的目的是刷新页面的时候还能进入之前的分页。

但也会导致地址栏中的参数数据合并到顶层的数据链中,例如:自动给同名的表单项设置默认值。如果不希望这个功能,可以设置 syncLocation: false 来关闭。

本文中的例子为了不相互影响都关闭了这个功能。 另外如果需要使用接口联动,需要设置syncLocation: false

前端一次性加载

如果你的数据并不是很大,而且后端不方便做分页和条件过滤操作,那么通过配置loadDataOnce实现前端一次性加载并支持分页和条件过滤操作

{
    "type": "crud",
    "syncLocation": false,
    "api": "/api/sample",
    "loadDataOnce": true,
    "columns": [
        {
            "name": "id",
            "label": "ID"
        },
        {
            "name": "engine",
            "label": "Rendering engine"
        },
        {
            "name": "browser",
            "label": "Browser"
        },
        {
            "name": "platform",
            "label": "Platform(s)"
        },
        {
            "name": "version",
            "label": "Engine version"
        },
        {
            "name": "grade",
            "label": "CSS grade",
            "sortable": true
        }
    ]
}

配置一次性加载后,基本的分页、快速排序操作将会在前端进行完成。如果想实现前端检索,需要用到数据映射功能:

{
    "type": "crud",
    "syncLocation": false,
    "api": "/api/sample",
    "loadDataOnce": true,
    "source": "${rows | filter:engine:match:keywords}",
    "filter":{
        "body": [
            {
                "type": "input-text",
                "name": "keywords",
                "label": "引擎"
            }
        ]
    },
    "columns": [
        {
            "name": "id",
            "label": "ID"
        },
        {
            "name": "engine",
            "label": "Rendering engine"
        },
        {
            "name": "browser",
            "label": "Browser"
        },
        {
            "name": "platform",
            "label": "Platform(s)"
        },
        {
            "name": "version",
            "label": "Engine version"
        },
        {
            "name": "grade",
            "label": "CSS grade"
        }
    ]
}

上例使用了数据映射中的filter过滤器,在前端实现了engine列的搜索功能。

**注意:**如果你的数据量较大,请务必使用服务端分页的方案,过多的前端数据展示,会显著影响前端页面的性能

动态列

since 1.1.6

在 1.1.6 之前的版本,只能通过 service + schemaApi 让后端返回 schema 配置来实现1.1.6 版本之后可以直接通过 crud 的数据接口返回了。 用这种方式可以简化动态列的实现,与 items 并列返回 columns 数组即即可。

{
    "type": "crud",
    "api": "/api/crud/dynamic?waitSeconds=1"
}

使用数据链中的数据

可以通过 source 属性来自定义去返回数据的字段,或者取数据域中的数据,比如

{
  "type": "page",
  "data": {
    "myItems": [
      {
        "id": 1
      }
    ]
  },
  "body": {
    "type": "crud",
    "source": "${myItems}",
    "columns": [
      {
        "name": "id",
        "label": "ID"
      }
    ]
  }
}

自定义点击行的行为

1.4.0 及以上版本

配置 itemAction 可以实现点击某一行后进行自定义操作,支持 action 里的所有配置,比如弹框、刷新其它组件等。

{
    "type": "crud",
    "api": "/api/sample",
    "syncLocation": false,
    "itemAction": {
      "type": "button",
      "actionType": "dialog",
      "dialog": {
        "title": "详情",
        "body": "当前行的数据 browser: ${browser}, version: ${version}"
      }
    },
    "columns": [
        {
            "name": "id",
            "label": "ID"
        },
        {
            "name": "engine",
            "label": "Rendering engine"
        },
        {
            "name": "browser",
            "label": "Browser"
        },
        {
            "name": "platform",
            "label": "Platform(s)"
        },
        {
            "name": "version",
            "label": "Engine version"
        },
        {
            "name": "grade",
            "label": "CSS grade"
        }
    ]
}

注意这个属性和 checkOnItemClick 冲突,因为都是定义行的点击行为,开启 itemActioncheckOnItemClick 将会失效。

属性表

属性名 类型 默认值 说明
type string type 指定为 CRUD 渲染器
mode string "table" "table" 、 "cards" 或者 "list"
title string "" 可设置成空,当设置成空时,没有标题栏
className string 表格外层 Dom 的类名
api API CRUD 用来获取列表数据的 api。
loadDataOnce boolean 是否一次性加载所有数据(前端分页)
loadDataOnceFetchOnFilter boolean true 在开启 loadDataOnce 时filter 时是否去重新请求 api
source string 数据映射接口返回某字段的值,不设置会默认使用接口返回的${items}或者${rows},也可以设置成上层数据源的内容
filter Form 设置过滤器,当该表单提交后,会把数据带给当前 mode 刷新列表。
filterTogglable boolean false 是否可显隐过滤器
filterDefaultVisible boolean true 设置过滤器默认是否可见。
initFetch boolean true 是否初始化的时候拉取数据, 只针对有 filter 的情况, 没有 filter 初始都会拉取数据
interval number 3000 刷新时间(最低 1000)
silentPolling boolean false 配置刷新时是否隐藏加载动画
stopAutoRefreshWhen string "" 通过表达式来配置停止刷新的条件
stopAutoRefreshWhenModalIsOpen boolean false 当有弹框时关闭自动刷新,关闭弹框又恢复
syncLocation boolean true 是否将过滤条件的参数同步到地址栏
draggable boolean false 是否可通过拖拽排序
itemDraggableOn boolean 表达式来配置是否可拖拽排序
saveOrderApi API 保存排序的 api。
quickSaveApi API 快速编辑后用来批量保存的 API。
quickSaveItemApi API 快速编辑配置成及时保存时使用的 API。
bulkActions Array<Action> 批量操作列表,配置后,表格可进行选中操作。
defaultChecked boolean false 当可批量操作时,默认是否全部勾选。
messages Object 覆盖消息提示,如果不指定,将采用 api 返回的 message
messages.fetchFailed string 获取失败时提示
messages.saveOrderFailed string 保存顺序失败提示
messages.saveOrderSuccess string 保存顺序成功提示
messages.quickSaveFailed string 快速保存失败提示
messages.quickSaveSuccess string 快速保存成功提示
primaryField string "id" 设置 ID 字段名。
perPage number 10 设置一页显示多少条数据。
defaultParams Object 设置默认 filter 默认参数,会在查询的时候一起发给后端
pageField string "page" 设置分页页码字段名。
perPageField string "perPage" 设置分页一页显示的多少条数据的字段名。注意:最好与 defaultParams 一起使用,请看下面例子。
perPageAvailable Array<number> [5, 10, 20, 50, 100] 设置一页显示多少条数据下拉框可选条数。
orderField string 设置用来确定位置的字段名,设置后新的顺序将被赋值到该字段中。
hideQuickSaveBtn boolean false 隐藏顶部快速保存提示
autoJumpToTopOnPagerChange boolean false 当切分页的时候,是否自动跳顶部。
syncResponse2Query boolean true 将返回数据同步到过滤器上。
keepItemSelectionOnPageChange boolean true 保留条目选择,默认分页、搜素后,用户选择条目会被清空,开启此选项后会保留用户选择,可以实现跨页面批量操作。
labelTpl string 单条描述模板,keepItemSelectionOnPageChange设置为true后会把所有已选择条目列出来,此选项可以用来定制条目展示文案。
headerToolbar Array ['bulkActions', 'pagination'] 顶部工具栏配置
footerToolbar Array ['statistics', 'pagination'] 底部工具栏配置
alwaysShowPagination boolean false 是否总是显示分页
affixHeader boolean true 是否固定表头(table 下)

注意除了上面这些属性CRUD 在不同模式下的属性需要参考各自的文档,比如

  • 默认 Table 模式里的列配置。
  • Cards 模式。
  • List 模式。