vue + element 实现可编辑列表(超详细)

1.适用场景

​ 当我们对于主子表数据同时保存的时候,我们又不想做弹窗一条一条的新增子表数据的时候,并且子表数据并不是特别复杂,完全可用table来操作,这种情况下可选择可编辑列表来完成主子表的同时操作。

2.功能简介

​ 基于Tlement-Table编写,并借助于一些表单元素控件。实现表格数据的填写。以及表格操作,包括:新增行,删除行,插入行,上移,下移,置顶,置底等功能。废话不多说直接上代码。

3.页面布局

​ 包含对每一行数据的简单校验。

<template>
  <el-row>
    <el-card>
      <el-row class="text-right m-b">
        <el-button type="primary" @click="handleAdd()">
          添加行
        </el-button>
        <el-button type="danger" plain @click="batchRemove">
          删除行
        </el-button>
        <el-button type="primary" @click="handleInsert">
          插入行
        </el-button>
        <el-button type="primary" @click="handleMove(1)">
          上移
        </el-button>
        <el-button type="primary" @click="handleMove(0)">
          下移
        </el-button>
        <el-button type="primary" @click="handleTopOrBottom(1)">
          置顶
        </el-button>
        <el-button type="primary" @click="handleTopOrBottom(0)">
          置底
        </el-button>
        <el-button type="primary" @click="clearList">
          清空
        </el-button>
      </el-row>
      <el-form ref="form2" :model="submitData" :rules="rules" label-width="150px">
        <el-row :span="24">
          <el-table
            ref="studentTable"
            :data="submitData.studentTableData"
            style="width: 100%"
            highlight-current-row
            stripe
            border
            :row-class-name="tableRowClassName"
            @current-change="currentChange"
            @selection-change="clickLogCheckboxHandler"
          >
            <el-table-column
              type="index"
              label="序号"
              width="80"
            />
            <el-table-column
              prop="studentCode"
              label="学号"
            >
              <template slot-scope="scope">
                <el-form-item :prop="'studentTableData.'+scope.$index+'.studentCode'" :rules="rules.studentTableDataRules.studentCode" label-width="0">
                  <el-input v-if="scope.row.showFormDom" v-model="scope.row.studentCode"/>
                  <div v-else>
                    {{ scope.row.studentCode }}
                  </div>
                </el-form-item>
              </template>
            </el-table-column>
            <el-table-column
              prop="studentName"
              label="学生姓名"
            >
              <template slot-scope="scope">
                <el-form-item :prop="'studentTableData.'+scope.$index+'.studentName'" :rules="rules.studentTableDataRules.studentName" label-width="0">
                  <el-input v-if="scope.row.showFormDom" v-model="scope.row.studentName"/>
                  <div v-else>
                    {{ scope.row.studentName }}
                  </div>
                </el-form-item>
              </template>
            </el-table-column>
            <el-table-column
              prop="studentAge"
              label="学生年龄"
            >
              <template slot-scope="scope">
                <el-form-item :prop="'studentTableData.'+scope.$index+'.studentAge'" :rules="rules.studentTableDataRules.studentAge" label-width="0">
                  <el-input v-if="scope.row.showFormDom" v-model="scope.row.studentAge"/>
                  <div v-else>
                    {{ scope.row.studentAge }}
                  </div>
                </el-form-item>
              </template>
            </el-table-column>
            <el-table-column
              prop="studentSex"
              label="学生性别"
            >
              <template slot-scope="scope">
                <el-form-item :prop="'studentTableData.'+scope.$index+'.studentSex'" :rules="rules.studentTableDataRules.studentSex" label-width="0">
                  <el-input v-if="scope.row.showFormDom" v-model="scope.row.studentSex"/>
                  <div v-else>
                    {{ scope.row.studentSex }}
                  </div>
                </el-form-item>
              </template>
            </el-table-column>
          </el-table>
        </el-row>
        <el-row class="text-center m-t">
          <el-button type="primary" @click="submitHandler">
            保存
          </el-button>
        </el-row>
      </el-form>
    </el-card>
  </el-row>
</template>

4.数据声明

data() {
    return {
	  // 选中的行
      selected: null,
      rowIndex: 0,
      // 提交数据
      submitData: {
        studentTableData: []
      },
	  // 校验信息
      rules: {
        studentTableDataRules: {
          studentName: [
            { required: true, message: '请输入学生姓名', trigger: 'blur' }
          ],
          studentCode: [
            { required: true, message: '请输入学号', trigger: 'blur' }
          ],
          studentAge: [
            { required: true, message: '请输入学生年龄', trigger: 'blur' }
          ],
          studentSex: [
            { required: true, message: '请输入学生性别', trigger: 'blur' }
          ]
        }
      },
	  // 多选情况下多选数据存储
      selecteds: []
    }
  }

5.功能实现

5.1 获取选中的行

注:多选情况。

clickLogCheckboxHandler(selection) {
      this.selecteds = selection
    }

5.2 为row添加索引

注:由于row中获取不到索引值,无法操作数组中的数据,因此增加此方法为row增加索引值。

tableRowClassName({ row, rowIndex }) {
      row.row_index = rowIndex
    }

5.3 新增行

注:此方法为列表底部新增一行,新增前会执行校验,并且新增行获得高亮;高亮方法为element-ui提供。

handleAdd() {
      const list = {
        studentCode: '',
        studentName: '',
        studentAge: '',
        studentSex: '',
        showFormDom: true
      }
      this.$refs['form2'].validate((valid) => {
        if (valid) {
          if (this.selected !== null) {
            const index = this.selected.row_index
            this.$set(this.submitData.studentTableData[index], 'showFormDom', false)
          }
          this.submitData.studentTableData.push(list)
          this.selected = list
          this.selected.row_index = this.submitData.studentTableData.length - 1
          this.$refs.studentTable.setCurrentRow(this.submitData.studentTableData[this.selected.row_index])
        } else {
          // 此处为验证失败代码
          this.$message.warning('请先为当前行填值!')
        }
      })
    }

5.4 删除行

注:删除当前选中行

batchRemove() {
      this.$confirm('确认删除选中记录吗?', '提示', {
        type: 'warning'
      })
        .then(() => {
          if (this.selected !== null) {
            this.submitData.studentTableData.splice(this.selected.row_index, 1)
            this.selected = null
            this.$message({
              message: '删除成功',
              type: 'success'
            })
          } else {
            this.$message({
              message: '请选择要删除的行!',
              type: 'warning'
            })
          }
        })
        .catch(() => {})
    }

5.5 插入行

注:在选中数据上方插入一个空行

handleInsert() {
      if (this.selected !== null) {
        const list = {
          studentCode: '',
          studentName: '',
          studentAge: '',
          studentSex: '',
          showFormDom: true
        }
        if (this.submitData.studentTableData.length > 0) {
          this.$refs['form2'].validate((valid) => {
            if (valid) {
              const index = this.selected.row_index
              this.$set(this.submitData.studentTableData[index], 'showFormDom', false)
              this.submitData.studentTableData.splice(index, 0, list)
              this.selected = list
              this.selected.row_index = index
              this.$refs.studentTable.setCurrentRow(this.submitData.studentTableData[index])
            } else {
              // 此处为验证失败代码
              this.$message.warning('请先为当前行填值!')
            }
          })
        } else {
          this.submitData.studentTableData.push(list)
          this.selected = list
          this.selected.row_index = 0
          this.$refs.studentTable.setCurrentRow(this.submitData.studentTableData[0])
        }
      } else {
        this.$message.warning('请选择插入位置!')
      }
    }

5.6 上移,下移

注:根据传入标识判断上移,下移(1:上移;0:下移)

handleMove(dir) {
      if (this.selected !== null) {
        const moveComm = (curIndex, nextIndex) => {
          const arr = this.submitData.studentTableData
          arr[curIndex] = arr.splice(nextIndex, 1, arr[curIndex])[0]
          return arr
        }
        this.submitData.studentTableData.some((val, index) => {
          if (val.studentCode === this.selected.studentCode) {
            if (dir === 1 && index === 0) {
              this.$message.warning('已在顶部!')
            } else if (dir === 0 && index === this.submitData.studentTableData.length - 1) {
              this.$message.warning('已在底部!')
            } else {
              const nextIndex = dir === 1 ? index - 1 : index + 1
              this.submitData.studentTableData = moveComm(index, nextIndex)
            }
            return true
          }
          return false
        })
      } else {
        this.$message.warning('请选择操作的行!')
      }
    }

5.7 置顶/置底

注:根据传入标识判断置顶/置底(1:置顶;0:置底)

handleTopOrBottom(dir) {
      if (this.selected !== null) {
        const moveComm = (curIndex, nextIndex) => {
          const arr = this.submitData.studentTableData
          arr[curIndex] = arr.splice(nextIndex, 1, arr[curIndex])[0]
          return arr
        }
        this.submitData.studentTableData.some((val, index) => {
          if (val.studentCode === this.selected.studentCode) {
            if (dir === 1 && index === 0) {
              this.$message.warning('已在顶部!')
            } else if (dir === 0 && index === this.submitData.studentTableData.length - 1) {
              this.$message.warning('已在底部!')
            } else {
              const nextIndex = dir === 1 ? 0 : this.submitData.studentTableData.length - 1
              this.submitData.studentTableData = moveComm(index, nextIndex)
            }
            return true
          }
          return false
        })
      } else {
        this.$message.warning('请选择操作的行!')
      }
    }

5.8 清空列表

注:vue基于数据驱动,清空列表只需要清空列表数组数据即可。

clearList() {
      this.$confirm('确认执行清空操作吗?', '提示', {
        type: 'warning'
      })
        .then(() => {
          this.submitData.studentTableData = []
          this.selected = null
          this.$message({
            message: '清空完成!',
            type: 'success'
          })
        })
    }

5.9 改变列表选中

注:由于这次编写的可编辑列表有这样一个特性,即:选中的行才可以编辑,未选中的行则是只读状态。因此在改变选中时应该增加一些操作。例如:对row.showFormDom的操作。还是直接看代码:

currentChange(newRow, oldRow) {
      if (this.selected !== null) {
        if (newRow.row_index !== this.selected.row_index) {
          this.$refs['form2'].validate((valid) => {
            if (valid) {
              const index = this.selected.row_index
              this.$set(this.submitData.studentTableData[newRow.row_index], 'showFormDom', true)
              this.$set(this.submitData.studentTableData[index], 'showFormDom', false)
              this.selected = newRow
            } else {
              // 此处为验证失败代码
              this.$refs.studentTable.setCurrentRow(this.submitData.studentTableData[this.selected.row_index])
              // this.$message.warning('请先为当前行填值!')
            }
          })
        }
      } else {
        this.$set(this.submitData.studentTableData[newRow.row_index], 'showFormDom', true)
        this.selected = newRow
      }
    }

5.10 最终操作:保存

注:这个应该无须赘述。

submitHandler() {
      this.$refs['form2'].validate((valid) => {
        if (valid) {
          this.$message({
            showClose: true,
            message: '请完善列表中的数据!',
            type: 'warning'
          })
        } else {
          this.$message({
            showClose: true,
            message: '请完善列表中的数据!',
            type: 'warning'
          })
        }
      })
    }

6 总结

​ 可编辑列表常用的基本功能大致就想到了这么多,在就是本demo中表单元素均用input实现,对于其他需求,可以替换为其他表单元素控件,例如:select等。也可针对于字段值的改变触发一些特定的方法来实现特定的需求。再就是上移、下移、置顶、置底均用到了studentCode这个属性,那么这个属性应该保持其唯一性,这里没有为其增加唯一性校验,实际开发中应该注意。再就是习惯js的=赋值,在这里很多采用了$set去赋值,有时间我会单独聊一下这样赋值的原因。

​ 最后希望大家开源自己,共同进步。

GitHub 加速计划 / eleme / element
54.06 K
14.63 K
下载
A Vue.js 2.0 UI Toolkit for Web
最近提交(Master分支:3 个月前 )
c345bb45 7 个月前
a07f3a59 * Update transition.md * Update table.md * Update transition.md * Update table.md * Update transition.md * Update table.md * Update table.md * Update transition.md * Update popover.md 7 个月前
Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐