editable.vue 5.4 KB
<style lang="scss">
.z-table-column__cell-editable {
  display: inline-flex;
  align-items: center;
  justify-content: space-between;
  width: 100%;
  .el-icon-edit {
    color: rgba(151, 151, 151, 0.5);
    &:hover {
      color: $primary;
    }
  }
  .el-icon-check {
    color: $green;
  }
  .el-icon-close {
    color: $red;
  }
  .el-icon-edit,
  .el-icon-check,
  .el-icon-close {
    cursor: pointer;
    margin-left: 5px;
    font-size: 14px;
  }
}
</style>

<template>
  <el-table :data="tableData | tableDataFilter" :size="tableSize" v-bind="_props" @header-click="onHeaderClick" @cell-click="onCellClick" @cell-dblclick="onCellDblclick">
    <slot name="left"></slot>
    <template v-for="(item, index) in columns">
      <el-table-column v-bind="item" :key="index">
        <slot :name="`header-${item.prop}`" slot="header"></slot>
        <template #default="{ row, column }">
          <cell-editor
            :disabled="disabled || item.editable === false"
            :editable="item.editable !== false && (row.$editable || (tableEditCell.index === row.$index && tableEditCell.prop === item.prop))"
            :component="item.component"
            :value="row[column.property]"
            @input="onCellInput"
            @edit-click="setEditCell(row, column)"
            @edit-confirm="onEditConfirm"
          >
            <template v-if="$scopedSlots[`editor-${item.prop}`]">
              <slot :name="`editor-${item.prop}`" :value="row[column.property]" :onInput="onCellInput"></slot>
            </template>
          </cell-editor>
        </template>
      </el-table-column>
    </template>
    <slot></slot>
    <slot name="append"></slot>
  </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: {
    cellEditor: {
      props: {
        value: [String, Number, Array, Object],
        component: { type: String, default: 'el-input' },
        editable: Boolean,
        disabled: Boolean,
      },
      watch: {
        editable(val) {
          if (val && this.component === 'el-input') {
            this.$nextTick(() => {
              this.$children[0] && this.$children[0].focus && this.$children[0].focus();
            });
          }
        },
      },
      render(h) {
        if (this.editable) {
          let editorRender = [
            h(this.component, {
              props: { value: this.value, size: 'mini' },
              on: {
                input: value => {
                  this.$emit('input', value);
                },
              },
            }),
          ];
          if (this.$scopedSlots.default) {
            editorRender = [this.$scopedSlots.default()];
          }
          if (!this.disabled) {
            const handlerItems = [h('i', { attrs: { title: '确定', class: 'el-icon-check' }, on: { click: () => this.$emit('edit-confirm', this.value) } })];
            // handlerItems.push(h('i', { attrs: { title: '取消', class: 'el-icon-close' }, on: { click: () => this.$emit('edit-confirm') } }));
            const handler = h('span', handlerItems);
            editorRender.push(handler);
          }
          return h('span', { class: 'z-table-column__cell-editable' }, editorRender);
        }
        const valueRender = [h('span', this.value)];
        if (!this.disabled) {
          valueRender.push(h('i', { attrs: { title: '编辑', class: 'el-icon-edit' }, on: { click: () => this.$emit('edit-click') } }));
        }
        return h('span', { class: 'z-table-column__cell-editable' }, valueRender);
      },
    },
  },
  props: {
    value: {
      type: Array,
      default() {
        return [];
      },
    },
    columns: {
      type: Array,
      default() {
        return [];
      },
    },
    clickable: Boolean,
    disabled: Boolean,
    ...tableProps,
  },
  watch: {
    value(val) {
      this.tableData = val || [];
    },
    data(val) {
      this.tableData = val || [];
    },
  },
  data() {
    return {
      tableData: this.value,
      tableRowTemplate: { $editable: true }, // 行数据模板
      tableEditCell: {}, // 正在编辑的单元格
      tableSelection: [], // 表格已选中
    };
  },
  filters: {
    tableDataFilter(value) {
      return value.map((item, index) => ({ ...item, $index: index }));
    },
  },
  methods: {
    onHeaderClick() {
      if (this.clickable) {
        this.cancelEditCell();
      }
    },
    onCellClick(row, column) {
      if (this.clickable) {
        if (row.$index !== this.tableEditCell.index || column.property !== this.tableEditCell.prop) {
          this.tableEditCell = {};
        }
      }
    },
    onCellDblclick(row, column) {
      if (this.clickable) {
        this.setEditCell(row, column);
      }
    },
    setEditCell(row, column) {
      this.tableEditCell = { index: row.$index, prop: column.property };
    },
    onEditConfirm(value) {
      this.$emit('cell-edit-confirm', { ...this.tableEditCell, value });
      this.cancelEditCell();
    },
    cancelEditCell() {
      this.tableEditCell = {};
    },
    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>