vue3 elementPlus table skeleton 表格加載狀態的骨架屏


之前用empty 插槽實現 不夠簡潔

全網搜索只有一個 elementui 的(vue2)

https://github.com/kangyonggan/vue-elementui-skeleton

 

扒下來改了下 

原版是js的 這里換成ts了

// skeletonDirective.ts
 1 import type { App, VNode } from 'vue'
  2 /** 配置 */
  3 interface ISkeletonOption {
  4     /** 指令名 */
  5     directiveName: string
  6     /** 行數 */
  7     rows: number
  8     /** 圓角 */
  9     radius: number
 10     /** 背景顏色 */
 11     bg: string
 12 }
 13 export default {
 14     install: (Vue: App, opts?: ISkeletonOption) => {
 15         // #38BB8F
 16         // #eaebed
 17         // #74CFB1
 18         opts ??= { directiveName: 'skeleton', rows: 5, radius: 5, bg: '#eaebed' }
 19         Vue.directive(opts?.directiveName, {
 20             updated(el: HTMLElement, binding, vnode) {
 21                 let { value } = binding
 22                 if (typeof value !== 'object') {
 23                     value = { loading: value }
 24                 }
 25                 // loading為true並且el的data-skeleton="0"或者空的時候畫骨架
 26                 if (value.loading && (!el.dataset.skeleton || el.dataset.skeleton === '0')) {
 27                     el.dataset.skeleton = '1'
 28                     // el-table(自識別:寬度、列數、行高。可配置:行數、圓角、背景色)
 29                     if (el.classList.contains('el-table')) {
 30                         // 隱藏空數據提示
 31                         const totalWidth = el.clientWidth
 32                         const emptyText = el.querySelector('.el-table__empty-block') as HTMLElement
 33                         if (emptyText) {
 34                             emptyText.style.display = 'none'
 35                         }
 36 
 37                         // 計算每一列的寬度
 38                         const colsWidth = []
 39                         let usedWidth = 0
 40                         let freeCount = 0
 41                         /** vnode.children[0].children[0].children[0]  這個路徑.... */
 42                         const [children] = (vnode.children as any)[0].children[0].children as VNode[]
 43                         /** 過濾出表頭 */
 44                         const Columns = (children.children as VNode[]).filter((e: any) => e.type.name == 'ElTableColumn')
 45                         for (let i = 0; i < Columns.length; i++) {
 46                             const item = Columns[i] as any
 47                             colsWidth.push(item.component.propsOptions.width * 1)
 48                             if (item.component.propsOptions.width) {
 49                                 usedWidth += item.component.propsOptions.width * 1
 50                             } else {
 51                                 freeCount++
 52                             }
 53                         }
 54 
 55                         // 沒指定寬度的列寬 = (總寬 - 已指定的列寬總和) / 未指定列寬的個數
 56                         const autoWidth = (totalWidth - usedWidth) / freeCount
 57                         for (let i = 0; i < colsWidth.length; i++) {
 58                             if (!colsWidth[i]) {
 59                                 colsWidth[i] = autoWidth
 60                             }
 61                         }
 62 
 63                         // 在tbody中畫骨架
 64                         const tbody = el.querySelector('.el-table__body tbody')
 65                         if (tbody) {
 66                             // 行數(缺省為5)
 67                             const rows = value.rows || opts?.rows
 68                             // 骨架屏背景色(缺省為#eaebed)
 69                             const bg = value.bg || opts?.bg
 70                             // 圓角(缺省為5)
 71                             const radius = value.radius || opts?.radius
 72                             for (let i = 0; i < rows; i++) {
 73                                 const tr = document.createElement('tr')
 74                                 tr.className = 'skeleton-tr el-table__row is-animated'
 75                                 for (let j = 0; j < colsWidth.length; j++) {
 76                                     const td = document.createElement('td')
 77                                     td.className = 'cell'
 78                                     const div = document.createElement('div')
 79                                     div.style.lineHeight = '15px'
 80                                     div.style.margin = '8px 0'
 81                                     div.style.background = bg
 82                                     div.style.borderRadius = `${radius}px`
 83                                     div.style.textIndent = '-999px'
 84 
 85                                     /** 隨機寬度*/
 86                                     div.style.width = `${Math.random() * 60 + 30}%`
 87 
 88                                     div.appendChild(document.createTextNode('.'))
 89                                     td.appendChild(div)
 90                                     tr.appendChild(td)
 91                                 }
 92                                 tbody.appendChild(tr)
 93                             }
 94                         }
 95                     }
 96                 } else if (!value.loading && el.dataset.skeleton === '1') {
 97                     // loading為false並且el的data-skeleton="1"的時候刪除骨架
 98                     el.dataset.skeleton = '0'
 99 
100                     // el-table
101                     if (el.classList.contains('el-table')) {
102                         const allSkeletons = el.querySelectorAll('.skeleton-tr')
103                         const tbody = el.querySelector('.el-table__body tbody') as HTMLElement
104                         for (let i = 0; i < allSkeletons.length; i++) {
105                             tbody.removeChild(allSkeletons[i])
106                         }
107                         const emptyText = el.querySelector('.el-table__empty-block') as HTMLElement
108                         if (emptyText) {
109                             emptyText.style.display = 'block'
110                         }
111                     }
112                 }
113             }
114         })
115 
116     }
117 }

用法 全局導入

// main.ts
import skeletonDirective from '/@/****/skeletonDirective'


app.use(skeletonDirective)

vue模板中

 1 <el-table v-skeleton="loading">
 2     <el-table-column label="xxx"/>
 3     <el-table-column label="xxx"/>
 4 </el-table>
 5 
 6 
 7 <el-table v-skeleton="{loading:loading,rows:10}">
 8     <el-table-column label="xxx"/>
 9     <el-table-column label="xxx"/>
10 </el-table>

 

效果如下

 

也可以用 empty插槽實現

<el-table class="my-table">
     <el-table-column label="xxx"/>
      <el-table-column label="xxx"/>

                <template #empty>
                    <el-row v-if="loading" :gutter="20">
                        <el-col v-for="item in 4" :key="item" :span="6">
                            <el-skeleton :rows="5" animated />
                        </el-col>
                    </el-row>
                    <div v-else style="text-align: center;">暫無數據</div>
                </template>

 </el-table>



<!--樣式--> <style lang='scss' scoped> .my-table { :deep() .el-table__empty-text { margin-top: 1rem; line-height: 25px; text-align: left; width: calc(100% - 20px); } :deep() .el-empty { padding-top: 0; } } </style>

 

 

水平有限,對vnode這玩意一知半解  (學不動了)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM