vue 表格组件 vxe-table 实现自定义多行编辑功能

需求与限制

vxe-table 内置的行编辑功能默认是单行编辑,同一时间只能激活一行。虽然它提供了 editConfig、editRules、CRUD 相关方法,但如果业务要求"同时编辑多行",就需要自己维护编辑状态。

  • 自定义多行编辑的核心思路很简单:
    • 给每一行数据增加一个 isEdit 标记;
    • 在列插槽中根据 row.isEdit 切换编辑组件和普通文本;
    • 编辑前备份原始数据,取消时还原;
    • 保存时调用接口,成功后关闭编辑态;
    • 可以额外实现"保存全部""取消全部"。

需要注意:一旦采用自定义多行编辑,就无法直接使用 vxe-table 内置的 CRUD 方法、editRules 数据校验等功能,这些都需要自行实现。

代码

html 复制代码
<template>
  <div>
    <vxe-grid v-bind="gridOptions">
      <template #name_default="{ row }">
        <vxe-input v-if="row.isEdit" v-model="row.name"></vxe-input>
        <span v-else>{{ row.name }}</span>
      </template>

      <template #sex_default="{ row }">
        <vxe-select v-if="row.isEdit" v-model="row.sex" :options="sexList"></vxe-select>
        <span v-else>{{ row.sex }}</span>
      </template>

      <template #age_default="{ row }">
        <vxe-input v-if="row.isEdit" v-model="row.age"></vxe-input>
        <span v-else>{{ row.age }}</span>
      </template>

      <template #action="{ row }">
        <vxe-button v-if="row.isEdit" mode="text" status="success" :loading="row.isLoading" @click="handleSave(row)">保存</vxe-button>
        <vxe-button v-else mode="text" status="primary" @click="handleEdit(row)">编辑</vxe-button>
        <vxe-button v-if="row.isEdit" mode="text" @click="cancelEdit(row)">取消</vxe-button>
      </template>
    </vxe-grid>
  </div>
</template>

<script setup>
import { ref, reactive } from 'vue'

const sexList = ref([
  { label: '男', value: 'Man' },
  { label: '女', value: 'Women' }
])

const gridOptions = reactive({
  border: true,
  showOverflow: true,
  rowConfig: {
    keyField: 'id'
  },
  columns: [
    { type: 'seq', width: 70 },
    { field: 'name', title: 'Name', slots: { default: 'name_default' } },
    { field: 'sex', title: 'Sex', slots: { default: 'sex_default' } },
    { field: 'age', title: 'Age', slots: { default: 'age_default' } },
    { field: 'action', title: '操作', width: 140, slots: { default: 'action' } }
  ],
  data: [
    { id: 10001, name: 'Test1', role: 'Develop', sex: 'Man', age: 28, address: 'test abc', isEdit: false, isLoading: false },
    { id: 10002, name: 'Test2', role: 'Test', sex: 'Women', age: 22, address: 'Guangzhou', isEdit: false, isLoading: false },
    { id: 10003, name: 'Test3', role: 'PM', sex: 'Man', age: 32, address: 'Shanghai', isEdit: false, isLoading: false },
    { id: 10004, name: 'Test4', role: 'Designer', sex: 'Women', age: 24, address: 'Shanghai', isEdit: false, isLoading: false }
  ]
})

const handleEdit = (row) => {
  row.isEdit = true
}

const cancelEdit = (row) => {
  row.isEdit = false
}

const handleSave = (row) => {
  row.isLoading = true
  // 模拟后端接口
  setTimeout(() => {
    row.isEdit = false
    row.isLoading = false
  }, 300)
}
</script>

核心实现解析

1. 通过自定义插槽切换编辑态

每一列都使用 vxe-table 的插槽,根据 row.isEdit 决定显示输入组件还是普通文本:

html 复制代码
<template #name_default="{ row }">
  <vxe-input v-if="row.isEdit" v-model="row.name" />
  <span v-else>{{ row.name }}</span>
</template>

因为 isEdit 是挂在每一行上的,所以可以同时把多行设置为 isEdit: true,从而实现多行同时编辑。

2. 编辑前备份,取消时还原

如果直接修改 row.name,点击取消时数据已经被改掉了。因此在进入编辑态时先保存一份快照:

js 复制代码
const handleEdit = (row) => {
  if (!row._originalData) {
    row._originalData = {
      name: row.name,
      sex: row.sex,
      age: row.age
    }
  }

  row.isEdit = true
}

取消时再还原:

js 复制代码
Object.assign(row, row._originalData)
delete row._originalData
row.isEdit = false

3.保存时过滤前端状态字段

isEdit、isLoading、_originalData、_isNew 都是前端状态,不应该提交给后端:

js 复制代码
const getSubmitData = (row) => {
  const data = { ...row }

  delete data.isEdit
  delete data.isLoading
  delete data._originalData
  delete data._isNew

  return data
}

4. 自行校验数据

由于不能使用内置 editRules,这里在保存前手动校验:

js 复制代码
const validateRow = (row) => {
  if (!row.name) {
    alert('姓名不能为空')
    return false
  }

  if (row.age === '' || row.age === null || row.age === undefined) {
    alert('年龄不能为空')
    return false
  }

  return true
}

实际项目中可以替换成 VxeUI.modal.message 或自己的表单校验逻辑

5. 批量保存与批量取消

保存全部时,找出所有处于编辑态的行,统一调用保存方法:

js 复制代码
const saveAll = async () => {
  const editRows = gridOptions.data.filter((row) => row.isEdit)
  if (!editRows.length) return

  savingAll.value = true
  try {
    await Promise.all(editRows.map((row) => handleSave(row)))
  } finally {
    savingAll.value = false
  }
}

取消全部时注意复制数组,避免取消新增行时删除数组导致遍历异常:

js 复制代码
;[...gridOptions.data].forEach((row) => {
  if (row.isEdit) {
    cancelEdit(row)
  }
})

注意事项与局限

  • 不能使用内置 CRUD 和校验
    • 自定义多行编辑后,vxe-table 内置的 insert、remove、editRules 等能力无法直接配合使用,需要自己维护。
  • 行数据必须有唯一 key
    • 建议配置 rowConfig.keyField: 'id',并保证每行 id 唯一。
  • 提交前剔除前端状态字段
    • isEdit、isLoading、_originalData、_isNew 只用于前端交互,提交接口前要删除。
  • 保存失败时保持编辑态
    • 示例中保存失败后不会关闭编辑态,方便用户重试。实际项目可以根据需求处理。
  • 新增行取消应删除
    • 示例通过 _isNew 标记新增行,取消时直接从表格数据中移除。
  • 数据量大时建议优化编辑状态管理
    • 当前方案把编辑状态直接挂在行数据上,简单直观,适合中小数据量。如果数据量很大,可以用 Set 单独存储正在编辑的行 id,减少对原始数据的污染。

通过给每行数据增加 isEdit 标记,再配合 vxe-table 的列插槽,就可以实现自定义多行编辑。虽然失去了一部分内置能力,但换来更高的灵活性,适合需要同时编辑多行、批量保存、自定义交互的表格场景。

如果项目对数据校验和 CRUD 依赖较重,也可以考虑继续使用 vxe-table 内置的单行编辑;如果必须多行编辑,则可以使用本文方案自行封装编辑状态、校验和保存逻辑。

https://vxetable.cn

相关推荐
不要试图纠正别人1 小时前
别让大模型算钱:我把优惠券组合优化写成了确定性算法
前端
志尊宝1 小时前
Vue3 零基础每日笔记(075):图片懒加载与骨架屏
javascript·vue.js·笔记
IT_Octopus1 小时前
【零基础入门 LLM 开发 · Day 11】:ChatOpenAI vs init_chat_model——一行换供应商
java·前端·javascript
柚yuzumi1 小时前
React 19 Hooks 入门:函数组件的四大法宝 (useState / useEffect / useRef / useContext)
前端·javascript·架构
deli0071 小时前
撒下 200 个种子点,平面为什么自己长成蜂窝?Voronoi 图实验室
前端
King禅2 小时前
JavaScript 调试实战指南:从 console.log 到 DevTools 深度运用
javascript
大文说跨境2 小时前
多账号环境隔离方案技术选型:指纹浏览器、VPS 与云手机的三种架构对比
java·开发语言·前端
咕白m6252 小时前
使用 C# 将 TIFF 转换为 PDF
前端·c#
全栈项目管理程序猿2 小时前
ArcGIS JS 基础教程(28):图层渲染顺序管理
前端·javascript