current position:Home>Realize editable cells based on vue3 and customize the data structure (add, delete and cancel)

Realize editable cells based on vue3 and customize the data structure (add, delete and cancel)

2022-04-29 20:34:47Separated chicken

table9.gif

Demand background

Due to the development needs of the company , We need to develop a set by ourselves erp System , For internal use of the company , It involves a large number of editable tables , and Element The table is relatively simple , Some complex functions are not satisfied , So I'm based on Element-plus Second encapsulation is carried out , On this basis, add the required functions , Meet the needs of the company .

On-line demo Exhibition If you are interested, you can give me star--》 Source code address

Using technology

  • element-plus Of table
  • vue3

Usage method

<edit-table
    :mode="radio"  //  It's for direction  top bottom hide
    :columns="column" //  Incoming header 
    :data="list" //  data 
    @add="add" //  Adding a new line will trigger 
    ref="table" // Reset method  table.value.reset() 
    :editableKeys="editableKeys"  //  Controls whether it is editable 
    @onChange="onChange"  //  When the data changes 
    @del="deleteAction" //  Delete trigger 
/>
 Copy code 

primary coverage

  • You can edit cells 、 Support input box input、 Support time 、 Support drop-down selection box
  • Data structure , You can customize the drop-down box , Custom data structure format
  • Custom display cell content
  • preservation 、 Delete 、 edit 、 Cancel
  • Add a new line 、 Submit data

Ideas

  • use columns Instead of el-table-column Cycle through
  • For each item add to edit Represents whether to edit
  • editableKeys Initialize incoming keys The default line to edit
  • Click save 、 Cancel to control edit The exhibition of

Complete code implementation

<template>
  <div class="m-edit-table">
    <div style="margin-top: 15px;margin-bottom: 15px" v-if="mode!=='hide'&&mode!=='bottom'">
      <el-button style="width: 100%" @click="add">
        <el-icon style="margin-right: 4px"><plus /></el-icon>  Add a row of data </el-button>
    </div>
    <el-table :data="transData" style="width: 100%" row-key="id" border>
      <template v-for="item in columns" >
        <el-table-column v-if="item.type" :type="item.type"  :width="item.width" :align="item.align" :fixed="item.fixed" :label="item.label"/>
        <el-table-column
            v-else
            :prop="item.name" :width="item.width" :align="item.align" :fixed="item.fixed" :label="item.label">
          <template #default="scope">
            <template v-if="!item.slot">
              <template v-if="item.readonly">
                {{ scope.row[item.name] }}
              </template>
              <template v-else-if="item.valueType==='select'">
                <el-select
                    clearable
                    :placeholder="` Please select `" v-model="scope.row[item.name]" v-if="scope.row.edit">
                  <el-option

                      v-for="ite in item.options"
                      :key="ite.value"
                      :label="ite.label"
                      :value="ite.value"
                  />
                </el-select>

                <span v-else>{{filterOption(item,scope)}}</span>

              </template>

              <template v-else-if="item.valueType==='date'">
                <el-date-picker
                    v-model="scope.row[item.name]"
                    type="date"
                    clearable
                    placeholder=" Please select "
                    v-if="scope.row.edit"
                />
                <span v-else>{{scope.row[item.name]||'--'}}</span>
              </template>
              <template v-else>
                <el-input clearable
                          placeholder=" Please enter " v-model="scope.row[item.name]" v-if="scope.row.edit"></el-input>
                <span v-else>{{scope.row[item.name]||'--'}}</span>
              </template>
            </template>
            <slot v-else :name="item.name" :item="item" :row="scope.row"></slot>
          </template>
        </el-table-column>
      </template>
      <el-table-column prop="operator" label=" operation " width="250px" fixed="right">
        <template #default="scope">
          <el-button
              v-if="scope.row.edit"
              type="success"
              size="small"
              icon="CircleCheckFilled"
              @click="confirmEdit(scope.row)"
          >
             preservation 
          </el-button>
          <el-button
              v-else
              type="primary"
              size="small"
              icon="Edit"
              @click="scope.row.edit=!scope.row.edit"
          >
             edit 
          </el-button>
          <el-popover
              trigger="click"
              v-model:visible="scope.row.visible" placement="top" :width="160">
            <p style="display: flex;align-items: center;margin-bottom: 10px">
              <el-icon color="#faad14" style="margin-right: 10px"><warning-filled /></el-icon>  Delete this line ?</p>
            <div style="text-align: right; margin: 0">
              <el-button size="small"  @click="scope.row.visible = false"
              > Cancel </el-button
              >
              <el-button size="small" type="primary" @click="deleteAction(scope.row)"
              > determine </el-button
              >
            </div>
            <template #reference>
              <el-button
                  icon="Delete"
                  @click="scope.row.visible = true" type="danger" size="small"> Delete </el-button>
            </template>
          </el-popover>
          <el-button
              v-if="scope.row.edit"
              type="primary"
              size="small"
              icon="Edit"
              @click="cancelEdit(scope.row)"
          >
             Cancel 
          </el-button>
        </template>
      </el-table-column>
    </el-table>
    <div style="margin-top: 15px" v-if="mode!=='hide'&&mode!=='top'">
      <el-button style="width: 100%" @click="add">
        <el-icon style="margin-right: 4px"><plus /></el-icon>  Add a row of data </el-button>
    </div>
  </div>
</template>
<script lang="ts" setup>
  import {computed, onMounted, ref, watch} from "vue";
  import {deepObjClone} from '@/utils/index'
  import { ElMessage,ElMessageBox  } from 'element-plus'
  import { reactive } from 'vue'
  const emit = defineEmits(['del','add','onChange'])
  let transData = ref([])

  let props = defineProps({
    columns:{
      type:Array,
      default:()=>[]
    },
    data:{
      type:Array,
      default:()=>[]
    },
    editableKeys:{
      type:Array,
      default:()=>[]
    },
    mode:{
      type:String,
      default:'bottom'
    }
  })
  const getData = ()=>{
    let arr = deepObjClone(transData.value)
    for(let item of arr){
      for(let attr in item){
        if(attr.includes('te__mp')){
          delete item[attr]
        }
      }
    }
    emit('onChange',arr)
  }

  let obj={}
  for(let item of props.columns){
    props.data.forEach(it=>{
      if(!obj[item.name]){
        obj[item.name] = null
      }
    })
  }

  const reset = ()=>{
    transData.value = props.data
    getData()
  }

  onMounted(()=>{
    watch(()=>props.data,(val)=>{
      // //  Conversion data 
      transData.value = deepObjClone(val)
      //  Store a temporary variable 
      for(let item of transData.value){
          if(props.editableKeys.includes(item.id)){
            item.edit = true
          }

         for(let attr in item){
           let temp  = `${attr}te__mp`
           item[temp] = item[attr]
         }

      }
      // console.log('transData',transData)
    },{
      immediate:true,
      deep:true
    })

  })




  const visible = ref(false)

  const handleSizeChange = (val: number) => {
    console.log(`${val} items per page`)
  }

  const listLoading = ref(false)

  //  preservation 
  const confirmEdit = (row)=>{
    row.edit = false
    for(let attr in row){
      if(attr.includes('te__mp')){
        row[(attr)] = row[(attr.replace('te__mp',''))]
      }
    }
    getData()
  }
  //  Cancel 
  const cancelEdit = (row)=>{
    row.edit=!row.edit
    for(let attr in row){
      if(!attr.includes('te__mp')){
        row[attr] = row[(attr+'te__mp')]
      }
    }
  }

  const formInline = reactive({
    user: '',
    region: '',
  })

  const onSubmit = () => {
    console.log('submit!')
  }

  const deleteAction = (row)=>{
    row.visible = false
    transData.value = transData.value.filter((item)=>item.id!==row.id)
    emit('del',row)
  }

  const add = ()=>{
    let id = ~~(Math.random() * 1000000).toFixed(0)
    let obj1 = Object.assign({},obj,{
      id:id,
      edit:true,
      visible:false,
    })

    for(let attr in obj1){
      let temp  = `${attr}te__mp`
      obj1[temp] = obj1[attr]
    }

    if(props.mode==='bottom'){
      transData.value.push(obj1)
    }
    if(props.mode==='top'){
      transData.value.unshift(obj1)
    }
  }

  const filterOption = (item,scope)=>{
    let obj = item.options.find(ite=>ite.value===scope.row[item.name])
    if(obj){
      return obj.label
    }
    return '--'
  }

  defineExpose({
    reset
  })
</script>
<style scoped>
.edit-input {
  padding-right: 100px;
}
.cancel-btn {
  position: absolute;
  right: 15px;
  top: 10px;
}
.inline-edit-table{
  width: 100%;
}
</style>
 Copy code 

copyright notice
author[Separated chicken],Please bring the original link to reprint, thank you.
https://en.qdmana.com/2022/04/202204292034426330.html

Random recommended