代码只提供思路,很多地方需要优化,硬搬到项目里可能会出错

表格效果

 

 代码部分

vxe-table我是全局注册的,首先自己去安装好vxe-table,版本4.4.2

<div class="tableBox">
      <div style="width: calc(100% - 40px)">
        <vxe-table
          ref="refTable"
          border
          show-overflow
          :data="tableData"
          :column-config="{ resizable: true }"
          :edit-config="{ trigger: 'click', mode: 'cell', showIcon: false }"
          :max-height="285"
          :menu-config="menuConfig"
          @menu-click="contextMenuClickEvent"
        >
          <vxe-column
            v-for="(item, index) in columns"
            :key="index"
            :field="item.field"
            :title="item.title"
            :edit-render="{ autofocus: '.vxe-input--inner' }"
            min-width="100"
          >
            <template #header>
              <div v-if="titleEdit[item.title]">
                <vxe-input
                  v-if="titleEdit[item.title]"
                  ref="inputRef"
                  v-model="titleEdit[item.title].title"
                  @blur="saves(item.title)"
                ></vxe-input>
              </div>
              <div
                v-else
                style="height: 34px; line-height: 34px"
                @click="edits(item.title)"
              >
                {{ item.title }}
              </div>
            </template>

            <template #edit="{ row }">
              <vxe-input v-model="row[item.field]" type="text"></vxe-input>
            </template>
          </vxe-column>
        </vxe-table>
      </div>
      <div class="AddColumn" @click="addColumn"><span>+</span></div>
    </div>
    <div class="AddRow" @click="addRow"><span>+</span></div>


import { defineComponent, ref, computed, reactive, nextTick, watch } from 'vue'
import { cloneDeep } from 'lodash-es'



 setup(props, { emit }) {
    const refTable = ref(null)
    const inputRef = ref()

    const tableData = ref<RowVO[]>([
      {
        id: 1,
        column1: '例:1',
        column2: '500',
        column3: '200',
        column4: '800',
        column5: '700',
        column6: '630',
      },
    ])

    const columns = ref([
      {
        id: 1,
        title: '',
        field: 'column1',
      },
      {
        id: 2,
        title: '2019-01',
        field: 'column2',
      },
      {
        id: 3,
        title: '2019-02',
        field: 'column3',
      },
      {
        id: 4,
        title: '2019-03',
        field: 'column4',
      },
      {
        id: 5,
        title: '2019-04',
        field: 'column5',
      },
      {
        id: 6,
        title: '2019-05',
        field: 'column6',
      },
    ])

   

//添加行
 const addRow = () => {
      let array = []
      const num = tableData.value.reduce((pre, cur) => {
        array.push(cur.id)
        pre = Math.max(...array)
        return pre
      }, 0)

//深拷贝第一行数据获取属性名,如果不用深拷贝会删掉表格原始数据导致下面contextMenuClickEvent函数获取不到row数据
      let columnObj = JSON.parse(JSON.stringify(tableData.value[0]))

//表格的每一条数据都会被加上_X_ROW_KEY属性,表格用来获取指定行数据,这里我们不需要
      delete columnObj._X_ROW_KEY

      let objKey = Object.keys(columnObj)

      let newData = {}

      objKey.forEach((item) => {
        if (item == 'id') {
          newData['id'] = num + 1
        } else {
          newData[item] = ''
        }
      })

      tableData.value.push(newData)
    }

//添加列
 const addColumn = () => {
      let array = []
      const num = columns.value.reduce((pre, cur) => {
        array.push(cur.id)
        pre = Math.max(...array)
        return pre
      }, 0)

      const newColumn = {
        id: num + 1,
        field: `column${num + 1}`,
        title: `column${num + 1}`,
      }
      columns.value.push(newColumn)
      tableData.value.forEach((item) => {
        item[`column${num + 1}`] = ''
      })
    }

    const titleEdit = reactive({}) //用来判断点击的表头单元格

//点击切换表头为input输入框
  const edits = (id: number) => {
      titleEdit[id] = cloneDeep(
        columns.value.filter((item) => id === item.id)[0],
      )
//自动获取焦点 vxe-table自带focus()事件
      nextTick(() => {
        inputRef.value.map((item) => {
          item.focus()
        })
      })
    }


//点击保存将 input框的值给columns  
  const saves = (id: number) => {
      if (!titleEdit[id]) return
      Object.assign(
        columns.value.filter((item) => id === item.id)[0],
        titleEdit[id],
      )
      delete titleEdit[id]
    }

 //右键菜单配置
    const menuConfig = reactive<VxeTablePropTypes.MenuConfig<RowVO>>({
      header: {
        options: [[{ code: 'removeCol', name: '删除列' }]],
      },
      body: {
        options: [[{ code: 'removeRow', name: '删除行' }]],
      },
    })
 //菜单选项处理逻辑
    const contextMenuClickEvent: VxeTableEvents.MenuClick<RowVO> = ({
      $table,
      column,
      menu,
      row,
    }) => {
      if ($table) {
        switch (menu.code) {
          case 'removeCol':

           //留一列防止删除完
            if (columns.value.length === 1) return

            columns.value = columns.value.filter(
              (item) => item.field !== column.field,
            )
            tableData.value.forEach((item) => {
              delete item[column.field]
            })

            break

          case 'removeRow':

           //留一行防止删除完
            if (tableData.value.length === 1) return

            tableData.value = tableData.value.filter(
              (item) => item.id !== row.id,
            )

            break
        }
      }
    }

//监听表头数据变化
 watch(
      () => columns.value,
      (val) => {
        console.log(val)
      },
      {
        deep: true,
        immediate: true,
      },
    )
//监听单元格数据变化
 watch(
      () => tableData.value,
      (val) => {
        console.log(val)
      },
      {
        deep: true,
        immediate: true,
      },
    )
  

    return {
      tableData,
      columns,
      addRow,
      addColumn,
      edits,
      saves,
      refTable,
      titleEdit,
      inputRef,
      menuConfig,
      contextMenuClickEvent,
    }
  },



//css部分


//表头内边距
 ::v-deep(.vxe-table--render-default .vxe-header--column:not(.col--ellipsis)) {
    padding: 5px 0;
  }
//清除表格背景
  ::v-deep(.vxe-table--header-wrapper) {
    background-color: transparent !important;
  }

.tableBox {
    // padding: 0 0 30px;
    display: flex;
    justify-content: flex-start;
    cursor: pointer;
    .AddColumn {
      display: flex;
      justify-content: center;
      align-items: center;
      border-top: 1px solid rgba(0, 0, 0, 0.15);
      border-right: 1px solid rgba(0, 0, 0, 0.15);
      border-bottom: 1px solid rgba(0, 0, 0, 0.15);
      width: 40px;
      height: auto;
      span {
        font-size: 25px;
        color: rgba(0, 0, 0, 0.25);
        user-select: none;
      }
      cursor: pointer;
    }
  }
  .AddRow {
    display: flex;
    justify-content: center;
    align-items: center;
    // margin-bottom: 30px;
    border-top: 1px solid rgba(0, 0, 0, 0.15);
    border-left: 1px solid rgba(0, 0, 0, 0.15);
    border-right: 1px solid rgba(0, 0, 0, 0.15);
    border-bottom: 1px solid rgba(0, 0, 0, 0.15);
    width: calc(100% - 40px);
    height: 40px;
    span {
      font-size: 25px;
      color: rgba(0, 0, 0, 0.25);
      user-select: none;
    }
    cursor: pointer;
  }

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐