怎么样实现一个快速编辑

205 阅读1分钟

需求实现目标:点击table的任意cell就可以编辑里面的内容

思路

  1. 表格里的cell有两种标签展示一个是普通的文本展示标签(div,span 等),一种是input标签
  2. 双击事件发生时切换两种标签的展示
  3. input失去焦点时再次切换标签展示
  4. 记录当前编辑的cell信息

关系图如下:

Untitled-2022-08-18-1630.png

1.使用elementUi实现一个普通的表格组件数据纯展示

<template>
  <div>
    <el-table :data="tableData.list" >
      <template v-for="col of tableColumns" :key="col.prop || col.label">
        <el-table-column
          v-slot="{ row, column }"
          :prop="col.prop"
          :label="col.label"
        >
            <span>{{ row[col.prop] }}</span>
        </el-table-column>
      </template>
    </el-table>
  </div>
</template>

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

const props = defineProps({
  tableData: {
    type: Object,
    default: ()=>[]
  },
  tableColumns: {
    type: Array,
    default: () => []
  },
})

</script>

2.判断是都处于编辑状态

<template>
  <div>
    <el-table :data="tableData.list" @cell-click="handleTableCellClick">
      <template v-for="col of tableColumns" :key="col.prop || col.label">
        <el-table-column
          v-slot="{ row, column }"
          :prop="col.prop"
          :label="col.label"
        >
            +++ 新增判断  根据是否处于编辑状态切换展示内容
            <template v-if="checkCellIsEditing(row,column)">
                <span>{{ row[col.prop] }}</span>
            </template>
            <template v-else>
                // 处于编辑状态所展示的内容
            </template>
        </el-table-column>
      </template>
    </el-table>
  </div>
</template>

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

const props = defineProps({
  tableData: {
    type: Object,
    default: ()=>[]
  },
  tableColumns: {
    type: Array,
    default: () => []
  },
})

+++ 新增部分
// 当前编辑cell数据
const inEditingCell = ref(null)
// 判断是否在编辑状态
function checkCellIsEditing (row, col) {
  return !!inEditingCell.value && inEditingCell.value.row.followId === row.followId && inEditingCell.value.col.property === col.property 
}
// 把当前点击的单元格保存到inEditingCell中
const handleTableCellClick = (row, col) => {
    if(checkCellIsEditing(row, col)) {
        return 
    }
    inEditingCell.value = {row,col}
}
</script>

3.定制一个处于可编辑状态的组件(该组件的表现形式并不唯一,所以我们可以由外部提供)

思路

Untitled-2022-08-19-1055.png

//编辑组件的完整代码如下
<script setup lang='jsx'>
const createQuickEditComponent = ({ submit, initialValue }) => ({
  setup() {
    const inputComponent = ref(null);
    const localValue = ref(initialValue);
    watchEffect(
      () => {
        if (inputComponent.value) {
          inputComponent.value.focus();
        }
      },
      {
        flush: "post",
      }
    );
    return { localValue, inputComponent };
  },
  render() {
    return (
      <el-input
        ref="inputComponent"
        v-model={this.localValue}
        onBlur={() => submit(this.localValue)}
      ></el-input>
    );
  },
});

</script>

QuickEditTable.vue与编辑组件交互

<template>
  <div>
    <el-table :data="tableData.list" @cell-click="handleTableCellClick">
      <template v-for="col of tableColumns" :key="col.prop || col.label">
        <el-table-column
          v-slot="{ row, column }"
          :prop="col.prop"
          :label="col.label"
        >
            //  根据是否处于编辑状态切换展示内容
            <template v-if="checkCellIsEditing(row,column)">
                <span>{{ row[col.prop] }}</span>
            </template>
            <template v-else>
                // 处于编辑状态所展示的内容
                <component :is="editCellComponent" />
            </template>
        </el-table-column>
      </template>
    </el-table>
  </div>
</template>

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

const props = defineProps({
  tableData: {
    type: Object,
    default: ()=>[]
  },
  tableColumns: {
    type: Array,
    default: () => []
  },
  +++ 新增 外部传进的编辑组件
  createQuickEditComponent:{
    type:Function,
    default:()=>()=>{}
  }
})

// 当前编辑cell数据
const inEditingCell = ref(null)
// 编辑组件
const editCellComponent = shallowRef(null)
// 判断是否在编辑状态
function checkCellIsEditing (row, col) {
  return !!inEditingCell.value && inEditingCell.value.row.followId === row.followId && inEditingCell.value.col.property === col.property 
}

+++ 新赠  click事件触发与编辑组件的一系列交互
const handleTableCellClick = (row, col) => {
   if(checkCellIsEditing(row, col)) {
        return
    }
    // 提交后清空数据
   const exit = () => { inEditingCell.value = null }
   const component =  requestQuickEdit({ row, col, exit })
   if (!component) {
     return
   }
   inEditingCell.value = { row, col }
   editCellComponent.value = component
}

+++ 新增 调用外部传的编辑组件
function requestQuickEdit({ row, col, exit }) {
  const initialValue = row[col.property]; // 初始值
  return props.createQuickEditComponent({
    submit(val) {
      exit();
      row[col.property] = val;
    },
    initialValue,
  });
}
</script>

4.组件使用

<template>
<QuickEditTable :table-columns="columns" :table-data="tableData" 
    :create-quick-edit-component="createQuickEditComponent" />
</template>

<script setup lang="jsx">
import { ref, reactive,watchEffect } from "vue";
import QuickEditTable from "./components/QuickEditTable.vue";

const columns = [
  { label: "日期", prop: "dateDesc" },
  { label: "存在问题", prop: "problem" },
  { label: "问题诊断", prop: "diagnosis" },
  { label: "解决办法/下次沟通关注点", prop: "solution" },
  { label: "备注", prop: "remark" },
];

const tableData = reactive({
  list: [
    {
      dateDesc: "入职第一周",
      problem: "problem",
      diagnosis: "diagnosis",
      solution: "solution",
      remark: "remark",
    },
  ],
});
    
// 创建编辑组件
const createQuickEditComponent = ({ submit, initialValue }) => ({
  setup() {
    const inputComponent = ref(null);
    const localValue = ref(initialValue);
    watchEffect(
      () => {
        if (inputComponent.value) {
          inputComponent.value.focus();
        }
      },
      {
        flush: "post",
      }
    );
    return { localValue, inputComponent };
  },
  render() {
    return (
      <el-input
        ref="inputComponent"
        v-model={this.localValue}
        onBlur={() => submit(this.localValue)}
      ></el-input>
    );
  },
});
</script>