您现在的位置是:首页 >技术杂谈 >Vue3封装ant-design-vue表格网站首页技术杂谈

Vue3封装ant-design-vue表格

Joey_Tribiani 2024-06-26 14:24:01
简介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>

风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。