自定义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事件

浙公网安备 33010602011771号