editable.vue 2.86 KB
<template>
  <el-table :data="tableData | tableDataFilter" :size="tableSize" v-bind="_props" @header-click="onHeaderClick" @cell-click="onCellClick" @cell-dblclick="onCellDblclick">
    <template v-for="(item, index) in columns">
      <el-table-column v-bind="item" :key="index">
        <template #default="{ row, column }">
          <table-column-cell
            :editable="row.$editable || (tableEditCell.index === row.$index && tableEditCell.prop === item.prop)"
            :type="item.type"
            :value="row[column.property]"
            @input="onCellInput"
          ></table-column-cell>
        </template>
      </el-table-column>
    </template>
  </el-table>
</template>

<script>
import TableNormal from './normal';
import tableProps from './props';
import { cloneDeep, get, set } from '../utils';

export default {
  name: 'TableEditable',
  extends: TableNormal,
  components: {
    tableColumnCell: {
      props: {
        value: [String, Number, Array, Object],
        type: { type: String, default: 'el-input' },
        editable: Boolean,
      },
      watch: {
        editable(val) {
          if (val && this.type === 'el-input') {
            this.$nextTick(() => {
              this.$children[0].focus();
            });
          }
        },
      },
      render(h) {
        if (this.editable) {
          return h(this.type, {
            props: { value: this.value, size: 'mini' },
            on: {
              input: value => {
                this.$emit('input', value);
              },
            },
          });
        }
        return h('span', this.value);
      },
    },
  },
  props: {
    value: {
      type: Array,
      default() {
        return [];
      },
    },
    columns: {
      type: Array,
      default() {
        return [];
      },
    },
    ...tableProps,
  },
  data() {
    return {
      tableData: this.value,
      tableRowTemplate: { $editable: true }, // 行数据模板
      tableEditCell: {}, // 正在编辑的单元格
      tableSelection: [], // 表格已选中
    };
  },
  filters: {
    tableDataFilter(value) {
      return value.map((item, index) => ({ ...item, $index: index }));
    },
  },
  methods: {
    onHeaderClick() {
      this.tableEditCell = {};
    },
    onCellClick(row, column, cell, event) {
      if (row.$index !== this.tableEditCell.index || column.property !== this.tableEditCell.prop) {
        this.tableEditCell = {};
      }
    },
    onCellDblclick(row, column, cell, event) {
      this.tableEditCell = { index: row.$index, prop: column.property };
    },
    onCellInput(value) {
      const tableData = cloneDeep(this.tableData);
      const tableRow = tableData[this.tableEditCell.index];
      set(tableRow, this.tableEditCell.prop, value);
      tableData[this.tableEditCell.index] = tableRow;
      this.$set(this.tableData, this.tableEditCell.index, tableRow);
    },
  },
};
</script>