el-easytable之EditTable:基于vue3jsx element ui实现的类似react antd design Pro的Editable

1,100 阅读5分钟

前言:el-easytable是基于vue3jsx  element ui参照antd design pro实现的复合表格和可编辑表格,里面的属性和用法基本一致,但是由于vue 的数据是双向绑定的所以可编辑表格也会有所不同,好的意见评论区留言。

editTable组件api基本和protable组件一摸一样的,重复的这里不再介绍

组件同时支持vue3 jsx 和 template,个人建议复杂表格最好使用jsx用法,简单,灵活,易扩展;因为受语法限制template代码量往往更多,处理更复杂

一、使用

1.安装

npm i el-easytable​​​​​​​ -S

2.引用

import { ProTable,EditTable } from "el-easytable"
import "el-easytable/dist/style.css";

二、EditTable 用法示例

源码下载   、  demo下载

1.基础用法

(1)单行编辑表格

template语法

<template>
  <EditTable
    headerTitle="可编辑表格"
    :search="false"
    :singleEdit="true"//单行编辑
    :columns="columns"
    v-model:form="state.form.data"//通过传form双向绑定表单值
  />
</template>

<script setup lang="ts" name="SingleRowEditDemo">
import { reactive, onMounted } from "vue";
import {EditTable} from "el-easytable";
import {data} from "../json.js";

const columns = [
  {label: "姓名", prop: "name", required: true},
  {
    label: "性别",
    prop: "gender",
    valueType: "select",
    // multiple: true,
    // remote: true,
    // valueEnum: () => [//静态
    //   {label: "男", value: 1, status: 'success'},
    //   {label: "女", value: 2, status: 'error'}
    // ]
    remoteMethod: ()=>[//远程获取选项数据,可搜索同原生el-select remote-method
      {label: "男", value: 1, status: 'success'},
      {label: "女", value: 2, status: 'error'}
    ]
  },
  {
    label: "年龄",
    prop: "age",
    valueType: "number",
    max: 150,
    min: 1,
    precision: 0,
    summary: true,
    // defaultValue: 12 
  },
  {
    label: "出生年月",
    prop: "date",
    valueType: "date",
    disabled: (_, row) => {
      return row.age === 27;
    },
    edit:false
  },
  {
    label: "籍贯",
    prop: "areaSelect",
    valueType: 'areaSelect',
    address: 'address',
    required: true,
  },
  {
    label: "个人介绍",
    prop: "textarea",
    valueType: "textarea"
  },
  {
    label: "操作",
    valueType: "action",
    width: 140,
    action: ["edit", "remove", "add"]
  }
];

const state = reactive({
  form: {
    data: []
  }
});
onMounted(async () => {
  state.form.data = data;
});
</script>


(2) 整表可编辑

这是个嵌套表格用例

jsx语法

<script lang="jsx" name="editTableJsx">
import {defineComponent, reactive, ref} from "vue";
import {EditTable} from "el-easytable";
import {ElMessage} from "element-plus";
import {data} from "../json.js";

const columns = [
  {label: "姓名", prop: "name", required: true},
  {
    label: "性别",
    prop: "gender",
    valueType: "select",
    // multiple: true,
    valueEnum: () => [
      {label: "男", value: 1, status: 'success'},
      {label: "女", value: 2, status: 'error'}
    ]
  },
  {
    label: "年龄",
    prop: "age",
    valueType: "numberString",
    max: 150,
    min: 1,
    precision: 0,
    summary: true,
    initValue: 12
  },
  {
    label: "出生年月",
    prop: "date",
    valueType: "date",
    disabled: (_, row) => {
      return row.age === 27;
    }
  },
  {
    label: "籍贯",
    prop: "diy",
    valueType: 'areaSelect',
    address: 'address'
  },
  {
    label: "个人介绍",
    prop: "textarea",
    valueType: "textarea"
  },
  {
    label: "自定义",
    prop: "diy",
    render: (_, row, index) => <div>
      <p>姓名:{row?.name}</p>
      <p>年龄:{row?.age}</p>
      <p>index:{index}</p>
    </div>
  },
  {
    label: "操作",
    valueType: "action",
    action: ["remove", "add"]
  }
];
export default defineComponent({
  setup() {
    const editRef = ref(null);
    const childRef = ref([]);
    const form = reactive({
      data
    });
    const onSubmit = async () => {
      const check = await editRef?.value?.onValid();
      if (!check) {
        ElMessage.warning("请完善表格必填(带*)项");
        return;
      }
      for (let i = 0; i < form.data?.length; i++) {
        const checks = await childRef.value[i]?.onValid?.();
        if (!checks) {
          ElMessage.warning("请完善嵌套表格必填(带*)项");
          return;
        }
      }
      ElMessage.success("操作成功");
    };
    return () => (
        <>
          <EditTable
              headerTitle="编辑列表jsx"
              ref={editRef}
              v-model:form={form.data}
              columns={columns}
              summary={{
                showSummary: true
              }}
              expand={({row, $index}) => {
                const child = row?.child || [];
                return row?.child?.length ? (
                    <EditTable
                        ref={el => el && (childRef.value[$index] = el)}
                        v-model:form={child}
                        columns={columns}
                    />
                ) : null;
              }}
          />
          <div className="flex-pc">
            <el-button type="primary" onClick={onSubmit}>
              提交
            </el-button>
          </div>
        </>
    );
  }
});
</script>


template语法

<template>
  <EditTable
      headerTitle="编辑列表"
      ref="editRef"
      v-model:form="state.form.data"
      :columns="columns"
      :summary="{
        showSummary: true
      }"
  >
    <template #expand="{ row, $index }">
      <EditTable v-if="row.child && row.child.length"
                 :ref="el => el && (childRef[$index] = el)"
                 v-model:form="row.child"
                 :columns="columns"
      />
    </template>
    <template #empty>
      <p>空空如也</p>
    </template>
  </EditTable>
  <div class="flex-pc">
    <el-button type="primary" @click="onSubmit">提交</el-button>
  </div>
</template>

<script setup lang="ts" name="editTableDemo">
// @ts-nocheck
import {reactive, ref, onMounted} from "vue";
import {EditTable} from "el-easytable";
import {ElMessage} from "element-plus";
import {data} from "../json.js";
//columns配置项支持其valueType对应的原生el组件的任何属性
const columns = [
  {label: "姓名", prop: "name", required: true},
  {
    label: "性别",
    prop: "gender",
    valueType: "select",
    valueEnum: (_,row) => row?.genderList||[
      {label: "男", value: 1, status: 'success'},
      {label: "女", value: 2, status: 'error'}
    ]
  },
  {
    label: "年龄",
    prop: "age",
    valueType: "number",
    max: 150,//最大值
    min:(_,row)=>row?.minAge|| 1,//最小值,当你需要用到当前行数据row,你可以在其callback中拿,当你使用的slot可在scope中取
    precision: 2,//小数位
    summary: true,//当前列汇总
  },
  {
    label: "出生年月",
    prop: "date",
    valueType: "date",//当你需要时间区间,增加属性 type:"daterange" 支持原生el-date-picker 的所有属性
    disabled: (_, row) => {
      return row.age === 27;
    }
  },
  {
    label: "籍贯",
    prop: "areaSelect",
    valueType: 'areaSelect',
    address: 'address'
  },
  {
    label: "个人介绍",
    prop: "textarea",
    valueType: "textarea"
  },
  {
    label: "自定义",
    prop: "diy",
    slot: {
      default: "diy"
    }
  },
  {
    label: "操作",
    valueType: "action",
    action:(_,row)=>row.id?["remove", "add"]:["add"]//只有新增的行可删除
  }
];
const editRef = ref(null);
const childRef = ref([]);
const state = reactive({
  form: {
    data,
    summary: {}
  }
});
onMounted(async () => {
  state.form.data = data;
});

const onSubmit = async () => {
  const check = await editRef?.value?.onValid();
  if (!check) {
    ElMessage.warning("请完善表格必填(带*)项");
    return;
  }
  for (let i = 0; i < state.form.data?.length; i++) {
    if (!childRef.value[i]) continue;
    const checks = await childRef.value[i]?.onValid?.();
    if (!checks) {
      ElMessage.warning("请完善嵌套表格必填(带*)项");
      return;
    }
  }
  ElMessage.success("操作成功");
};
</script>


三、api

插槽

插槽名描述
toolBarRender表格上方按钮
tableAlertRender表头汇总行
expand表格每行的嵌套
自定义,需要与columns种的slot中对应表格每列的插槽,具体参见 columns slot属性
  • 自定义插槽用法-action

<template #action="{ row, action, $index }">
  <el-link class="mr-10" type="primary" @click="action?.add?.(row)"
  >复制
  </el-link>
  <el-link class="mr-10" type="error" @click="action?.remove?.($index)"
  >删除
  </el-link>
</template>

  {
    label: '操作',
    valueType: 'action',
    slot: {
        default: 'action'//名字与 template中#后名字对应
    }
}

属性

  • EditTable支持所有element plus Table中所有的属性
  • 以下所列是基于其原有属性的扩展,除了拥有ProTable的全部属性外,EditTable独有的属性
属性名默认值类型描述用法
form{}Object双向绑定v-model:form绑定的值比如你再js中已经有了const state=reactive({form:{}});
那么在组件属性中添加v-model:form={state.form}实现表格值双向绑定
canDelLastfalseBoolean是否可以删除最后一行,默认为false表示不可删除最后一行且当data数据为空时会默认设置为仅有一行各项为空的表格
true表示可以删除最后一行直至为空,且data数据为空时表格为空显示'暂无数据'
canDelLast={true}
singleEditfalseBoolean是否单行编辑表格singleEdit={true}
onEditfalseBoolean点击单行编辑按钮事件onEdit={(item, row, index)=>{}
saveEditfalseBoolean保存单行编辑按钮事件saveEdit={(item, row, index)=>{}
cancelEditfalseBoolean取消单行编辑按钮事件cancelEdit={(item, row, index)=>{}

request属性

<EditTabel
    params={{type: query.type}}
    v-model:form="state.form.data"
    request={async (params) => state.form.data = await getUserList({...params, code: '001'})}
/>

rowSelection属性

<ProTabel
    :rowSelection="{ 
          type: 'radio',//默认多选,type=radio为单选
          rowKey: 'age',//关键字段key
          onSelectionChange:(selection)=>{},
          selectable:(row, index)=>{},//selectable用于控制可筛选行的方法
      }"
/>

columns属性

  • columns支持element plus Table中对应的所有的属性
  • 以下所列是:除了拥有ProTable的全部columns属性外,EditTable独有的columns属性
属性名类型描述用法
onChangeFunction单元格的change事件,3个参数,具体参见onChange属性onChange:(val,row,index) => {}
editBoolean|Function当前单元格是否是可编辑的表单,默认为true,为false时将直接渲染成对应值,同样3个参数edit:(item,row,index)=>row.status===1
disabledBoolean|Function当前单元格表单disabled值,同样3个参数disabled:(item,row,index)=>row.status===1
maxNumber|FunctionvalueType:'number'最大值,同样3个参数max:(item,row,index)=>row.maxNum
minNumber|FunctionvalueType:'number'最小值,同样3个参数min:(item,row,index)=>row.minNum
addParamsObjectvalueType:'action'添加行时默认填充值, addParams: {feeList: [{}],goodsList: []} 

valueType属性

属性名描述
select自定义选择
asyncSelect异步选择 ,具体参见对应插件 ,不可编辑时/proTable中内部使用span,若需回显请使用disalbed控制其是否可编辑
enumsSelect静态枚举 ,具体参见对应插件 ,不可编辑时/proTable中内部使用span ,若需回显请使用disalbed控制其是否可编辑
remoteSelect模糊查询 ,具体参见对应插件 ,不可编辑时/proTable中内部使用span ,若需回显请使用disalbed控制其是否可编辑
date日期,完全支持element内部所有属性,不可编辑时/proTable中 内部调用?.format?.('YYYY-MM-DD'),默认处理为日期格式
time时间,完全支持element内部所有属性,不可编辑时/proTable中 内部调用?.format?.('YYYY-MM-DD hh:mm:ss'),默认处理为时间格式
timeRange时间区间,同时需要定义startTime:'开始字段名',endTime:'结束字段名'
upload文件,完全支持项目Upload组件内部所有属性,proTable中不可编辑仅展示
input输入框 ,valueType属性的默认值,为此值时可不写
number不可编辑时:为null展示'0.00'(小数位由precision属性设置)为非'-',可编辑时为:数值输入框
index索引,不可编辑
ellipsis仅不可编辑时/proTable中,超出单行宽度... 悬浮展示全部
textarea文本域,不可编辑时/proTable中超出三行宽度... 悬浮展示全部
action操作
switch切换按钮仅editTable
numberString数字字符,可以0开头的数值
currency仅proTable,默认两位消暑的数值
areaSelect省市区级联选择
  • valueType:'areaSelect'的具体用法
{
    label: '起运地', 
    required:true,
    prop:'startPlaceCodeList',//回显字段,值需要是[省code,市code,区code]类型
    valueType: 'areaSelect',
    address:'startAddress',//address:详细地址字段
    onChange:(val, row) => {
        //val返回一个对象,你可以任意处理提交给后端
        /*
          {
            codes:[省code,市code,区code],
            names:[省,市,区]     
          }
        */
        const {codes, names} = val || {}
        row.startPlace = names?.join?.('/')
        row.startPlaceCode = codes?.join?.('/')
    }
}

summary属性

{
    summary:{
        sumText:"总回款金额",   // sumText:汇总合计文案,默认为'汇总
        prop:"amount",        // prop:参与汇总计算字段名,默认为当前项的'prop'值 
        precision:2,// precision:汇总计算总值保留小数位数,默认0位 
    }
}

render属性

{
    render:(item, row, index) => {
        return (
            <el-button type="primary"
                       onClick={() => func({id: row.id})}
            >激活</el-button>
        )
    }
}
// item:当前配置项对象  
// row:当前行数据项 
// index:当前列index

onChange属性

onChange:(val, row, index) => {
    if (val === 1) {
        row.num = 0
    }
}
// val:当前表单改变的值 
// row:当前行数据项 
// index:当前列index

方法

  • 以下所列是:除了拥有ProTable的全部columns属性外,EditTable独有的columns属性
方法名接收参数类型描述用法
onValid表格的校验方法refs.value?.onValid() 

具体用法

const refs = ref(null)

const submit = async () => {
    //提交时必填校验
    const check = refs.value?.onValid()
    if (!check) {
        ElMessage.warning('请完善表格必填信息')
        return false
    }
    await api()
}

return () => (
    <div>
      <EditTable ref={refs}/>
      <button onClick={submit}>提交</button>
  </div>
)

四、vue3使用jsx需要做什么

这里基于vite打包工具介绍

1.安装支持jsx编译的插件

npm i @vitejs/plugin-vue-jsx -S

2.配置vite.config.js

import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import vueJsx from '@vitejs/plugin-vue-jsx'

export default defineConfig({
  plugins: [vue(),vueJsx()],
})