自定义table组件

三个页面复制过去可以直接用,功能还不够完善,基础模板
包含 自定义表头及多级表头、显示隐藏、检索、是否分页、操作


示例 页面代码

<template>
    <div>
        <Table :tableRules="tableRules" :tableData="tableData" :load="load" @getList="getList" @search="search" />
    </div>
</template>

<script setup>
import Table from './table.vue'

const dialogVisible = ref(false)

const load = (
    row,
    treeNode,
    resolve
) => {
    setTimeout(() => {
        console.log('load')
    }, 1000)
}
const tableData = ref([
    {
        id: 12,
        date: '2016-05-03',
        name: '@Tom1',
        children: [],
        ztone:123,
        zttwo:123,
        opl:'第二列名称',
        allTh:123456
    },
    {
        id: 16,
        date: '2016-05-02',
        name: '@Tom2',
        zone: 12,
        children: [
            {
                id: 31,
                date: '2016-05-01',
                name: 'wangxiaohu',
                children: [
                    {
                        id: 301,
                        date: '2016-05-01',
                        name: 'R301',
                    },
                    {
                        id: 302,
                        date: '2016-05-01',
                        name: 'R99',
                    }
                ]
            },
            {
                id: 32,
                date: '2016-05-01',
                name: 'wangxiaohu',
            },
        ]
    },
    {
        id: 19,
        date: '2016-05-04',
        name: '@Tom3',
    }
])

const tableRules = ref({
    leftFixed: 1,
    isFixed: true,
    heights: 640,
    isPagination: true,
    total: 24,
    rules: [
        {
            prop: 'index',
            label: '序列',
            width: '160px',
            hasChilded: false,
            show: true,
            type: 'sequence',
        },
        {
            prop: 'name', //table-column prop,
            label: '名称', //table-column label,
            width: '160px', //table-column width,
            hasChilded: false,
            show: true,
            type: 'other',
            align:'center'
        }, {
            prop: 'date', //table-column prop,
            label: '时间', //table-column label,
            width: '120px', //table-column width,
            hasChilded: false,
            show: false,
            type: 'other',
            align:'center'
        }, {
            prop: 'allTh', //table-column prop,
            label: '第3列父级', //table-column label,
            width: '240px', //table-column width,
            hasChilded: true,
            show: true,
            type: 'other',
            children: [
                {
                    prop: 'zone', //table-column prop,
                    label: '次级1长文字', //table-column label,
                    width: '120px', //table-column width,
                    hasChilded: true,
                    show: true,
                    type: 'other',
                    children: [
                        {
                            prop: 'ztone', //table-column prop,
                            label: '子级1', //table-column label,
                            width: '60px', //table-column width,
                            hasChilded: false,
                            type: 'number',
                            align:'right',
                            show: true
                        }, {
                            prop: 'zttwo', //table-column prop,
                            label: '子级2', //table-column label,
                            width: '60px', //table-column width,
                            hasChilded: false,
                            type: 'number',
                            show: true
                        }
                    ]
                }, {
                    prop: 'ztwo', //table-column prop,
                    label: '次级2', //table-column label,
                    width: '120px', //table-column width,
                    hasChilded: false,
                    type: 'number',
                    show: true
                }
            ]
        }, {
            prop: 'opl', //table-column prop,
            label: '名称2', //table-column label,
            width: '120px', //table-column width,
            hasChilded: false,
            type: 'other',
            show: false
        }, {
            prop: 'operate', //table-column prop,
            label: '操作', //table-column label,
            width: '200px', //table-column width,
            hasChilded: false,
            show: true,
            type: 'operate',
            operate: [
                {
                    label: '编辑',
                    function: (row) => {
                        console.log('update', row)
                        return
                    },
                    type: 'primary'
                },
                {
                    label: '删除',
                    function: (row) => {
                        console.log('delete', row)
                        return
                    },
                    type: null
                }
            ]
        },
    ]
})


const getList = (data) => {
    console.log('getList', data)
}
const search = (data) => {
    console.log('search', data)
}
</script>

<style scoped></style>
父组件代码

<template>
    <div class="header">
        <div>
            <span> 显示 </span>
            <el-select style="width: 96px;" v-model="pageSize" class="m-2" placeholder="Select" @change="handleSizeChange">
                <el-option v-for="item in options" :key="item" :label="item" :value="item" />
            </el-select>
            <span> 项 </span>
        </div>
        <div style="display: flex;">
            <el-input v-model="searchInput" class="w-50 m-2" placeholder="关键字检索" :prefix-icon="Search" />
            <el-button type="primary" style="margin:0 8px;" @click="search">检索</el-button>

            <el-dropdown :hide-on-click="false" trigger="click">
                <el-button :icon="Grid" />
                <template #dropdown>
                    <el-dropdown-menu>
                        <el-dropdown-item v-for="menu in rulesList">
                            <el-checkbox v-model="menu.show" :label="menu.label" size="small"
                                @change="setMenuShow(menu.prop, menu.parent)" />
                        </el-dropdown-item>
                    </el-dropdown-menu>
                </template>
            </el-dropdown>
        </div>
    </div>
    <el-table :data="data" style="width: 100%;" :height="isFixed ? heights : 600" lazy :load="props.load" row-key="id" border 
        tree-props="{ children: 'children' }">
        <template v-for="(item, index) in rules">
            <el-table-column header-align="center" :align="item.align" v-if="item.type == 'sequence' && item.show"
                width="150" :label="item.label" :prop="item.prop"></el-table-column>
            <!-- 操作栏 -->
            <el-table-column header-align="center" :align="item.align"
                v-if="item.type == 'operate' && item.show && item.operate" :label="item.label" :prop="item.prop"
                :width="item.width" style="text-align: center;" :fixed="index < leftFixed">
                <template #default="scope">
                    <el-button v-for="btn in item.operate" :type="btn.type" @click="(el) => btn.function(scope.row, el)">{{
                        btn.label
                    }}</el-button>
                </template>
            </el-table-column>
            <!-- 判断有无childed -->
            <chile-table v-else-if="item.hasChilded && item.show && item.type == 'other'" :children="item.children"
                :label="item.label" :width="item.width" :leftFixed="leftFixed" />
            <el-table-column header-align="center" :align="item.align"
                v-else-if="!item.hasChilded && item.show && item.type == 'other'" :prop="item.prop" :label="item.label"
                :width="item.width" style="text-align: center;" :fixed="index < leftFixed">
            </el-table-column>

        </template>
    </el-table>
    <el-pagination v-if="isPagination" class="pagination" v-model:current-page="currentPage" v-model:page-size="pageSize"
        :page-sizes="[10, 20, 50, 100]" :disabled="disabled" :background="background"
        layout="total, prev, pager, next, jumper" :total="total" @current-change="handleCurrentChange" />
</template>
  
<script  setup>
import { onMounted, watch } from 'vue'
import ChileTable from './childTable.vue'
import { Search, Grid } from '@element-plus/icons-vue'
const props = defineProps(["tableData", "load", "tableRules"]);
const emits = defineEmits(['getList', 'search'])
const { leftFixed, isFixed, heights, isPagination, total } = props.tableRules

const currentPage = ref(1)
const pageSize = ref(10)
const background = ref(true)
const disabled = ref(false)
const options = ref([10, 20, 50, 100])
const searchInput = ref('')
const data = ref([])
const rulesList = ref([])
const rules = ref(props.tableRules.rules)

onMounted(() => {
    tableDataChange(props.tableData)
    changeRules(rules.value)
})
//监听tableData
watch(
    () => props.tableData,
    (newProps) => {
        tableDataChange(newProps); //这里看到新值
    }
)

const handleSizeChange = (row) => {
    currentPage.value = 1
    pageSize.value = row
    const page = {
        current: 1,
        size: row
    }
    emits('getList', page)
}
const handleCurrentChange = (row) => {
    currentPage.value = row
    const page = {
        current: row,
        size: pageSize.value
    }
    emits('getList', page)
}

const search = () => {
    let param = {
        input: searchInput.value,
        current: currentPage.value,
        size: pageSize.value
    }
    emits('search', param)
}

const changeRules = (rules) => {
    rulesList.value = treeToArr(rules, [], [])
}
const setMenuShow = (str, parent) => {
    if (parent.length == 0) {
        let strIndex = rules.value.findIndex(it => it.prop == str)
        if (strIndex >= 0) {
            rules.value[strIndex].show = !rules.value[strIndex].show
        }
        return
    }
    let list = [...parent, str]
    let rule = rules.value
    searchChild(rule, list, 0 , {})
}
const searchChild = (rule, parent, index, levelUp) => {// 开启递归
    // 根据列表中prop值匹配对应的数据
    const item = rule.find(it => it.prop == parent[index])
    let num = index + 1
    if (item.children && num < parent.length) { // 只对非末端节点进行递归
        searchChild(item.children, parent, num, item)
    } else {
        item.show = !item.show
        let fsj = levelUp.children.filter(it=>it.show)
        if(fsj.length == 0){
            levelUp.hasChilded = false
        }else{
            levelUp.hasChilded = true
        }
    }
}

// 正向-树形结构转平铺
// 从外到内依次递归,有 children 则继续递归
function treeToArr(data, res, parent) {
    data.forEach((item) => {
        res.push({
            prop: item.prop,
            label: item.label,
            show: item.show,
            parent: parent
        });
        if (item.children && item.hasChilded) {
            treeToArr(item.children, res, [...parent, item.prop]);
        }
    });
    return res;
};


const tableDataChange = (prop) => {
    //自定义序列号
    const num = pageSize.value * (currentPage.value - 1)

    let newTable = prop.map((item, index) => ({
        ...item,
        index: num + index + 1,
        children: item.children && item.children.length ? setChildren(item.children, num + index + 1) : null
    })
    )
    data.value = newTable
}

const setChildren = (child, indexNum) => {
    let list = []
    child.map((item, index) => {
        let childIndex = indexNum + '.' + (index + 1)
        let itChild = {
            ...item,
            index: childIndex,
            children: item.children && item.children.length ? setChildren(item.children, childIndex) : null
        }
        list.push(itChild)

    })
    return list

}






// leftFixed 左侧列固定
// isFixed 表头固定判断
// heights 表头固定高度
// const tableData = [ 表格数据格式需要与 rules 对应
//     {
//         id:1,
//         date:'2016-05-01',
//         name: '@Tom',
//         children: [
//             {
//                 id: 2,
//                 date: '2016-05-01',
//                 name: 'wangxiaohu',
//             },
//             {
//                 id: 3,
//                 date: '2016-05-01',
//                 name: 'wangxiaohu',
//             },
//         ]
//     },
// ]

// tableRules规则
// {
//     prop:表格prop,
//     label:表格label,
//     width:表格列宽,
//     hasChilded:判断是否有子级,用到子级一定要写true,否则children仍然不会生效,
//     show:单元列是否隐藏,
//     //特殊操作栏
//     type:sequence,text,operate  序列、文本、操作
//     operate: [
//             {
//                 label: '编辑',
//                 function: (row) => {
//                     return
//                 },
//                 type: 'primary'
//             },
//             {
//                 label: '删除',
//                 function: (row) => {
//                     return
//                 },
//                 type: null
//             }
//         ]
// }




</script>

<style>
.header {
    display: flex;
    height: 48px;
    justify-content: space-between;
    background-color: #fcfcfd;
    align-items: center;
    margin: 0 8px;
}

.text {
    display: flex;
    align-items: center;
}

.pagination {
    position: absolute;
    right: 32px;
    margin-top: 8px;
}
</style>
子组件代码 包含组件调用自己

<template>
  <el-table-column header-align="center" :label="label" :width="width" style="text-align: center;">
    <template v-for="(item, index) in props.children">
      <chile-table v-if="item.hasChilded && item.show" :children="item.children" :label="item.label" width="item.width"
        :leftFixed="leftFixed" />
      <el-table-column header-align="center" :align="item.align" v-else-if="!item.hasChilded && item.show"
        :prop="item.prop" :label="item.label" width="item.width" style="text-align: center;" :fixed="index < leftFixed" />
    </template>
  </el-table-column>
</template>
 
<script>
export default {
  name: 'ChileTable'
}
</script>
<script setup>

const props = defineProps(["children", "label", "width", "leftFixed"]);


</script>
<style scoped >
.text {
  display: flex;
  align-items: center;
}

.number {
  display: flex;
  align-items: right;
}
</style>

 

 

所有内容都是根据配置动态生成,包括控制多级表头显隐,内容显示位置,父级按钮click事件

posted @ 2023-05-11 17:43  月亮已落  阅读(97)  评论(0)    收藏  举报