该文仅仅用来记录自己使用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>
<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>