• 【Vue3+Vite+Ts+element-plus】 使用tsx 动态表格封装


    系列文章目录

    【Vue3+Vite+Ts+element-plus】
    超级详细 最新 vite4+vue3+ts+element-plus+eslint-prettier 项目搭建流程
    【Vue3+Vite+Ts+element-plus】使用tsx实现左侧栏菜单无限层级封装
    【Ts 系列】
    TypeScript 从入门到进阶之基础篇(一) ts类型篇



    前言

    在我们日常开发中 会经常用到表格 特别是后台管理系统 几乎大部分页面都涉及到表格,使用好的表格封装能为我们节省很多开发时间,也能让代码清晰易懂,接下来我们将从零使用tsx 二次封装一下element-plus的表格。

    一、必要插件安装

    我们需要创建一个vue3项目 我用的是vite去创建vue3+ts 项目的 ,这里就不多讲项目的创建了 ,如果要详细的项目搭建流程可参考:超级详细 最新 vite4+vue3+ts+element-plus+eslint-prettier 项目搭建流程
    在使用tsx之前 我们要安装一些插件 使我们的项目支持tsx

    1.安装

    //下面3种安装方式选择一种 推荐pnpm
    yarn add @vitejs/plugin-vue-jsx
    //or
    npm install @vitejs/plugin-vue-jsx -D
    //or
    pnpm install @vitejs/plugin-vue-jsx -D
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    2.配置

    在 vite.config.ts 文件中挂载

    import vueJsx from '@vitejs/plugin-vue-jsx'
    export default defineConfig({
     plugins: [ vueJsx()]
    })
    
    • 1
    • 2
    • 3
    • 4

    tsconfig.json 文件中

    {
      // include 需要包含tsx
    "include": ["src/*", "src/**/*.vue", "src/**/*.tsx", "src/**/*.jsx", "src/**/*.ts", "src/**/*.js"],
     "compilerOptions": {
        // 在.tsx文件里支持JSX
        "jsx": "preserve",
     }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    二、table 封装实现步骤

    1.创建

    首先我们在项目的 components 文件夹下 新建 DynamicTable 文件夹, 并在DynamicTable* 文件夹下新建 DynamicTable.vue文件

    2.封装

    在封装表格之前 我们要了解 在通常的表格中 有哪些情况会出现 例如: 表格可能涉及到分页 、可能涉及到接口的请求、也可能表格中的每一行 可内容自定义 等情况 。首先我们先将表格和分页写出来先 代码如下

    <script lang="tsx">
    import { ElTable } from 'element-plus'
    export default {
      props: {
        //表格数据
        tableData: {
          type: Array,
          default: () => [],
        },
        //表格列数据
        tableColumns: {
          type: Array,
          default: () => [],
        },
        //表格配置项
        configuration: {
          type: Object,
          default: () => ({}),
        },
        //分页下拉选择
        pageSizes: {
          type: Array,
          default: () => [10, 20, 50, 100],
        },
        //分页组件的样式
        paginationLayout: {
          type: String,
          default: () => 'total,sizes,prev, pager, next,jumper',
        },
        //是否为分页按钮添加背景色
        background: {
          type: Boolean,
          default: true,
        },
        //请求函数
        request: {
          type: Function,
          default: () => {
            return Function
          },
        },
        //是否开启单选
        highlightCurrentRow: {
          type: Boolean,
          default: () => {
            return false
          },
        },
        //是否在打开页面时默认加载表格请求 默认为 true
        isLoadRequest: {
          type: Boolean,
          default: () => {
            return true
          },
        },
        //表格勾选的数据
        selectedList: {
          type: Array,
          default: () => {
            return []
          },
        },
        //获取表格勾选的行数据
        getAllSelectedList: {
          type: Array,
          default: () => {
            return []
          },
        },
        //表格行禁用
        tableDisable: {
          type: Function,
          default: () => {
            return Function
          },
        },
        //分页是否只有一页时隐藏
        hideOnSinglePage: {
          type: Boolean,
          default: () => {
            return false
          },
        },
        //是否自定义转换
        isCustomConversion: {
          type: Boolean,
          default: () => {
            return false
          },
        },
        //返回转换好的数据
        customConversion: {
          type: Function,
          default: () => {
            return Function
          },
        },
        //是否自定义分页
        isCustomPage: {
          type: Boolean,
          default: () => {
            return false
          },
        },
        // 接收自定义分页返回的参数
        customPage: {
          type: Function,
          default: () => {
            return Function
          },
        },
      },
      emits: [
        'handleSelectionChange',
        'update:selectedList',
        'update:getAllSelectedList',
        'update:tableData',
        'getResultData',
        'selectable',
      ],
      setup(props, { slots, expose, emit }) {
        const singleTableRef = ref<InstanceType<typeof ElTable>>()
    
        //表格加载
        const loading = ref(false)
    
        const clientWidth = ref(document.documentElement.clientWidth)
    
        onMounted(() => {
          window.onresize = () => {
            return (() => {
              clientWidth.value = document.documentElement.clientWidth
            })()
          }
        })
    
        const getWidth = (width: string) => {
          return `${clientWidth.value * (parseInt(width) / 1440.0)}px`
        }
    
        //设置表格 Column/
        const setTableColumn = () => {
          return props.tableColumns.map((item: any) => {
            if (!item.slot) {
              return (
                <el-table-column
                  {...item}
                  width={
                    item.type === 'selection' ? getWidth(item.width) : item.width
                  }
                  selectable={selectable}
                ></el-table-column>
              )
            } else {
              return (
                <el-table-column
                  {...item}
                  selectable={selectable}
                  width={
                    item.slotName === 'operation'
                      ? getWidth(item.width)
                      : item.width
                  }
                >
                  {{
                    default: (scope: any) => slots[item.slotName]?.(scope),
                  }}
                </el-table-column>
              )
            }
          })
        }
    
        /**
         * 分页相关配置
         */
        //表格数据
        const tableData = ref(props.tableData as any[])
        //第几页
        const currentPage = ref(1)
        //每页的数量
        const pageSize = ref(10)
        //总数量
        const total = ref(props.tableData.length)
    
        const getData = ref({})
    
        watch(
          props.tableData,
          (nel) => {
            tableData.value = nel
          },
          { deep: true },
        )
    
        //数据请求
        const refreshTable = async (datas: any = {}, reset = true) => {
          if (reset) {
            currentPage.value = 1
          }
          loading.value = true
          getData.value = datas
          try {
            const { result }: any = await props.request({
              ...datas,
              pageNo: props.isCustomPage ? 1 : currentPage.value,
              pageSize: pageSize.value,
            })
    
            const { totalPage }: any = result.pageInfo
            //判断要跳转的页是否没有了 是的话回到接口返回的最后一页
            if (!reset && totalPage < currentPage.value && currentPage.value > 1) {
              currentPage.value = totalPage
              refreshTable(datas, reset)
            }
    
            //表格数据转换赋值
            tableData.value = result
            emit('getResultData', result, tableData.value)
            //总条数赋值
            total.value = props.isCustomPage
              ? props.customPage()
              : result.pageInfo && result.pageInfo.totalSize
              ? Number(result.pageInfo.totalSize)
              : 0
            loading.value = false
          } catch (err) {
            tableData.value = []
            loading.value = false
          }
        }
    
        //是否默认开启请求
        props.isLoadRequest ? refreshTable(getData.value) : ''
    
        onMounted(() => {
          getData.value = {}
        })
    
        //page-size 改变时触发
        const handleSizeChange = (val: number) => {
          console.log(`${val} items per page`)
          refreshTable(getData.value, false)
        }
    
        //current-page 改变时触发
        const handleCurrentChange = (val: number) => {
          console.log(`current page: ${val}`)
          refreshTable(getData.value, false)
        }
    
        //勾选事件
        const selectedList = ref<string[]>([])
        const handleSelectionChange = (val: any) => {
          selectedList.value = val.map((item: any) => item.id)
          emit('update:getAllSelectedList', val)
          emit('update:selectedList', selectedList.value)
        }
    
        const selectable = (row: any) => {
          return props.tableDisable(row)
        }
    
        //选中项高亮
        const tableRowClassName = ({ row }: { row: any }) => {
          return selectedList.value.indexOf(row.id) ? 'warning-row' : 'success-row'
        }
    
        /**获取页数 和每页数量 */
        const getPageSizeAndCurrentPage = () => {
          return {
            currentPage: currentPage.value,
            pageSize: pageSize.value,
          }
        }
    
        /**设置页数 和每页数量 */
        const setCurrentPage = (current: any) => {
          currentPage.value = current
        }
    
        const tableRowStype = () => {
          return {
            borderRadius: '15px',
            overflow: 'hidden',
          }
        }
    
        // 使用 expose 暴露组件内部的方法
        expose({
          refreshTable,
          getPageSizeAndCurrentPage,
          setCurrentPage,
        })
    
        return () => (
          <div class="table-style table">
            <el-table
              max-height="800px"
              ref="singleTableRef"
              class="custom-table"
              row-key="id"
              v-loading={loading.value}
              data={tableData.value}
              row-class-name={tableRowClassName}
              row-style={tableRowStype}
              cell-style={{ textAlign: 'center' }}
              onSelectionChange={handleSelectionChange}
              {...props.configuration}
            >
              {setTableColumn()}
            </el-table>
            <el-pagination
              small
              v-model:current-page={currentPage.value}
              v-model:page-size={pageSize.value}
              total={total.value}
              highlightCurrentRow={props.highlightCurrentRow}
              background={props.background}
              layout={props.paginationLayout}
              onCurrentChange={handleCurrentChange}
              onSizeChange={handleSizeChange}
              popper-class="popperClass"
              hideOnSinglePage={props.hideOnSinglePage}
            ></el-pagination>
          </div>
        )
      },
    }
    </script>
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221
    • 222
    • 223
    • 224
    • 225
    • 226
    • 227
    • 228
    • 229
    • 230
    • 231
    • 232
    • 233
    • 234
    • 235
    • 236
    • 237
    • 238
    • 239
    • 240
    • 241
    • 242
    • 243
    • 244
    • 245
    • 246
    • 247
    • 248
    • 249
    • 250
    • 251
    • 252
    • 253
    • 254
    • 255
    • 256
    • 257
    • 258
    • 259
    • 260
    • 261
    • 262
    • 263
    • 264
    • 265
    • 266
    • 267
    • 268
    • 269
    • 270
    • 271
    • 272
    • 273
    • 274
    • 275
    • 276
    • 277
    • 278
    • 279
    • 280
    • 281
    • 282
    • 283
    • 284
    • 285
    • 286
    • 287
    • 288
    • 289
    • 290
    • 291
    • 292
    • 293
    • 294
    • 295
    • 296
    • 297
    • 298
    • 299
    • 300
    • 301
    • 302
    • 303
    • 304
    • 305
    • 306
    • 307
    • 308
    • 309
    • 310
    • 311
    • 312
    • 313
    • 314
    • 315
    • 316
    • 317
    • 318
    • 319
    • 320
    • 321
    • 322
    • 323
    • 324
    • 325
    • 326
    • 327
    • 328
    • 329
    • 330
    • 331
    • 332

    3、使用

    在需要使用的引入使用 并 创建一个ts文件用于存放列的配置即可

        <dynamic-table
          ref="dynamicTableRef"
          class="dynamic-table"
          :request="request.listPagesBrand"
          :tableColumns="tableColumns"
          :isLoadRequest="false"
          :tableDisable="selectable"
        >
          <template #internal="scope">
            {{ scope.row.internal === 1 ? '是' : '否' }}
          </template>
          <template #operation="scope">
            <el-button
              type="primary"
              link
              @click="editbrand(scope.row.id)"
              :disabled="!!Number(scope.row.internal)"
            >
              编辑
            </el-button>
            <el-divider direction="vertical" />
            <delete-button
              :data="[scope.row.id]"
              :request="request.delBatchBrand"
              @requestCallback="() => submitSearch(false)"
              :disabled="!!Number(scope.row.internal)"
              type="primary"
              link
            ></delete-button>
          </template>
        </dynamic-table>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    //Table 表头定义
    const tableColumns: any = [
      {
        type: 'selection',
        width: '70px',
      },
      {
        prop: 'brandName',
        label: '品牌',
        showOverflowTooltip: true,
      },
      {
        prop: 'englishLogo',
        label: '英文标识',
        showOverflowTooltip: true,
      },
      {
        slot: true,
        slotName: 'internal',
        label: '内置',
        showOverflowTooltip: true,
      },
      {
        slot: true,
        slotName: 'operation',
        width: '200px',
        label: '操作',
      },
    ]
    
    export default tableColumns
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

  • 相关阅读:
    es6 数据类型
    npm ERR! path /Users/apple/.npm/_cacache/index-v5/11/77/cf18d9ab54d565b57fb3
    【分布式技术专题】「分布式技术架构」MySQL数据同步到Elasticsearch之N种方案解析,实现高效数据同步
    Unity技术手册-初识编辑器(下)
    HTML5期末大作业:美妆网页主题网站设计——清新的手工肥皂网站展示(4页)HTML+CSS+JavaScript
    长尾词-长尾词软件-长尾词挖掘工具免费
    猿创征文 | Docker笔记:Docker网络知识介绍
    图神经网络时间序列预测,神经网络预测未来数据
    系统学习Python——字符串(str):字符串格式化表达-[基础知识]
    iOS 关于 UICollectionView常见使用方法
  • 原文地址:https://blog.csdn.net/weixin_49014702/article/details/133241446