中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

如何用Vue3實現可復制表格

發布時間:2022-12-13 09:20:18 來源:億速云 閱讀:127 作者:iii 欄目:開發技術

這篇“如何用Vue3實現可復制表格”文章的知識點大部分人都不太理解,所以小編給大家總結了以下內容,內容詳細,步驟清晰,具有一定的借鑒價值,希望大家閱讀完這篇文章能有所收獲,下面我們一起來看看這篇“如何用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實現可復制表格”這篇文章的內容,相信大家都有了一定的了解,希望小編分享的內容對大家有幫助,若想了解更多相關的知識內容,請關注億速云行業資訊頻道。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

修武县| 汾西县| 民县| 合川市| 修文县| 长沙县| 新安县| 左云县| 芒康县| 舟山市| 塔城市| 海林市| 沙洋县| 西乡县| 沙田区| 临城县| 睢宁县| 宿州市| 宁远县| 凤山县| 河源市| 获嘉县| 德兴市| 呼和浩特市| 梧州市| 应城市| 阳泉市| 澄城县| 西华县| 宁化县| 甘泉县| 青阳县| 罗平县| 怀仁县| 波密县| 德兴市| 昆明市| 黎城县| 怀集县| 师宗县| 新安县|