Skip to content

百度 amis 低代码

amis 是一个低代码前端框架,它使用 JSON 配置来生成页面,可以减少页面开发工作量,极大提升效率。
参考:https://aisuda.bce.baidu.com/amis/zh-CN/docs/concepts/linkage

为了实现用最简单方式来生成大部分页面,amis 的解决方案是基于 JSON 来配置,它的独特好处是:

  • 不需要懂前端
  • 不受技术更新的影响
  • 可以使用可视化页面编辑器来制作页面:一般前端可视化编辑器只能用来做静态原型,而 amis 可视化编辑器做出的页面是可以直接上线的。

amis 的其它亮点:

  • 提供完整的界面解决方案:其它 UI 框架必须使用 JavaScript 来组装业务逻辑,而 amis 只需 JSON 配置就能完成完整功能开发,包括数据获取、表单提交及验证等功能,做出来的页面不需要经过二次开发就能直接上线;
  • 大量的内置组件:其它 UI 框架大部分都只有最通用的组件,如果遇到一些稍微不常用的组件就得自己找第三方,而这些第三方组件往往在展现和交互上不一致,整合起来效果不好,而 amis 则内置大量组件,包括了富文本编辑器、代码编辑器、diff、条件组合、实时日志等业务组件,绝大部分中后台页面开发只需要了解 amis 就足够了;
  • 支持扩展:除了低代码模式,还可以通过 自定义组件 来扩充组件,实际上 amis 可以当成普通 UI 库来使用,实现 90% 低代码,10% 代码开发的混合模式,既提升了效率,又不失灵活性;
  • 容器支持无线嵌套:可以通过嵌套来满足各种布局及展现需求;
  • 经历长时间的实战考验:amis 在百度内部得到了广泛使用,在 6 年多的时间里创建了 5 万页面,从内容审核到机器管理,从数据分析到模型训练,amis 满足了各种各样的页面需求,最复杂的页面有超过 1 万行 JSON 配置。

JSON 配置使得 amis 更适合做有大量常见 UI 组件的页面,但对于面向普通客户(toC)的页面,往往追求个性化的视觉效果,这种情况下用 amis 就不合适,实际上绝大部分前端 UI 组件库也都不适合,只能定制开发。有些复杂的前端功能,比如 可视化编辑器,其中有大量定制的拖拽操作,这种需要依赖原生 DOM 实现的功能无法使用 amis。但对于某些交互固定的领域,比如图连线,amis 后续会有专门的组件来实现。

一个普通的页面大致这个样子: alt text 对应的 json 数据如下:

json
{
  "title": "浏览器内核对 CSS 的支持情况",
  "remark": "嘿,不保证数据准确性",
  "type": "page", // 组件类型:也是渲染器类型
  "body": {
    "type": "crud", // 组件类型
    "draggable": true, // 是否可拖动排序
    "syncLocation": false,//是否过滤条件参数同步到地址栏
    "api": "/amis/api/mock2/sample",//初始化数据api
    "keepItemSelectionOnPageChange": true,//分页时是否保留用户的选择
    //是否根据列元素的searchable属性,自动生成查询条件表单
    "autoGenerateFilter": true,
    "bulkActions": [  // 配置工具栏左侧的批量操作
      {
        "type": "button", //组件类型button
        "label": "批量删除", // 组件显示文案
        "data": { // 组件绑定的数据
          "a": "1",
          "b": "2",
          "c": {
            "e": "3",
            "f": "4",
            "g": "5"
        },
        "actionType": "ajax", // 动作类型:进行Ajax请求
        //请求接口,可以是对象形式,可以获取其他name的参数
        "api": "delete:/amis/api/mock2/sample/${ids|raw}",
        "api": {
          "url": "/amis/api/mock2/form/saveForm",
          "method": "post",
          "data": {
            "name": "${name}",
            "email": "${email}",
            "&": "${c}",
            "描述:":"可以获取json中其他name的变量,&映射data数据c"
          }
        },
        "confirmText": "确定要批量删除?" // 确认文本
      },
      {
        "type": "button",
        "label": "批量修改",
        "actionType": "dialog", //动作类型:需要弹窗
        "dialog": {
          "title": "批量编辑",
          "name": "sample-bulk-edit",
          "body": {
            "type": "form",
            "api": "/amis/api/mock2/sample/bulkUpdate2",
            "body": [
              {
                "type": "hidden",
                "name": "ids"
              },
              {
                "type": "input-text",
                "name": "engine",
                "label": "Engine"
              }
            ]
          }
        }
      }
    ],
    // 快速编辑后用来批量保存的api
    "quickSaveApi": "/amis/api/mock2/sample/bulkUpdate",
    // 快速编辑配置成及时保存时的api
    "quickSaveItemApi": "/amis/api/mock2/sample/$id",
    "headerToolbar": [ // 配置顶部工具栏
      "bulkActions",
      {
        "type": "button",
        "label": "重置测试数据",
        "actionType": "ajax",
        "size": "sm", // 组件大小
        "api": "/amis/api/mock2/sample/reset"
      },
      "export-excel",
      {
        "type": "tpl", // 组件类型:导出模板
        "tpl": "一共有 ${count} 行数据。",
        "className": "v-middle"
      },
      {
        "type": "columns-toggler", // 控制列的按钮
        "align": "right",//对齐方式
        "draggable": true
      },
      {
        "type": "drag-toggler", // 进行排序的按钮
        "align": "right"
      }
    ],
    "footerToolbar": [ // 配置底部工具栏
      "statistics", // 显示数据统计
      "switch-per-page", // 显示切换每页大小的组件
      "pagination" // 显示分页组件
    ],
    "columns": [ // 表格列的配置
      {
        "name": "id", // 对应数据字段
        "label": "ID", // 显示文案
        "width": 20, // 列的宽度
        "sortable": true, //是否可排序
        "type": "text", //组件类型——渲染器类型
        "searchable": { // 可根据此字段进行搜索
          "type": "input-text",
          "name": "id",
          "label": "主键",
          "placeholder": "输入id"
        }
      },
      {
        "name": "browser",
        "label": "Browser",
        "searchable": {
          "type": "select",
          "name": "browser",
          "label": "浏览器",
          "placeholder": "选择浏览器",
          "options": [ // 配置搜索选项
            {
              "label": "Internet Explorer ",
              "value": "ie"
            },
            {
              "label": "AOL browser",
              "value": "aol"
            },
            {
              "label": "Firefox",
              "value": "firefox"
            }
          ]
        }
      },
      {
        "name": "platform",
        "label": "平台",
        "popOver": { // 显示popover弹窗
          "trigger": "hover", // 触发方式:hover
          "body": {
            "type": "tpl",
            "tpl": "就是为了演示有个叫 popOver 的功能"
          }
        },
        "sortable": true,
        "type": "text"
      },
      {
        "name": "grade",
        "label": "CSS 等级",
        "type": "select",
        "options": [
          "A",
          "B",
          "C",
          "D",
          "X"
        ]
      },
      {
        "type": "operation", // 配置最后一列的操作按钮
        "label": "操作",
        "width": 100,
        "buttons": [
          {
            "type": "button",
            "actionType": "ajax",
            "label": "删除",
            "confirmText": "您确认要删除?",
            "api": "delete:/amis/api/mock2/sample/$id"
          }
        ]
      }
    ]
  }
}

另一个例子: alt text json 为:

json
{
  "type": "page",
  "toolbar": [
    {
      "type": "form",
      "panelClassName": "mb-0",
      "title": "",
      "body": [
        {
          "type": "select", //select组件
          "label": "区域",
          "name": "businessLineId",
          "selectFirst": true, // 默认选中第一个
          "mode": "inline", //内容区的展示模式
          "options": ["北京", "上海"],
          "checkAll": false // 多选条件下,是否可全选
        },
        {
          "label": "时间范围",
          "type": "input-date-range", //时间范围选择器
          "name": "dateRange",
          "inline": true,
          //支持相对值。-2mins: 2分钟前 +2days:2天后。可用单位: `min`、`hour`、`day`、`week`、`month`、`year`。所有单位支持复数形式。
          "value": "-1month,+0month",
          "inputFormat": "YYYY-MM-DD", //显示的时间格式
          "format": "YYYY-MM-DD", //提交的时间格式
          "closeOnSelect": true,
          "clearable": false
        }
      ],
      "actions": [], //按钮集合,会固定在底部显示
      "mode": "inline",
      "target": "mainPage",
      "submitOnChange": true,
      "submitOnInit": true
    }
  ],
  "body": [
    {
      "type": "grid",
      "columns": [
        {
          "type": "panel",
          "className": "h-full",
          "body": {
            "type": "tabs",
            "tabs": [
              {
                "title": "消费趋势",
                "tab": [
                  {
                    "type": "chart", //chart图表组件,默认Echarts图表库
                    "config": {
                      // 图表配置
                      "title": {
                        "text": "消费趋势"
                      },
                      "tooltip": {},
                      "xAxis": {
                        "type": "category",
                        "boundaryGap": false,
                        "data": ["一月", "二月", "三月", "四月", "五月", "六月"]
                      },
                      "yAxis": {},
                      "series": [
                        {
                          "name": "销量",
                          "type": "line",
                          "areaStyle": {
                            "color": {
                              "type": "linear",
                              "x": 0,
                              "y": 0,
                              "x2": 0,
                              "y2": 1,
                              "colorStops": [
                                {
                                  "offset": 0,
                                  "color": "rgba(84, 112, 197, 1)"
                                },
                                {
                                  "offset": 1,
                                  "color": "rgba(84, 112, 197, 0)"
                                }
                              ],
                              "global": false
                            }
                          },
                          "data": [5, 20, 36, 10, 10, 20]
                        }
                      ]
                    }
                  }
                ]
              },
              { "title": "账户余额", "tab": "0" }
            ]
          }
        },
        {
          "type": "panel",
          "className": "h-full",
          "body": [
            {
              "type": "chart",
              "config": {
                "title": { "text": "使用资源占比" },
                "series": [
                  {
                    "type": "pie",
                    "data": [
                      { "name": "BOS", "value": 70 },
                      { "name": "CDN", "value": 68 },
                      { "name": "BCC", "value": 48 },
                      { "name": "DCC", "value": 40 },
                      { "name": "RDS", "value": 32 }
                    ]
                  }
                ]
              }
            }
          ]
        }
      ]
    },
    {
      "type": "crud",
      "className": "m-t-sm",
      "api": "/amis/api/mock2/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" }
      ]
    }
  ]
}

Amis 的使用

amis 有两种使用方法:

  • JS SDK,可以用在任意页面中
  • React,可以用在 React 项目中

SDK 版本适合对前端或 React 不了解的开发者,它不依赖 npm 及 webpack,可以像 Vue/jQuery 那样外链代码就能使用。

html
<!-- 引入CSS样式文件 -->
<head>
  <link rel="stylesheet" href="sdk.css" />
</head>

<body>
  <!-- 配置挂载点 -->
  <div id="root" class="app-wrapper"></div>

  <!-- 引入Amis SDK -->
  <script src="sdk.js"></script>

  <script type="text/javascript">
    (function () {
      let amis = amisRequire('amis/embed');

      // 通过替换下面这个配置来生成不同页面
      let amisJSON = {
        type: 'page',
        title: '表单页面',
        body: {
          type: 'form',
          name:'formname',
          mode: 'horizontal',
          api: '/saveForm',
          body: [
            { label: 'Name',type: 'input-text',name: 'name'},
            { label: 'Email',type: 'input-email',name: 'email'}
          ]
        }
      };

      // 这里是初始 props,一般不用传。 默认是中文
      let initProps= { locale: 'en-US' }

        // 下面是一些可选的外部控制函数
      let config= {
        // 在 sdk 中可以不传,用来实现 ajax 请求,但在 npm 中这是必须提供的
        fetcher: (url, method, data,responseType, config,headers) => {},
        // 全局 api 请求适配器,也可以针对某个特定接口单独处理。
        requestAdaptor(api) {
          // 支持异步,可以通过 api.mockResponse 来设置返回结果,跳过真正的请求发送
          // 此功能自定义 fetcher 的话会失效
          // api.context 中包含发送请求前的上下文信息
          return api;
        }
        // 全局 api 适配器。 也可以针对某个特定接口单独处理。
        responseAdaptor(api, payload, query, request, response) {
          return payload;
        }
        // 用来接管页面跳转,比如用 location.href 或 window.open,或者自己实现 amis 配置更新
        jumpTo: (to: string, action?: Action, ctx?: object)  => { location.href = to; },
        // 用来实现地址栏更新
        updateLocation: (to, replace) => {},
        //设置阻止路由跳转的钩子函数,用来实现 form 未保存提前离开时出现确认框。
        blockRouting:(fn: (nextLocation:any) => void | string) => () => void,
        // 用来判断是否目标地址当前地址。
        isCurrentUrl: url => boolean,
        // 用来配置弹窗等组件的挂载位置
        getModalContainer: () => document.getElementsByTagName('body')[0],
        // 可以通过它懒加载自定义组件
        loadRenderer:(schema: any, path: string) => Promise<Function>,
        // Form 表单验证失败时在 notify 消息提示中是否隐藏详细信息
        hideValidateFailedDetail: boolean,
        // 用来实现复制到剪切板
        copy: (contents: string, options?: {silent: boolean, format?: string})=>{},
        // 用来实现通知
        notify: (type, msg) => {},
        // 用来实现提示
        alert: content => {},
        // 用来实现确认框。
        confirm: content => {},
        // 主题,默认是 default,还可以设置成 cxd 或 dark,但记得引用它们的 css,比如 sdk 目录下的 cxd.css
        theme: 'cxd'
        // 用来实现用户行为跟踪,详细请查看左侧高级中的说明
        tracker: (eventTracker) => {},
        // 开发时使用 localhost 地址,而线上使用 xxx.com.
        replaceText: {
          HOST: 'http://localhost'
        },
        // Toast提示弹出位置,默认为'top-center'
        toastPosition: 'top-right' | 'top-center' | 'top-left' | 'bottom-center' | 'bottom-left' | 'bottom-right' | 'center'
      }

      /**
       * 生成页面并挂载到指定dom
       * schemeJSON
       * initProps
       * config
       * @return 可以获取到 amis 渲染的内部信息
       */
      let amisScoped = amis.embed('#root', amisJSON,initProps,config);


    //来获取到所有表单的值,需要注意 page 和 form 都需要有 name 属性。
    amisScoped.getComponentByName('page.formname').getValues()

    //修改表单中的值。
    amisScoped.getComponentByName('page.formname').setValues({'name1': 'othername'})

    //调用 amis 中的通用动作和目标组件的动作
    amisScoped.doAction(actions, ctx)

    //依次执行了toast提示、ajax请求、dialog弹窗、给目标组件赋值动作
    amisScoped.doAction(
      [
        {
          actionType: 'toast',
          args: {
            msg: '${amisUser.name}, ${myName}'
          }
        },
        {
          actionType: 'ajax',
          api: {
            url: '/amis/api/mock2/form/saveForm',
            method: 'post'
          }
        },
        {
          actionType: 'dialog',
          dialog: {
            type: 'dialog',
            title: '弹窗',
            body: [
              {
                type: 'tpl',
                tpl: '<p>对,你打开了弹窗</p>',
                inline: false
              }
            ]
          }
        },
        {
          actionType: 'setValue',
          componentId: 'name',
          args: {
            value: '${myName}'
          }
        }
      ],
      {
        myName: 'amis'
      }
    );

    // updateProps 方法来更新下发到 amis 的属性
    // udpateSchema 方法来更新更新内容配置。
    amisScoped.updateProps(
      {
        // 新的属性对象
      } /*, () => {} 更新回调 */
    );

    //单页应用,在离开当前页面的时候通常需要销毁实例
    amisScoped.unmount();
    // 可以基于 SDK 版本封装成 component 供 vue 使用,具体请参考示例:https://github.com/aisuda/vue2-amis-demo

    })();
  </script>
</body>

根据 MIT 许可证发布