文章目录
前言
elementui中的table组件,表格中想要自由地渲染内容,是一种比较麻烦的事情,比如你表格中想要某一列插入一个button按钮,是不是要用插槽,那下一次某一列插入一个图片,又得新开一种插槽或者类别。
那么,有没有什么方法,能够通过配置,直接配置一个组件的方式,让表格的那一列直接渲染对应的组件。elementui table中并不提供这样的配置。所以需要开发人员自己封装。
CustomTable
在element UI 中Table组件的基础上,封装一个可以自定义渲染的table
<template>
<!-- 表格 -->
<el-table
:data="tableData"
v-loading="loading"
:empty-text="'暂无数据'"
v-bind="$attrs"
>
<!-- :border="true" -->
<template v-for="column in tableColumn" :key="column?.label">
<el-table-column v-if="column.cols" v-bind="{ ...column }">
<!-- 列名合并,存在cols的情况下 -->
<template v-for="col in column.cols" :key="column?.label + col?.label">
<el-table-column v-bind="{ ...col }">
<template #default="scope">
<!-- 自定义render -->
<span v-if="col?.render" :class="col?.class" :style="col?.style ? col.style : {}">
{{ col?.render(scope.row[col.prop], scope.row) }}
</span>
<!-- 自定义render component 将当前列的数据和这一整行的数据都传给component函数 -->
<div v-else-if="col?.component" v-html="renderVNode(col.component(scope.row[col.prop], scope.row))"> </div>
<span
v-else
:style="col?.style ? col.style : {}"
:class="col?.class"
:title="scope.row[col.prop]"
:data-message="scope.row[col.prop]"
>{{ scope.row[col.prop] }}</span
>
</template>
</el-table-column>
</template>
</el-table-column>
<el-table-column v-else v-bind="{ ...column }">
<template #default="scope">
<!-- 自定义render字符串 当前列的数据和这一整行的数据都传给render函数-->
<span v-if="column?.render" :style="column?.style ? column.style : {}" :class="column?.class">
{{ column?.render(scope.row[column.prop], scope.row) }}
</span>
<!-- 自定义render component 将当前列的数据和这一整行的数据都传给component函数 -->
<div v-else-if="column?.component" v-html="renderVNode(column.component(scope.row[column.prop], scope.row))"></div>
<span
v-else
:style="column?.style ? column.style : {}"
:class="column?.class"
:title="scope.row[column.prop]"
:data-message="scope.row[column.prop]"
>{{ scope.row[column.prop] }}</span
>
</template>
</el-table-column>
</template>
</el-table>
</template>
<script lang="ts" setup name="customTable">
const props = defineProps({
tableData: {
type: Array,
default: () => [],
},
tableColumn: {
type: Array<any>,
default: () => [],
},
loading: {
type: Boolean,
default: false,
},
});
const generateRandomString = (length) => {
const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
const charactersLength = characters.length;
let result = '';
for (let i = 0; i < length; i++) {
const randomIndex = Math.floor(Math.random() * charactersLength);
result += characters.charAt(randomIndex);
}
return result;
};
// 通过renderVNode 接收一个vnode节点来渲染component
const renderVNode = (vnode: VNode) => {
const tempDiv = document.createElement('div');
const parentDiv = document.createElement('div');
const id = generateRandomString(10);
parentDiv.appendChild(tempDiv);
tempDiv.id = id;
// 利用createApp和render将vnode挂载成一个新的app
const Comp = createApp({
render: () => vnode,
});
// 将这个新的vue app 挂载到对应的dom上
nextTick(() => {
// 但要放置到nextTick里面,以防止dom还没放置到table的对应位置,就先挂在了
Comp.mount('#' + id);
});
// 返回一个html dom ,利用v-html挂载到table中对应的位置
return parentDiv.innerHTML;
};
</script >
如何使用
<template>
<custom-table maxHeight="100vh" :tableColumn="tableColumn" :tableData="tableData" />
</template>
<script lang="ts" setup name="test">
import componentTest from '../componentTest.vue';
import customTable from '../components/customTable.vue';
const tableColumn = ref([
{
label: '名称',
prop: 'name',
align: 'center',
minWidth: 130,
fixed: true,
component: (value, row) => {
return h('div', { onClick: () => handleClick(row?.name) }, [
h('span', { style: { fontSize: '14px', display: 'inline-block' } }, value),
h('p', { class: 'dealer-scale' }, row?.scale),
]);
},
},
{
label: '订单',
prop: 'order',
component: (value, row) => {
return h(componentTest, {title: value})
},
style: { color: '#C00017' },
},
{ label: '本年',
prop: 'open',
style: { color: '#C00017' }
},
{ label: '费效比',
prop: 'efficiency',
style: { color: '#038810' },
render: (value,rows) => {
console.log(value, rows)
return value + '%'
}
},
{ label: '处罚', prop: 'punish', style: { color: '#0000aa' } },
]);
const tableData = ref([
// name order open efficiency punish scale
{
name: 'xxxx有限公司',
order: '23455 万',
open: '234 万瓶',
efficiency: '1.3 %',
punish: '3 次',
scale: '10亿以上',
},
{
name: '软件有限公司',
order: '23456 万',
open: '234 万瓶',
efficiency: '1.3 %',
punish: '3 次',
scale: '5千万~1亿',
},
]);
</script>
tableColumn 属性
属性名 | 类型 | 解释 |
---|---|---|
style | string | 能够单独配置某一列的样式 |
class | string | 能够单独配置某一列的class名称 |
render | function | 接受两个参数 (value, row) value是当前行当前列的数据,row是当前行的数据 返回的值会渲染在table对应的列中 |
component | function | 接受两个参数 (value, row) value是当前行当前列的数据,row是当前行的数据,返回一个vnode,通过vue3自带的h函数实现 |
fixed | boolean | 是否让当且列固定 |
… | … | … |
剩下的属性 | … | 查看element ui 中table 的Column配置 https://element.eleme.io/#/zh-CN/component/table#table-column-attributes |
h函数
function h(
type: string | Component, // 元素/组件类型
props?: object | null, // 属性/Props
children?: VNodeChildren // 子节点(字符串、数组、插槽等)
): VNode
创建原生元素
import { h } from 'vue'
// 等价于 <div class="box" id="app">Hello</div>
h('div', { class: 'box', id: 'app' }, 'Hello')
// 带事件
h('button', { onClick: () => console.log('Clicked!') }, 'Click me')
创建组件
import MyComponent from './MyComponent.vue'
// 传递 props 和插槽
h(MyComponent, {
title: 'Demo',
onClick: () => {} // 监听自定义事件
}, {
default: () => h('span', '默认插槽'),
footer: () => h('div', 'Footer 内容')
})
动态生成
const items = ['A', 'B', 'C']
h('ul',
items.map(item => h('li', item))
)