vue.js

关注公众号 jb51net

关闭
首页 > 网络编程 > JavaScript > javascript类库 > vue.js > Vue3封装ant-design-vue

Vue3封装ant-design-vue表格的详细代码

作者:Joey_Tribiani

这篇文章主要介绍了Vue3封装ant-design-vue表格,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

Vue3封装ant-design-vue表格

说明

该文仅仅用来记录自己使用Vue3基于ant-design-vue开发的时候对Table组建的二次封装,因为在vue中,组件不能像react中那样作为一种JS数据类型随意传递,需要使用slot,因此个人认为不够灵活,但是为了解决这个问题就是用插槽对ant-design-vue的Table做了二次封装,封装后使用非常灵活:

封装代码

<template>
  <Table
    :row-key="rowKey"
    :row-selection="rowSelection"
    :pagination="pagination"
    :scroll="{ x: 'auto', y: scrollY }"
    :columns="columns"
    :loading="loading"
    :data-source="dataSource"
  >
    <template v-slot:headerCell="{ column }">
      <span
        class="title2"
        :style="{ whiteSpace: 'nowrap', fontWeight: 'normal' }"
        >{{ $t(column.title as string) }}</span
      >
    </template>
    <template v-slot:bodyCell="{ column, record }">
      <template v-for="slotName of slots">
        <slot
          v-if="slotName === column.dataIndex"
          :name="slotName"
          :column="column"
          :record="record"
        ></slot>
      </template>
      <span v-if="!slots.includes(column.dataIndex as string)">{{
        parseDefaultValue(record, column.dataIndex as string)
      }}</span>
    </template>
  </Table>
</template>
<script lang="ts" setup>
import { Table, TablePaginationConfig } from "ant-design-vue";
import { TableColumns } from "@/models/base.model";
import { isNull } from "@/util";
import { TableRowSelection } from "ant-design-vue/es/table/interface";
const {
  columns,
  dataSource,
  slots = [],
  pagination = false,
  scrollY,
  rowSelection = undefined,
  rowKey,
  loading,
} = defineProps<{
  columns: TableColumns[];
  pagination?: false | TablePaginationConfig;
  dataSource: any[];
  slots?: string[];
  scrollY?: number;
  rowSelection?: TableRowSelection;
  rowKey?: string;
  loading?: boolean;
}>();
const parseDefaultValue = (record: Record<string, any>, dataIndex: string) => {
  const dataIndexs = dataIndex.split(".");
  let result = record;
  dataIndexs.forEach((element) => {
    if (result) {
      result = result[element];
    }
  });
  if (isNull(result)) {
    return "-";
  }
  return result;
};
</script>

使用

<script lang="ts" setup>
import VlanConfig from "@/views/VlanConfig.vue";
import { Button, Input, InputNumber, message, Modal } from "ant-design-vue";
import { reactive, watch } from "vue";
import vlanonfigController from "@/controllers/vlanConfig.controller";
import useDataFetch from "@/hooks/useDataFetch";
import CommonTable from "../common/CommonTable.vue";
import { NoPaddingTableColumns, TableColumns } from "@/models/base.model";
import Iconfont from "../layouts/Iconfont.vue";
import { TableRowSelection } from "ant-design-vue/es/table/interface";
import { VlanInfo } from "@/models/vlanConfig.model";
import { $deleteConfirm } from "@/util";
import EditButtons from "../common/EditButtons.vue";
import { useI18n } from "vue-i18n";
enum OperationType {
  EDIT,
  DELETE,
}
const { t } = useI18n();
const data = useDataFetch(vlanonfigController.getVlanList, true);
const state = reactive({
  selectedVlanIds: [],
  onOperationVlan: undefined as VlanInfo,
  operationType: null as OperationType,
  addModalOpen: false,
  confirmLoading: false,
  editData: {
    vlan_id: undefined,
    vlan_name: undefined,
  },
});
const handleDelete = (vlan: VlanInfo) => {
  state.onOperationVlan = vlan;
  state.operationType = OperationType.DELETE;
  $deleteConfirm({
    title: t("vlan.deleteVlanTitle", { name: vlan.vlan_name }),
    content: t("deleteContent"),
    onOk() {
      return new Promise<void>((resolve) => {
        setTimeout(() => {
          resolve();
        }, 1000);
      });
    },
  });
};
const restoreEditData = () => {
  state.editData = { vlan_id: undefined, vlan_name: undefined };
};
const handleEdit = (vlan: VlanInfo) => {
  state.operationType = OperationType.EDIT;
  restoreEditData();
  state.onOperationVlan = vlan;
};
const handleSelectChange: TableRowSelection["onSelect"] = (ids: number[]) => {
  console.log("in");
  state.selectedVlanIds = ids;
};
const columns = [
  new TableColumns("vlan.vlanId", "vlan_id"),
  new TableColumns("vlan.vlanName", "vlan_name"),
  new NoPaddingTableColumns("", "operation", undefined, 240),
];
const deleteDisabled = $computed(() => !state.selectedVlanIds.length);
const handleMultiDelete = () => {
  console.log(state.selectedVlanIds);
};
const handleEditOk = (vlan: VlanInfo) => {
  state.onOperationVlan = undefined;
  data.setEditDataToOriginData();
};
const handleEditCancel = (vlan: VlanInfo) => {
  state.onOperationVlan = undefined;
  data.resetData();
  console.log("cancel");
};
const checkOperationDisabled = (id: string) => {
  return (
    state.onOperationVlan?.nanoid === id &&
    state.operationType === OperationType.EDIT
  );
};
const handleAddVlanClick = () => {
  state.addModalOpen = true;
};
const hanldeAddVlanCancel = () => {
  state.addModalOpen = false;
  restoreEditData();
};
const hanldeAddVlanOk = () => {
  const { vlan_id, vlan_name } = state.editData;
  if (!vlan_id || !vlan_name) {
    message.error(t("vlan.addError"));
    return;
  }
  state.confirmLoading = true;
  return new Promise<void>((resolve) => {
    setTimeout(() => {
      resolve();
      data.refresh();
      hanldeAddVlanCancel();
      state.confirmLoading = false;
    }, 1000);
  });
};
const isEdit = $computed(() => state.operationType === OperationType.EDIT);
</script>
<template>
  <VlanConfig>
    <template #rightExtra>
      <div style="bottom: 12px" class="flex-start relative-position">
        <Button
          :disabled="deleteDisabled"
          @click="handleMultiDelete"
          danger
          type="primary"
          >{{ $t("vlan.deleteVlan") }}</Button
        >
        <Button
          @click="handleAddVlanClick"
          :style="{ marginLeft: '16px' }"
          type="primary"
          >{{ $t("vlan.addVlan") }}</Button
        >
      </div>
    </template>
    <CommonTable
      :row-key="'nanoid'"
      :rowSelection="{
        selectedRowKeys: state.selectedVlanIds,
        onChange: handleSelectChange,
      }"
      :slots="['operation', 'vlan_id', 'vlan_name']"
      :dataSource="data.clonedData.value"
      :columns="columns"
    >
      <template #vlan_id="{ record }">
        <div
          v-if="isEdit && record.nanoid === state.onOperationVlan?.nanoid"
          class="flex-start"
        >
          <InputNumber
            style="margin-right: 8px"
            v-model:value="record.vlan_id"
          ></InputNumber>
          <EditButtons
            @ok="handleEditOk(record as VlanInfo)"
            @cancel="handleEditCancel(record as VlanInfo)"
          />
        </div>
      </template>
      <template #vlan_name="{ record }">
        <div
          v-if="isEdit && record.nanoid === state.onOperationVlan?.nanoid"
          class="flex-start"
        >
          <Input
            style="width: 200px; margin-right: 8px"
            v-model:value="record.vlan_name"
          ></Input>
          <EditButtons
            @ok="handleEditOk(record as VlanInfo)"
            @cancel="handleEditCancel(record as VlanInfo)"
          />
        </div>
      </template>
      <template #operation="{ record }">
        <div class="flex-start flex-nowrap">
          <Button
            type="text"
            :disabled="checkOperationDisabled(record.nanoid)"
            @click="() => handleEdit(record as VlanInfo)"
            class="flex"
          >
            <Iconfont
              icon="ic_edit"
              :disabled="checkOperationDisabled(record.nanoid)"
              :primary="!checkOperationDisabled(record.nanoid)"
            ></Iconfont>
            <span
              :class="{
                'primary-color': !checkOperationDisabled(record.nanoid),
              }"
              >{{ $t("edit") }}</span
            >
          </Button>
          <Button
            type="text"
            @click="() => handleDelete(record as VlanInfo)"
            class="flex"
          >
            <Iconfont icon="ic_edit" primary></Iconfont>
            <span class="primary-color">{{ $t("delete") }}</span>
          </Button>
        </div>
      </template>
    </CommonTable>
  </VlanConfig>
  <!-- 添加vlan对话框 -->
  <Modal
    :width="400"
    @cancel="hanldeAddVlanCancel"
    @ok="hanldeAddVlanOk"
    centered
    :title="$t('vlan.addVlan')"
    :confirmLoading="state.confirmLoading"
    :open="state.addModalOpen"
  >
    <div style="padding: 24px 0 16px">
      <div class="flex-btw flex-nowrap" :style="{ marginBottom: '24px' }">
        <span class="title1 white-s-nowrap">{{ $t("vlan.vlanId") }}</span>
        <InputNumber
          :min="1"
          :max="4093"
          v-model:value="state.editData.vlan_id"
          style="width: 264px"
        ></InputNumber>
      </div>
      <div class="flex-btw flex-nowrap">
        <span class="title1 white-s-nowrap">{{ $t("vlan.vlanName") }}</span>
        <Input
          v-model:value="state.editData.vlan_name"
          style="width: 264px"
        ></Input>
      </div>
    </div>
  </Modal>
</template>
<style scoped lang="scss"></style>

ant-design-vue Table封装

ant-design-vue Table

封装表格主要功能:
1、表格加载(源数据支持数组和接口方法传递)
2、表格分页
3、表格伸缩列
4、支持单击选中行
5、表格支持列显示和隐藏(同时也可以查看AVue,具有相同的功能,AVue 组件已经封装,可直接使用;此处仅供需要情景使用)

第一步

安装支持vue的可拖动控件

npm install vue-draggable-resizable -S

第二步

利用ant和vue-draggable-resizeable封装自己的表格
Table.js如下

import { Table } from 'ant-design-vue'
import Vue from 'vue'
// 引入vue-draggable-resizable,用于表格列伸缩
import VueDraggableResizable from 'vue-draggable-resizable'
// TableOption用于表格列显示或隐藏
import TableOption from './TableOption'
// 注册组件
Vue.component('vue-draggable-resizable', VueDraggableResizable)
Vue.component('table-option', TableOption)
const componentName = 'drag-table'
const DragTable = {
 name: componentName,
 props: Object.assign({}, Table.props, {
   // 返回 Promise<{ currPage, totalCount, list: any[] }> 的获取数据的函数,用于内部管理数据加载
   data: { type: Function },
   // 是否开启:单击行则选中行
   selectOnClick: { type: Boolean, default: true },
   // 默认翻到第 1 页
   pageNum: { type: Number, default: 1 },
   // 默认分页大小 10 行
   pageSize: { type: Number, default: 10 },
   // 是否显示分页大小切换下拉框
   showSizeChanger: { type: Boolean, default: true },
   // 是否显示分页器
   showPagination: { type: [String, Boolean], default: 'auto' },
   // 指定表格当前页数的url 例如:/users/2
   pageURI: { type: Boolean, default: false },
   // 是否展示序号列
   showIndex: { type: Boolean, default: true },
   customCell: { type: Function }
 }),
 data() {
   return {
     localLoading: false, // 加载标识
     localDataSource: [], // 表格源数据
     localPagination: Object.assign({}, this.pagination), // 分页对象,合并ant默认分页数据
     localScroll: {},
     // 表格列显隐
     filterValue: [],
     originColumns: []
   }
 },
 computed: {
   localKeys() {
     return [...Object.keys(this.$data), ...Object.keys(this._computedWatchers), ...Object.keys(this).filter(k => k.startsWith('local'))]
   },
   // 处理最大显示长度后的列
   localColumns(){
     return this.originColumns.filter(col => !this.filterValue.includes(col.dataIndex || col.key || col.title))
   },
   // 表格伸缩列(该属性是ant表格中覆盖默认的 table 元素的属性components)
   localComponents(){
     const headerComponent = {}
     headerComponent.header ={}
     headerComponent.header.cell = (h, props, children) => {
       const { key, ...restProps } = props
       const col = this.columns.find(col => {
         const k = col.dataIndex || col.key
         return k === key
       })
       if (!col) {
         return h('th', { ...restProps }, [...children])
       }
       const dragProps = {
         key: col.dataIndex || col.key,
         class: 'table-draggable-handle',
         attrs: {
           w: 8,
           x: parseFloat(col.width),
           z: 1,
           axis: 'x',
           draggable: true,
           resizable: false,
           onDragStart: (e) => {
             e.stopPropagation()
           }
         },
         on: {
           dragging: (x) => {
             col.width = Math.max(x, 35)
             this.computeWidth()
           }
         }
       }
       const drag = h('vue-draggable-resizable', { ...dragProps })
       return <th {...restProps} title={col.title} width={col.width} class="resize-table-th">
         {children}
         { drag }
       </th>
     }
     return headerComponent
   }
 },
 watch: {
   loading(val) {
     this.localLoading = val
   },
   // 表格源数据
   dataSource: {
     handler(val) {
       this.localDataSource = val
     },
     immediate: true
   },
   'localPagination.current'(val) {
     this.pageURI && this.$router.push({
       ...this.$route,
       params: Object.assign({}, this.$route.params, { pageNo: val })
     })
   },
   pageNum(val) {
     Object.assign(this.localPagination, { current: val })
   },
   pageSize(val) {
     Object.assign(this.localPagination, { pageSize: val })
   },
   showSizeChanger(val) {
     Object.assign(this.localPagination, { showSizeChanger: val })
   },
   scroll() {
     this.calcLocalScroll()
   },
   columns: {
     handler(val) {
       const data = []
       // 表格添加序号列
       if (this.showIndex) {
         data.push({
           title: '序号',
           dataIndex: 'sort',
           width: 50,
           customRender: 
             (text, record, index) => record.total 
               ? record.total 
               :  `${(this.localPagination.current - 1) * (this.localPagination.pageSize) + (index + 1) || (index + 1)}`
         })
       }
       this.originColumns = data.concat(val)
       // 超出后显示省略号 不支持操作列、和排序一并使用
       this.originColumns.forEach((col)=>{
         if(col.dataIndex || col.key) {
           col.ellipsis = true
         }
       })
     },
     immediate: true
   }
 },
 created() {
   // 判断格是传进数据源还是远程数据接口方法
   if (this.data) {
     // 合并分页数据
     const { pageNo } = this.$route.params
     const localPageNum = this.pageURI ? (pageNo && parseInt(pageNo)) : this.pageNum
     this.localPagination = ['auto', true].includes(this.showPagination)
       ? Object.assign({}, this.localPagination, {
         showQuickJumper: true,
         current: localPageNum,
         pageSize: this.pageSize,
         showSizeChanger: this.showSizeChanger,
         pageSizeOptions: ['10', '20', '40', '80', '120']
       })
       : false
     // 调用接口获得数据
     this.loadData()
   } else {
     // 源数据传入不支持分页
     this.localPagination = false
   }
   window.addEventListener('resize', this.calcLocalScroll)
 },
 mounted() {
   setTimeout(() => {
     this.calcLocalScroll()
     this.resetColumns()
   })
 },
 destroyed() {
   window.removeEventListener('resize', this.calcLocalScroll)
 },
 methods: {
   /**
    * 表格限制最大宽度/高度计算,用于滾动显示
    */
   calcLocalScroll() {
     const localScroll = { ...(this.scroll || {}) }
     // 根据自己的页面计算除表格外其他组件占据的高度,从而得出表格最大高度,也可自适应显示表格
     const extraDis = (this.$store.getters.multiTab ? 40 : 0 ) + 56 + 104 + (this.pagination ? 6 : 0) + (this.$scopedSlots.footer ? 33 : 0)
     localScroll.x = localScroll.x || this.$el.offsetWidth - 20
     localScroll.y = localScroll.y || document.body.clientHeight - ((this.$el || {}).offsetTop || 128) - extraDis 
     this.localScroll = localScroll
     // 计算表格列宽度
     this.computeWidth()
   },
   /**
    * 表格重新加载方法
    *@param {object} option 对象属性: {boolean} isBackToFirstPage 如果参数为 true, 则强制刷新到第一页
    *                        对象属性: {boolean} isResetOption 如果参数为 true, 则重置显隐配置项
    *                        对象属性: {boolean} layoutTag 如果参数为 true, 则重新计算表格限制最大宽度/高度
    */
   refresh({ isBackToFirstPage = false, isResetOption = false, layoutTag = false } = { isBackToFirstPage: false, isResetOption: false, layoutTag: false }) {
     if(layoutTag) {
       this.calcLocalScroll()
     } else {
       isResetOption && this.resetColumns()
       isBackToFirstPage && (this.localPagination = Object.assign({}, {
         current: 1,
         pageSize: this.pageSize
       }))
       this.loadData()
     }
   },
   /**
    * 加载数据方法
    * @param {{ page: number, limit: number }} pagination 分页选项器
    * @param {{ [field: string]: string }} filters 过滤条件
    * @param {{ field: string, order: 'asc' | 'desc' }} sorter 排序条件
    */
   loadData(pagination, filters, sorter = {}) {
     this.localLoading = true
     const result = this.data({
       page: (pagination && pagination.current) ||
         this.showPagination && this.localPagination.current || this.pageNum,
       limit: (pagination && pagination.pageSize) ||
         this.showPagination && this.localPagination.pageSize || this.pageSize,
       sidx: sorter.field,
       order: sorter.order && sorter.order.slice(0, sorter.order.length - 3),
       ...filters
     })
     // 对接自己的通用数据接口需要修改下方代码中的 r.currPage, r.totalCount, r.list
     if ((typeof result === 'object' || typeof result === 'function') && typeof result.then === 'function') {
       result.then(r => {
         r = r || { currPage: 1, totalCount: 0, list: [] }
         this.localPagination = this.showPagination
           ? Object.assign({}, this.localPagination, {
             showQuickJumper: true,
             current: r.currPage, // 返回结果中的当前分页数
             total: r.totalCount, // 返回结果中的总记录数
             showSizeChanger: this.showSizeChanger,
             pageSize: (pagination && pagination.pageSize) || this.localPagination.pageSize
           })
           : false
         // 为防止删除数据后导致页面当前页面数据长度为 0 ,自动翻页到上一页
         if (r.list.length === 0 && this.showPagination && this.localPagination.current > 1) {
           this.localPagination.current--
           this.loadData()
           return
         }
         // 这里用于判断接口是否有返回 r.totalCount 且 this.showPagination = true 且 pageNo 和 pageSize 存在 且 totalCount 小于等于 pageNo * pageSize 的大小
         // 当情况满足时,表示数据不满足分页大小,关闭 table 分页功能
         try {
           if ((['auto', true].includes(this.showPagination) && r.totalCount <= (r.pageNo * this.localPagination.pageSize))) {
             this.localPagination.hideOnSinglePage = true
           }
         } catch (e) {
           this.localPagination = false
         }
         this.localDataSource = r.list // 返回结果中的数组数据
         this.localLoading = false
       })
     }
   },
   /**
    * 自定义行。可以配置表格行的相关事件,此处主要定义表格单击选中行,没有复选框或者单选框得表格可以屏蔽该功能
    * @param {*} record 
    */
   localCustomRow(record) {
     const rowCustomer = this.customRow ? this.customRow(record) : {}
     if (!this.selectOnClick || !this.rowSelection) {
       return rowCustomer
     }
     if (!rowCustomer.on) {
       rowCustomer.on = {}
     }
     // 单击选中行需要判断是单选框还是多选框,表格多选或单选框得使用会在后续发文章补充。
     const selectOnClickHandler = () => {
       const { type, selectedRowKeys } = this.rowSelection
       if (selectedRowKeys.includes(record[this.rowKey]) && !type) {
         this.rowSelection.selections.splice(this.rowSelection.selections.findIndex(r => r === record), 1)
         selectedRowKeys.splice(selectedRowKeys.findIndex(r => r === record[this.rowKey]), 1)
       } else if(!type) {
         this.rowSelection.selections.push(record)
         selectedRowKeys.push(record[this.rowKey])
       } else {
         this.rowSelection.selectedRow = record
         selectedRowKeys.splice(0, 1, record[this.rowKey])  
       }
     }
     if (rowCustomer.on.click) {
       const originalClickHandler = rowCustomer.on.click
       rowCustomer.on.click = e => {
         originalClickHandler(e)
         selectOnClickHandler(e, record)
       }
     } else {
       rowCustomer.on.click = selectOnClickHandler
     }
     return rowCustomer
   },
   /**
    * 对表格设置width(来避免表头和内容的错位)
    */
   computeWidth() {
     const fullWidth = (this.localScroll.x || this.$el?.offsetWidth) - (this.rowSelection ? 60 : 0)
     if(!isNaN(fullWidth) && fullWidth >= 0) {
       const remain = this.originColumns.reduce((obj, col) => {
         if(!this.filterValue.includes(col.dataIndex || col.key)) {
           if(col.width) {
             obj.colWidthSum =obj.colWidthSum - (typeof width === 'string' && col.width.endsWith('%')
               ? parseFloat(col.width) * fullWidth / 100
               : parseFloat(col.width))
           } else {
             obj.noWidthColCount += 1
           }
         }
         return obj
       }, {colWidthSum: fullWidth, noWidthColCount: 0})
       // 平均宽度
       const averageWidth = remain.colWidthSum / remain.noWidthColCount
       const lastIndex = this.originColumns.length - ( this.originColumns[this.originColumns.length-1].fixed ? 2 : 1) 
       // 设置默认列宽,最少显示为7个字符
       // 最后一列默认不设置宽度,避免列宽改变时影响其他列
       this.originColumns.forEach((col,index) => {
         if(index !== lastIndex  && averageWidth !== Infinity && !col.width) {
           Vue.set(col, 'width', averageWidth > 150 ? averageWidth : 150)
           remain.colWidthSum = remain.colWidthSum - (averageWidth > 150 ? averageWidth : 150)
         } else if(index === lastIndex) {
           const minWidth = col.width || 150
           remain.colWidthSum = (remain.colWidthSum + (col.width || 0)).toFixed()
           Vue.set(col, 'width', remain.colWidthSum < minWidth ? minWidth : undefined)
         }
       })
     }
   },
   /**
    * 表格列重置,主要使用在数据使用数据数组的表格
    */
   resetColumns() {
     this.filterValue = []
     this.filterShow = !this.filterShow
   }
 },
// 渲染表格方法
 render(h) {
   const props = {}
   // 表格属性
   Object.keys(Table.props).forEach(k => {
     const localKey = `local${k.substring(0, 1).toUpperCase()}${k.substring(1)}`
     // if(k === 'columns'){}
     if (this.localKeys.includes(localKey)) {
       props[k] = this[localKey]
     } else if (this[k] != null) {
       props[k] = this[k]
     }
   })
   const on = { ...this.$listeners }
   this.data && (on.change = this.loadData)
   return (
     <div class={`${componentName}-wrapper`} style="position: relative;">{[
       props.showHeader && h('table-option', {
         ref: 'tableOption',
         style: {
           float: 'right',
           marginTop:'-25px',
           marginRight: '5px'
         },
         props: {
           columns: this.originColumns,
           noCheckedValues: this.filterValue
         },
         on: {
           filter: (noCheckedValues) => this.filterValue = noCheckedValues
         }
       }),
       h('a-table', {
         props,
         on,
         scopedSlots: { ...this.$scopedSlots }
       },
         Object.keys(this.$slots).map(name => (
           <template slot={name}>{this.$slots[name]}</template>
         ))
       )
       ]}
     </div>
   )
 }
}
export default DragTable

TableOption.vue组件

<template>
  <div>
    <a-tooltip placement="leftTop" title="表格列显示配置">
      <a-button @click="handleClick" class="optionBtn"><a-icon type="table" /></a-button>
    </a-tooltip>
    <div v-if="visible" class="table-select">
      <a-checkbox
        :checked="options.length === checkedValues.length"
        @change="onCheckAllChange"
      >
        全选/反选
      </a-checkbox>
      <a-checkbox-group
        :options="options"
        v-model="checkedValues"
        @change="selectChange"
      />
    </div>
  </div>
</template>
<script>
const componentName = 'ebig-table-option'
const TableOption= {
  name: componentName,
  props: {
    columns: { type: Array, default: ()=>([])},
    noCheckedValues: { type:Array, default: ()=>([]) }
  },
  data() {
    return {
      checkedValues: [],
      visible: false
    }
  },
  watch: {
    noCheckedValues: {
      handler(val) {
        this.checkedValues = this.options.filter(col => !val.includes(col.value)).map(c => c.value)
      },
      immediate: true
    }
  },
  computed: {
    options() {
      return this.columns.map(col => {
        const key = col.dataIndex || col.key
        return { label: col.title || col.slots.title, value: key + '' }
      })
    },
    allKeys: vm => vm.options.map(o => o.value)
  },
  methods: {
    handleClick() {
      this.visible = !this.visible
    },
    onCheckAllChange(e) {
      this.checkedValues = e.target.checked ? this.allKeys : []
      const noCheckedValues = e.target.checked ? [] : this.allKeys
      this.$emit('filter', noCheckedValues)
    },
    selectChange(checkedValues) {
      const noCheckedValues = this.allKeys.filter(key => !checkedValues.includes(key))
      this.$emit('filter', noCheckedValues)
    }
  }
}
export default TableOption
</script>
<style lang="less">
.optionBtn {
  padding: 0 4px !important;
  height: auto !important;
  opacity: 0.4;
  &:hover {
    opacity: 1;
  }
}
.ant-checkbox-group-item + .ant-checkbox-group-item {
  display: block
}
.table-select {
  position: absolute;
  background:#fff;
  border:1px solid #ecedef;
  top: 5px;
  right: 0;
  z-index: 100000;
  padding: 10px 0 10px 10px;
  width: 180px;
  max-height: 100%;
  overflow: auto;
  &::-webkit-scrollbar {
    width: 5px;
  }
  &::-webkit-scrollbar-thumb {
    border-radius: 8px;
    background-color: rgb(177, 175, 175);
  }
  &::-webkit-scrollbar-thumb:hover {
    border-radius: 10px;
    background-color: #22212177;
  }
}
</style>

使用

表格使用案例

<template>
  <a-card style="paddingTop: 50px">
    <drag-table
      ref="table"
      size="small"
      row-key="id"
      :columns="columns"
      :data="loadData"
    />
  </a-card>
</template>
<script>
import DragTable from './Table'
export default {
  name: 'drag-table-example',
  components: { DragTable },
  data() {
    return {
      columns: [
        { dataIndex: 'name', title: '姓名' },
        { dataIndex: 'sex', title: '性别' },
        { dataIndex: 'age', title: '年龄' },
        { dataIndex: 'school', title: '学校' }
      ],
      dataSource: []
    }
  },
  methods: {
    loadData() {
      return Promise.resolve({
        currPage: 1,
        pageSize: 10,
        totalCount: 9,
        totalPage: 1,
        list: [
          { id: 1, name: '张三', sex: '男', age: 18, school: '测试高级学校1'},
          { id: 2, name: '李四', sex: '女', age: 16, school: '测试高级学校2'},
          { id: 3, name: '王五', sex: '男', age: 15, school: '测试高级学校3'},
          { id: 4, name: '张红', sex: '女', age: 17, school: '测试高级学校4'},
          { id: 5, name: '陈平', sex: '男', age: 20, school: '测试高级学校5'},
        ]
      })
    }
  }
}
</script>

antdv-基础表格拖拽

到此这篇关于Vue3封装ant-design-vue表格的文章就介绍到这了,更多相关Vue3封装ant-design-vue内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文