vue3+element plus多级表头

如果有更好的实现方法欢迎交流。

动态表格

<template>
    <slot name="title"></slot>
    <el-table :data="tableData" v-bind="attrs" :tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
    >
      <el-table-column type="index" v-if="props.index.show" :label="props.index.label" :align="props.index.align" :index="props.indexFn"></el-table-column>
      <el-table-column type="selection" v-if="props.index.show"  :align="props.index.align" ></el-table-column>
      <!-- 动态生成多级表头 -->
      <el-table-column
          v-for="column in colData"
          :key="column.id"
          :label="column.label"
          :prop="column.prop"
          :width="column.width"
          :show-overflow-tooltip="column.tooltip"
          :fixed="column.fixed"
          :sortable="column.sortable"
          :align="column.align">
        <template #header>
          <slot name="header" :rowData="{label:column.label,unit:column.unit}">{{column.label}}</slot>
        </template>
        <template #default="scope" v-if="(column.children || []).length > 0">
          <!-- 递归生成子表头 -->
          <el-table-column
              v-for="child in column.children || []"
              :key="child.id"
              :label="child.label"
              :prop="child.prop"
              :width="child.width"
              :show-overflow-tooltip="child.tooltip"
              :fixed="child.fixed"
              :sortable="child.sortable"
              :align="child.align">
            <template #header>
              <slot name="header" :rowData="{label:child.label,unit:child.unit}">{{child.label}}</slot>
            </template>
            <template #default="scope1"  v-if="(child.children || []).length > 0">
              <!-- 支持更深层级的表头 -->
              <el-table-column
                  v-for="grandChild in child.children || []"
                  :key="grandChild.id"
                  :label="grandChild.label"
                  :prop="grandChild.prop"
                  :width="grandChild.width"
                  :show-overflow-tooltip="grandChild.tooltip"
                  :fixed="grandChild.fixed"
                  :sortable="grandChild.sortable"
                  :align="grandChild.align">
                <template #header>
                  <slot name="header" :rowData="{label:grandChild.label,unit:grandChild.unit}">{{grandChild.label}}</slot>
                </template>
                <template #default="scope">
                  <div v-if="grandChild.label == '操作'">
                    <slot name="button" :rowData="scope.row"></slot>
                  </div>
                  <div v-else>
                    <slot name="default" :rowData="scope.row">{{scope.row[grandChild.prop]}}</slot>
                  </div>
                </template>
              </el-table-column>
            </template>
            <template #default="scope" v-else>
              <div v-if="child.label == '操作'">
                <slot name="button" :rowData="scope.row"></slot>
              </div>
              <div v-else>
                <slot name="default" :rowData="scope.row">{{scope.row[child.prop]}}</slot>
              </div>
            </template>
          </el-table-column>
        </template>
        <template #default="scope" v-else>
          <div v-if="column.label == '操作'">
            <slot name="button" :rowData="scope.row"></slot>
          </div>
          <div v-else>
            <slot name="default" :rowData="scope.row">{{scope.row[column.prop]}}</slot>
          </div>
        </template>
      </el-table-column>
    </el-table>
</template>

<script setup lang="ts">
import {onMounted, ref, toRef, useAttrs, watch} from "vue";
interface columnInterface{
  label:String,
  prop:String,
  height:Number
}

// 使用useAttrs获取未声明的props
const attrs = toRef(useAttrs());
let props = defineProps<{
  data:{
    type:Array<Object>,
    required: true,
  },
  column: {
    type: Array<columnInterface>,
    required: true,
  },
  index?: {
    type:Object,
    default: {show:false,label:'序号',align:'center'},
  },
  selection?: {
    type:Object,
    default: {show:false,align:'center'},
  },
  indexFn?:{
    type:Function,
    default:()=>{}
  }
}>()
let tableData = toRef(props.data)
let colData = ref()
watch(() => props.column,(newValue,oldValue)=>{
  console.log('newValue',newValue)
  colData.value = buildTableColumns(newValue)

},{ deep: true })
onMounted(() => {
})

interface TableColumn {
  id: number;
  label: string;
  prop?: string;
  width?: string | number;
  align?: 'left' | 'center' | 'right';
  children?: TableColumn[];
  tooltip?: true|false;
  fixed?:'left'|'right';
  sortable?:true|false
}

/**
 * 将扁平的列配置转换为树形结构
 */
function buildTableColumns(items: TableColumn[]): TableColumn[] {
  const map: Record<number, TableColumn> = {};
  const tree: TableColumn[] = [];

  items.forEach(item => {
    map[item.id] = { ...item, children: [] };
  });

  items.forEach(item => {
    if (item.children && item.children.length > 0) {
      // 如果有直接定义的children,使用它
      map[item.id].children = item.children.map(child => map[child.id] || child);
    } else if (item.pid !== undefined && map[item.pid]) {
      // 否则根据pid关联到父节点
      map[item.pid].children.push(map[item.id]);
    } else {
      // 没有父节点的作为根节点
      tree.push(map[item.id]);
    }
  });

  return tree;
}
</script>

<style scoped>

</style>

调用按钮和测试页面

<template>
<el-dialog v-bind="attr">
  <el-tree
      ref="treeRef"
      style="max-width: 600px"
      :props="treeProps"
      :data="data"
      show-checkbox
      node-key="id"
  />
  <el-button @click="getCheckedKeys"></el-button>
</el-dialog>

</template>

<script setup lang="ts">

import {ref, useAttrs, watch} from "vue";
import type {TreeInstance, TreeKey} from "element-plus";

let attr = useAttrs()
let data = ref()
let treeProps={
  id:Number,
  label:'label',
  children: 'children',
}
let treeRef = ref<TreeInstance>()

function getCheckedKeys(){
  console.log(treeRef.value!.getCheckedKeys(false))
  let ids = treeRef.value!.getCheckedKeys(false)
  emits('exportColList',ids)
}


interface colDataInterface{
  id: number;
  label: string;
  prop?: string;
  width?: string | number;
  align?: 'left' | 'center' | 'right';
  children?: colDataInterface[];
  tooltip?: true|false;
  fixed?:'left'|'right';
  sortable?:true|false
}

let emits = defineEmits<{ (e: "exportColList", list: TreeKey[]):void}>()

let props = defineProps<{
  colData:{
    type:Array<colDataInterface>,
  }
}>()
/**
 * 将扁平的列配置转换为树形结构
 */
function buildTableColumns(items: colDataInterface[]): colDataInterface[] {
  const map: Record<number, colDataInterface> = {};
  const tree: colDataInterface[] = [];

  items.forEach(item => {
    map[item.id] = { ...item, children: [] };
  });

  items.forEach(item => {
    if (item.children && item.children.length > 0) {
      // 如果有直接定义的children,使用它
      map[item.id].children = item.children.map(child => map[child.id] || child);
    } else if (item.pid !== undefined && map[item.pid]) {
      // 否则根据pid关联到父节点
      map[item.pid].children.push(map[item.id]);
    } else {
      // 没有父节点的作为根节点
      tree.push(map[item.id]);
    }
  });

  return tree;
}
watch(()=>props.colData,(newValue,oldValue) => {
  let arr = buildTableColumns(newValue)
  data.value = arr
},{immediate:true,deep:true})
</script>

<style scoped>

</style>
<!--动态表格-->
<template>
  <el-button @click="testButton = true">测试</el-button>
  <dynamic-button v-model="testButton" :col-data="originalColumns" @exportColList="getEditList"></dynamic-button>
  <table-commonts  :column="originalColumnsTemp"  size="small" fit border :height="500" :data="tableData"
                   show-summary :index="{show:true,label:'序号',align:'center'}" style="width: 100%"
                   :index-fn="(val) => {return (val+1)*2}" :selection="{show:true,align:'center'}"
                   @selection-change="handleSelectionChange"
  >
    <template #button="data">
      <el-row>
        <el-col :span="12">
          <el-button @click="test(data)" link type="primary">保存</el-button>
        </el-col>
        <el-col :span="12">
          <el-button link type="primary">删除</el-button>
        </el-col>
      </el-row>
    </template>
    <template #header="scope">
      <div>{{scope.rowData.label}}</div>
      <div>{{scope.rowData.unit}}</div>
    </template>
  </table-commonts>
</template>

<script setup lang="ts">
import {onMounted, ref} from "vue";

import TableCommonts from "@/common/tableCommonts.vue";
import DynamicButton from "@/components/dynamicButton.vue";

let testButton = ref(false)
let originalColumnsTemp = ref()
function handleSelectionChange(val){
  console.log(val)
}
const tableData = ref([
  {name:'111'}
])

function test(val){
  console.log(val)
}
function getEditList(data){
  originalColumnsTemp.value = originalColumns.value.filter(item =>{
    return data.includes(item.id)
  })
}
// 示例数据 - 多级表头配置
const originalColumns = ref([
  { id: 1, label: '分公司' ,align:'center',unit:'',prop:'name'},
  { id: 2, label: '采油厂' ,align:'center',unit:''},
  { id: 3, label: '站' ,align:'center',unit:''},
  { id: 4, label: '出站' ,align:'center',unit:''},
  { id: 5, label: '出站名称' ,align:'center',unit:'',pid:4},
  { id: 6, label: '达标率' ,align:'center',unit:'',pid:4},
  { id: 7, label: '主要指标达标率' ,align:'center',unit:'',pid:4},
  { id: 8, label: '含油' ,align:'center',unit:'',pid:4},
  { id: 9, label: '悬浮物' ,align:'center',unit:'',pid:4},
  { id: 10, label: '粒径中值' ,align:'center',unit:'',pid:4},
  { id: 10, label: '腐蚀速率' ,align:'center',unit:'',pid:4},
  { id: 11, label: 'SRB' ,align:'center',unit:'',pid:4},
  { id: 12, label: '铁细菌' ,align:'center',unit:'',pid:4},
  { id: 13, label: '腐生菌' ,align:'center',unit:'',pid:4},
  { id: 14, label: '总铁' ,align:'center',unit:'',pid:4},
  { id: 15, label: '含硫' ,align:'center',unit:'',pid:4},
  { id: 16, label: 'CO₂' ,align:'center',unit:'',pid:4},
  { id: 17, label: 'PH值' ,align:'center',unit:'',pid:4},
  { id: 18, label: '操作' ,align:'center',unit:'',pid:4},

]);

onMounted(() => {
  originalColumnsTemp.value = originalColumns.value
})
</script>

<style scoped>

</style>
Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐