最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

利用Vue3實(shí)現(xiàn)可復(fù)制表格的方法詳解

 更新時(shí)間:2022年12月12日 16:09:28   作者:何期驟雨降青霄  
表格是前端非常常用的一個(gè)控件,本文主要為大家介紹了Vue3如何實(shí)現(xiàn)一個(gè)簡(jiǎn)易的可以復(fù)制的表格,文中的示例代碼講解詳細(xì),需要的可以參考一下

前言

表格是前端非常常用的一個(gè)控件,但是每次都使用v-for指令手動(dòng)繪制tr/th/td這些元素是非常麻煩的。同時(shí),基礎(chǔ)的 table 樣式通常也是不滿足需求的,因此一個(gè)好的表格封裝就顯得比較重要了。

最基礎(chǔ)的表格封裝

最基礎(chǔ)基礎(chǔ)的表格封裝所要做的事情就是讓用戶只關(guān)注行和列的數(shù)據(jù),而不需要關(guān)注 DOM 結(jié)構(gòu)是怎樣的,我們可以參考 AntDesign,columns dataSource 這兩個(gè)屬性是必不可少的,代碼如下:

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>
      )
    }
  }
})

需要關(guān)注一下的是 Column 中有一個(gè) slotName 屬性,這是為了能夠自定義該列的所需要渲染的內(nèi)容(在 AntDesign 中是通過(guò) TableColumn 組件實(shí)現(xiàn)的,這里為了方便直接使用 slotName)。

實(shí)現(xiàn)復(fù)制功能

首先我們可以手動(dòng)選中表格復(fù)制嘗試一下,發(fā)現(xiàn)表格是支持選中復(fù)制的,那么實(shí)現(xiàn)思路也就很簡(jiǎn)單了,通過(guò)代碼選中表格再執(zhí)行復(fù)制命令就可以了,代碼如下:

export const Table = defineComponent({
  props: {
      // ...
  },
  setup(props, { slots, expose }) {
    // 新增,存儲(chǔ)table節(jié)點(diǎn)
    const tableRef = ref<HTMLTableElement | null>(null)

    // ...
    
    // 復(fù)制的核心方法
    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')
    }
    
    // 將復(fù)制方法暴露出去以供父組件可以直接調(diào)用
    expose({ copy })

    return (() => {
      return (
        // ...
      )
    }) as unknown as { copy: typeof copy } // 這里是為了讓ts能夠通過(guò)類型校驗(yàn),否則調(diào)用`copy`方法ts會(huì)報(bào)錯(cuò)
  }
})

這樣復(fù)制功能就完成了,外部是完全不需要關(guān)注如何復(fù)制的,只需要調(diào)用組件暴露出去的 copy 方法即可。

處理表格中的不可復(fù)制元素

雖然復(fù)制功能很簡(jiǎn)單,但是這也僅僅是復(fù)制文字,如果表格中有一些不可復(fù)制元素(如圖片),而復(fù)制時(shí)需要將這些替換成對(duì)應(yīng)的文字符號(hào),這種該如何實(shí)現(xiàn)呢?

解決思路就是在組件內(nèi)部定義一個(gè)復(fù)制狀態(tài),調(diào)用復(fù)制方法時(shí)把狀態(tài)設(shè)置為正在復(fù)制,根據(jù)這個(gè)狀態(tài)渲染不同的內(nèi)容(非復(fù)制狀態(tài)時(shí)渲染圖片,復(fù)制狀態(tài)是渲染對(duì)應(yīng)的文字符號(hào)),代碼如下:

export const Table = defineComponent({
  props: {
      // ...
  },
  setup(props, { slots, expose }) {
    const tableRef = ref<HTMLTableElement | null>(null)
    // 新增,定義復(fù)制狀態(tài)
    const copying = ref(false)

    // ...
    const getTdContent = (
      text: any,
      record: TableRecord,
      index: number,
      slotName?: string,
      slotNameOnCopy?: string
    ) => {
      // 如果處于復(fù)制狀態(tài),則渲染復(fù)制狀態(tài)下的內(nèi)容
      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
      // 將復(fù)制行為放到 nextTick 保證復(fù)制到正確的內(nèi)容
      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')
        
        // 別忘了把狀態(tài)重置回來(lái)
        copying.value = false
      })
    }
    
    expose({ copy })

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

測(cè)試

最后我們可以寫一個(gè)demo測(cè)一下功能是否正常,代碼如下:

<template>
  <button @click="handleCopy">點(diǎn)擊按鈕復(fù)制表格</button>
  <c-table
    :columns="columns"
    :data-source="dataSource"
    border="1"
    style="margin-top: 10px;"
    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: '序號(hào)', dataIndex: 'serial' },
  { title: '班級(jí)', dataIndex: 'class' },
  { title: '姓名', dataIndex: 'name' },
  { title: '狀態(tài)', dataIndex: 'status', slotName: 'status', slotNameOnCopy: 'statusOnCopy' }
]

const dataSource = [
  { serial: 1, class: '三年級(jí)1班', name: '張三' },
  { serial: 2, class: '三年級(jí)2班', name: '李四' },
  { serial: 3, class: '三年級(jí)3班', name: '王五' },
  { serial: 4, class: '三年級(jí)4班', name: '趙六' },
  { serial: 5, class: '三年級(jí)5班', name: '宋江' },
  { serial: 6, class: '三年級(jí)6班', name: '盧俊義' },
  { serial: 7, class: '三年級(jí)7班', name: '吳用' },
  { serial: 8, class: '三年級(jí)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 }
  }
})

到此這篇關(guān)于利用Vue3實(shí)現(xiàn)可復(fù)制表格的方法詳解的文章就介紹到這了,更多相關(guān)Vue3可復(fù)制表格內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 淺析vue cli3 封裝Svgicon組件正確姿勢(shì)(推薦)

    淺析vue cli3 封裝Svgicon組件正確姿勢(shì)(推薦)

    這篇文章主要介紹了vue cli3 封裝Svgicon組件正確姿勢(shì),本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-04-04
  • 使用vue-cli創(chuàng)建vue2項(xiàng)目的實(shí)戰(zhàn)步驟詳解

    使用vue-cli創(chuàng)建vue2項(xiàng)目的實(shí)戰(zhàn)步驟詳解

    相信大部分Vue開(kāi)發(fā)者都使用過(guò)vue-cli來(lái)構(gòu)建項(xiàng)目,它的確很方便,但對(duì)于很多初級(jí)開(kāi)發(fā)者來(lái)說(shuō),還是要踩不少坑的,下面這篇文章主要給大家介紹了關(guān)于使用vue-cli創(chuàng)建vue2項(xiàng)目的實(shí)戰(zhàn)步驟,需要的朋友可以參考下
    2023-01-01
  • vue項(xiàng)目中自動(dòng)導(dǎo)入svg并愉快的使用方式

    vue項(xiàng)目中自動(dòng)導(dǎo)入svg并愉快的使用方式

    這篇文章主要介紹了vue項(xiàng)目中自動(dòng)導(dǎo)入svg并愉快的使用方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-11-11
  • Vue自定義指令的使用實(shí)例介紹

    Vue自定義指令的使用實(shí)例介紹

    作為使用Vue的開(kāi)發(fā)者,我們對(duì)Vue指令一定不陌生,諸如v-model、v-on、等,同時(shí)Vue也為開(kāi)發(fā)者提供了自定義指令的api,熟練的使用自定義指令可以極大的提高了我們編寫代碼的效率,讓我們可以節(jié)省時(shí)間開(kāi)心的摸魚
    2023-04-04
  • vue中重定向redirect:‘/index‘,不顯示問(wèn)題、跳轉(zhuǎn)出錯(cuò)的完美解決

    vue中重定向redirect:‘/index‘,不顯示問(wèn)題、跳轉(zhuǎn)出錯(cuò)的完美解決

    這篇文章主要介紹了vue中重定向redirect:‘/index‘,不顯示問(wèn)題、跳轉(zhuǎn)出錯(cuò)的完美解決方案,本文通過(guò)圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧
    2020-09-09
  • Vue.js點(diǎn)擊切換按鈕改變內(nèi)容的實(shí)例講解

    Vue.js點(diǎn)擊切換按鈕改變內(nèi)容的實(shí)例講解

    今天小編就為大家分享一篇Vue.js點(diǎn)擊切換按鈕改變內(nèi)容的實(shí)例講解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2018-08-08
  • Vue3中解決組件間css層級(jí)問(wèn)題的最佳實(shí)踐

    Vue3中解決組件間css層級(jí)問(wèn)題的最佳實(shí)踐

    <Teleport> 是 Vue 3 中引入的一個(gè)內(nèi)置組件,用于將組件的內(nèi)容渲染到 DOM 中的指定位置,而不受組件層級(jí)結(jié)構(gòu)的限制,本文給大家介紹了Vue3使用Teleport解決組件間css層級(jí)問(wèn)題的最佳實(shí)踐,需要的朋友可以參考下
    2025-02-02
  • Vuepress使用vue組件實(shí)現(xiàn)頁(yè)面改造

    Vuepress使用vue組件實(shí)現(xiàn)頁(yè)面改造

    這篇文章主要為大家介紹了Vuepress使用vue組件實(shí)現(xiàn)頁(yè)面改造示例過(guò)程詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-07-07
  • vue.js計(jì)算屬性computed用法實(shí)例分析

    vue.js計(jì)算屬性computed用法實(shí)例分析

    這篇文章主要介紹了vue.js計(jì)算屬性computed用法,結(jié)合實(shí)例形式分析了vue.js使用computed方式進(jìn)行屬性計(jì)算的相關(guān)操作技巧,需要的朋友可以參考下
    2018-07-07
  • vue3數(shù)組或?qū)ο筚x值不更新解決方法示例

    vue3數(shù)組或?qū)ο筚x值不更新解決方法示例

    這篇文章主要為大家介紹了vue3數(shù)組或?qū)ο筚x值不更新解決方法示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-11-11

最新評(píng)論

阳新县| 广灵县| 九江县| 望奎县| 易门县| 革吉县| 观塘区| 青阳县| 保康县| 报价| 绥宁县| 长顺县| 宜兰县| 舟山市| 扶沟县| 克拉玛依市| 繁峙县| 合肥市| 锦州市| 观塘区| 夏津县| 疏附县| 洪雅县| 镇雄县| 河间市| 合山市| 桐梓县| 襄城县| 鞍山市| 石嘴山市| 河北省| 泸水县| 凉城县| 隆林| 南昌市| 贵定县| 曲靖市| 乌鲁木齐县| 会东县| 嘉义市| 洪泽县|