Skip to content

表格组件

架构

bash
├── src
   ├── components
   ├── chooseTable
      ├── lib (引入的第三方库)
    └── xxx.json
      ├── src (源码)
   ├── index.vue
      ├── index.ts

index.ts

ts
import { type App } from 'vue'
import chooseTable from './src/index.vue'

// 让这个组件可以通过use的形式使用
export default {
    install(app: App) {
        app.component('YJ-choose-table', chooseTable)
    }
}

interface/chooseTable/type.ts

ts
export interface TableOptions {}

components/chooseTable/src/index.vue

  • 自己用的时候一定要修改 getlist 这个方法,根据自己接口返回的数据格式来修改
vue
<template>
    <el-card class="my-table-card">
        <div v-if="title !== ''" slot="header" class="my-table-header">
            <span v-if="totalFlag" class="title">共计{{ total }}条</span>
            <span class="title">{{ title }}</span>
            <span class="btn-box">
                <slot name="btn"></slot>
            </span>
        </div>
        <el-table
            v-loading="tableLoading"
            :data="tableList"
            :highlight-current-row="true"
            tooltip-effect="dark"
            border
            style="width: 100%"
            @selection-change="selectionChange"
            @sort-change="sortChange"
        >
            <el-table-column v-if="isSelection" type="selection" align="center" width="50"></el-table-column>
            <slot></slot>
        </el-table>
        <el-pagination
            v-if="hasPagination"
            background
            class="my-table-pagination"
            :current-page="currPage"
            :page-sizes="pageSizes"
            :page-size="limit"
            layout="total, sizes, prev, pager, next, jumper"
            :total="total"
            @size-change="handleSizeChange"
            @current-change="handleCurrentChange"
        >
        </el-pagination>
    </el-card>
</template>

<script setup>
    import { ElMessage } from 'element-plus'
    import { ref, watch, onMounted, onUnmounted } from 'vue'
    import { useRoute } from 'vue-router'
    const route = useRoute()
    const emit = defineEmits(['data'])
    const props = defineProps({
        title: {
            type: String,
            default: ''
        },
        api: {
            type: Function,
            required: true
        },
        search: {
            type: Object,
            default: () => {
                return {}
            }
        },
        isSelection: {
            type: Boolean,
            default: false
        },
        pageRecordName: {
            type: String,
            default: ''
        },
        totalFlag: {
            type: Boolean,
            default: false
        },
        hasPagination: {
            type: Boolean,
            default: true
        },
        fixedTableHeader: {
            type: Boolean,
            default: true
        },
        pageSizes: {
            type: Array,
            default: () => {
                return [10, 20, 50, 100, 200]
            }
        }
    })

    const scroll = ref(0)
    const mySearch = ref(props.search)
    const multipleSelection = ref([])
    const tableList = ref([])
    const tableLoading = ref(false)
    const total = ref(0)
    const currPage = ref(1)
    const totalPage = ref(1)
    const limit = ref(20)
    const myPageRecordName = ref(props.pageRecordName ? props.pageRecordName : route.fullPath)
    const order_field = ref('')
    const order_sort = ref('')
    const handleScroll = () => {
        scroll.value = document.documentElement.scrollTop || document.body.scrollTop // 滚动条距顶部距离
        const containerEleTable = document.querySelector(`.el-table`) // el-table元素
        const containerEleHeader = document.querySelector(`.el-table .el-table__header-wrapper`) // el-table元素的子元素el-table__header
        const containerEleHeaderLeftFixed = document.querySelector(
            `.el-table .el-table__fixed .el-table__fixed-header-wrapper`
        ) // el-table有左边fixed的table元素的子元素el-table__header
        const containerEleHeaderRightFixed = document.querySelector(
            `.el-table .el-table__fixed-right .el-table__fixed-header-wrapper`
        ) // el-table有右边fixed的table元素的子元素el-table__header

        const containerEleBody = document.querySelector(`.el-table .el-table__body-wrapper`) // el-table元素的子元素el-table__body

        const containerEleHeaderTh = document.querySelectorAll(`.el-table thead tr th `)

        const offsetTop = containerEleTable.offsetTop - 50 // 头部固定条高度
        const table_w = window.getComputedStyle(containerEleTable).width
        const height = window.getComputedStyle(containerEleHeader).height // el-table元素的子元素el-table__header高度

        if (scroll.value > offsetTop) {
            // el-table元素的子元素el-table__header距顶部50个单位时
            containerEleHeader.classList.add('table-header-fixed') // el-table元素的子元素el-table__header添加样式class类名 table-header-fixed

            containerEleBody.style.marginTop = height // el-table元素的子元素el-table__body添加距marginTop,否则会有抖动

            containerEleHeader.style.width = table_w // 控制el-table__header-wrapper宽度,解决el-table__header-wrapper超出table

            if (containerEleHeaderLeftFixed) {
                containerEleHeaderLeftFixed.classList.add('table-header-fixed')

                containerEleHeaderLeftFixed.style.width = table_w
            }
            if (containerEleHeaderRightFixed) {
                containerEleHeaderRightFixed.classList.add('table-header-right-fixed')
            }
        } else {
            containerEleHeader.classList.remove('table-header-fixed')
            if (containerEleHeaderLeftFixed) {
                containerEleHeaderLeftFixed.classList.remove('table-header-fixed')
            }
            if (containerEleHeaderRightFixed) {
                containerEleHeaderRightFixed.classList.remove('table-header-right-fixed')
            }

            containerEleBody.style.marginTop = 0
        }
        containerEleHeaderTh.forEach(ele => {
            // 找到el-table__header-wrapper下边的th有is-hidden类名的删除is-hidden类名
            if (ele.classList.contains('is-hidden')) {
                ele.classList.add('is-hidden-z')
            }
        })
    }
    // 初始化
    const init = (reset = true) => {
        if (reset) {
            currPage.value = 1
        }
        getList()
    }
    // 获取数据
    const getList = () => {
        tableLoading.value = true
        const data = mySearch.value
        data.page = currPage.value
        data.size = limit.value
        if (order_field.value != '') {
            data.order_field = order_field.value
        }
        if (order_sort.value != '') {
            data.order_sort = order_sort.value
        }

        props
            .api(data)
            .then(res => {
                if (res.code === 200) {
                    if (props.hasPagination) {
                        // 有分页
                        if (typeof res.total !== 'undefined') {
                            totalPage.value = Math.ceil(res.total / limit.value)
                            total.value = res.total
                            tableList.value = []
                            tableList.value = tableList.value.concat(res.list)
                        } else {
                            if (typeof res.total !== 'undefined') {
                                totalPage.value = Math.ceil(res.total / limit.value)
                                total.value = res.total
                                tableList.value = []
                                let result = res.list || res.data.data
                                tableList.value = tableList.value.concat(result)
                            } else {
                                currPage.value = res.curentpage
                                totalPage.value = res.page
                                total.value = res.total
                                tableList.value = res.list
                            }
                        }
                    } else {
                        if (res.list) {
                            tableList.value = res.list
                        } else {
                            tableList.value = res.data
                        }
                    }
                    emit('data', res.data)
                }
                if (res.status == 200) {
                    if (res.list) {
                        tableList.value = res.list
                    } else {
                        tableList.value = res.data
                    }
                    emit('data', res.data)
                }
                tableLoading.value = false
            })
            .catch(() => {
                ElMessage.warning('请求失败了')
                tableLoading.value = false
            })
    }

    const savePage = () => {
        if (!myPageRecordName.value) {
            return false
        }
        window.sessionStorage.setItem(
            myPageRecordName.value,
            JSON.stringify({ page: currPage.value, size: limit.value })
        )
    }

    const getPageRecord = () => {
        let data = window.sessionStorage.getItem(myPageRecordName.value)
        if (!data || typeof data === 'undefined' || data === 'null') {
            return false
        }
        data = JSON.parse(data)
        currPage.value = data.page
        limit.value = data.size
    }

    const handleCurrentChange = page => {
        currPage.value = page
        getList()
        savePage()
    }

    const handleSizeChange = size => {
        currPage.value = 1
        limit.value = size
        getList()
        savePage()
    }

    const selectionChange = val => {
        multipleSelection.value = val
    }
    const getSelection = () => {
        return multipleSelection.value
    }

    const getTableDate = () => {
        const tableDate = Object.assign({
            tableList: tableList.value,
            limit: limit.value,
            currPage: currPage.value
        })
        return tableDate
    }

    const sortChange = column => {
        order_field.value = column.prop
        order_sort.value = column.order.replace('ending', '')
        getList()
    }

    watch(
        () => {
            return props.search
        },
        val => {
            mySearch.value = val
        }
    )

    onMounted(() => {
        // 设置分页
        getPageRecord()
        init(false) // 初始化
        if (props.fixedTableHeader) {
            window.addEventListener('scroll', handleScroll)
        }
    })
    onUnmounted(() => {
        window.removeEventListener('scroll', handleScroll)
    })
    defineExpose({
        init,
        getSelection,
        getTableDate
    })
</script>

<style lang="scss" scoped>
    .my-table-card {
        :deep(.el-card__header) {
            padding: 10px 20px;
        }
        .my-table-header {
            display: flex;
            justify-content: space-between;
            .title {
                font-size: 12px;
                font-weight: bold;
                line-height: 32px;
            }
        }
        :deep(.el-table__header thead tr th) {
            background: #f9f9f9;
        }

        :deep(.el-table--small .el-table__body td) {
            padding: 5px 0;
        }
        .my-table-pagination {
            margin-top: 10px;
        }
    }
    .my-table-card {
        :deep(.el-table) {
            .table-header-fixed {
                position: fixed;
                top: 50px;
                left: auto;
                z-index: 100;
                border-top: 1px solid #dfe6ec;
            }
            .table-header-right-fixed {
                position: fixed;
                top: 50px;
                right: 46px;
                z-index: 200;
                border-top: 1px solid #dfe6ec;
            }
            .el-table__header {
                thead {
                    tr {
                        background: rgba(0, 0, 0, 0);
                    }
                }
            }
        }
    }
    :deep(.is-hidden-z) {
        opacity: 0;
        // top: -50px;
    }
    :deep(.el-scrollbar__bar) {
        &.is-horizontal {
            height: 16px !important;
            background: #dfe6ec !important;
        }
    }
</style>

父组件调用

vue
<template>
    <div class="useManagePage">
        <el-button type="primary" @click="getselectdata" style="padding-left: 20px; padding-right: 20px"
            >获取选中数据</el-button
        >

        <YJ-choose-table :api="getUserManageList" :search="searchdata" ref="userManageRef" :isSelection="true">
            <el-table-column prop="id" label="文章id" width="180" align="center" />
            <el-table-column prop="userId" label="用户ID" width="180" align="center" />
            <el-table-column prop="title" label="文章名称" align="center" />
            <el-table-column prop="body" label="内容" align="center" />
        </YJ-choose-table>
    </div>
</template>

<script setup lang="ts">
    import { ElMessage } from 'element-plus'
    import { ref } from 'vue'
    import { getUserManageList } from '@/components/chooseTable/lib/api'
    const searchdata = ref({})
    const userManageRef: any = ref(null)

    const getselectdata = () => {
        console.log(userManageRef.value.getSelection())
        let result: any = []
        userManageRef.value.getSelection().forEach((item: any) => {
            result.push(item.id)
        })
        let str = result.join(',')
        ElMessage({
            message: `选中了id是${str}数据`,
            type: 'success'
        })
    }
</script>

<style lang="scss" scoped>
    .useManagePage {
        padding-top: 30px;
        padding-left: 15px;
        padding-right: 15px;
    }
</style>