跳转到内容

EditableProTable

EditableProTable 复用 ProTable 的列模型、编辑状态机和校验逻辑,面向“整张表就是一个表单字段”的场景。它默认关闭搜索、分页、工具选项和窗口聚焦刷新。

何时使用

  • 需要在表格中连续新增、修改或删除多条记录,并把整张表作为一个受控值提交。
  • 需要复用 ProTable 的列配置、校验和插槽,同时默认关闭查询、分页等浏览型能力。
  • 只编辑单条记录、且仍以查询浏览为主时,可以直接使用 ProTable 的内建编辑能力。

示例

受控编辑、新建记录与组件实例

查看完整代码
vue
<script setup lang="ts">
import { ref } from 'vue'
import {
  EditableProTable,
  type EditableConfig,
  type EditableProTableInstance,
  type ProColumns,
  type ProKey,
  type RecordCreatorProps,
} from 'antdv-next-pro'

type Member = Record<string, unknown> & {
  id: number
  name: string
  role: '开发' | '设计' | '测试'
  allocation: number
  active: boolean
}

const tableRef = ref<EditableProTableInstance<Member>>()
const value = ref<Member[]>([
  { id: 1, name: '林默', role: '开发', allocation: 80, active: true },
  { id: 2, name: '周芮', role: '设计', allocation: 60, active: true },
])
const editableKeys = ref<ProKey[]>([])
const lastAction = ref('editableKeys 由 v-model 完整控制')
let sequence = 3

const columns: ProColumns<Member>[] = [
  {
    title: '成员',
    dataIndex: 'name',
    valueType: 'text',
    formItemProps: { rules: [{ required: true, message: '请输入成员姓名' }] },
  },
  {
    title: '角色',
    dataIndex: 'role',
    valueType: 'select',
    valueEnum: { 开发: '开发', 设计: '设计', 测试: '测试' },
  },
  { title: '投入比例', dataIndex: 'allocation', valueType: 'percent' },
  { title: '参与项目', dataIndex: 'active', valueType: 'switch' },
  { title: '操作', valueType: 'option', width: 160 },
]

const editable: EditableConfig<Member> = {
  type: 'multiple',
  async onSave(_key, record) {
    await new Promise((resolve) => setTimeout(resolve, 120))
    lastAction.value = `已保存「${record.name}」`
  },
}

const recordCreatorProps: RecordCreatorProps<Member> = {
  record: () => ({
    id: sequence++,
    name: '',
    role: '开发',
    allocation: 50,
    active: true,
  }),
  position: 'bottom',
  creatorButtonText: '添加成员',
  newRecordType: 'dataSource',
}

const editFirst = () => {
  const first = value.value[0]
  if (first && tableRef.value?.startEditable(first.id)) {
    lastAction.value = '正在编辑第一行'
  }
}

const patchFirst = () => {
  const first = value.value[0]
  if (first && tableRef.value?.setRowData(first.id, { allocation: 100 })) {
    lastAction.value = 'setRowData 已浅合并第一行'
  }
}
</script>

<template>
  <div class="demo-frame vp-raw">
    <p class="demo-label">LIVE · CONTROLLED VALUE + COMPONENT REF</p>
    <div class="demo-actions">
      <span>编辑中:{{ editableKeys.length ? editableKeys.join(', ') : '无' }}</span>
      <span>{{ lastAction }}</span>
      <button type="button" @click="editFirst">编辑第一行</button>
      <button type="button" @click="patchFirst">投入改为 100%</button>
    </div>
    <EditableProTable
      ref="tableRef"
      v-model:value="value"
      v-model:editable-keys="editableKeys"
      :columns="columns"
      :editable="editable"
      :record-creator-props="recordCreatorProps"
      :max-length="5"
      row-key="id"
      @values-change="lastAction = `完整 value 已更新,共 ${value.length} 行`"
      @editable-error="lastAction = '保存失败,编辑状态已保留'"
    >
      <template #header-role="{ column }"> {{ column.title }}(共享列插槽) </template>
    </EditableProTable>
  </div>
</template>

<style scoped>
.demo-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px;
  margin-bottom: 14px;
  color: #64748b;
  font-size: 12px;
}

.demo-actions button {
  padding: 5px 10px;
  border: 1px solid #bfd2e7;
  border-radius: 6px;
  background: #fff;
  color: #1768d3;
  cursor: pointer;
}
</style>

受控数据与编辑状态

v-model:value 是完整数据的唯一受控入口,不再提供语义冲突的 dataSourcedefaultValue 可提供非受控初始值。

vue
<EditableProTable
  v-model:value="members"
  v-model:editable-keys="editableKeys"
  :columns="columns"
  :editable="{ type: 'multiple', onSave, onDelete }"
  :record-creator-props="recordCreatorProps"
  row-key="id"
/>

editable.type 可设为 singlemultipleonSaveonCancelonDelete 支持 Promise;保存或删除返回 false 时保持当前编辑/数据状态。列级 editable 可根据记录决定是否允许编辑。

校验与生命周期

校验规则放在列的 formItemProps.rules

ts
const columns: ProColumns<Member>[] = [
  {
    title: '姓名',
    dataIndex: 'name',
    valueType: 'text',
    formItemProps: {
      rules: [{ required: true, message: '请输入姓名' }],
    },
  },
]

const editable: EditableConfig<Member> = {
  type: 'multiple',
  async onSave(key, record, origin) {
    await api.save(record)
  },
  async onDelete(key, record) {
    await api.remove(record.id)
    return true
  },
}

校验不通过时 saveEditable 返回 false;生命周期抛错会触发 editable-errorformItemProps Prop 则用于将整个 EditableProTable 包装为一个 Antdv Next FormItem,方便嵌入外层表单。

新建记录

ts
const recordCreatorProps: RecordCreatorProps<Member> = {
  record: () => ({
    id: crypto.randomUUID(),
    name: '',
  }),
  position: 'bottom',
  parentKey: undefined,
  newRecordType: 'dataSource',
  creatorButtonText: '添加成员',
}
字段说明
record记录对象或每次创建时执行的工厂函数
positiontop / bottom,默认底部
parentKey树形数据的新记录父节点
newRecordTypedataSource 立即写入模型;cache 保存成功后写入
creatorButtonText创建按钮文本

每条新记录必须产生唯一 rowKeymaxLength 按展开后的树形记录总数计算;达到上限后隐藏创建按钮,通过 ref 调用 addEditRecord 时也会遵守该限制。

共用字段核心与 FormItem 边界

EditableProTable 不维护单独的字段映射,而是通过 ProTable 复用 ProFormFields 的裸控件核心。treeSelectslidersegmented 可和既有 valueType 一样用于可编辑列;checked 型字段和异步选项也使用同一套 v-model、竞态与错误处理。

列级 formItemProps 只提供单元格校验和字段配置,不创建嵌套 FormItem。组件根级的 formItemProps 则只负责把整张 EditableProTable 包装为一个外层 FormItem,二者职责不会交叉。自定义编辑器仍使用列级 renderFormItem,并通过 context.update(value) 写回草稿。

API

属性

Prop类型说明
columnsProColumns<T>[]与 ProTable 共用的列模型
value / defaultValueT[]受控值与非受控初始值
editableKeysProKey[]当前编辑行,支持双向绑定
editablefalse | EditableConfig<T>编辑模式和生命周期
recordCreatorPropsfalse | RecordCreatorProps<T>创建行配置
maxLengthnumber最大记录数
formItemPropsRecord<string, unknown>外层 Antdv Next FormItem 配置
onValuesChange(values, changedRecord) => void完整数据变化回调
onTableChange(pagination, filters, sorter) => void表格状态回调
request / params / postData与 ProTable 相同可选远程初始化/刷新
toolbar / rowSelection / columnsState与 ProTable 相同可按需重新启用相关能力
polling / manualRequest与 ProTable 相同远程请求控制
scroll / size / bordered与 ProTable 相同表格外观

事件

onValuesChange / onTableChange Props 与 @values-change / @table-change 是同一条 Vue 监听通道的两种写法,请任选一种。每次变化只派发一次,不要为同一个处理函数同时写两种语法:

事件参数说明
update:valuerowsv-model:value 更新
update:editable-keyskeysv-model:editable-keys 更新
values-changerows, changedRecord任一记录变化
table-changepagination, filters, sorter表格状态变化
request-errorerror远程请求失败
editable-errorerror编辑生命周期失败

插槽

EditableProTable 会将插槽全部转发给内部 ProTable,因此可以使用:

  • toolbar-titletoolbar-actions
  • header-${columnKey}
  • cell-${columnKey}${columnKey},参数为 { value, record, index, column, editable }
  • 底层 Antdv Next Table 的其他插槽。

组件实例

EditableProTableInstance<T> 包含 ProTable 的全部实例方法,并额外提供整表读写:

ts
const editableRef = ref<EditableProTableInstance<Member>>()

editableRef.value?.startEditable(memberId)
const first = editableRef.value?.getRowData(0)
const all = editableRef.value?.getRowsData()
editableRef.value?.setRowData(memberId, { allocation: 100 })
方法说明
getRowData(indexOrKey)数字先按精确 rowKey 查找,再回退为顶层索引
getRowsData()读取当前完整数据副本
setRowData(indexOrKey, value)按同样的 key/索引规则查找,浅合并后更新 v-model:value

继承的方法包括 reloadresetsetPageInfoclearSelectedfullScreenscrollTostartEditablesaveEditablecancelEditableaddEditRecord

基于 MIT 许可发布