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这玩意一知半解  (学不动了)

posted @ 2022-04-05 01:23  兽妞  阅读(1554)  评论(0编辑  收藏  举报