v3+ts table组件基础封装(支持多级表头结构)

 1.子组件中

<template>
  <div class="content">
    <div class="searchItem">
      <el-form inline v-if="props.isShow">
        <el-form-item>
          <el-button type="primary" @click="append">添加{{ props.configurationName }}</el-button>
        </el-form-item>
      </el-form>
      <el-table
        :data="tableData"
        style="width: 100%"
        :border="props.border"
        @selection-change="handleSelectionChange"
        @row-click="rowClick"
        :show-summary="showSummary"
        :summary-method="getSummaries"
      >
        <el-table-column type="selection" width="40" v-if="isSelect" />
        <el-table-column label="序号" v-if="isSerial" width="90" fixed="left">
          <template v-slot="{ $index }">
            {{ (paginationForm.pageNum - 1) * paginationForm.pageSize + $index + 1 }}
          </template>
        </el-table-column>
        <template v-for="(header, index) in normalizedColumns" :key="index">
          <el-table-column
            v-if="header.children && header.children.length > 0 && header.isShow"
            :label="header.label"
            :prop="header.property"
            :fixed="header.fixed"
            :width="header.width"
          >
            <template v-for="(child, childIndex) in header.children" :key="childIndex">
              <el-table-column
                v-if="header.isShow"
                :label="child.label"
                :prop="child.property"
                :width="child.width"
                :sortable="child.sortable"
                :show-overflow-tooltip="child.showOverflowTooltip"
                :fixed="child.fixed"
              >
                <!-- 自定义行内容插槽 -->
                <template #default="scope">
                  <slot name="row-slot" :row="scope.row" :column="child" :index="scope.$index">
                    {{ scope.row[child.property] }}
                  </slot>
                </template>
              </el-table-column>
            </template>
          </el-table-column>
          <el-table-column
            v-else-if="!header.children && header.isShow"
            :label="header.label"
            :prop="header.property"
            :sortable="header.sortable"
            :width="header.width"
            :fixed="header.fixed"
          >
            <!-- 自定义行内容插槽 -->
            <template #default="scope">
              <slot name="row-slot" :row="scope.row" :column="header" :index="scope.$index">
                {{ scope.row[header.property] }}
              </slot>
            </template>
          </el-table-column>
        </template>
        <slot name="append"></slot>
      </el-table>
      <el-pagination
        background
        :current-page="paginationForm.pageNum"
        :page-size="paginationForm.pageSize"
        :page-sizes="[10, 20, 50, 100, 200]"
        layout="total, sizes, prev, pager, next"
        :total="paginationForm.total"
        @size-change="handleSizeChange"
        @current-change="handleCurrentChange"
        style="margin-top: 20px"
      />
    </div>
  </div>
</template>

<script setup lang="ts">
  import { computed, defineEmits, defineProps, ref, VNode } from 'vue';
  import { ElTable, TableColumnCtx } from 'element-plus';
  import http from '@/libs/service/http';

  const paginationForm = reactive({
    pageNum: 1,
    pageSize: 10,
    total: 0,
  });

  interface User {
    date: string;
    name: string;
    address: string;
  }

  interface SummaryMethodProps<T = any> {
    columns: TableColumnCtx<T>[];
    data: T[];
  }

  interface Column {
    label: string;
    property?: string;
    width?: string;
    children?: Column[];
    sortable?: boolean;
    showOverflowTooltip?: boolean;
    fixed?: string | boolean;
    isShow?: boolean;
  }

  // 定义组件props
  const props = defineProps<{
    isShow?: boolean;
    isSelect?: boolean;
    border?: boolean;
    total?: number;
    configurationName?: string;
    columns: Column[];
    isPagination?: boolean;
    columnType?: boolean;
    isSerial?: boolean;
    searchForm?: object;
    showSummary?: boolean; //是否展示合计行
    tableRequest?: string;
    tableTitle?: string;
  }>();
  const tableData = ref([]);
  // 定义组件emits
  const emits = defineEmits(['appendList', 'currentChange', 'rowClick']);

  const multipleSelection = ref<User[]>([]);

  // 处理多级表头数据结构
  const normalizedColumns = computed(() => {
    return normalizeColumns(props.columns);
  });

  // 处理多级表头递归函数
  function normalizeColumns(columns: Column[]): Column[] {
    return columns.map((column) => {
      if (column.children && column.children.length > 0) {
        column.children = normalizeColumns(column.children); // 递归处理子列
      }
      return column;
    });
  }

  // 处理选择改变事件
  const handleSelectionChange = (val: User[]) => {
    multipleSelection.value = val;
  };

  // 处理每页条数改变事件
  const handleSizeChange = (val: number) => {
    paginationForm.pageSize = val;
    getTableList();
  };

  // 处理当前页数改变事件
  const handleCurrentChange = (val: number) => {
    paginationForm.pageNum = val;
    getTableList();
  };

  // 处理行点击事件
  const rowClick = (row: any) => {
    emits('rowClick', row);
  };

  // 添加事件
  const append = () => {
    emits('appendList');
  };

  /**
   * 获取列表数据
   */
  const getTableList = () => {
    const resData = {
      current: paginationForm.pageNum,
      size: paginationForm.pageSize,
      ...props.searchForm,
    };
    const tableRequest = props.tableRequest;
    if (tableRequest) {
      http.get(tableRequest, { ...resData }).then((res: any) => {
        if (props.tableTitle == '数据大盘') {
          res.records.forEach((item) => {
               //填写接口返回的逻辑
          });
        }
        tableData.value = res.records;
        paginationForm.total = res.total;
      });
    }
  };

  /**
   * 合计行
   * 若当前行值为0则不参与计算
   */
  const getSummaries = (param: SummaryMethodProps) => {
    const { columns, data } = param;
    const sums: (string | VNode)[] = [];

    columns.forEach((column, index) => {
      if (index === 0) {
        sums[index] = h('div', {}, ['总计/均值']);
        return;
      }
      if (column.property == 'taskTime') {
        sums[index] = h('div', {}, ['-']);
        return;
      }

      const values = data
        .map((item) => Number(item[column.property]))
        .filter((value) => value !== 0);

      if (!values.every((value) => Number.isNaN(value))) {
        const sum = values.reduce((prev, curr) => {
          const value = Number(curr);
          if (!Number.isNaN(value)) {
            if (column.property == 'roi') {
              return prev + value / values.length;
            } else {
              return prev + value;
            }
          } else {
            return prev;
          }
        }, 0);

        // 控制小数位数为两位,不进行四舍五入
        const formattedSum = column.property == 'roi' ? `${sum.toFixed(1)}` : `${sum.toFixed(2)}`;
        sums[index] = formattedSum;
      } else {
        const values1 = data.map((item) => item[column.property]).filter((value) => value != 0);
        if (values1.length) {
          const percentageValues = values1
            .map((value) => {
              if (typeof value === 'string' && value.includes('%')) {
                return Number(value.split('%')[0]);
              } else {
                return NaN;
              }
            })
            .filter((value) => !Number.isNaN(value));
          if (percentageValues.length) {
            const rowValue = percentageValues
              .map((item) => Number(item))
              .filter((value) => value != 0);
            const average = rowValue.reduce((prev, curr) => prev + curr, 0) / rowValue.length || 0;

            sums[index] = `${(Math.floor(average * 100) / 100).toFixed(2)}%`;
          } else {
            sums[index] = '0';
          }
        } else {
          sums[index] = '0';
        }
      }
    });
    return sums;
  };

  onMounted(() => {
    getTableList();
  });

  defineExpose({
    getTableList,
  });
</script>

<style lang="less">
  .content {
    margin-top: 10px;
    border: 1px solid #eee;
    padding: 20px;
    border-radius: 6px;
  }

  .el-table__fixed {
    background-color: #f0f0f0;
  }
</style>

 代码中的:

<el-table-column v-if="columnType" label="操作" width="240">
<template #default="{row}">
<slot name="append" :data="row"></slot>
</template>
</el-table-column>

等同于以下写法

<slot name="append"></slot> 是使用插槽的两种方法

2.父组件中使用

<initTable
      ref="DataMarketRef"
      :isShow="false"
      :columns="columnsList"
      :searchForm="searchForm"
      :tableRequest="Operate.queryDataDashboard"
      isSerial
    >
      <template #row-slot="{ row, column }">
        <div v-if="column.property == 'loginNum'">
          {{列内容}}
        </div>
      </template>    
      <template #append>
        <el-table-column label="操作" width="240" fixed="right">
          <template v-slot="scope">
          </template>
        </el-table-column>
      </template>
    </initTable>



<!-- <template #append="row"> -->
       <!-- <el-button type="primary" @click="bindProject('upDate',row.data)">增加</el-button>-->
       <!-- <el-button type="info" @click="changeLog(row.data)">修改记录</el-button>-->
<!-- </template> -->
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值