Skip to content
导航栏

在 Form 里嵌套 List 控件

List控件与Table控件的区别

  • List是一个纯前端的组件,数据来源于value属性,可以直接使用Formwiths关联表的数据,而Table的数据来源需要从服务器上读取。
  • ListForm里可进行/新增/删除/修改,而Table不能新增数据。

Step1 定义 List

/lists目录下新增一个List类型的定义,文件名格式是plan_item.list.json

LIST DSL在结构上大致与Table一样。需要配置fields.list,layout.list.columns

json
{
  "action": {
    "bind": {
      "model": "plan.item"
    }
  },
  "layout": {
    "list": {
      "columns": [
        { "name": "单品", "width": 8 },
        { "name": "计划数量", "width": 8 },
        { "name": "入库数量", "width": 8 }
      ]
    }
  },
  "fields": {
    "list": {
      "id": {
        "bind": "id",
        "edit": {
          "props": { "placeholder": "请输入 ID" },
          "type": "InputNumber"
        },
        "view": { "props": {}, "type": "Text" }
      },
      "单品": {
        "bind": "sku_id",
        "edit": {
          "props": {
            "itemProps": {
              "rules": [{ "required": true }]
            },
            "xProps": {
              "$remote": {
                "process": "yao.component.SelectOptions",
                "query": {
                  "model": "material.sku",
                  "label": "sku_sn",
                  "value": "id"
                }
              }
            }
          },
          "type": "Select"
        }
      },
      "入库数量": {
        "bind": "amount",
        "edit": {
          "props": {
            "placeholder": "只读字段",
            "disabled": true
          },
          "type": "InputNumber"
        },
        "view": { "props": {}, "type": "Text" }
      },
      "计划数量": {
        "bind": "amount_plan",
        "edit": {
          "props": {
            "itemProps": {
              "rules": [{ "required": true }]
            },
            "min": 0
          },
          "type": "InputNumber"
        },
        "view": { "props": {}, "type": "Text" }
      }
    }
  }
}

Step2 Form 配置

Form的配置文件中加一个字段配置。

字段的编辑控件的类型需要设置为List

编辑属性里需要把name设置成上面配置list的名称。

字段绑定的字段的类型需要是json,比如这里绑定的字段items的类型需要是一个json list

可选配置"showLabel": true,List控件里的字段显示标签

jsonc
{
  "fields": {
    "form": {
      "物资清单": {
        "bind": "items",
        "edit": {
          "type": "List",
          "props": {
            "name": "plan_item", //重要
            "showLabel": true
          }
        }
      }
    }
  }
}

完成以上的操作,就可以在 Form 看到一个列表控件。控件中的行项目可以拖动调整顺序。

list in form

Step3 保存数据。

你可能会遇到这个问题

List 删除项目后,列表被清空

List控件的数据保存操作需要在FormHook里进行拦截处理。

Form里使用save事件的Hook

/yao-wms/forms/plan.form.json

json
{
  "name": "计划",
  "action": {
    "bind": {
      "model": "plan",
      "option": {
        "withs": {
          "items": {}
        }
      }
    },
    "save": {
      "process": "scripts.plan.Save" //保存在这里
    },
    "before:delete": "scripts.plan.BeforeDeletePlan" //删除在这里
  }
}

处理脚本:/yao-wms/scripts/plan.js,详细逻辑请查注释。

如果是FormList控件的数据是主从表关系,需要考虑联动删除与保存逻辑。

js
/**自定义保存数据函数 */
function Save(data) {
  //先保存主表,获取id后再保存从表
  var id = Process('models.plan.Save', data);
  AfterSave(id, data);
}

/**
 * 更新 Items
 */
function AfterSave(id, payload) {
  var items = payload.items || {};
  var deletes = items.delete || [];
  //当有删除项目时,数据保存在items.data里
  //如果没有删除项目,项目items
  var data = items.data || items || [];

  if (data.length > 0 || deletes.length > 0) {
    // 忽略实际数据 ( 通过 record 计算获取)
    for (var i in data) {
      delete data[i].amount;
      if (typeof data[i].id === 'string' && data[i].id.startsWith('_')) {
        //新增项目,在前端会生成唯一字符串,
        //由于后台使用的自增长ID,不需要生成的唯一字符串,由数据库生成索引
        delete data[i].id;
      }
    }

    // 保存物品清单
    var res = Process('models.plan.item.EachSaveAfterDelete', deletes, data, {
      plan_id: id,
    });
    if (res.code && res.code > 300) {
      console.log('Plan:AfterSave Error:', res);
      return id;
    }
  }

  // 生成计划标签
  var plan_sn = payload['plan_sn'];
  if (!plan_sn) {
    return Process('models.plan.Save', { id: id, plan_sn: MakeSN(id) });
  }

  return id;
}

/**
 * 生成计划标签 (1+id)
 * 标签: 类目(6)-SKU(8)-**计划(6)**-Item(9)
 *      100001 20000001 000000 400000001
 */
function MakeSN(id) {
  var sn = id.toString();
  return sn.padStart(6, '0');
}

数据删除

数据的删除需要考虑表格数据批量删除与表单单个删除。

js
/**
 * 关联表删除
 * before Delete Plan
 * @param {number} id Plan id
 */
function BeforeDeletePlan(id) {
  // console.log("delete Plan with id:", id);
  let rows = Process('models.plan.item.DeleteWhere', {
    wheres: [{ column: 'plan_id', value: id }],
  });

  //remembe to return the id in array format
  return [id];
}

/**
 * 关联表批量删除
 * before Delete Plan Batch
 * @param {array} param0 Plan object
 */
function BeforeDeletePlanIn({ wheres }) {
  let array = wheres[0].value || [];
  array.forEach((element) => {
    BeforeDeletePlan(element);
  });

  return array;
}

完整的例子

请查看项目源代码:yao-wms