【Ant design vue】antd实现动态增减表单项,支持赋初始值

封装组件

基于官网最朴实无华的例子,做了一下延伸。封装的组件,支持一下功能:

  • 一行可有多个表单项
  • 表单项可赋初始值(如:应用在编辑场景!)
  • 可在一个页面上多次复用该组件!
<template>
  <div>
    <div class="dynamic-wrap">
      <div v-for="item in keysList" :key="item">
        <a-row :gutter="24">
          <a-col :xs="24" :sm="12">
            <a-form-item label="姓名" :labelCol="labelCol" :wrapperCol="wrapperCol">
              <a-input
                placeholder="请填写姓名"
                v-decorator="[
                  `${title}Name[${item}]`,
                  {
                    initialValue: arr[item] ? arr[item].name : undefined,
                    rules: [
                      {
                        required: true,
                        pattern: /^(?:[\u3400-\u4DB5\u4E00-\u9FEA\uFA0E\uFA0F\uFA11\uFA13\uFA14\uFA1F\uFA21\uFA23\uFA24\uFA27-\uFA29]|[\uD840-\uD868\uD86A-\uD86C\uD86F-\uD872\uD874-\uD879][\uDC00-\uDFFF]|\uD869[\uDC00-\uDED6\uDF00-\uDFFF]|\uD86D[\uDC00-\uDF34\uDF40-\uDFFF]|\uD86E[\uDC00-\uDC1D\uDC20-\uDFFF]|\uD873[\uDC00-\uDEA1\uDEB0-\uDFFF]|\uD87A[\uDC00-\uDFE0])+$/,
                        message: '请填写正确的姓名!'
                      }
                    ]
                  }
                ]"
                style="width: 90%; margin-right: 8px"
                :maxlength="64"
              />
            </a-form-item>
          </a-col>
          <a-col :xs="24" :sm="12">
            <a-form-item label="出生日期" :labelCol="labelCol" :wrapperCol="wrapperCol">
              <a-date-picker
                placeholder="请选择出生日期"
                v-decorator="[
                  `${title}Birthday[${item}]`,
                  {
                    initialValue: arr[item] ? moment(arr[item].birthday, 'YYYY-MM-DD') : null,
                    rules: [{ required: true, message: '请选择出生日期!' }]
                  }
                ]"
                style="width: 90%; margin-right: 8px"
              />
            </a-form-item>
          </a-col>
          <a-col :xs="24" :sm="12">
            <a-form-item label="国籍" :labelCol="labelCol" :wrapperCol="wrapperCol">
              <a-select
                placeholder="请选择国籍"
                type="list"
                show-search
                :filter-option="filterOption"
                v-decorator="[
                  `${title}Country[${item}]`,
                  {
                    initialValue: arr[item] ? arr[item].country : undefined,
                    rules: [{ required: true, message: '请选择国籍!' }]
                  }
                ]"
                style="width: 90%; margin-right: 8px"
              >
                <a-select-option v-for="(item, index) in allCountry" :key="item.code">
                  {{ item.cnFullName }}
                </a-select-option>
              </a-select>
            </a-form-item>
          </a-col>
          <a-col :xs="24" :sm="12">
            <a-form-item label="身份证号" :labelCol="labelCol" :wrapperCol="wrapperCol">
              <a-input
                placeholder="请填写身份证号"
                v-decorator="[
                  `${title}IdNumber[${item}]`,
                  {
                    initialValue: arr[item] ? arr[item].idNumber : undefined,
                    rules: [
                      {
                        required: true,
                        pattern: /^[1-9]\d{5}(?:18|19|20)\d{2}(?:0[1-9]|10|11|12)(?:0[1-9]|[1-2]\d|30|31)\d{3}[\dXx]$/,
                        message: '请填写身份证号!'
                      }
                    ]
                  }
                ]"
                style="width: 90%; margin-right: 8px"
                :maxlength="18"
              />
            </a-form-item>
          </a-col>

          <div v-if="hasAddress">
            <a-col :xs="24" :sm="12">
              <a-form-item label="住址" :labelCol="labelCol" :wrapperCol="wrapperCol">
                <a-input
                  v-decorator="[
                    `${title}Address[${item}]`,
                    {
                      initialValue: arr[item] ? arr[item].address : undefined,
                      rules: [
                        {
                          required: true,
                          message: '请输入住址'
                        }
                      ]
                    }
                  ]"
                  placeholder=""
                  style="width: 90%; margin-right: 8px"
                  :maxlength="128"
                />
              </a-form-item>
            </a-col>
          </div>
          <div v-else>
            <a-col :xs="24" :sm="12">
              <a-form-item label="股权百分比(%)" :labelCol="labelCol" :wrapperCol="wrapperCol">
                <a-input
                  placeholder="请填写股权百分比(%)"
                  v-decorator="[
                    `${title}EquityPercentage[${item}]`,
                    {
                      initialValue: arr[item] ? arr[item].equityPercentage : undefined,
                      rules: [{ required: true, pattern: /^\d{1,}$/, message: '请填写正确的股权百分比(%)!' }]
                    }
                  ]"
                  style="width: 90%; margin-right: 8px"
                  :maxlength="3"
                />
              </a-form-item>
            </a-col>
          </div>
          <a-col :xs="24" :sm="24" :offset="3">
            <a-form-item :labelCol="labelCol" :wrapperCol="wrapperCol">
              <a-button
                type="danger"
                ghost
                class="deleteRowBtn"
                v-if="keysList.length > 1"
                @click="() => removeRow(item)"
                icon="delete"
              >
                删除
              </a-button>
            </a-form-item>
          </a-col>
        </a-row>
      </div>
      <a-row>
        <a-col :xs="24" :sm="24" :offset="3">
          <a-form-item>
            <a-button type="dashed" class="addRowBtn" @click="addRow" icon="plus">新增</a-button>
          </a-form-item>
        </a-col>
      </a-row>
    </div>
  </div>
</template>

<script>
import moment from 'moment'
import { AllCOUNTRY } from './country-constant'
export default {
  name: 'DynamicForm',
  props: {
    title: {
      type: String,
      default: ''
    },
    hasAddress: {
      type: Boolean,
      default: false
    },
    arr: {
      type: Array,
      default: function() {
        return []
      }
    }
  },
  data() {
    return {
      labelCol: {
        xs: { span: 24 },
        sm: { span: 6 }
      },
      wrapperCol: {
        xs: { span: 24 },
        sm: { span: 16 }
      },
      allCountry: AllCOUNTRY,
      id: 0,
      keysList: [],
      moment
    }
  },
  created() {
    this.form = this.$form.createForm(this)
    this.init()
  },
  methods: {
    // 初始化
    init() {
      const arr = [0]
      if (this.arr.length !== 0) {
        for (let i = 1; i < this.arr.length; i++) {
          arr.push(i)
          this.id = this.id + 1
        }
      }
      console.log(this.arr)
      this.keysList = arr
    },
    // 移除某行
    removeRow(k) {
      if (this.keysList.length === 1) {
        // 如果存在可以移除所有行的情况,把条件改为this.keysList.length === 0即可
        return
      }
      this.keysList = this.keysList.filter(item => item !== k)
    },
    // 新增一行
    addRow() {
      this.id = this.id + 1
      this.keysList = this.keysList.concat(this.id)
    },
    filterOption(input, option) {
      return option.componentOptions.children[0].text.toLowerCase().indexOf(input.toLowerCase()) >= 0
    }
  }
}
</script>
<style lang="less" scoped>
.dynamic-wrap {
  padding-top: 10px;
}
.minusRowBtn {
  color: #f5222d;
  background: #fff1f0;
  border-color: #ffa39e;
  padding-right: 7px;
  padding-left: 7px;
  height: 29px;
  margin-left: 10px;
}
.deleteRowBtn {
  width: 120%;
  margin-left: -6px;
}
.addRowBtn {
  width: 80%;
  color: #1890ff;
  border-color: #91d5ff;
  margin-left: -3px;
}
</style>

组件的使用

<template>
  <div class="padding: 20px;">
    <a-form :form="form">
      <dynamic-form
        :title="`${PARTONE}`"
        :arr="arr"
      />
    </a-form>
    <a-button style="margin-top: 25px;" type="primary" @click="handleSubmit">
      提交
    </a-button>
    <div style="margin-top: 15px;">{
   { param.field }}</div>
  </div>
</template>

<script>
import DynamicForm from './DynamicForm'
const PARTONE = 'partOne'

export default {
     
  name: 'DynamicFormWrap',
  components: {DynamicForm },
  data () {
    return {
      form: this.$form.createForm(this),
      arr: [ // 模拟从接口获取到的数据(如编辑场景)
        {
           birthday: "2021-05-16T16:00:00.000Z",
           country: "CN",
           equityPercentage: "2",
           idNumber: "110101199003079913",
            name: "深谷",
       }],
      PARTONE,
      param: {
        field: '' // 模拟接口接收的参数
      }
    }
  },
  methods: {
    // 提交
    handleSubmit () {
      const {form: {validateFields } } = this
      validateFields((errors, values) => {
     
        if (!errors) {
         const partOneArr = []
         values[`${PARTONE}Name`].forEach((item, index) => {
            const obj = {
              name: values[`${PARTONE}Name`][index],
              birthday: values[`${PARTONE}Birthday`][index],
              country: values[`${PARTONE}Country`][index],
              idNumber: values[`${PARTONE}IdNumber`][index],
              equityPercentage: values[`${PARTONE}EquityPercentage`][index]
            }
            partOneArr.push(obj)
          })  
         this.param.field = JSON.stringify(partOneArr)   
         }
      })
    }
  }
}
</script>
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,670评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,928评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,926评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,238评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,112评论 4 356
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,138评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,545评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,232评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,496评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,596评论 2 310
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,369评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,226评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,600评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,906评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,185评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,516评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,721评论 2 335

推荐阅读更多精彩内容