可以直接编辑并保存的表格

772 阅读5分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第15天,点击查看活动详情

一、前言

今天来试试Naive库的数据表格data table,类似这种封装好的table表格,之前有试用过几个公司封装过的table,也是这种将表格的列内容,用这种option的形式,动态配置好表格的内容,封装的方式各有不同,但是感觉都一般,用起来有种得不偿失的感觉。这次试试流行naive的框架里的数据表格data-table,看上去似乎还是比较nb的,我们来试试看吧。

二、data-table

在试官方例子的过程中,真的越来越感觉在用excel表格了,确实是有点nb。

目前我将script标签里的setup去掉了

image.png

之前在setup里,和官网给的例子不太兼容,因为setup里不需要return是直接执行的,会导致许多问题。

好接下来我们开始

(1) 首先还是引入一下

我在全局引入了NDataTable和NButton这两个组件,当然也可以在文件里按需引入,但请注意似乎只会生效一个。

import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
import {
  create,
  NButton,
  NIcon,
  NIconWrapper,
  NDataTable
} from 'naive-ui'
const naive = create({
  components: [NButton, NIcon, NIconWrapper, NDataTable]
})
createApp(App).use(naive).mount('#app')

然后局部引入了vue的相关api,同时引入了naive-ui的DataTableColumns,用来撰写每一行的内容。

import type { DataTableColumns } from 'naive-ui'
import { h, defineComponent, ref, nextTick } from 'vue'
复制代码

(2)编写编辑模式

用defineComponent编写编辑模式函数,这里props负责规定好传入参数的规范,value是修改后的值,onUpdateValue则是更新函数。将这两个值传入setup函数里,等待备用。

在setup里返回一个div,这个div挂载了点击函数handleOnClick,用来修改编辑状态,若是编辑状态,则修改div里的元素为input框,若不是编辑模式,则修改div里的元素为一个普通的值。

同时在编辑模式下,返回的input框,还挂载了onChange和onBlur事件,在值发生变化,或者焦点状态发生变化时,都会触发handleChange函数,一是更新值到对应的数据里去,二是取消编辑状态。

// 编辑的模式
const ShowOrEdit = defineComponent({
    props: {
      value: [String, Number],
      onUpdateValue: [Function, Array]
    },
    setup (props) {
      const isEdit = ref(false)
      const inputRef = ref(null)
      const inputValue = ref(props.value)
      function handleOnClick () {
        isEdit.value = true
        nextTick(() => {
          inputRef.value.focus()
        })
      }
      function handleChange () {
        props.onUpdateValue(inputValue.value)
        isEdit.value = false
      }
      return () =>
        h(
          'div',
          {
            onClick: handleOnClick
          },
          isEdit.value
            ? h(NInput, {
              ref: inputRef,
              value: inputValue.value,
              onUpdateValue: (v:string) => {
                inputValue.value = v
              },
              onChange: handleChange,
              onBlur: handleChange
            })
            : props.value
        )
    }
  })

(3)用函数定义好数据的内容

这里我们用一种函数的方式定义好表格的内容,一共三行,键值对也很清晰,这里需要注意的是,需要有个唯一的值,给data-table当主键,我选用了key属性,该属性唯一,适合当主键。

const createData = () => [
  {
      key: 0,
      name: 'John Brown',
      age: '32',
      address: 'New York No. 1 Lake Park'
    },
    {
      key: 1,
      name: 'Jim Green',
      age: '42',
      address: 'London No. 1 Lake Park'
    },
    {
      key: 2,
      name: 'Joe Black',
      age: '32',
      address: 'Sidney No. 1 Lake Park'
    }
]

(4)在export default defineComponent里,书写剩余逻辑

第一步

先引用上面定义好的createData函数创建好数据, 我试过直接定义data,但对于这种在页面上会被修改到的数据,要实现双向绑定的话,vue3官网似乎是要求必须用ref声明的

const data = ref(createData()) 

第二步

在createColumns定义好每一列的内容,包括每一列的函数调用等等,比如第一二列我写成了可以自由切换编辑状态的模式,所以我需要在对应的render里调用了第二小节定义好的编辑模型ShowOrEdit,传入value值和onUpdateValue的具体函数逻辑。同时如果还挂载了其他自定义函数,比如我挂载在第四列按钮上的函数play,需要在createColumns里提前声明。

 const createColumns = ({
  play
}: {
  play: (row: Song) => void
}): DataTableColumns<Song> => {
  return [
    {
      title: 'name',
      key: 'name',
      align: 'center',
      render (row, index) {
        return h(
          ShowOrEdit,
          {
            value: row.name,
            onUpdateValue (v:string) {
              data.value[index].name = String(v)
            }
          }
        )
      }
    },
    {
      title: 'Age',
      key: 'age',
      align: 'center',
      resizable: true,
      ellipsis: {
        tooltip: true,
        lineClamp: 2,
        expandTrigger: 'click'
      },
      render (row, index) {
        return h(
          ShowOrEdit,
          {
            value: row.age,
            onUpdateValue (v:string) {
              data.value[index].age = v
            }
          }
        )
      }
    },
    {
      title: 'address',
      key: 'address',
      align: 'center',
      resizable: true,
      minWidth: 100,
      maxWidth: 500
    },
    {
      title: 'Action',
      key: 'actions',
      render (row) {
        return h(
          NButton,
          {
            strong: true,
            tertiary: true,
            size: 'small',
            onClick: () => play(row)
          },
          { default: () => 'Play' }
        )
      }
    }
  ]
}

第三步

用第二步的函数生成好columns,并且将要挂载在按钮上的play函数,具体的逻辑执行内容完善。我是挂载了useMessage组件,该组件效果和element的message效果差不多。下面函数的内容不过是将拿到的行数据里的name值,用弹窗提示的方式弹出而已。(这里useMessage的使用还有点小坑,后期跟你们说一声)

const columns = createColumns({
  play (row: Song) {
    const message = useMessage()
    message.info(`Play ${row.name}`) // 相当于vue2的message弹窗提示
  }
})

第四步

由于我们使用的export default defineComponent,所以必须像vue2那样,将需要用的值都return出来,页面上才能正常使用,不然会找不到对应的变量。

const pagination = false as const // 断言函数设置pagination为false
// 必须像vue2那样return出来
return {
  data,
  columns,
  pagination
}

第五步

在template里写好表格,key值用我们的row.key充当,其他属性基本对号入座即可。

<div v-if="data.length > 0" style="width: 1200px;height: 1200px;">
DATA-TABLE表格
<n-data-table
  :key="(row) => row.key"
  :columns="columns"
  :data="data"
  :pagination="pagination"
  :bordered="true"
/>

(5)来看看效果吧

可以看到我们之间点击内容,就会生成input框,可以编辑,可以保存,基本靠拢excel了

input框测试.gif

三、小结

今天尝试了naive的数据表格,只试用了官网的其中一个例子--可切换的可编辑表格,官网给的说法就是,不太简单,但胜在好看 。整个过程试验下来,体验感还不错,功能都让人眼前一亮,就是可能需要根据项目做一点适配,刚开始用,适配起来还有点难度,走了不少弯路,大家感兴趣可以去官网看看,例子也比较丰富,可以试着敲一敲。

ps: 我是地霊殿__三無,大家一起学习,共同进步!

Snipaste_2022-07-19_15-30-26.jpg