vue3 elementUi table自由渲染组件

发布于:2025-03-15 ⋅ 阅读:(13) ⋅ 点赞:(0)

前言

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))
)


网站公告

今日签到

点亮在社区的每一天
去签到