vue3 + antd二次封装a-table组件

前置条件

vue版本 v3.3.11
ant-design-vue版本 v4.1.1

内容梗概

二次封装a-table组件,大大提高工作效率和降低项目维护成本;

先看效果图

在这里插入图片描述

代码区域

utils.js文件

// 用于模拟接口请求
export const getRemoteTableData = (data = [], time = 1000) => {
    return new Promise((resolve) => {
        setTimeout(() => {
            const retObj = {
                list: data,
                total: 100,
                pageSize: 10,
                pageNum: 1,
            }
            resolve(retObj)
        }, time)
    })
}

// 指定范围随机数
export const getRandomNumber = (min, max) => {
    return Math.floor(Math.random() * (max - min + 1) + min);
}

// 判断空值
export const isEmpty = (value) => {
    if (Array.isArray(value)) {
        return !value.length
    } else if (Object.prototype.toString.call(value) === "[object Object]") {
        return !Object.keys(value).length
    } else {
        return [null, undefined, ''].includes(value)
    }
}

//  数字格式化
export const formatNumber = (num) =>
    num ? (num + "").replace(/\d{1,3}(?=(\d{3})+(\.\d*)?$)/g, "$&,") : (isEmpty(num) ? '' : 0)

// 百分比格式化
export const formatPercent = (percent, n = 2) => isEmpty(percent) ? '' : `${(+percent).toFixed(n)}%`

// 金额格式化
export const formatMoney = (num) => isEmpty(num) ? "" : formatNumber(num.toFixed(2))

my-table.vue组件

<template>
  <div>
    <div class="attach-buttons-wrap">
      <a-button
        style="margin-right: 10px"
        v-bind="btn.props"
        @click="btn.onclick({ selectedRowKeys })"
        v-for="btn in attachButtons"
        :key="btn.name"
        :disabled="
          typeof btn?.props?.disabled === 'function'
            ? btn?.props?.disabled(selectedRowKeys)
            : btn?.props?.disabled ?? false
        "
        >{{ btn.name }}</a-button
      >
    </div>
    <a-table
      v-bind="{
        loading: localLoading,
        pagination: localPagination,
        rowSelection: localRowSelection,
        rowKey: 'id',
        ...$attrs,
      }"
      :dataSource="dataSource"
    >
      <!-- 自定义渲染单元格 -->
      <template #bodyCell="{ text, record, index, column }">
        <!-- 操作列 -->
        <template v-if="column.dataIndex === 'operation'">
          <a
            v-for="(item, itemIndex) in column.buttons"
            :key="itemIndex"
            @click="item.onclick({ text, record, index, column })"
            :style="{
              marginRight: '12px',
              display: btnShow({ item, text, record, index, column })
                ? 'inline-block'
                : 'none',
              ...(item.style || {}),
            }"
            >{{ item.name }}</a
          >
        </template>
        <!-- 序号 -->
        <template v-if="column.type === 'serial'">
          {{
            (localPagination.current - 1) * localPagination.pageSize + index + 1
          }}
        </template>
        <!-- 百分比 -->
        <template v-else-if="column.type === 'percent'">{{
          formatPercent(text)
        }}</template>
        <!-- 数值 -->
        <template v-else-if="column.type === 'number'">{{
          formatNumber(text)
        }}</template>
        <!-- 金额 -->
        <template v-else-if="column.type === 'money'">{{
          formatMoney(text)
        }}</template>
        <!-- 列插槽 -->
        <template v-else-if="columnSlots.includes(column.dataIndex)">
          <template v-for="slot in columnSlots" :key="slot">
            <slot
              :name="`column-${slot}`"
              v-bind="{ text, record, index, column }"
            ></slot>
          </template>
        </template>
        <!-- 自定义列渲染 -->
        <template v-else-if="typeof column?.customRender === 'function'">
          <!-- 渲染customRender -->
          <component
            :is="column.customRender"
            v-bind="{ text, record, index, column }"
          ></component>
        </template>
      </template>
      <!-- 插槽透传 -->
      <template v-for="(value, name) in $slots" v-slot:[name]="slotProps">
        <slot :name="name" v-bind="slotProps"></slot>
      </template>
    </a-table>
  </div>
</template>

<script setup>
import { ref, reactive, computed, onMounted, useSlots, unref } from "vue";
import { formatPercent, formatMoney, formatNumber } from "@/common/utils";
const props = defineProps({
  data: {
    type: [Function, Array],
    default: () => [],
  },
  // 附属操作按钮
  attachButtons: {
    type: Array,
    default: () => [],
  },
});
const emits = defineEmits(["refresh"]);
const slots = useSlots();
const columnSlots = ref([]);
const createColumnSlots = () => {
  columnSlots.value = Object.keys(slots)
    .filter((x) => x.indexOf("column") !== -1)
    .map((x) => x.split("-")[1]);
};

const btnShow = computed(
  () =>
    ({ item, text, record, index, column }) =>
      typeof item?.show == "function"
        ? item.show({ text, record, index, column })
        : item.show ?? true
);
// 列表数据
const dataSource = ref([]);
// 分页
const localPagination = reactive({
  current: 1,
  pageSize: 10,
  total: 0,
  showTotal: (total) => `${total} 条记录`,
  showSizeChanger: true,
  showQuickJumper: true,
  onChange: (current, size) => {
    localPagination.current = current;
    localPagination.pageSize = size;
    loadData({ current, pageSize: size });
  },
  pageSizeOptions: ["10", "20", "30", "40", "50"],
});
// 是否分页
const isPagination = ref(false);
// loading状态
const localLoading = ref(false);
const selectedRowKeys = ref([]);
// 选择列
const onSelectChange = (rowKeys) => {
  selectedRowKeys.value = rowKeys;
};
const localRowSelection = computed(() => {
  return {
    selectedRowKeys: unref(selectedRowKeys),
    onChange: onSelectChange,
  };
});

const loadData = (pagination) => {
  localLoading.value = true;
  const params = isPagination.value
    ? {
        pageNo: pagination?.current
          ? pagination.current
          : localPagination.current,
        pageSize: pagination?.pageSize
          ? pagination.pageSize
          : localPagination.pageSize,
      }
    : {};
  if (!props.data) {
    dataSource.value = [];
    return;
  }
  if (Array.isArray(props.data)) {
    dataSource.value = props.data;
    localLoading.value = false;
  } else {
    props
      .data(params)
      .then((retObj) => {
        const { list, total, pageSize, pageNum } = retObj;
        isPagination.value = retObj.hasOwnProperty("list");
        if (isPagination.value) {
          localPagination.total = total || 0;
          localPagination.pageSize = pageSize;
          localPagination.pageNum = pageNum;
          dataSource.value = list?.length ? list : [];
          if (list?.length === 0 && localPagination.current > 1) {
            localPagination.current--;
            loadData();
          }
        } else {
          dataSource.value = retObj?.length ? retObj : [];
        }
      })
      .finally(() => (localLoading.value = false));
  }
};

// 刷新表格数据
const refresh = (isInit = false) => {
  // 页码重置1
  if (isInit) {
    localPagination.current = 1;
    localPagination.total = 0;
    emits("refresh");
  }
  loadData();
};

onMounted(() => {
  createColumnSlots();
  loadData();
});

defineExpose({ refresh });
</script>

<style lang="scss" scoped>
.attach-buttons-wrap {
  margin-bottom: 10px;
}
</style>

使用该组件

<template>
  <div style="padding: 40px">
    <MyTable
      ref="myTable"
      :data="getDataFromApi"
      :columns="columns"
      :attachButtons="attachButtons"
    >
      <template #column-tags="{ record }">
        <a-tag v-for="tag in record.tags" :key="tag">
          {{ tag.toUpperCase() }}
        </a-tag>
      </template>
      <template #headerCell="{ column }">
        <template v-if="column.dataIndex === 'tags'">测试表头(列插槽)</template>
      </template>
    </MyTable>
  </div>
</template>

<script lang="jsx" setup>
import MyTable from "@/components/table/index.vue";
import { ref } from "vue";

import { getRemoteTableData, getRandomNumber } from "@/common/utils";
const myTable = ref(null);

const columns = [
  {
    title: "序号",
    dataIndex: "name",
    type: "serial",
  },
  {
    title: "姓名",
    dataIndex: "name",
  },
  {
    title: "年龄",
    dataIndex: "age",
  },
  {
    title: "性别",
    dataIndex: "sex",
  },
  {
    title: "士兵数量",
    dataIndex: "score",
    type: "number",
  },
  {
    title: "铁骑兵占比",
    dataIndex: "percent",
    type: "percent",
  },
  {
    title: "军费",
    dataIndex: "price",
    type: "money",
  },
  {
    title: "标签",
    dataIndex: "tags",
  },
  {
    title: "自定义渲染列",
    dataIndex: "custom",
    customRender: ({record}) => <div><a>{record.name} </a><span style="color: green">年龄:{record.age}</span></div>
  },
  {
    title: "操作",
    dataIndex: "operation",
    buttons: [
      {
        name: "查看",
        onclick: ({ record }) => {
          console.log("查看", record);
        },
        show: ({ record }) => record.name === "诸葛亮4",
      },
      {
        name: "编辑",
        onclick: ({ record }) => {
          console.log("编辑", record);
        },
      },
      {
        name: "删除",
        onclick: ({ record }) => {
          console.log("删除", record);
        },
        style: {
          color: "red",
        },
      },
    ],
    width: 180,
  },
];

const getDataFromApi = async () => {
  let getData = Array.from({ length: 10 }, (_, i) => i).map((x, i) => ({
    id: getRandomNumber(1, 10000000000000),
    name: `诸葛亮${i + 1}`,
    age: getRandomNumber(10, 100),
    sex: ``,
    price: getRandomNumber(1000, 100000),
    percent: getRandomNumber(1, 100),
    score: getRandomNumber(100000, 1000000000),
    tags: ["OK", "YES"],
  }));
  return await getRemoteTableData([...getData]);
};

const attachButtons = ref([
  {
    name: "刷新",
    onclick: () => myTable.value.refresh(true)
  },
  {
    name: "批量删除",
    onclick: ({selectedRowKeys}) => {console.log('selectedRowKeys', selectedRowKeys)},
    props: {
      type: 'primary',
      danger: true,
      disabled: (selectedRowKeys) => !selectedRowKeys.length
    }
  },
   {
    name: "导出Excel",
    onclick: () => {console.log('导出文件')},
    props: {
      type: 'primary',
    },
  }
])
</script>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/454786.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

STM32第八节:位带操作——GPIO输出和输入

前言 我们讲了GPIO的输出&#xff0c;虽然我们使用的是固件库编程&#xff0c;但是最底层的操作是什么呢&#xff1f;对&#xff0c;我们学习过51单片机的同学肯定学习过 sbit 修改某一位的高低电平&#xff0c;从而实现对于硬件的控制。那么我们现在在STM32中有没有相似的操作…

【数据结构】Set的使用

文章目录 一、Set的使用1.Set的常用方法&#xff1a;1.boolean add(E e)2.void clear()3.boolean contains(Object o)4.boolean remove(Object o)5.int size()6.boolean isEmpty()7.Object[] toArray()8.boolean containsAll(Collection<?> c)9.boolean addAll(Collecti…

学习Vue(1)环境搭建与运行一个vue项目

下载node.js 下载地址&#xff1a;下载 | Node.js 中文网 安装 双击下载好的安装文件&#xff0c;选择安装路径即可。 安装完成&#xff0c;输入命令&#xff1a;nodel -v&#xff0c;查看版本&#xff0c;正常显示版本即安装成功。 自定义全局安装路径和缓存路径&#xff0…

SpringCloud-深度理解ElasticSearch

一、Elasticsearch概述 1、Elasticsearch介绍 Elasticsearch&#xff08;简称ES&#xff09;是一个开源的分布式搜索和分析引擎&#xff0c;构建在Apache Lucene基础上。它提供了一个强大而灵活的工具&#xff0c;用于全文搜索、结构化搜索、分析以及数据可视化。ES最初设计用…

用Origin快速拟合荧光寿命、PL Decay (TRPL)数据分析处理

需要准备材料&#xff1a;Origin、PL Decay数据txt文件 首先打开Origin画图软件 导入数据&#xff0c;按照下图箭头操作直接导入 双击你要导入的PL Decay的txt数据文件&#xff0c;然后点OK 继续点OK 数据导入后首先删除最大光子数之前的无效数据&#xff0c;分析的时候用…

每天五分钟计算机视觉:图像数据不足带来的问题和解决办法

本文重点 在当今的数字时代,图像数据的应用已经渗透到各个领域,包括但不限于计算机视觉、机器学习、自动驾驶、医疗诊断等。然而,当图像数据不足时,会引发一系列问题,对相关应用产生负面影响。 尤其是计算机视觉领域,图像数据尤为珍贵和稀缺,如果计算机视觉的任务中,如…

政务云安全风险分析与解决思路探讨

1.1概述 为了掌握某市政务网站的网络安全整体情况&#xff0c;在相关监管机构授权后&#xff0c;我们组织人员抽取了某市78个政务网站进行安全扫描&#xff0c;通过安全扫描&#xff0c;对该市政务网站的整体安全情况进行预估。 1.2工具扫描结果 本次利用漏洞扫描服务VSS共扫…

基于Spring Boot的疗养院管理系统的设计与实现

传统办法管理信息首先需要花费的时间比较多&#xff0c;其次数据出错率比较高&#xff0c;而且对错误的数据进行更改也比较困难&#xff0c;最后&#xff0c;检索数据费事费力。因此&#xff0c;在计算机上安装疗养院管理系统软件来发挥其高效地信息处理的作用&#xff0c;可以…

使用html+css制作一个发光立方体特效

使用htmlcss制作一个发光立方体特效 <!DOCTYPE html> <html lang"zh-CN"><head><meta charset"UTF-8" /><meta name"viewport" content"widthdevice-width, initial-scale1.0" /><title>Documen…

【机器学习300问】36、什么是集成学习?

一、什么是集成学习&#xff1f; &#xff08;1&#xff09;它的出现是为了解决什么问题&#xff1f; 提高准确性&#xff1a;单个模型可能对某些数据敏感或者有概念偏见&#xff0c;而集成多个模型可以提高预测的准确性。让模型变稳定&#xff1a;一些模型&#xff0c;如决策…

Css基础——溢出文字省略号表达

1. 单行文本溢出显示省略号&#xff1a; 1.1、方法&#xff1a; 1. 先强制一行内显示文本*/ white-space: nowrap; &#xff08; 默认 normal 自动换行&#xff09; 2. 超出的部分隐藏*/ overflow: hidden; 3. 文字用省略号替代超出的部分*/ text-overflow: ellipsis; 1.2、代…

【他山之石】BlueLM蓝心大模型的落地

note 预训练&#xff1a;我们采用了混合精度训练以及梯度缩放策略&#xff0c;因为这可以减少训练周期&#xff0c;训练周期较少会节省模型训练所需的时间和资源。选择回放训练能够让模型定向学会一类知识信息。Loss 预测确保在训练过程前对损失进行估计&#xff0c;以保证训练…

javaweb day16 mysql

mysql 安装&#xff1a; 企业开发使用方法 安装虚拟机代替服务器 数据模型 创建数据库 写法 sql简介

KNN算法对鸢尾花进行分类:添加网格搜索和交叉验证

优化——添加网格搜索和交叉验证 from sklearn.datasets import load_iris from sklearn.model_selection import train_test_split from sklearn.preprocessing import StandardScaler from sklearn.neighbors import KNeighborsClassifier from sklearn.model_selection imp…

GNN/GCN自己学习

一、图的基本组成 V&#xff1a;点&#xff08;特征&#xff09; E&#xff1a;边 U&#xff1a;图&#xff08;全局特征&#xff09; 二、用途 整合特征&#xff08;embedding)&#xff0c;做重构 三、邻接矩阵 以图像为例&#xff0c;每个像素点周围都有邻居&#xff0c;…

Composer创建ThinkPHP无法获取最新版本的问题

composer安装TP&#xff08;截止目前最新版本为8.0&#xff09; composer create-project topthink/think tp 一开始直接给我安装成TP6了&#xff0c;原因就是我系统的PHP版本不是8.0以上&#xff0c;所以不支持最新的TP版本&#xff0c;就会默认安装之前稳定的版本。解决这个…

说说JVM的垃圾回收机制

简介 垃圾回收机制英文为Garbage Collection, 所以我们常常称之为GC。那么为什么我们需要垃圾回收机制呢&#xff1f;如果大家有了解过Java虚拟机运行时区域的组成(JVM运行时存在&#xff0c;本地方法栈&#xff0c;虚拟机方法栈&#xff0c;程序计数器&#xff0c;堆&#xf…

linux安全--CentOS7安装Tomcat,远程管理ManagerApp

目录 1.Tomcat安装 2.Tomcat远程管理 1.Tomcat安装 下载安装包并解压 tar xf apache-tomcat-7.0.54.tar.gz -C /usr/local/apache-tomcat_7.0.54/tomcat启停 启动 ./startup.sh 停止 ./shutdown.sh 2.Tomcat远程管理 找到tomcat文件夹中webapps/manager/META-INF/contex…

(黑马出品_高级篇_04)SpringCloud+RabbitMQ+Docker+Redis+搜索+分布式

&#xff08;黑马出品_高级篇_04&#xff09;SpringCloudRabbitMQDockerRedis搜索分布式 微服务技术——可靠性消息服务 今日目标服务异步通信-高级篇1.消息可靠性1.1.生产者消息确认1.1.1.修改配置1.1.2.定义Return回调1.1.3.定义ConfirmCallbac…

temu英国电商市场洞察:2月份商品销售数据分析Python数据采集Api

文章目录 引言可视化分析数据展示商品类目占比分析销售额分析价格区间占比分析各类目新品占比分析 写在最后 引言 在美国市场稳步增长基础上&#xff0c;Temu也不断加快全球步伐&#xff0c;现已进入47个国家&#xff0c;光23年9月份就进驻了10个国家&#xff0c;创造单月扩张国…