效果图如下:

涉及到技术点包括:

父组件给子组件传值props、动态赋值 refs 对DOM进行操作,slot具名插槽的使用,插槽通过slot-scope给父组件传值

父组件代码:

<template>
    <div class="body">
      <div class="table">
        <div class="filter font-bold">vue自定义Table表格组件</div>
        <div class="padding-left-l padding-right-l">
          <my-table :fields="fields" :data="tableData" :opFields="opFields">
            <template slot="opField1" slot-scope="val">
                <span class="pointer color-blue" @click="share" :data-id="val.recordId">分享</span>
                <span class="margin-left-l color-blue pointer">更多</span>
            </template>  
          </my-table>
        </div>  

      </div>
    </div>
</template>

<script>
/*
       名称:vue自定义Table表格组件
       功能:封装Html的table,可直接编辑单元格内容,光标失去焦点时更新表格数据,利用slot插槽绑定操作按钮(编辑,删除、分享等)
            涉及到技术点包括:父组件给子组件传值、refs 对DOM进行操作,slot具名插槽的使用,插槽通过slot-scope给父组件传值            
       作者:唐赢   
       时间:2023-1-14
*/
  import MyTable from '@/components/Table' //引入我们写好的递归组件
  export default {
    name: 'Main',
    components: {
      MyTable
    },
    data () {
      return {

        tableData: [
            {
              "id":1,
              "name": "关于放假的通知.pdf",
              "createUser":"唐赢",
              "createTime":"2023-1-15"
            },
            {
              "id":2,
              "name": "关于复课的通知.pdf",
              "createUser":"唐赢",
              "createTime":"2023-2-15",
            }
          ],
          fields: [
            {title:'名称',fieldName:'name',width:'',canEdit: true},
            {title:'创建时间',fieldName:'createTime',width:'15%',canEdit: false},
            {title:'创建人',fieldName:'createUser',width:'10%',canEdit: false}
          ],
          opFields : [
            {name:'opField1', title:"操作", width:'25%'}
          ]
        
      }
    },
    methods: {
      share(event) {
      let id = event.target.getAttribute("data-id");
        alert("您要分享的id是:" + id);
      }
       
      
    }
  }
</script>

  <!-- Add "scoped" attribute to limit CSS to this component only -->
  <style scoped>
  .body {
    display: flex;
    justify-content: center;
    margin-top: 73px;
    width: 100%;    
  }
  .table {
    background-color: #fff;
    width: 1080px;
    min-height: 800px;
    box-shadow: 0px 3px 6px rgba(0, 0, 0, .1);
    margin-bottom: 10px;
  }
  .filter {
    display: flex;
    height: 60px;
    align-items:center;
    background-color: #fff;
    font-size: 18px;
    justify-content: center;
    border-bottom: 1px solid rgba(0, 0, 0, .2);;
  }

  </style>
  

子组件代码

<template>
    <table border="0" cellspacing="0" cellpadding="4" bgcolor="#fff"  align="left" width="100%"  style="line-height:28px">
      <tr>
        <th v-for="item in fields" align="left" :width="item.width" >{{item.title}}</th>
        <th v-for="item in opFields" align="left" :width="item.width" >{{item.title}}</th>
      </tr>
      <tr v-for="record in data" >
        <td v-for="field in fields">
            <span v-if="!showInput(record, field)" :ref="'text_' + field.fieldName+'_' + record.id">{{getValue(record, field)}}</span>
            <span v-if="!showInput(record, field) && field.canEdit" class="margin-left-l pointer">
                <img class="logo-18" src="@/assets/images/edit.png" @click="fileNameEdit" :data-field="field.fieldName" :data-id="record.id"/>
            </span>
            <input v-if="showInput(record, field)" type="text" v-model="fileName" @blur="fileNameBlur" :ref="'input_' + field.fieldName+'_' + record.id">            
        </td>
        <td v-for="field in opFields">
            <slot :name="field.name" :recordId="record.id"></slot>
        </td>       
      </tr>
    </table>
</template>
<script>

export default {
    name: 'MyTable',  
    props: {
        opFields: {},
        fields: {},
        data:{}
    },
    data() {
        return {
            editMode: false,
            editId: 0,
            editName:'',
            fileName:""
        }
    },
    computed: {

    },
    methods: {

      getValue(record, field) {
        return record[field.fieldName];

      },
      showInput(record, field) {
        return record.id == this.editId && field.fieldName == this.editName;
      },
      fileNameEdit(event) {

        let id = event.target.getAttribute("data-id");
        let fieldName = event.target.getAttribute("data-field");
        this.$nextTick(() => {
            console.log('text_' + fieldName + '_' + id)
            let value = this.$refs['text_' + fieldName + '_' + id][0].innerHTML;
            this.fileName = value;
        })
        this.editMode = true;
        this.editId = id;
        this.editName = fieldName;
        this.$nextTick(() => {
            let value = 'input_' + fieldName + '_' + id;
            this.$refs[value][0].focus();

        })
      },
      fileNameBlur(event) {
        this.data.forEach((i,index) => {           
            if (i.id == this.editId) {
                i[this.editName] = this.fileName;
            }
        })
        console.log(this.data);
        this.editMode = false;
        this.editId = 0;
        this.editName = "";
        console.log(event);
      }
        
    }
}
</script>
<style scoped>
    td{
        border-bottom: 1px solid rgba(0, 0, 0, .1) ;
    
    }

    th{
        border-bottom: 1px solid rgba(0, 0, 0, .1) ;
    }

    input {
        border-radius: 5px;
        padding-left: 10px;
        padding-right: 10px;
        font-size: 14px;
        width: 95%;
        height: 25px;
        border: 1px solid #0070f9 ;
    }
</style>

要点说明:

1.插槽slot

子组件的id值绑定到插槽

父组件通过slot-scope(vue3是scope)获取插槽值到 val, <span>元素的data-id属性引用该值,以便点击事件时获取该值

2.动态赋值refs

对每行不同id,不同字段的dom元素进行ref标识

点编辑时获取对应的id,获取当前值,开启编辑编辑状态后,将文本框进行赋值,要注意的时,组件在v-if=‘false’时,refs是无法找到

操作演示地址:https://lusun.com/v/zaI2Jp2FvZG

源码地址:https://download.csdn.net/download/gdgztt/87388383

Logo

前往低代码交流专区

更多推荐