Heim >Web-Frontend >View.js >So implementieren Sie kopierbare Tabellen mit Vue3

So implementieren Sie kopierbare Tabellen mit Vue3

WBOY
WBOYnach vorne
2023-05-11 20:19:121658Durchsuche

Die grundlegendste Tabellenkapselung

Die einfachste Tabellenkapselung ermöglicht es Benutzern, sich nur auf Zeilen- und Spaltendaten zu konzentrieren, ohne auf die DOM-Struktur zu achten. Wir können auf verweisen AntDesign, columns dataSource Diese beiden Attribute sind wichtig. Der Code lautet wie folgt: 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(&#39;copy&#39;)
    }
    
    // 将复制方法暴露出去以供父组件可以直接调用
    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(&#39;copy&#39;)
        
        // 别忘了把状态重置回来
        copying.value = false
      })
    }
    
    expose({ copy })

    return (() => {
      return (
        // ...
      )
    }) as unknown as { copy: typeof copy }
  }
})

Was Aufmerksamkeit erfordert, ist ColumnslotName-Attribut in /code>, mit dem der Inhalt der Spalte angepasst werden kann, die gerendert werden muss (in AntDesign erfolgt dies über TableColumn code > Von der Komponente implementiert, hier wird <code>slotName der Einfachheit halber direkt verwendet.

Implementieren Sie die Kopierfunktion

Zunächst können wir die zu kopierende Tabelle manuell auswählen und ausprobieren. Wir haben festgestellt, dass die Tabelle das Auswählen und Kopieren unterstützt, sodass die Implementierungsidee sehr einfach ist und führen Sie dann den Kopierbefehl aus:

<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 &#39;vue&#39;
import { Table as CTable } from &#39;../components&#39;
import arrowUpIcon from &#39;../assets/arrow-up.svg&#39;

const columns = [
  { title: &#39;序号&#39;, dataIndex: &#39;serial&#39; },
  { title: &#39;班级&#39;, dataIndex: &#39;class&#39; },
  { title: &#39;姓名&#39;, dataIndex: &#39;name&#39; },
  { title: &#39;状态&#39;, dataIndex: &#39;status&#39;, slotName: &#39;status&#39;, slotNameOnCopy: &#39;statusOnCopy&#39; }
]

const dataSource = [
  { serial: 1, class: &#39;三年级1班&#39;, name: &#39;张三&#39; },
  { serial: 2, class: &#39;三年级2班&#39;, name: &#39;李四&#39; },
  { serial: 3, class: &#39;三年级3班&#39;, name: &#39;王五&#39; },
  { serial: 4, class: &#39;三年级4班&#39;, name: &#39;赵六&#39; },
  { serial: 5, class: &#39;三年级5班&#39;, name: &#39;宋江&#39; },
  { serial: 6, class: &#39;三年级6班&#39;, name: &#39;卢俊义&#39; },
  { serial: 7, class: &#39;三年级7班&#39;, name: &#39;吴用&#39; },
  { serial: 8, class: &#39;三年级8班&#39;, name: &#39;公孙胜&#39; },
]

const table = ref<InstanceType<typeof CTable> | null>(null)
const handleCopy = () => {
  table.value?.copy()
}
</script>

<style scoped>
.status-icon {
  width: 20px;
  height: 20px;
}
</style>

Auf diese Weise ist die Kopierfunktion nicht erforderlich. Sie müssen nur den Kopieren-Methode, die von der Komponente bereitgestellt wird. <h3></h3>Umgang mit nicht kopierbaren Elementen in der Tabelle<p></p>Obwohl die Kopierfunktion sehr einfach ist, kopiert sie nur Text. Wenn die Tabelle einige nicht kopierbare Elemente (z. B. Bilder) enthält, müssen diese durch entsprechende ersetzt werden Textsymbole beim Kopieren, wie erreicht man das? <p></p>Die Lösung besteht darin, einen Kopierstatus innerhalb der Komponente zu definieren, den Status beim Aufrufen der Kopiermethode auf „Kopieren“ festzulegen und entsprechend diesem Status unterschiedliche Inhalte zu rendern (Bilder im Nicht-Kopierstatus zu rendern und entsprechende Textsymbole im zu rendern). Zustand kopieren), Code wie folgt: 🎜<pre class="brush:js;">import { defineComponent, ref, nextTick } from &amp;#39;vue&amp;#39; import type { PropType } from &amp;#39;vue&amp;#39; interface Column { title: string; dataIndex: string; slotName?: string; slotNameOnCopy?: string; } type TableRecord = Record&lt;string, unknown&gt;; export const Table = defineComponent({ props: { columns: { type: Array as PropType&lt;Column[]&gt;, required: true, }, dataSource: { type: Array as PropType&lt;TableRecord[]&gt;, default: () =&gt; [], }, rowKey: { type: Function as PropType&lt;(record: TableRecord) =&gt; string&gt;, } }, setup(props, { slots, expose }) { const tableRef = ref&lt;HTMLTableElement | null&gt;(null) const copying = ref(false) const getRowKey = (record: TableRecord, index: number) =&gt; { 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 ) =&gt; { if (copying.value &amp;&amp; slotNameOnCopy) { return slots[slotNameOnCopy]?.(text, record, index) } if (slotName) { return slots[slotName]?.(text, record, index) } return text } const copy = () =&gt; { copying.value = true nextTick(() =&gt; { if (!tableRef.value) return const range = document.createRange() range.selectNode(tableRef.value) const selection = window.getSelection() if (!selection) return if (selection.rangeCount &gt; 0) { selection.removeAllRanges() } selection.addRange(range) document.execCommand(&amp;#39;copy&amp;#39;) copying.value = false }) } expose({ copy }) return (() =&gt; { return ( &lt;table ref={tableRef}&gt; &lt;tr&gt; {props.columns.map(column =&gt; { const { title, dataIndex } = column return &lt;th key={dataIndex}&gt;{title}&lt;/th&gt; })} &lt;/tr&gt; {props.dataSource.map((record, index) =&gt; { return ( &lt;tr key={getRowKey(record, index)}&gt; {props.columns.map((column, i) =&gt; { const { dataIndex, slotName, slotNameOnCopy } = column const text = record[dataIndex] return ( &lt;td key={dataIndex}&gt; {getTdContent(text, record, i, slotName, slotNameOnCopy)} &lt;/td&gt; ) })} &lt;/tr&gt; ) })} &lt;/table&gt; ) }) as unknown as { copy: typeof copy } } })</pre>🎜Test🎜🎜Endlich können wir eine Demo schreiben, um zu testen, ob die Funktion normal ist. Der Code lautet wie folgt: 🎜rrreee🎜Angehängt ist der vollständige Code: 🎜rrreee

Das obige ist der detaillierte Inhalt vonSo implementieren Sie kopierbare Tabellen mit Vue3. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Dieser Artikel ist reproduziert unter:yisu.com. Bei Verstößen wenden Sie sich bitte an admin@php.cn löschen