Vue可编辑表格的实现——单元格编辑表格

一.准备阶段

1.在views中创建table实例页并配置路由
<template>
  <div>
  </div>
</template>

<script>
import { getTableData } from '@/api/data'
export default {
  data () {
    return {
      tableData: [],
      columns: [
        { key: 'name', title: '姓名' },
        { key: 'age', title: '年龄', editable: true },
        { key: 'email', title: '邮箱', editable: true }
      ]
    }
  },
  mounted () {
    getTableData().then(res => {
      this.tableData = res.data
    })
  }
}
</script>

2.在api文件夹中创建data.js,用来获取表格数据
import axios from './index'

export const getTableData = () => {
  return axios.request({
    url: '/getTableData',
    method: 'get'
  })
}

3.在mock文件夹中的response文件夹创建data.js,用来生成数据
import { doCustomTimes } from '@/lib/tools'
import Mock from 'mockjs'

export const getTableData = () => {
  const template = {
    name: '@name',
    'age|18-25': 0,
    email: '@email'
  }
  let arr = []
  doCustomTimes(5, () => {
    arr.push(Mock.mock(template))
  })
  return arr
}

4.在mock文件夹中的inedx.js文件中进行配置
import Mock from 'mockjs'
import { getTableData } from './response/data'
Mock.mock(/\/getTableData/, 'get', getTableData)
export default Mock
5.在lib文件夹创建tools.js,配置方法
export const doCustomTimes = (times, callbakc) => {
  let i = -1
  while (++i < times) {
    callbakc()
  }
}

自此我们可以获取到mock生成的随机数据

6.在components文件夹中新建edit-table文件夹,在其中新建index.js和edit-table并进行配置

index.js

<template>
<!-- 有两个重要值,一个定义列的数组(在table.vue中的colums),一个定义数据的数组 -->
  <Table :columns="columns" :data="value"></Table>
</template>
<script>
export default {
  name: 'EditTable',
  props: {
    columns: {
      type: Array,
      default: () => []
    },
    value: {
      type: Array,
      default: () => []
    }
  }
}
</script>

7.在table.vue父组件中传入并注册组件
import EditTable from '_c/edit-table'

 components: {
    EditTable
  }
<template>
  <div>
    <edit-table :colums='colums' :data="tableData"></edit-table>
  </div>
</template>

二.单元格编辑表格

1.由于我们不能直接修改父组件传过来的数据,需要在子组件中抛出一个事件,父组件接受这个事件,在事件的回调函数里进行相关操作。我们在edit-table子组件中定义一个insideColumns

mounted () {
    // console.log(this.columns)
    // 使用map方法做映射,item代表每列对象
    const insideColumns = this.columns.map(item => {
      // // 如果传进来的item对象有render对象,直接return
      // if (item.render) return item
      // // 如果没有则判断editable是否为true,
      // else if (!item.editable) return item
      if (!item.render && item.editable) {
        item.render = (h, { row, index, column }) => {
          console.log(row, index, column)
          // 使用JSX写法,直接在括号在写标签 ,注意括号在包括的所有内容必须有有一个大标签包裹
          // 逻辑和变量都要用花括号包裹
          // 最外层需要设置一个根元素
          return (
            <div>
              {row[column.key]}
            </div>
          )
        }
        return item
      } else return item
    })
    this.insideColumns = insideColumns
  }
  <Table :columns="insideColumns" :data="value"></Table>

在data中定义

data () {
    return {
      insideColumns: [],
      // 使用表格的行号和key值即可以确定一个单元格
      edittingId: ''
    }
  }

2.如何拿到当前点击按钮的元素,并为其设置id
定义method中的方法,验证当前单元格

methods: {
    handleClick ({ row, index, column }) {
      console.log({ row, index, column })
    }
  }

定义一个按钮

<div>
   {row[column.key]}
   <i-button on-click={this.handleClick.bind(this, { row, index, column })}>编辑</i-button>
</div>

至此我们得到了当前点击的元素,接下来给它修改edittingId
3.有了edittingId ,我们就可以判断当前单元格的具体位置

 methods: {
    handleClick ({ row, index, column }) {
      console.log({ row, index, column })
      this.edittingId = `${column.key}_${index}`
    }
  },

4.继续配置内容
配置input控件

<div>
	{row[column.key]}
	<i-button on-click={this.handleClick.bind(this, { row, index, column })}>编辑</i-button>
	<i-input value={row[column.key]} style="width: 50px;"></i-input>
</div>

效果如图
Vue可编辑表格的实现——单元格编辑表格_第1张图片
5.由于按钮和表单不能同时存在,需要进行判断

return (
     <div>
       {this.edittingId === `${column.key}_${index}` ?
        <i-input value={row[column.key]} style="width: 50px;"></i-input>
        : <i-button on-click={this.handleClick.bind(this, { row, index, column })}>编辑</i-button>}
        {row[column.key]}
     </div>
       )

Vue可编辑表格的实现——单元格编辑表格_第2张图片
6.如何将修改内容替换?
6.1首先给input绑定一个on-input事件

<i-input value={row[column.key]} style="width: 50px;" on-input={this.handleInput}></i-input>

6.2在data中定义编辑的内容

data () {
    return {
      insideColumns: [],
      // 使用表格的行号和key值即可以确定一个单元格
      edittingId: '',
      // 每次输入内容时就放在edittingContent中
      edittingContent: ''
    }
  }

6.3在methods中定义

  methods: {
    handleClick ({ row, index, column }) {
      console.log({ row, index, column })
      this.edittingId = `${column.key}_${index}`
    },
    handleInput (newValue) {
      this.edittingContent = newValue
    }
  },

6.4设置按钮的状态值编辑和保存的切换
将判断条件保存为常量,便于重复使用

item.render = (h, { row, index, column }) => {
          const isEditting = this.edittingId === `${column.key}_${index}`
<div>
              { isEditting ? <i-input value={row[column.key]} style="width: 50px;" on-input={this.handleInput}></i-input> : <span>{row[column.key]}</span>}
              <i-button on-click={this.handleClick.bind(this, { row, index, column })}>{isEditting ? '保存' : '编辑'}</i-button>
            </div>

6.5在handleClick中判断当前点击为保存状态还是编辑状态
6.5.1由于要修改tableData,我们用v-model绑定tableData
在父组件table.vue中

    <edit-table :columns='columns' v-model="tableData"></edit-table>

在子组件edit-table中

<Table :columns="insideColumns" :data="value"></Table>

并将props中的值设置为value

props: {
    columns: {
      type: Array,
      default: () => []
    },
    value: {
      type: Array,
      default: () => []
    }
  },

6.5.2修改this.value,不能直接修改,智能修改value深拷贝后的数组,这里我们下载clonedeep插件
安装好clonedeep插件后引入

import clonedeep from 'clonedeep'

在edit-table.vue页面中

handleClick ({ row, index, column }) {
      console.log({ row, index, column })
      if (this.edittingId === `${column.key}_${index}`) {
        // 如果为true说明当前点击的按钮为编辑状态,需要定义点击按钮保存逻辑
        let tableData = clonedeep(this.value)
        tableData[index][column.key] = this.edittingContent
        // 触发一个$emit事件,把修改好的tableData数组传给父组件
        this.$emit('input', tableData)
        // 保存当前编辑元素的数据,导出,newValue用来告诉父组件更新后的值是什么
        this.$emit('on-edit', { row, index, column, newValue: this.edittingContent })
        // 点击保存后应该将edittingId设为空字符串,按钮值变为编辑状态
        this.edittingId = ''
        this.edittingContent = ''
      } else {
        this.edittingId = `${column.key}_${index}`
      }
    }

6.5.3在父组件中接受on-edit事件

    <edit-table :columns='columns' v-model="tableData" @on-edit="handleEdit"></edit-table>

methods: {
    handleEdit ({ row, index, column, newValue }) {
      console.log({ row, index, column, newValue })
    }
  }

6.5.4如果我们从后端获取了新的数据是的表头发生了变化,columns的值会发生更新,我们在edit-table.vue中使用watch监听数据
把mounted中的逻辑封装起来

handleColumns () {
    // console.log(this.columns)
    // 使用map方法做映射,item代表每列对象
      const insideColumns = this.columns.map(item => {
      // // 如果传进来的item对象有render对象
      // if (item.render) return item
      // // 如果没有则判断editable是否为true,
      // else if (!item.editable) return item
        if (!item.render && item.editable) {
          item.render = (h, { row, index, column }) => {
            const isEditting = this.edittingId === `${column.key}_${index}`
            console.log(row, index, column)
            // 使用JSX写法,直接在括号在写标签 ,注意括号在包括的所有内容必须有有一个大标签包裹
            // 逻辑和变量都要用花括号包裹
            // 最外层需要设置一个根元素
            return (
              <div>
                { isEditting ? <i-input value={row[column.key]} style="width: 50px;" on-input={this.handleInput}></i-input> : <span>{row[column.key]}</span>}
                <i-button on-click={this.handleClick.bind(this, { row, index, column })}>{isEditting ? '保存' : '编辑'}</i-button>
              </div>
            )
          }
          return item
        } else return item
      })
      this.insideColumns = insideColumns
    }

在mounted中执行一次

在这里插入代码片

如果mounted更新,我们再执行一次

watch: {
    columns () {
      this.handleColumns()
    }
  }

实现效果如图
Vue可编辑表格的实现——单元格编辑表格_第3张图片

你可能感兴趣的:(Vue,vue,js)