WholeProcessPlatform/frontend/src/components/BasicTable/index.vue
2026-05-26 19:30:22 +08:00

214 lines
5.4 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="table-container" ref="tableContainerRef">
<a-table
size="small"
:loading="loading"
:row-selection="enableRowSelection ? rowSelection : undefined"
:data-source="tableData"
:columns="columns"
:pagination="paginationConfig"
:scroll="scrollConfig"
:row-key="rowKey"
@change="handleTableChange"
>
<!-- 透传插槽支持自定义列内容 -->
<template v-for="slot in Object.keys($slots)" #[slot]="scope" :key="slot">
<slot :name="slot" v-bind="scope"></slot>
</template>
</a-table>
</div>
</template>
<script setup lang="ts">
import { ref, computed, onMounted, watch, nextTick } from "vue";
import { calcTableScrollY } from "@/utils/index";
// --- Types ---
interface Props {
columns: any[];
scrollY?: string | number; // 优先使用传入的固定值
listUrl: (params: any) => Promise<any>;
data?: any[];
enableRowSelection?: boolean;
rowKey?: string;
searchParams?: Record<string, any>;
defaultPageSize?: number;
isPage?: boolean;
getCheckboxProps?: (record: any) => any;
transformData?: (res: any) => { records: any[]; total: number };
}
const props = withDefaults(defineProps<Props>(), {
enableRowSelection: false,
rowKey: "id",
isPage: true,
data: () => [],
searchParams: () => ({}),
defaultPageSize: 20,
getCheckboxProps: undefined,
transformData: undefined,
scrollY: undefined,
});
const emit = defineEmits<{
(e: "data-loaded", params: any, data: any): void;
(e: "selection-change", selectedRowKeys: string[], selectedRows: any[]): void;
}>();
// --- State ---
const loading = ref(false);
const tableData = ref<any[]>([]);
const total = ref(0);
const page = ref(1);
const size = ref(props.defaultPageSize);
const selectedRowKeys = ref<string[]>([]);
const selectedRows = ref<any[]>([]);
const lastFilter = ref<Record<string, any> | undefined>(undefined);
const tableContainerRef = ref<any>(null);
const tableScrollY = ref<number>(0); // ✅ 新增:存储容器高度
// --- Computed Scroll Config ---
const scrollConfig = computed(() => {
const config: any = {
x: "100%",
y: 0,
};
// // 1. 如果父组件传入了固定的 scrollY优先使用
if (props.scrollY !== undefined && props.scrollY !== null) {
config.y = props.scrollY;
}
// 2. 否则,如果检测到容器有高度,则使用容器高度作为滚动高度
else if (tableScrollY.value > 0) {
config.y = tableScrollY.value;
}
console.log(config);
return config;
});
// --- Row Selection ---
const rowSelection = computed(() => ({
selectedRowKeys: selectedRowKeys.value,
onChange: (keys: string[], rows: any[]) => {
selectedRowKeys.value = keys;
selectedRows.value = rows;
emit("selection-change", keys, rows);
},
getCheckboxProps: props.getCheckboxProps
? props.getCheckboxProps
: (record: any) => ({}),
}));
// --- Pagination Config ---
const paginationConfig = computed(() => ({
total: total.value,
current: page.value,
pageSize: size.value,
showSizeChanger: true,
showQuickJumper: true,
showTotal: (total: number) => `${total}`,
pageSizeOptions: ["20", "50", "100"],
}));
// --- Methods ---
const getList = async (filter?: Record<string, any>) => {
if (props.data.length > 0) return;
loading.value = true;
tableData.value = [];
total.value = 0;
if (filter !== undefined) {
lastFilter.value = filter;
}
console.log(props.searchParams);
try {
const params = {
...props.searchParams,
skip: page.value,
take: size.value,
filter: lastFilter.value,
};
if (!props.isPage) {
delete params.take;
delete params.skip;
}
const res = await props.listUrl(params);
let finalRecords: any[] = [];
let finalTotal: number = 0;
if (props.transformData) {
const result = props.transformData(res);
finalRecords = result.records || [];
finalTotal = result.total || 0;
} else {
finalRecords = res?.data?.records || res?.data?.data || res?.data || [];
finalTotal = res.data?.data?.total || res?.data?.total || res?.total || 0;
}
tableData.value = finalRecords;
total.value = finalTotal;
emit("data-loaded", params, { records: finalRecords, total: finalTotal });
} catch (error) {
console.error("Fetch table data error:", error);
tableData.value = [];
total.value = 0;
} finally {
loading.value = false;
}
};
const handleTableChange = (pag: any) => {
page.value = pag.current;
size.value = pag.pageSize;
getList();
};
const reset = () => {
page.value = 1;
size.value = props.defaultPageSize;
selectedRowKeys.value = [];
selectedRows.value = [];
getList();
};
const refresh = () => {
getList();
};
const clearSelection = () => {
selectedRowKeys.value = [];
selectedRows.value = [];
};
defineExpose({
loading,
getList,
reset,
refresh,
tableData,
clearSelection,
getSelected: () => ({
keys: selectedRowKeys.value,
rows: selectedRows.value,
}),
});
const observer = new ResizeObserver(() => {
tableScrollY.value = calcTableScrollY(tableContainerRef.value);
});
onMounted(() => {
nextTick(() => {
tableScrollY.value = calcTableScrollY(tableContainerRef.value);
if (tableContainerRef.value) {
observer.observe(tableContainerRef.value);
}
});
if (props.data && props.data.length > 0) {
tableData.value = props.data || [];
}
});
</script>
<style scoped lang="scss"></style>