antdv table组件封装成全局组件以及携带自定义表头展示

本文介绍了如何在Vue.js中封装一个动态表格组件,包括设置表格列显示、自定义展开图标、列筛选等功能,并通过Vuex进行状态管理,存储用户自定义的列设置。组件中使用了Ant Design Vue库,提供了全选和全不选列的选项,并展示了在实际项目中的使用方式。此外,还展示了如何在main.js中全局注册该组件,以及在子组件中根据生命周期控制渲染。
摘要由CSDN通过智能技术生成
1、封装table组件
// tableBox.vue
<template>
  <div class="table-c">
    <a-table
      :expandIcon="customIcon"
      :columns="tableColumns"
      :data-source="dataSource"
      :pagination="pagination"
      :loading="loading"
      :rowSelection="rowSelection"
      @change="change"
      @expand="expand"
    >
      <template
        v-for="i in tableColumns"
        :key="i.key"
        v-slot:[i.slots?.customRender]="{ record }"
      >
        <slot :name="i.slots?.customRender" :record="record"></slot>
      </template>

      <template v-slot:[expandedRowRender]="{ record }">
        <slot name="expandedRowRender" :record="record"></slot>
      </template>
    </a-table>
    <a-modal
      title="列表设置"
      v-model:visible="visible"
      :width="800"
      :confirmLoading="tableHeadLoading"
      :bodyStyle="{
        paddingBottom: '10px',
      }"
    >
      <a-checkbox-group v-model:value="tableHead" class="checked-item">
        <a-row>
          <a-col
            :span="5"
            v-for="check in columns"
            :key="check.key"
            :offset="1"
          >
            <a-checkbox :value="check.key" :disabled="check.key == 'action'">
              <span
                v-text="check.key == 'action' ? '操作' : check.title"
              ></span>
            </a-checkbox>
          </a-col>
        </a-row>
      </a-checkbox-group>
      <template #footer>
        <div class="pop-btn-box">
          <div class="btn-left">
            <a-button @click="checkAll">全选</a-button>
            <a-button @click="checkNoAll">全不选</a-button>
          </div>
          <div class="btn-right">
            <a-button @click="handleHeadCancel">返回</a-button>
            <a-button type="primary" @click="setConfirm" class="ok-btn"
              >确定</a-button
            >
          </div>
        </div>
      </template>
    </a-modal>
  </div>
</template>
<script setup>
import { h, reactive, ref } from "vue";
import { RightOutlined, SettingOutlined } from "@ant-design/icons-vue";
import { message } from "ant-design-vue";
import { useStore } from "vuex";
const store = useStore();
const props = defineProps({
  columns: {
    // 弹窗显示的所有项
    type: Array,
    default: [],
  },
  pagination: {
    type: Object,
    default: {
      hideOnSinglePage: true,
    },
  },
  rowSelection: {
    type: Object,
    default: undefined,
  },
  dataSource: {
    type: Array,
    default: [],
  },
  expandedRowRender: {
    type: String,
    default: "",
  },
  routerName: {
    type: String,
    default: "",
  },
  loading:{
    type:Boolean,
    default:false
  }
});
let tableColumns = ref([]);
let visible = ref(false);
let tableHead = ref([]);
let initTabHead = [];
const tableHeadLoading = ref(false);
const attrsName = ref("");
attrsName.value = props.routerName;
const emit = defineEmits(["change", "expand"]);
//构造表格列
const createColumns = () => {
  const arr = props.columns.map((item) => {
    let obj = { ...item };
    obj.isShow = item.isShow != undefined ? item.isShow : true;
    obj.dataIndex = item.key;
    !obj.slots && item.isSlot ? (obj.slots = { customRender: item.key }) : "";
    item.key === "action"
      ? (obj.title = h(SettingOutlined, {
          class: "set-icon",
          onClick: () => {
            visible.value = true;
          },
        }))
      : "";
    return obj;
  });
  initTabHead = [...arr]; // 用于后续操作
  tableHead.value = arr // 控制全选以及选中的项
    .filter((i) => {
      if (i.isShow) {
        return i;
      }
    })
    .map((t) => t.key);
  return arr;
};
let showColumns = createColumns();
showColumns = showColumns.filter((i) => i.isShow == true);
tableColumns.value = showColumns; // 表格显示的所有项,isShow 为true时
const checkAll = () => {
  let showArr = [];
  initTabHead.forEach((tab) => {
    tab.isShow = true;
    showArr.push(tab);
  });
  tableHead.value = showArr
    .filter((i) => {
      if (i.isShow) {
        return i;
      }
    })
    .map((t) => t.key);
};
const checkNoAll = () => {
  let showArr = [];
  initTabHead.forEach((tab) => {
    tab.isShow = false;
    if (tab.key == "action") {
      tab.isShow = true;
      showArr.push(tab);
    }
  });
  tableHead.value = showArr
    .filter((i) => {
      if (i.isShow) {
        return i;
      }
    })
    .map((t) => t.key);
};
//设置显示那些列
const setConfirm = () => {
  tableHeadLoading.value = true;
  let showArr = [];
  let setArr = [];
  initTabHead.forEach((tab) => {
    tab.isShow = false;
    tableHead.value.forEach((el) => {
      if (tab.key == el) {
        tab.isShow = true;
        showArr.push(tab);
        setArr.push(el);
      }
    });
  });
  tableHeadLoading.value = false;
  if (showArr.length < 2) {
    message.info("至少选中两项");
  } else {
    tableColumns.value = showArr;
    let obj = Object.assign({}, store.state.tableHeadObj);
    obj[attrsName.value] = [...setArr];
    store.commit("setTableHeadObj", obj);
    message.success("设置成功");
    visible.value = false;
  }
};
const handleHeadCancel = () => {
  createColumns(); // 重置
  visible.value = false;
};
//分页改变事件
const change = (pagination, filters, sorter) => {
  emit("change", { pagination, filters, sorter });
};
const expand = (expanded, record) => {
  emit("expand", { expanded, record });
};
//自定义展开图标
const customIcon = (arg) => {
  if (
    arg.record.children?.length ||
    props.expandedRowRender == "expandedRowRender"
  ) {
    return h(RightOutlined, {
      class: arg.record.key + "expand",
      style: { transition: "all 0.3s", cursor: "pointer", padding: "5px" },
      onclick: async (e) => {
        let dom = document.getElementsByClassName(arg.record.key + "expand")[0];
        arg.expanded
          ? (dom.style.cssText = "transform:rotateZ(0)")
          : (dom.style.cssText = "transform:rotateZ(90deg)");
        arg.onExpand(arg.record, e);
      },
    });
  } else {
    return h("span", { style: { display: "inline-block", with: "14px" } });
  }
};
</script>
<style lang="less" scoped>
.table-c {
  margin-top: 0.08rem;
}
.checked-item {
  margin-left: -24px;
}
.ant-checkbox-group {
  width: 100%;
}
.ant-checkbox-wrapper {
  padding: 2px 8px;
  margin-bottom: 0.1rem;
  border-radius: 4px;
  white-space: nowrap;
  width: 100%;
  &:hover {
    background: @table-head-set-bg;
  }
}
.ant-checkbox-wrapper-checked {
  background: @table-head-set-bg;
}
.pop-btn-box {
  display: flex;
  justify-content: space-between;
  .btn-right {
    .ok-btn {
      color: @btn-primary-color;
    }
  }
}
</style>

2、在main.js中把组件全局上
import {
  createApp
} from 'vue';
import App from './App.vue';
import router from './router/index.js';
import store from './store';
import {
  changeSize
} from './utils/common.js';
import antd from 'ant-design-vue';
import tableBox from "@/components/tableBox.vue";
import 'ant-design-vue/dist/antd.less';
import './assets/common/style.less';
import 'nprogress/nprogress.css'

changeSize()
const app = createApp(App)
app.component('table-box', tableBox);
app.use(antd).use(router).use(store).mount('#app')
3、在组件中的使用, 因为父子组件的生命周期的原因,所以使用v-if控制子组件的渲染
// index.vue
<table-box
  v-if="isShow"
  :columns="state.columns"
  :dataSource="state.tableData"
  :pagination="pagination"
  :rowSelection="rowSelection"
  @change="handleTableChange"
  :routerName="routerName"
>
	<template v-slot:expandedRowRender="{ record }">
      <span>{{ record }}</span>
  </template>
  <template #action="{ record }">
    <a-popover title="">
      <template #content>
        <div
          class="action-box"
          style="display: flex; flex-direction: column"
        >
          <a @click="handleEdit(true, record)">修改</a>
          <a @click="handleDel(record.id)" href="javascript:void(0)"
            >删除</a
          >
        </div>
      </template>
      <MenuOutlined />
    </a-popover>
  </template>
</table-box>
<script setup>
import { useRoute } from "vue-router";
const route = useRoute();
const routerName = ref(route.name);
const isShow = ref(false);
const expandedRowRender = ref("expandedRowRender");
const state = reactive({
 tableData: [],
 columns: [
   {
     title: "厂商名称",
     key: "firmName",
     dataIndex: "firmName",
     ellipsis: true,
   },
   {
     title: "简称",
     key: "abbreviation",
     dataIndex: "abbreviation",
     ellipsis: true,
   },
   {
     title: "地址",
     key: "address",
     dataIndex: "address",
     ellipsis: true,
   },
   {
     title: "联系人",
     key: "liaison",
     dataIndex: "liaison",
   },
   {
     title: "联系电话",
     key: "liaisonPhone",
     dataIndex: "liaisonPhone",
     ellipsis: true,
   },
   {
     key: "action",
     isSlot: true,
     slots: {
       customRender: "action",
     },
   },
 ],
 selectedRowKeys: [],
});
const getTableData = (formData) => {
  for (let i = 0; i < 12; i++) {
    state.tableData.push({
      id: i,
      key: i,
      firmName: "江苏汇水创",
      abbreviation: "汇水创",
      address: "高塘石",
      liaison: "陈辉",
      liaisonPhone: "1371507698",
    });
  }
};
const rowSelection = {
  onChange: (selectedRowKeys, selectedRows) => {
    state.selectedRowKeys = selectedRowKeys;
    console.log(
      `selectedRowKeys: ${selectedRowKeys}`,
      "selectedRows: ",
      selectedRows
    );
  },
};
// 切换页
const handleTableChange = (page, filters, sorter) => {
  pagination.current = page.current;
  pagination.pageSize = page?.pageSize;
  // console.log(filters); // 选中值为数组(单选直接拿第一项,多选把数组转化成字符串拼接 - 看后台需要什么数据结构)
  if (filters.type) {
    // 必须判断,未操作切换页会报错(无type属性)
    formData.type = filters.type[0];
  }
  getTableData(formData);
};
onMounted(async () => {
 // 判断是否已经设置过表头,有则使用
  if (
    sessionStorage.tableHeadObj &&
    JSON.parse(sessionStorage.tableHeadObj)[routerName.value]
  ) {
    let arr = JSON.parse(sessionStorage.tableHeadObj)[routerName.value];
    for (let i = 0; i < state.columns.length; i++) {
      if (arr.indexOf(state.columns[i].key) == -1) {
        state.columns[i].isShow = false;
      }
    }
    isShow.value = true;
  } else {
    isShow.value = true;
  }
  getTableData(formData);
});
</script>
4、表头数据vuex + sessionStorage (key为路由的名称 – 唯一性)
// store.js
import {
  createStore
} from "vuex";
export default createStore({
  state: {
    tableHeadObj: sessionStorage.tableHeadObj ? JSON.parse(sessionStorage.tableHeadObj): {}
  },
  mutations: {
    setTableHeadObj(state, obj) {
      state.tableHeadObj = obj;
      sessionStorage.tableHeadObj = JSON.stringify(state.tableHeadObj);
    }
  }
})
5、效果:

![在这里插入图片描述](https://img-blog.csdnimg.cn/c7db7b608e494f2390983242dca63207.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5b-D6Iul5ZCR6ZizKCrvv6PvuLbvv6Mp,size_20,color_FFFFFF,t_70,g_se,x_16
在这里插入图片描述
在这里插入图片描述

后来者居上:脾气都给了前者,耐心都给了后者

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值