如何用Vue3实现可复制表格

广告:宝塔Linux面板高效运维的服务器管理软件 点击【 https://www.bt.cn/p/uNLv1L 】立即购买

如何用Vue3实现可复制表格

最基础的表格封装

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

import { defineComponent } from 'vue'import type { PropType } from 'vue'interface Column {  title: string;  dataIndex: string;  slotName?: string;}type TableRecord = Record<string, unknown>;export const Table = defineComponent({  props: {    columns: {      type: Array as PropType<Column[]>,      required: true,    },    dataSource: {      type: Array as PropType<TableRecord[]>,      default: () => [],    },    rowKey: {      type: Function as PropType<(record: TableRecord) => 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 (        <table>          <tr>            {props.columns.map(column => {              const { title, dataIndex } = column              return <th key={dataIndex}>{title}</th>            })}          </tr>          {props.dataSource.map((record, index) => {            return (              <tr key={getRowKey(record, index)}>                {props.columns.map((column, i) => {                  const { dataIndex, slotName } = column                  const text = record[dataIndex]                  return (                    <td key={dataIndex}>                      {getTdContent(text, record, i, slotName)}                    </td>                  )                })}              </tr>            )          })}        </table>      )    }  }})
登录后复制

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

实现复制功能

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

export const Table = defineComponent({  props: {      // ...  },  setup(props, { slots, expose }) {    // 新增,存储table节点    const tableRef = ref<HTMLTableElement | null>(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 方法即可。

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

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

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

export const Table = defineComponent({  props: {      // ...  },  setup(props, { slots, expose }) {    const tableRef = ref<HTMLTableElement | null>(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测一下功能是否正常,代码如下:

<template>  <button @click="handleCopy">点击按钮复制表格</button>  <c-table    :columns="columns"    :data-source="dataSource"    border="1"        ref="table"  >    <template #status>      <img class="status-icon" :src="arrowUpIcon" />    </template>    <template #statusOnCopy>      →    </template>  </c-table></template><script setup lang="ts">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<typeof CTable> | null>(null)const handleCopy = () => {  table.value?.copy()}</script><style scoped>.status-icon {  width: 20px;  height: 20px;}</style>
登录后复制

附上完整代码:

import { defineComponent, ref, nextTick } from 'vue'import type { PropType } from 'vue'interface Column {  title: string;  dataIndex: string;  slotName?: string;  slotNameOnCopy?: string;}type TableRecord = Record<string, unknown>;export const Table = defineComponent({  props: {    columns: {      type: Array as PropType<Column[]>,      required: true,    },    dataSource: {      type: Array as PropType<TableRecord[]>,      default: () => [],    },    rowKey: {      type: Function as PropType<(record: TableRecord) => string>,    }  },  setup(props, { slots, expose }) {    const tableRef = ref<HTMLTableElement | null>(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 (        <table ref={tableRef}>          <tr>            {props.columns.map(column => {              const { title, dataIndex } = column              return <th key={dataIndex}>{title}</th>            })}          </tr>          {props.dataSource.map((record, index) => {            return (              <tr key={getRowKey(record, index)}>                {props.columns.map((column, i) => {                  const { dataIndex, slotName, slotNameOnCopy } = column                  const text = record[dataIndex]                  return (                    <td key={dataIndex}>                      {getTdContent(text, record, i, slotName, slotNameOnCopy)}                    </td>                  )                })}              </tr>            )          })}        </table>      )    }) as unknown as { copy: typeof copy }  }})
登录后复制

以上就是如何用Vue3实现可复制表格的详细内容,更多请关注9543建站博客其它相关文章!

广告:SSL证书一年128.66元起,点击购买~~~

9543建站博客
一个专注于网站开发、微信开发的技术类纯净博客。
作者头像
admin创始人

肥猫,知名SEO博客站长,14年SEO经验。

上一篇:为什么uniapp打包总是丢失数据?怎么解决?
下一篇:怎么设置HTML

发表评论

关闭广告
关闭广告