动态表格与其校验

137 阅读2分钟

动态表格

框架(Vue2) 组件(ant-design-vue 1.7)

背景:

在实习期间,参与过很多的后台管理项目,有着这样的需求:实现表格的动态添加,且需要每一项都要能进行编辑与删除作用,且还需进行校验。

具体大概需求如下

image.png

实现手段

总体实现起来难度不是很大,本质上是使用框架table中的template

<template slot="name" scope="text,record">
     /* record 就是一行的数据 只需要将每一行数据和对应的input进行双向绑定 */
     /* slot所对应即是columns上的数据dataIndex */
     <a-input v-model="record.name" />
</template>

同理也能实现其他的表单形式

<template slot="name" scope="text,record">
     /* record 就是一行的数据 只需要将每一行数据和对应的input进行双向绑定 */
     /* slot所对应即是columns上的数据dataIndex */
     <a-select style="width: 100%" v-model="record.degree">
          <a-select-option v-for="item in selectList" :key="item.value" :value="item.value">
              {{item.label}}
          </a-select-option>
     </a-select>
</template>

现在实现数据上处理后 就要实现数据的增删操作 增加一行的操作直接在对应数据数组添加新一行的数据

<template slot="footer">
          <div style="padding: 5px;text-align: center;border: 1px solid blue" @click="addRow()">新增数据</div>
</template>

<script>
    /* 需要每一行数据的独一性 */
    import { v4 } from 'uuid'
    data() {
        return {
            initModel: {}
            validateModel: {},
        }
    } 

    
    addRow() {
      /* initModel 是我定义的初始化数据的对象  */
      /* 需要注意一点 现只是浅拷贝 如果initModel存在多层结构 可能造成所有数据都是公用同一个对象 */
      this.model.push({ id: v4(), ...this.initModel })
    },
</script>

至于删除操作等同于新增 就不一一写代码了(还是找到需要删除的行 在原数据数组进行修改就好

校验

先只能对每一行的数据都设置一个validate对象来控制当行数据 的某一项是不是满足要求的。

/* 在每个tmeplate设置一个动态的class 负责该数据错误时的显示 */
<template slot="name" scope="text,record">
    <a-input :class="{'has-error': record.validate.name }"  v-model="record.name"/>
</template>

<script>
   data() {
    return {
        initModel: {}
        validateModel: {},
    }
   }

   /* 在提交时候进行所有数据校验 */
   submit() {
      let val = true
      this.model.forEach(ele => {
        /* 当validate为false 不会去判断后面的 */
        val = validateObj(ele) && val
      })
      if (val) {
        alert('表单数据正常')
      }
    },
    
    /* 增加validate对象 */
    addRow() {
      this.model.push({ id: v4(), ...this.initModel, validate: { ...this.validateModel } })
    },
</script>

具体逻辑校验 先阶段只是对数据设置必填的做法(具体改动可根据具体业务

/**
 * 动态表格 校验数据
 * 
 * @param {Object} obj 
 * @returns 该表单值是否违反提交情况
 */
const validateObj = (obj) => {
    if (obj === null || obj === {}) {
        return
    }
    let res = true
    for (const key in obj.validate) {
        if (obj[key] === undefined || obj[key] === '' || obj[key] === null) {
            obj.validate[key] = true
            res = false
        }
        else {
            obj.validate[key] = false
        }
    }
    return res
}

显示效果: image.png 正确会显示为:

image.png

优化手段

现阶段很多逻辑都是学死 然后当然能通过配置项形式进行抽离 但基本的逻辑处理上大概如此 而且在表单的校验上不能像form组件一样直接在下面进行错误显示(比较可惜的地方)

样例代码

<template>
  <div class="dyamic-table">
    <div class="title">
      <h1>动态表格:</h1>
      <a-button @click="submit">校验</a-button>
    </div>
    <div class="content">
      <a-table 
        bordered 
        :scoll="1088" 
        :data-source="model" 
        :columns="columns" 
        rowKey="id"
      >
        <template slot="name" scope="text,record">
          <a-input :class="{'has-error': record.validate.name }" placeholder="请输入姓名" v-model="record.name"></a-input>
        </template>
        <template slot="age" scope="text, record">
          <a-input :class="{'has-error': record.validate.age }" placeholder="请输入年龄" v-model="record.age"></a-input>
        </template>
        <template slot="degree" scope="text, record">
          <a-select :class="{'has-error': record.validate.degree }" placeholder="请选择" style="width: 100%" v-model="record.degree">
            <a-select-option v-for="item in selectList" :key="item.value" :value="item.value">{{item.label}}</a-select-option>
          </a-select>
        </template>
        <template slot="action" scope="text, record">
          <div style="display: flex; justify-content: space-around;">
            <a @click="delRow(record)">删除</a>
          </div>
        </template>
        <template slot="footer">
          <div style="padding: 5px;text-align: center;border: 1px solid blue" @click="addRow()">新增数据</div>
        </template>
      </a-table>
    </div>
  </div>
</template>

<script>
import { v4 } from 'uuid'
import validateObj from '../helper.js'
export default {
  data() {
    return {
      selectList: [
        {
          value: 0,
          label: '小学'
        },
        {
          value: 1,
          label: '初中'
        },
        {
          value: 2,
          label: '高中'
        },
        {
          value: 3,
          label: '大学'
        },
        {
          value: 4,
          label: '研究生'
        },
        {
          value: 5,
          label: '博士'
        }
      ],
      model: [],
      initModel: {
        name: undefined,
        age: undefined,
        degree: undefined
      },
      validateModel: {
        name: false,
        age: false,
        degree: false
      },
      columns: [
        {
          title: '姓名',
          dataIndex: 'name',
          align: 'center',
          width: '200',
          scopedSlots: { customRender: 'name' }
        },
        {
          title: '年龄',
          dataIndex: 'age',
          align: 'center',
          scopedSlots: { customRender: 'age' }
        },
        {
          title: '学历',
          palceholder: '请选择',
          width: '150',
          align: 'center',
          dataIndex: 'degree',
          scopedSlots: { customRender: 'degree' }
        },
        {
          title: '操作',
          dataIndex: 'action',
          align: 'right',
          scopedSlots: { customRender: 'action' }
        }
      ]
    }
  },

  methods: {
     submit() {
      let val = true
      this.model.forEach(ele => {
        /* 当validate为false 不会去判断后面的 */
        val = validateObj(ele) && val
      })
      if (val) {
        alert('表单数据正常')
      }
    },
    addRow() {
      this.model.push({ id: v4(), ...this.initModel, validate: { ...this.validateModel } })
    },
    delRow({ id }) {
      this.model = this.model.filter(ele => ele.id !== id)
    }
  }
}
</script>

<style lang="less" >
.dyamic-table {
  padding: 50px;
  width: 77%;
  .ant-table-footer {
    background: #fff;
  }
  .title {
    display: flex;
    justify-content: space-between;
  }
  .has-error {
    border-color: #f5222d;
  }
}
</style>

其他常用组件 github也可以看看支持一下的:后台组件