当前位置: 首页 > news >正文

vue3 elementUi table自由渲染组件

文章目录

    • 前言
    • CustomTable
    • 如何使用
    • tableColumn 属性
    • h函数
      • 创建原生元素
      • 创建组件
      • 动态生成

前言

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 属性

属性名类型解释
stylestring能够单独配置某一列的样式
classstring能够单独配置某一列的class名称
renderfunction接受两个参数 (value, row) value是当前行当前列的数据,row是当前行的数据 返回的值会渲染在table对应的列中
componentfunction接受两个参数 (value, row) value是当前行当前列的数据,row是当前行的数据,返回一个vnode,通过vue3自带的h函数实现
fixedboolean是否让当且列固定
剩下的属性查看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))
)

相关文章:

  • 使用computed计算属性实现购物车勾选
  • 【leetcode hot 100 105】从前序与中序遍历序列构造二叉树
  • 【从零开始】Air780EPM的LuatOS二次开发——OneWire协议调试注意事项!
  • C++之list类及模拟实现
  • 《C#上位机开发从门外到门内》3-5:基于FastAPI的Web上位机系统
  • 【NLP】 1. 文本在计算机里的表示: One-Hot, sparse vector, bag of words
  • MCU的应用场景:从智能家居到工业控制
  • 【Go】无法访问 proxy.golang.org 进行依赖下载
  • spring bean的生命周期和循环依赖
  • 量子信息理论入门:探索量子世界的奇妙信息处理方式
  • .gitignore 文件用于 Git 应忽略的文件夹的格式
  • 详细讲一下 Webpack 主要生命周期钩子流程(重难点)
  • SpringBoot美发门店管理系统开发与设计
  • 网页制作18-Javascipt图像特效の图片闪烁
  • MySQL 横向衍生表(Lateral Derived Tables)
  • element ui设置结束时间为23:59:59
  • VSTO(C#)Excel开发12:多线程的诡异
  • DLMS电能表通讯协议学习笔记
  • 蓝桥杯 第五天 2021 国赛 第 5 题 最小权值
  • Secs/Gem第一讲 · 总结精华版(基于secs4net项目的ChatGpt介绍)
  • 长三角铁路今日预计发送390万人次,昨日客发量同比增长10.5%
  • 因雷雨、沙尘等天气,这些机场航班运行可能受影响
  • 人形机器人,最重要的还是“脑子”
  • 遍体鳞伤就是击不倒,这是国米老男孩最后的倔强
  • 拍摄《我们这一代》的肖全开展“江浙沪叙事”
  • 辽宁辽阳市白塔区一饭店发生火灾,当地已启动应急响应机制