优维低代码:Best Practice

EASYOPS_youwei · · 1012 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

 

 

导语

优维低代码技术专栏,是一个全新的、技术为主的专栏,由优维技术委员会成员执笔,基于优维7年低代码技术研发及运维成果,主要介绍低代码相关的技术原理及架构逻辑,目的是给广大运维人提供一个技术交流与学习的平台。

连载第十二期

《编排详解:Best Practice》

# Storyboard 最佳结构

  • 第一级路由type:route
  • Provider 的声明尽量放到路由第一级
  • 一级菜单统一在一级menu写,这样一级菜单也直接复用了

过去:

  • provider 重复定义,也不清楚到底我用了哪些接口

我们能看到很多 provider 是在bricks[]定义的,比如:

{
  "bricks": [
    {
      "brick": "providers-of-cmdb.instance-api-post-search",
      "bg": true
    },
    {
      "brick": "providers-of-cmdb.cmdb-object-api-get-object-all",
      "bg": true
    }
    {
      ...
    }
  ]
}
  • Menu 重复定义

我们能看到很多同学将menu抽出一个函数,然后在 ts 里面直接调用,但实际上这样生成的storyboard.json也是每个path都定义了一次的。

有人会说,有什么关系呢,反正我就一个函数嘛。因为生成的 storyboard.json 越大,意味着页面首次加载越慢,大伙看下 162 目前的 bootstrap 接口的数据量及耗时就知道了

建议:

{
  "app": { "name": "敏捷管理", "id": "agile", "homepage": "/agile" },
  "routes": [
    {
      "path": "${APP.homepage}",
      "menu": {
        ...
      },
      "providers": [
        "providers-of-cmdb.instance-api-post-search",
        "providers-of-cmdb.instance-api-get-detail",
        "providers-of-cmdb.cmdb-object-api-get-object-all",
        "providers-of-cmdb.instance-api-delete-instance",
        "providers-of-cmdb.instance-api-update-instance-v2",
        "providers-of-cmdb.instance-api-create-instance",
        "providers-of-cmdb.instance-api-aggregate-count-v2",
        "providers-of-cmdb.instance-api-import-instance",
        "providers-of-cmdb.instance-api-group-instance",
        "providers-of-topboard.topboard-api-create-issue",
        "providers-of-topboard.topboard-api-create-comment",
        "providers-of-topboard.topboard-api-update-comment",
        "providers-of-topboard.topboard-api-update-issue",
        "providers-of-topboard.topboard-api-update-issue-step",
        "developers.provider-providers-sub-menu",
        "developers.provider-service-data",
        "developers.providers-of-brick-story",
        "developers.providers-of-brick-docs",
        "providers-of-notify.oplog-api-list-operation-log"
      ],
      "type": "routes",
      "routes": [
        ...
      ]
    }
  ]
}

# 多用事件回调(Callback)

上面说到,provider 都定义在一级路由,那这里有同学可能会说,调用了provider后我希望弹出提示框呢,怎么搞?

事实上,很多时候也是因为此,所以要多次定义 provider

框架提供了新的 callback 能力,可以直接在 callback 写对应的处理,更加简单直接,代码量直接少 1/4。

{
  brick: "presentational-bricks.modal-confirm",
  properties: {
    id: "sprint-complete-confirm",
    title: "确定关闭迭代?",
    content: "未完成任务将转移到需求池"
  },
  events: {
    "confirm.ok": [
      {
        target: "providers-of-cmdb\\.instance-api-update-instance-v2",
        method: "resolve",
        args: [
          "_SPRINT",
          "${sprintId}",
          {
            status: "completed"
          }
        ],
        callback: {
          success: [
            {
              action: "message.success",
              args: ["迭代关闭成功,进入下一个迭代"]
            },
            {
              action: "history.push",
              args: ["${APP.homepage}/product/${productId}/sprint"]
            }
          ],
          error: {
            action: "handleHttpError"
          }
        }
      },


    ]
  }
}

另外,大伙是否注意到message.success,我们已经将
presentational-bricks.brick-utils的能力封装到框架了,意味着不用再写下面的定义了

{
  brick: "presentational-bricks.brick-utils",
  bg: true
}

# callback 的时候别只写 success,而不写 error

如果是单元测试的话,这就是路径没覆盖。

# 什么时候 provider 应该用 id 来调用

如果有用到provider的暂存数据能力,比如调用了updateArgs,updateArgsAndExecute,setArgs,setArgsAndExecute,这种情况下就应该去声明id,用id来调用。

如果直接providers-of-xx.xxx调用的话,很容易就被其他编排者污染了你的参数。

# 尽量依照调用顺序去传递数据,不要去改别人内部的东西

场景一:表格里面有个按钮去调用 provider

{
  "brick": "presentational-bricks.brick-table",
  "properties": {
    "columns": [
      {
        "title": "Tools",
        "width": "180px",
        "useBrick": [
          {
            "brick": "basic-bricks.general-custom-buttons",
            "transform": {
              "dataSource": "@{rowData}"
            },
            "properties": {
              "isMoreButton": true,
              "alignment": "start",
              "customButtons": [
                {
                  "isDropdown": false,
                  "tooltip": "Active Issue",
                  "icon": "like",
                  "eventName": "issue.like",
                  "buttonType": "link"
                }
              ]
            },
            "events": {
              "issue.like": [
                {
                  "target": "#updateProvider",
                  "method": "executeWithArgs",
                  "args": [
                    "_ISSUE",
                    "${EVENT.detail.instanceId}",
                    { "status": "active" }
                  ]
                }
              ]
            }
          }
        ]
      }
    ]
  }
}

大伙注意到,数据的传递方向是 table -> button -> provider。尽量不要在某个地方去给provider去updateArgs

现在我们交互原子构件(比如button、custom-buttons、brick-link)都具备数据(dataSource或detail字段)的暂存能力了

场景二:点击按钮弹出 modal,modal 里面有个 form 表单

这个场景比较复杂,现在还没有一个最佳实践,用新的 custom template 封装吧。准备后面专门做个modal-form来解决

# 在没有地方暂存数据的时候,记得 html element 是可以任意存储数据的

事实上,你可以在 event 或者 lifeCycle 的时候,给某个构件写入任意属性去暂存数据,这样在这个构件的事件发出来的时候,你可以通过${EVENT.target.xxx}获得数据

{
  brick: "agile.comment-brick",
  properties: {
    id: "issue-comment",
    placeholder: "说点什么"
  },
  events: {
    "add.comment": {
      target:
        "providers-of-topboard\\.topboard-api-create-comment",
      method: "resolve",
      args: [
        {
          body: "${EVENT.detail.body}",
          author: [
            { instanceId: "${SYS.userInstanceId}" }
          ],
          issue: [
            {
              instanceId: "${EVENT.target.issueInstanceId}"
            }
          ]
        }
      ],
      callback: {


      }
    }
  }
}

# 动态构件列表渲染

general-card-list卡片列表我们封的比较死,只能使用到里面的card-item,而且他是一个老版的template,很难用事件来触发他更新数据,怎么办?我们现在有个神奇list-container+userBrick机制,可以随便搞动态。

{
  brick:
    "basic-bricks.list-container",
  properties: {
    containerStyle: {
      display: "grid",
      gap: "20px",
      gridTemplateColumns:
        "repeat(auto-fill, minmax(130px, 1fr))"
    },
    useBrick: {
      brick:
        "presentational-bricks.entry-card-item",
      transform: {
        cardTitle: "@{objectName}",
        id: "@{objectId}",
        iconColor: "@{iconColor}",
        icon: "@{icon}",
        urlTemplate:
          "${APP.homepage}/@{objectId}"
      }
    }
  }
}

# modal,drawer 等弹窗容器(默认不可见)类型的构件:

  • 设置portal: true
  • 不要设置bg: true

# 搜索框都放在 search-bar 里面,这个构件已经按设计做好了间距

searchable-table足够强大,但有些时候也不够灵活,故专门开发了search-bar容器,用于常见的search-bar+brick-table。不要再自己写 css 啦

#在表格里面添加链接,不要用general-button type=link,而是用brick-link

因为:

  • button 会有 margin-left
  • button 的文字不能拖动选择复制

# 复杂的数据加工,记得我们除了有 pipe 之外,还可以直接写 js 表达式

注意transform里面的<% %>

{
  "columns": [
    {
      "title": "任务数",
      "dataIndex": "issues.length",
      "key": "issues",
      "useBrick": {
        "brick": "presentational-bricks.brick-value-mapping",
        "transform": {
          "value": "<% _.filter(DATA.rowData.issues, item => item.resolution || item.resolution===\"\").length + \" / \" + DATA.rowData.issues.length %>"
        },
        "properties": {
          "mapping": {
            "*": {
              "color": "blue"
            }
          }
        }
      }
    },
    {
      "title": "完成进度",
      "dataIndex": "progress",
      "key": "progress",
      "useBrick": {
        "brick": "presentational-bricks.basic-progress",
        "transform": {
          "value": "<% Math.round(_.filter(DATA.rowData.issues, item => item.resolution || item.resolution===\"\").length/DATA.rowData.issues.length*100) %>"
        },
        "properties": {
          "configProps": {
            "size": "small"
          },
          "value": 75,
          "colorMap": [
            {
              "progress": 60,
              "color": "red"
            },
            {
              "progress": 80,
              "color": "orange"
            },
            {
              "progress": 100,
              "color": "green"
            }
          ],
          "type": "line"
        }
      }
    }
  ]
}

最终效果是:

 

# 根据不同的条件触发不同的动作

  1. 可以通过框架提供的if能力,但那个意味着需要写两个,他控制粒度只能到brick级别
  2. 用script-brick提供的条件事件。如下,根据 url 参数是否具备fullscreen参数来做不同的渲染

 

以上就是今天关于优维低代码的分享,截止这一期基于构建框架的代码开发的所有内容都分享完了,希望对你有所收获!


有疑问加站长微信联系(非本文作者)

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

1012 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传