Maison> interface Web> Voir.js> le corps du texte

如何用Vue3实现可复制表格

WBOY
Libérer: 2023-05-11 20:19:12
avant
1529 Les gens l'ont consulté

最基础的表格封装

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

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<(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 (  {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)}
) } } })
Copier après la connexion

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

实现复制功能

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

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会报错 } })
Copier après la connexion

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

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

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

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

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 } } })
Copier après la connexion

测试

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

  
Copier après la connexion

附上完整代码:

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<(record: TableRecord) => 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 } } })
Copier après la connexion

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Étiquettes associées:
source:yisu.com
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn