如何用Vue3实现可复制表格

最基础的表格封装

最基础基础的表格封装所要做的事情就是让用户只关注行和列的数据,而不需要关注 dom 结构是怎样的,我们可以参考 antdesign,columns datasource 这两个属性是必不可少的,代码如下:

  1. import { defineComponent } from 'vue'import type { PropType } from 'vue'interface Column {  title: string;  dataIndex: string;  slotName?: string;}type TableRecord = Record;export const Table = defineComponent({  props: {    columns: {      type: Array as PropType,      required: true,    },    dataSource: {      type: Array as PropType,      default: () => [],    },    rowKey: {      type: Function as PropType string>,    }  },  setup(props, { slots }) {    const getRowKey = (record: TableRecord, index: number) => {      if (props.rowKey) {        return props.rowKey(record)      }      return record.id ? String(record.id) : String(index)    }    const getTdContent = (      text: any,      record: TableRecord,      index: number,      slotName?: string    ) => {      if (slotName) {        return slots[slotName]?.(text, record, index)      }      return text    }    return () => {      return (        
                      {props.columns.map(column => {              const { title, dataIndex } = column              return             })}                    {props.dataSource.map((record, index) => {            return (                              {props.columns.map((column, i) => {                  const { dataIndex, slotName } = column                  const text = record[dataIndex]                  return (                                      )                })}                          )          })}        
{title}
                      {getTdContent(text, record, i, slotName)}                    
      )    }  }})

登录后复制

需要关注一下的是 Column 中有一个 slotName 属性,这是为了能够自定义该列的所需要渲染的内容(在 AntDesign 中是通过 TableColumn 组件实现的,这里为了方便直接使用 slotName)。

实现复制功能

首先我们可以手动选中表格复制尝试一下,发现表格是支持选中复制的,那么实现思路也就很简单了,通过代码选中表格再执行复制命令就可以了,代码如下:

  1. export const Table = defineComponent({  props: {      // ...  },  setup(props, { slots, expose }) {    // 新增,存储table节点    const tableRef = ref(null)    // ...        // 复制的核心方法    const copy = () => {      if (!tableRef.value) return      const range = document.createRange()      range.selectNode(tableRef.value)      const selection = window.getSelection()      if (!selection) return      if (selection.rangeCount > 0) {        selection.removeAllRanges()      }      selection.addRange(range)      document.execCommand('copy')    }        // 将复制方法暴露出去以供父组件可以直接调用    expose({ copy })    return (() => {      return (        // ...      )    }) as unknown as { copy: typeof copy } // 这里是为了让ts能够通过类型校验,否则调用`copy`方法ts会报错  }})

登录后复制

这样复制功能就完成了,外部是完全不需要关注如何复制的,只需要调用组件暴露出去的 copy 方法即可。

处理表格中的不可复制元素

虽然复制功能很简单,但是这也仅仅是复制文字,如果表格中有一些不可复制元素(如图片),而复制时需要将这些替换成对应的文字符号,这种该如何实现呢?

立即学习“前端免费学习笔记(深入)”;

解决思路就是在组件内部定义一个复制状态,调用复制方法时把状态设置为正在复制,根据这个状态渲染不同的内容(非复制状态时渲染图片,复制状态是渲染对应的文字符号),代码如下:

  1. export const Table = defineComponent({  props: {      // ...  },  setup(props, { slots, expose }) {    const tableRef = ref(null)    // 新增,定义复制状态    const copying = ref(false)    // ...    const getTdContent = (      text: any,      record: TableRecord,      index: number,      slotName?: string,      slotNameOnCopy?: string    ) => {      // 如果处于复制状态,则渲染复制状态下的内容      if (copying.value && slotNameOnCopy) {        return slots[slotNameOnCopy]?.(text, record, index)      }      if (slotName) {        return slots[slotName]?.(text, record, index)      }      return text    }        const copy = () => {      copying.value = true      // 将复制行为放到 nextTick 保证复制到正确的内容      nextTick(() => {        if (!tableRef.value) return          const range = document.createRange()        range.selectNode(tableRef.value)        const selection = window.getSelection()        if (!selection) return          if (selection.rangeCount > 0) {          selection.removeAllRanges()        }        selection.addRange(range)        document.execCommand('copy')                // 别忘了把状态重置回来        copying.value = false      })    }        expose({ copy })    return (() => {      return (        // ...      )    }) as unknown as { copy: typeof copy }  }})

登录后复制

测试

最后我们可以写一个demo测一下功能是否正常,代码如下:

  1.               @@##@@                    import { ref } from 'vue'import { Table as CTable } from '../components'import arrowUpIcon from '../assets/arrow-up.svg'const columns = [ { title: '序号', dataIndex: 'serial' }, { title: '班级', dataIndex: 'class' }, { title: '姓名', dataIndex: 'name' }, { title: '状态', dataIndex: 'status', slotName: 'status', slotNameOnCopy: 'statusOnCopy' }]const dataSource = [ { serial: 1, class: '三年级1班', name: '张三' }, { serial: 2, class: '三年级2班', name: '李四' }, { serial: 3, class: '三年级3班', name: '王五' }, { serial: 4, class: '三年级4班', name: '赵六' }, { serial: 5, class: '三年级5班', name: '宋江' }, { serial: 6, class: '三年级6班', name: '卢俊义' }, { serial: 7, class: '三年级7班', name: '吴用' }, { serial: 8, class: '三年级8班', name: '公孙胜' },]const table = ref<InstanceType | null>(null)const handleCopy = () => { table.value?.copy()}.status-icon { width: 20px; height: 20px;}

登录后复制

附上完整代码:

  1. import { defineComponent, ref, nextTick } from 'vue'import type { PropType } from 'vue'interface Column {  title: string;  dataIndex: string;  slotName?: string;  slotNameOnCopy?: string;}type TableRecord = Record;export const Table = defineComponent({  props: {    columns: {      type: Array as PropType,      required: true,    },    dataSource: {      type: Array as PropType,      default: () => [],    },    rowKey: {      type: Function as PropType string>,    }  },  setup(props, { slots, expose }) {    const tableRef = ref(null)    const copying = ref(false)    const getRowKey = (record: TableRecord, index: number) => {      if (props.rowKey) {        return props.rowKey(record)      }      return record.id ? String(record.id) : String(index)    }    const getTdContent = (      text: any,      record: TableRecord,      index: number,      slotName?: string,      slotNameOnCopy?: string    ) => {      if (copying.value && slotNameOnCopy) {        return slots[slotNameOnCopy]?.(text, record, index)      }      if (slotName) {        return slots[slotName]?.(text, record, index)      }      return text    }    const copy = () => {      copying.value = true      nextTick(() => {        if (!tableRef.value) return          const range = document.createRange()        range.selectNode(tableRef.value)        const selection = window.getSelection()        if (!selection) return          if (selection.rangeCount > 0) {          selection.removeAllRanges()        }        selection.addRange(range)        document.execCommand('copy')        copying.value = false      })    }        expose({ copy })    return (() => {      return (        
                      {props.columns.map(column => {              const { title, dataIndex } = column              return             })}                    {props.dataSource.map((record, index) => {            return (                              {props.columns.map((column, i) => {                  const { dataIndex, slotName, slotNameOnCopy } = column                  const text = record[dataIndex]                  return (                                      )                })}                          )          })}        
{title}
                      {getTdContent(text, record, i, slotName, slotNameOnCopy)}                    
      )    }) as unknown as { copy: typeof copy }  }})

登录后复制如何用Vue3实现可复制表格

以上就是如何用Vue3实现可复制表格的详细内容,更多请关注【创想鸟】其它相关文章!

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。

点点赞赏,手留余香

给TA打赏
共0人
还没有人赞赏,快来当第一个赞赏的人吧!
    编程技术

    vue3怎么使用element-plus的dialog

    2025-4-1 16:32:39

    编程技术

    Vue3之副作用函数与依赖收集实例分析

    2025-4-1 16:32:45

    0 条回复 A文章作者 M管理员
    欢迎您,新朋友,感谢参与互动!
      暂无讨论,说说你的看法吧
    个人中心
    购物车
    优惠劵
    今日签到
    私信列表
    搜索