一次偶然的机会,让我遇见了amis之排错总结(二)

2,444 阅读9分钟

书接上回,万字总结。

表单请求注意事项

发送请求时, 表单默认会将当前数据域中的数据都会被携带。

表单项的addOn属性的局限性,及如何替代

谈到这个属性,真的说他方便呢,他却是方便,因为只需要配置一些属性就会有一个想要的功能。但是这个addOn属性最大的不足就是触发事件的时候,获取表单项的值依旧是原来旧的值,只有改变当前表单项的时候才获取的是最新值。

目前有一个需求就是,获取其他表单项的值然后发送请求,将数据会写到当前表单项中,通过addOn是完成不了的。

这里介绍一下自己的试错过程。

// 改变其他表单项的值时,更新当前表单项获取的数据。
onEvent: {
  change: {
    actions: [
      {
        actionType: "setValue",
        // 当前表单项需要通过service组件包裹。
        componentId: "serviceAppId",
        args: {
          value: {
            s: "${event.data.value}",
            g: "${g}"
          }
        }
      }
    ]
  }
}


{
    type: "service",
    id: "serviceAppId",
    className: "edit-form-service",
    // "api": {
    //   "method": "post",
    //   "url": "",
    //   data: {
    //     simplename: "${simplename}",
    //     gametype: "${gametype}"
    //   }
    // },
    // // 不初始化
    // initFetch: false,
    body: [
      {
        type: "input-text",
        name: "id",
        // 我们可以通过将回写的数据定位默认值,重新生成的数据定为name属性值
        "label": "id",
        // "addOn": {
        //   "type": "button",
        //   "label": "生成Id",
        //   onClick: `
        //     console.log(props.data)
        //   `
        //   // actionType: "ajax",
        //   // "api": {
        //   //   "method": "post",
        //   //   "url": "",
        //   //   data: {
        //   //     s: "${s}",
        //   //     g: "${g}"
        //   //   }
        //   // }
        // }
      },
      {
        type: "button",
        label: "生成id",
        onClick: `
            console.log("++++++++===", props) // 这里打印发现获取的还是以前的值
            if(props.data.s && (props.data.g || props.data.g == 0)){
              props.onAction(event, 
                {
                  actionType: "ajax",
                  api: {
                    method: "post",
                    url: "",
                    data: {
                      s: props.data.s,
                      g: props.data.g
                    }
                  }
                }
              )
            }else {
              console.log("======", props)
            }
          `
      }
    ]
},

这样并没有做到更新数据。所以这样方法行不通。于是就想着通过自定义事件,也就是上面的onClick来尝试一下,发现依旧不可以。所以放弃了。

于是自己写了一个demo来测试一下,发现单个button组件点击时是可以获取到最新的表单项的值的。所以就有了这个想法。

这里使用onClick属性的好处还有一个,就是发送的表单项数据是必须携带的,所以可以通过js做验证提示。

{
    type: "wrapper",
    // 这里需要通过css调整一下布局
    className: "edit-form-wrapper",
    body: [
      {
        type: "input-text",
        name: "id",
        "label": "Id",
      },
      {
        type: "button",
        label: "生成Id",
        // 这里需要通过css调整一下布局
        className: "edit-button",
        // 需要注意的是,这里不能使用魔法字符串。因为${}获取变量的值和js在``获取变量的值冲突,会报错。所以需要直接使用""来设置。
        onClick: `
            if(props.data.s && (props.data.g || props.data.g == 0)){
              props.onAction(event, 
                {
                  actionType: "ajax",
                  api: {
                    method: "post",
                    url: "",
                    data: {
                      s: props.data.s,
                      g: props.data.g
                    }
                  }
                }
              )
            }else {
              console.log("======", props)
              // 这里提示用户哪些内容没有输入,做请求验证的
            }
          `
      }
    ]
  },

即使实现了功能,但是也还需要调整一下布局,然后会很难看

.edit-form-wrapper {
  margin-bottom: 24px;
  padding: 0 !important;
  position: relative;
}

.edit-form-wrapper .edit-button {
  position: absolute;
  right: 178px;
  top: 0px;
}

多个表单项合并为一个大的表单项

image.png

  {
    type: "wrapper",
    // 这个是调整布局样式的
    className: "form-wrapper-reset-lg",
    body: [
      {
        type: "tpl",
        tpl: "充当大分类的标题"
      },
      {
        "name": "",
        "type": "list-select",
        "optionType": "button",
        // 加上他表示单选,并且可以取消选择
        "clearable": true,
        label: '',
        "options": [
          {
            "label": "",
            "value": "1"
          }
        ]
      },
      {
        "name": "",
        "type": "list-select",
        "optionType": "button",
        // 加上他表示单选,并且可以取消选择
        "clearable": true,
        label: ' ',
        "options": [
          {
            "label": "",
            "value": "1"
          }
        ]
      },
      {
        "name": "service",
        "type": "list-select",
        // 加上他表示单选,并且可以取消选择
        "clearable": true,
        "optionType": "button",
        label: '',
        "options": [
          {
            "label": "",
            "value": "1"
          },
          {
            "label": "",
            "value": "2"
          },
          {
            "label": "",
            "value": "3"
          }, 
          {
            "label": "",
            "value": "4"
          }
        ]
      }
    ]
  },
    /* 表单item内wrapper样式 */
    .form-wrapper-reset-lg {
      padding: 0 !important;
      margin-bottom: 24px !important;
    }
    .form-wrapper-reset-lg  > .antd-TplField {
      max-width: 33.3333333333%;
      display: block;
      text-align: right;
      margin-bottom: 10px;
    }

    .form-wrapper-reset-lg  > .antd-TplField::after {
      content: "";
      padding: 6px;
      font-size: 0;
      width: 0;
      height: 0;
    }

表单项中的选项需要分组展示 (重点)

以前是通过wrapper包裹,然后通过list-select组件来做的(这个组件交互体验比checkboxes要好)。但是这种就把该选项框分成了若干个字段。需要后端提供分组个数的字段,这样显然不符合我们想要的需求。

  {
    type: "wrapper",
    className: "form-wrapper-reset-lg",
    body: [
      {
        type: "tpl",
        tpl: "该表单项的标题"
      },
      {
        "name": "realAccount",
        "type": "list-select",
        "optionType": "button",
        // 多选
        // multiple: true,
        // 加上他表示单选,并且可以取消选择
        "clearable": true,
        label: '账号: ',
        "options": [
          {
            "label": "",
            "value": "1"
          },
          {
            "label": "",
            "value": "2"
          }
        ]
      },
      {
        "name": "realGuess",
        "type": "list-select",
        "optionType": "button",
        // 多选
        // multiple: true,
        // 加上他表示单选,并且可以取消选择
        "clearable": true,
        label: ': ',
        "options": [
          {
            "label": "",
            "value": "4"
          },
          {
            "label": "",
            "value": "8"
          }
        ]
      }
    ]
  },

需要调整样式

    /* 表单item内wrapper样式 */
    .form-wrapper-reset-lg {
      padding: 0 !important;
      margin-bottom: 24px !important;
    }
    .form-wrapper-reset-lg  > .antd-TplField {
      max-width: 33.3333333333%;
      display: block;
      text-align: right;
      margin-bottom: 10px;
    }

    .form-wrapper-reset-lg  > .antd-TplField::after {
      content: "";
      padding: 6px;
      font-size: 0;
      width: 0;
      height: 0;
    }

这种就是上面(多个表单项合并为一个大的表单项)介绍的。

但是我们需要提供一个字段来做到分组展示。这里我们可以使用两种方式

  • select + mode: "group"
  • checkboxes + optionType: "button"

这两种方式都可以完成我们的需求,区别就在于

  • select体验不是很好,他是提供的下拉菜单展示的,不能立刻见到item选项。但是他的样式不需要我们调整。
  • checkboxes这个体验很好,但是官方设置的样式不好看,需要我们自己调整样式。 image.png
    /* 调整编辑框中复选框的样式 */
    .edit-form-checkboxes .antd-CheckboxesControl-group .antd-CheckboxesControl-groupLabel {
      font-size: var(--fontSizeMd);
      margin-bottom: 10px;
      margin-top: 2px;
    }

    .edit-form-checkboxes .antd-CheckboxesControl.is-inline .antd-Checkbox--button {
      margin-bottom: 10px;
      margin-right: 10px;
    }

    .edit-form-checkboxes .antd-Checkbox--button.antd-Checkbox--checkbox {
      padding-right: 0;
    }

    .edit-form-checkboxes .antd-Checkbox.antd-Checkbox--checkbox.antd-Checkbox--button > i + span {
      padding: 0 var(--Checkbox-paddingX) 0 0;
    }

调整样式后,让他更接近ist-selectimage.png

二者都不能满足的要求:如果设置了mutiple: true开启多选时,它并不能做到每个分组。只能设置该checkboxes全部item的多选和单选。所以需要我们通过每个label来提示用户当前分组的多选还是单选。这样还是做不到限制。

如何获取后端返回一个字段内部属性,但是字段值是一个json类型字符串

通过内部过滤器来解决,因为不能使用JSON.parse

    // $jsonObject | toJson} 将其转为对象
    // ${$jsonObject | toJson}.name} 对象中取出name值
    data: {
      appregurl: "${$jsonObject | toJson}.name}"
    },

注意,之所以我们把它提取到data中,是因为我们在编辑表单中不能直接转换获取,界面会卡死。但是在crud列表中就可以直接使用。 image.png

如果编辑表单回写的字段名和保存编辑后传递给后端的字段不一样时

    // 当字段传递不一样时,我们可以通过value属性来映射回写的字段名
    {
         name: "Key",
         value: "${key}"
    }

定制dialog下方的actions 按钮

默认情况下,定制的按钮,执行完逻辑后不会关闭弹框。需要配置close: true来让其关闭。

  {
    "label": "点击",
    "type": "button",
    "actionType": "dialog",
    loading: false,、
    // 控制显影可以写js表达式
    visibleOn: "status != 100",
    "dialog": { 
      "title": "弹框",
      closeOnOutside: true,
      size: "xs",
      body: "弹框吗?",
      actions: [
        // 点击取消关闭弹框
        {
          "type": "button",
          "actionType": "close",
          "label": "取消"
        },
        // 点击确定,请求数据,然后更新页面,然后关闭弹框
        {
          "type": "button",
          "actionType": "ajax",
          "label": "确定",
          // 请求完数据后关闭dialog
          "close": true,
          level: "primary",
          api: {
            url: "",
            method: "post",
            dataType: "form-data",
            data:{
            }
          },
          // 请求成功后刷新页面
          "reload": "CrudName"
        }
      ]
    }
  },

处理返回的不符合预期的数据

下面是后端返回的select不符合预期,我们需要修改返回字段。

source: {
  url: "",
  method: "post",
  data: {},
  // 处理不符合预期的数据
  "responseData": {
    // "&": "$$",
    // items表示从哪个字段取值
    // items 是因为数据直接放在了 data 中,如果是放在其他字段中就换成对应的字段名。
    "options": "${items|pick:label~name,value~account}"
  }
},

编辑列表项时,如果不需要编辑其中的表单项,但是需要提交字段

当我们编辑某个列表项时,单个列表项的字段有时候不需要编辑,但是需要传递给后端。这时候我们分两种情况。

  • 自己写传递的接口数据。这时候我们可以获取点击当前表单项的所有字段了。
api: {
  url: "",
  method: "post",
  // 这里需要我们自己写出传递给后端的字段,可以获取crud当前行的所有属性
  data:{
    id: "${id}",
    name: "${name}",
    age: "${age}",
  },
},
  • 直接让amis获取当前表单中的所有字段,然后传递给后端。这时候我们就必须将不需要修改的字段也设置成一个表单项了。要不然该字段将不会传递给后端。我们只需设置隐藏属性就行hidden: true
  {
     type: "input-text",
     name: "id", 
     hidden: true,
  }
 api: {
  url: "",
  method: "post",
  // 这里直接写获取全部表单项,或者不设置data
  data: {
    "&": "$$"
  },
},

对于表单项项特别多的表单modal,该如何处理呢

  • 设置"columnCount": 2表单属性,或者分组group组件。这样设置有一个确定,就是表单项层次不齐,需要将相同改格式(输入框,下拉框放一行;按钮分组选择器等放一行;即就是相同高度的内容放一行不然布局会很难看)。 image.png
  • 在form表单中设置flex布局,来让表单项两列显示。这样就好看多了。
"body": [{
  type: "flex",
  alignItems: "flex-start",
  justify: "space-between",
  items: [
    {
      type: "wrapper",
      style: {
        flex: "0 0 50%"
      },
      body: [
        {
          type: "input-text",
          name: "b", 
          "label": "B",
          required: true, 
          // 表单验证规则
          validations: {
            isRequired: true
          },
          // 验证错误是提示的内容
          "validationErrors": {
            "isRequired": "请填写b"
          },
        }
      ]
    },
    // 另一半
    {
      type: "wrapper",
      style: {
        flex: "0 0 50%"
      },
      body: [
        {
          type: "select",
          name: "a",
          label: "A",
          options: [
            {
              label: '否',
              value: 0
            },
            {
              label: '是',
              value: 1
            }
          ]
        }
      ]
    }
  ]
}
]

image.png 虽然两边表单项不会对齐,但是中间不会出现大片空白了。

json组件作为编辑表单的表单项

这里碰见了一个问题,提交的时候可能会出现多余的字符串包裹。 image.png

  {
    type: "wrapper",
    className: "edit-form-json",
    body: [
      {
        type: "tpl",
        tpl: "label"
      },
      {
        "type": "json",
        "name": "jsonMultiple",
        "ellipsisThreshold": 60,
        label: "a",
        "mutable": true,
        "jsonTheme": "eighties",
      }
    ]
  },

样式, 表单size为lg时。

    /* 编辑表单中json组件的样式 */
    .edit-form-json {
      display: flex;
      padding: 0 !important;
      margin-bottom: 24px;
    }

    .edit-form-json .object-key-val {
      padding: 20px;
    }

    .edit-form-json .antd-TplField {
      display: inline-block;
      font-size: var(--Form-item-fontSize);
      width: 33.33%;
      text-align: right;
      position: relative;
      right: 10px;
    }

    .edit-form-json .antd-JsonField {
      margin-left: 6px;
      flex: 0 0 49%;
    } 

由app组件侧边导航栏集成在app组件中的问题

因为他的父label和子label没有层级关系所以不可以设置折叠当前导航。如果对于一个管理系统导航特别多的时候,它将特别难找到目标导航。

又因为导航栏是集成在app组件,没有暴露出扩展的接口,所以我们不能直接在导航栏本身做修改。

这里我们采用一种会话框展示所有导航的方式折中。

当时想着在dialog组件中使用nav组件来达到效果。但是把nav组件设置为横向展示的时候,布局出现了大的问题。所以放弃了。

"stacked": false,横向展示,对于多级导航,布局会出现问题。 image.png

然后就想着使用tabs组件来做展示。处理好数据后,本来想打算把tabs的body属性做一个整体展示时,但是他好像不支持获取变量的方式。最后会把body数组中的每一项展示成object Object。发现只有可以获取变量的属性才可以通过${}取值,body不可以。

然后就想着自定义tabs组件的body属性。

处理数据


(function() {
  const menuList = getItem("menuList")
  window.roleMenuList = []
  for(let i = 1; i < menuList.length; i++) {
    if(menuList[i].children) {
      roleMenuList[i] = {
        title: menuList[i].label,
        body: []
      }
      for(let btnElement of menuList[i].children) {
        roleMenuList[i].body.push({
          type: "button",
          level: "primary",
          actionType: "link",
          link: btnElement.url,
          label: btnElement.label,
          icon: btnElement.icon,
          close: true,
          ...btnElement
        })
      }
    }else {
      roleMenuList[i] = {
        title: menuList[i].label,
        body: []
      }
    }
  }

  window.roleMenuList = roleMenuList.filter(item => item)
})()

组件封装

"type": "tabs",
"tabsMode": "simple",
className: "menu-list-tabs",
source: "${window:roleMenuList}",
tabs: [
  {
    title: "${title}",
    body: [
      // {
      //   "type": "list-select",
      //   "name": "select",
      //   "label": "",
      //   "clearable": true,
      //   source: "${body}",
      //   onClick: "console.log('dwadwadwa')"
      // }
      {
        type: 'custom',
        name: 'myName',
        label: '自定义组件',
        onMount: (dom, value, onChange, props) => {
          let btns = "";
          for(let element of props.data.body) {
            btns += `
              <button 
                class="antd-Button antd-Button--primary antd-Button--sm" 
                data-link=${element.url}
                >
                  ${element.label}
                </buttonclass=data-link=$>
            `
          }
          // 事件委托
          dom.onclick = event => {
            // 获取link,跳转
            const link = event.target.dataset.link;
            // document.getElementsByClassName("menu-list-dialog")[0].setAttribute("class", "amis-dialog-widget antd-Modal antd-Modal--lg menu-list-dialog antd-Modal--1th hidden")
            // document.getElementsByClassName("menu-list-dialog")[0].remove()
            history.push(link)
            // 这个属实没想到,关闭dialog组件
            props.onAction(
              event,
              {
                type: 'action',
                label: '',
                close: true
              }
            )
          };
          dom.innerHTML = btns;
        },
        // 这个组件好像就没有销毁
        onUnmount: (props) => {
          console.log("销毁")
          // document.getElementsByClassName("menu-list-dialog")[0].setAttribute("class", "amis-dialog-widget antd-Modal antd-Modal--lg menu-list-dialog antd-Modal--1th")
        }
      }
    ]
  }
]

上面的这种是一个方法,但是今天去看官网发现,只有连续嵌套三层label才会有折叠的效。

"pages": [
    {
      "url": "/",
      "redirect": "/user"
    },
    {
      "label": "A",
      className: "is-unfolded has-sub",
      "children": [
        {
          "label": "A1",
          "url": "/a1",
          "icon": "fa fa-table",
          "schemaApi": "jsonp:/pages/A1/A1.js?callback=jsonpCallback"
        },
        {
          "label": "A2",
          "url": "/a2",
          "icon": "fa fa-times",
          "schemaApi": "jsonp:/pages/A2/A2.js?callback=jsonpCallback"
        }
      ]
    }
]
// 变成这样
"pages": [
    {
      "url": "/",
      "redirect": "/user"
    },
    {
     + label: "",
     + children: [
     +   {
          "label": "A",
          className: "is-unfolded has-sub",
          "children": [
            {
              "label": "A1",
              "url": "/a1",
              "icon": "fa fa-table",
              "schemaApi": "jsonp:/pages/A1/A1.js?callback=jsonpCallback"
            },
            {
              "label": "A2",
              "url": "/a2",
              "icon": "fa fa-times",
              "schemaApi": "jsonp:/pages/A2/A2.js?callback=jsonpCallback"
            }
         ]
      +  }
      + ]
    }
 ]

根据不同的选择框,切换不同的请求地址

目前有一个需求,就是根据用户选择的不同的select元素,然后列表(crud)请求的路径将不同。

    data: {
      type: "page",
      body: {
        type: "form",
        id: "valicodeCrudId",
        debug: true,
        data: {
          url: "/api/test/url1"
        },
        api: {
          url: "${url}",
          method: "get"
        },
        body: [
          {
            type: "select",
            label: "请选择",
            name: "select",
            options: [
              {
                value: 1,
                label: "url1",
                url: "/api/test/url1"
              },
              {
                value: 2,
                label: "url2",
                url: "/api/test/url2"
              }
            ],
            onEvent: {
              change: {
                actions: [
                  {
                    actionType: "setValue",
                    componentId: "valicodeCrudId",
                    args: {
                      value: {
                        url: "${event.data.options[event.data.value - 1].url}",
                      }
                    }
                  },
                  // {
                  //   actionType: "toast",
                  //   args: {
                  //     msg: "${event.data | json}"
                  //   }
                  // }
                ]
              }
            }
          }
        ]
      }
    },

image.png image.png

button组件reload属性的问题 (crud与表单的联动暂时还没有找到解决办法)

今天用这个属性联动crud查出的数据和其他表单组件进行数据联动。

出现的问题就是,我必须得请求两次crud接口,才会把数据填充到下方的表单中。

下面这个案例还原了项目中的全部情况,initFetch如果设置为true,那么他只需要点击一下就行,因为我们在加载这个页面的时候就已经请求了一次crud的接口,所以还是请求的两次。这样解决效果并不是很好,因为进入页面就已经拿到了列表数据。

    data: {
      type: "page",
      body: [
        {
          type: "crud",
          api: {
            method: "get",
            url: "/api/test/crudReloadForm"
          },
          initFetch: false,
          filter: {
            debug: true,
            body: [
              {
                type: "input-text",
                label: "aaaaaa",
                name: "wwws"
              },
              {
                type: "submit",
                label: "测试reload",
                reload: "formSecond?zh=${items[0]}"
              }
            ]
          },
          columns: [
            {
              name: "a",
              label: "a title"
            },
            {
              name: "b",
              label: "b title"
            },
            {
              name: "c",
              label: "c title"
            }
          ]
        },
        {
          type: "flex",
          items: [
            {
              type: "form",
              name: "formSecond",
              body: [
                {
                  type: "input-text",
                  label: "pppppp",
                  name: "zh.b"
                },
              ],
              actions: [
                {
                  type: "button",
                  label: "提交",
                  actionType: "ajax",
                  api: "/api/test/addtableitem",
                }
              ]
            }
          ]
        }
      ]
    },

button-reload.gif crud与form表单的联动暂时还没有找到解决办法。

下面我们再来看看两个表单直接通过reload来联动数据会发生什么情况。

通过下方的gif,显然form中通过reload联动数据是不会出现问题的,只需要请求一次form接口就行。

    data: {
      type: "page",
      body: [
        {
          type: "form",
          api: {
            url: "/api/test/addtableitem",
            method: "get"
          },
          body: [
            {
              type: "input-text",
              label: "pppppp",
              name: "p"
            },
            {
              type: "submit",
              label: "测试reload",
              reload: "formSecond?result=${result}"
            }
          ],
        },
        {
          type: "flex",
          items: [
            {
              type: "form",
              name: "formSecond",
              body: [
                {
                  type: "input-text",
                  label: "rrrrrr",
                  name: "result"
                },
              ]
            }
          ]
        }
      ]
    },

form-button-reload.gif

crud与form表单的联动解决办法

这种更新问题,可能是amis库底层的问题,我们在实现的时候不可能去解决这个bug,所以我们将舍弃crud组件,将采用form + table组件代替crud组件,实现自己的crud组件。

    data: {
      type: "page",
      body: [
          {
          type: "form",
          className: "form-filter-style",
          title: "",
          // debug: true,
          api: {
            url: "/api/test/crudReloadForm",
            method: "get"
          },
          body: {
            type: "group",
            body: [
              {
                size: "md",
                type: 'input-text',
                name: 'a',
                placeholder: '请填写',
                value: "a",
                clearable: true,
              },
              {
                type: "button",
                actionType: "reset",
                label: "重置"
              },
              {
                type: "submit",
                level: "primary",
                label: "查询",
                reload: "formSecond?items=${items[0].a},tableName?items=${items}"
              }
            ],
          },
          // 自定义搜索按钮
          actions: []
        },
        {
          "type": "service",
          name: "tableName",
          body: [
            {
              type: "table",
              name: "tableName",
              source: "$items",
              columns: [
                {
                  name: "a",
                  label: "a title"
                },
                {
                  name: "b",
                  label: "b title"
                },
                {
                  name: "c",
                  label: "c title"
                }
              ],
              "placeholder": "您还没有创建任何实例"
            }
          ]
        },
        {
          type: "form",
          name: "formSecond",
          body: [
            {
              type: "input-text",
              label: "pppppp",
              name: "items"
            },
          ]
        }
      ]
    },

form-table-button-reload.gif

在crud组件中添加item时,阻止数据域中的数据会写在表单中

默认情况下,我们的数据都会回写在dialog弹出的表单中,不管是新增还是编辑。显然,编辑时就应该这样做。但是新增的时候需要禁止这种数据回写。

所以我们就可以这样。主要就是在dialog中添加这个数据。

// 移除crud数据域中的数据回写。
data: {
    "&": "$$", 
    status: "__undefined"
},
{
    label: '添加',
    type: 'button',
    actionType: 'dialog',
    level: 'primary',
    dialog: {
      title: '添加',
      className: 'dialog-title',
      closeOnOutside: true,
      size: 'md',
      // 移除crud数据域中的数据回写。
      data: {
        "&": "$$", 
        status: "__undefined"
      },
      body: {
        debug: true,
        type: 'form',
        labelWidth: 200,
        horizontal: {
          left: 4,
          right: 6,
        },
        body: [
          {
            type: "input-text",
            name: "id",
            label: "id",
          }
        ]
      }
    }
},

但是结合到form表单查询时,如果form表单有值,那么它也将会回写到添加表单中。所以这种方式还是有缺陷的,我们最好手动在当前表单组家中设置data属性来清空数据。 image.png

    {
        type: "form",
        data: {
            id: "",
            product: "",
            ...
        }
    }

对于配置disabled属性的表单项不生效的解决办法

我们可以设置css属性来达到效果。在对应的表单项上加上forbid-form-itemclass属性。

    /* 禁止表单项 */
    .forbid-form-item  input[name="a"],
    .forbid-form-item input[name="b"],
    .forbid-form-item input[name="c"],
    .forbid-form-item input[name="d"],
    .forbid-form-item input[name="e"],
    .forbid-form-item input[name="f"]
    {
      pointer-events: none !important;
    }

但是上面这种做法解决了不输入的问题,但是他是阻止了用户操作,所以也不能复制。

那么我们可以通过readOnly这个属性来做到。在表单组件中加上readOnly属性。

 {
      type: "input-text",
      label: "a",
      id: "a",
      className: "forbid-form-item",
      name: "appkey",
      // "disabled: true,
      readOnly: true,
 }

image.png

当我们通过visibleOn来做到控制表单按钮显隐时,点击回车后依旧会发送请求

以前是通过visibleOn来做页面内权限控制的,但是当我们隐藏按钮后,点击回车,依旧会发送请求,这是因为表单中默认点击回车会发送请求。我们可以通过该属性来阻止点击回车发送请求。

    preventEnterSubmit: true,

发送请求时不携带空数据字段

当我们有这个需求的时候,我们可以手动设置请求发送的字段,而不能通过"&": "$$"来获取发送的数据字段了

"data": {
    // "&": "$$",
    "myName": "${name|default:undefined}",
    "myEmail": "${email|default:undefined}"
  }

这样,当myName, myEmail字段为空的时候,将不会被携带发送。

处理一些不规范的数据

一般处理数据,都会使用这些方式。

  • 处理返回的数据

     responseData: {
         "options": "${items|pick:label~myLabel,value~myValue}"
     }
    
  • 请求拦截器。在这里也可以对一些数据做过滤。

     requestAdaptor: function (api) {
          return {
            ...api,
            data: {
              ...api.data, // 获取暴露的 api 中的 data 变量
              foo: 'bar' // 新添加数据
            }
          };
        }
    
  • 响应拦截器, 就很方便内部可以写js代码。

    adaptor: function (payload, response) {
          return {
            ...payload
          };
        }
    

    举个例子

    adaptor: function (payload, response) {
        for(let item of payload.data.items) {
            const appid = item.appid;
            for(let gameItem of getItem("gameList")) {
                if(appid == gameItem.value) {
                    item.appName = /(.+)(.+)/.exec(gameItem.name)[1]
                }
            }
        }
        return {
            ...payload
        };
    }
    
  • 设置请求的变化值

    url中依赖的值变化,默认会去请求接口。但是url参数以外的值变化将不会请求接口,如果想要添加依赖值,我们就可以设置该属性。注意多个依赖值可以使用逗号分隔。

     trackExpression: "${a},${b}"
    

    当我们依赖的值发生变化的时候,我们需要重新请求接口。这个属性可以设置依赖的值。

根据下拉菜单点击不同的item时,获取当前点击的item对象做一些处理

今天有一个需求,主要是根据当前点击的下拉菜单的item,来获取到当前item中的一些属性做表单展示。因为每个item的属性不同,所以我们需要拿到当前item来展示不同的表单内容。

首先我想到的是,通过监听change事件,然后设置setValue动作更新当前选中的item对象。这样我们拿到当前select对象和当前点击的value值,但是我们不能获取当前item对象。并且不能获取当前点击的index,这样也就不会获取当前item了。

既然上面的方法不行,那么我就自定义一个select组件吧。想着amis也可以作为组件库使用,然后调用Select组件时,报bind of undefined,于是就查询amis官网,然后就看见了这个onChange。其实当时也是使用了onChange事件,想着他和onClick一样,是通过设置字符串使用的,打印一些内容,没有响应,然后就放弃了。

正确的解决办法。

// 提前定义好值。
data: {
  jsonfields: ""
},

  {
    type: "select",
    name: "channelId", 
    label: "===",
    searchable: true,
    required: true, 
    // 表单验证规则
    validations: {
      isRequired: true
    },
    // 验证错误是提示的内容
    validationErrors: {
      isRequired: "请选择==="
    },
    source: {
      url: "",
      method: "get",
      data: {},
      // 处理不符合预期的数据
      responseData: {
        options: "${items|pick:label~channelName,value~channelId,fields~fields}"
      }
    },
    // 这个方法就是来处理数据的
    onChange: (...args) => {
      args[3].data.jsonfields = JSON.parse(args[2].selectedOptions[0]?.fields)
    }
  }

提交表单后,重置表单项

起初,想使用reset这个action,但是因为需要自定义提交按钮,所以触发ajax这个action后,再去执行reset这个action发现不起作用。

然后通过reset-and-submit这个action时,他不能获取表单项中的数据然后提交。

最终在form组件中找到下面这个属性来达到我们想要的结果。

    "resetAfterSubmit": true

设置表单验证不生效

发现这个原因其实也很奇怪的。因为需要控制动作权限,按钮的隐藏。所以需要自定义actions,然后type: button,然后设置的表单验证就不生效了。但是通过表单自己的提交按钮,他会生效。然后就想着把type: button设置成type: "submit",问题就解决了。

actions: [
    {
      // type一定要设置成submit
      type: "submit",
      label: "====",
      level: "primary",
      visibleOn: "showPermit('id')",
      actionType: "ajax",
      api: {
        url: "",
        method: "get",
        data: {
          a: "${a}",
          b: "${b}",
        },
      },
    },
]

dialog的反馈框的缺陷

dialog中的feedback属性,只有当dialog发送请求时,才会弹出feedback。

对于这个需求就无能为力了。就是对于点击一个按钮,然后弹出编辑表单,然后点击确认后进行二次确认是否要发送请求。这个需求是第一次点击按钮不回去发送请求,所以feedback将不会生效。

官网中的介绍:feedback 反馈弹框是指,在ajax请求后,可以显示一个弹框,进行反馈操作。

这个需求目前还没找到解决办法。

对于面包屑的显示隐藏

官网并没有介绍面包屑相关的属性。所以可以看源码中137行定义了三个关于面包屑的属性。

 /**

   * 显示面包屑路径。

   */

  showBreadcrumb?: boolean; // 默认值是true

  /**

   * 显示面包屑完整路径。

   */

  showFullBreadcrumbPath?: boolean;

  /**

   * 显示面包屑首页路径。

   */

  showBreadcrumbHomePath?: boolean;

对于登记比较少菜单,我们根本不需要设置面包屑,很难看。

image.png

crud列数据做脱敏处理

我们可以通过设置一个脱敏的字段来代替真实的字段值。

  data: {
      type: "page",
      data: {
        myItems: [
          {
            "id": 12222222222,
            sercet: "1222***2222"
          },
          {
            "id": 12131311313,
            sercet: "1213***1313"
          },
          {
            "id": 13143543656,
            sercet: "1314***3656"
            
          },
        ]
      },
      body: {
        type: "crud",
        source: "${myItems}",
        columns: [
          {
            // 列数据绑定脱敏后的值
            name: "sercet",
            label: "id",
            copyable: {
              content: "${id}"
            }
            // "type": "button",
            // label: "",
            // onClick: function(event, props) {
            //   props.onAction(event, {actionType:'copy', "content": "${id}"})
            // }
          },
          {
            type: "operation",
            label: "操作",
            buttons: [
              {
                type: "button",
                level: "link",
                label: "编辑",
                actionType: "dialog",
                loading: false,
                dialog: {
                  title: "修改",
                  closeOnOutside: true,
                  size: "md",
                  body: {
                    type: "form",
                    horizontal: {
                      left: 4,
                      right: 6
                    },
                    api: {
                      url: "/api/test/url1",
                      method: "post",
                      dataType: "form-data",
                      data:{
                        "&": "$$",
                      },
                    },
                    body: [
                      {
                        // 编辑这里还绑定原来的值。
                        name: "id",
                        type: "input-text",
                        label: "id",
                      },
                    ]
                  }
                }
              }
            ]
          }
        ]
      }
    },

image.png

在列中处理返回的数据

一个需求,就是后端返回的数据需要处理。

  • 我们可以通过响应拦截器来处理数据。(处理一个复杂的数据)
  • 使用type: static来自定义列组件渲染器。(处理简单数据,并且调用amis内置的函数处理)
    data: {
      type: "page",
      data: {
        myItems: [
          {
            time: "2018-07-23T14:19:25",
            num1: 1,
            num2: 2
          },
          {

            time: "2018-07-23T00:19:25",
            num1: 1,
            num2: 313
          },
          {
            time: "2018-07-23T03:19:25",
            num1: 1,
            num2: 43
            
          },
        ]
      },
      body: {
        type: "crud",
        source: "${myItems}",
        columns: [
          {
            label: "id",
            type: "static",
            tpl: "${SUM(num1, num2)}"
          },
        ]
      }
    },

image.png 这里有一个缺点,就是不能调用自定义函数来处理数据。

这里就有一个需求,就是后端返回的时间格式是utc格式,我们需要格式化为本地时间。这样我们定义一个函数,在${}中是不能使用的。所以就很无赖。

但是我们可以多次调用内置的函数处理

我们可以加一个8小时,然后再做转化。"${DATETOSTR(SUM(TIMESTAMP(time, 'x'), 28800000))}"

data: {
      type: "page",
      data: {
        myItems: [
          {
            time: "2018-07-23T14:19:25",
            num1: 1,
            num2: 2
          },
          {

            time: "2018-07-23T00:19:25",
            num1: 1,
            num2: 313
          },
          {
            time: "2018-07-23T03:19:25",
            num1: 1,
            num2: 43
            
          },
        ]
      },
      body: {
        type: "crud",
        source: "${myItems}",
        columns: [
          // 时间戳转本地时间
          {
            label: "id",
            type: "static",
            tpl: "${DATETOSTR(SUM(TIMESTAMP(time, 'x'), 28800000))}"
          },
        ]
      }
    },

image.png

目前想要解决的问题但是没有找到方法

定制一个没有太多内容的404页面。

image.png 404页面不是一个完整的,他只是在内容区域展示404。