web/src/components/table/table.tsx

214 lines
5.8 KiB
TypeScript

import {
TableColumnData as BaseColumn,
TableData as BaseData,
Table as BaseTable,
Message,
} from "@arco-design/web-vue";
import { merge } from "lodash-es";
import { PropType, computed, defineComponent, reactive, ref, watch } from "vue";
import { Form, FormInstance, FormModal, FormModalInstance, FormModalProps, FormProps } from "../form";
import { config } from "./table.config";
type DataFn = (search: Record<string, any>, paging: { page: number; size: number }) => Promise<any>;
/**
* 表格组件
* @see src/components/table/table.tsx
*/
export const Table = defineComponent({
name: "Table",
props: {
/**
* 表格数据
* @description 可以是数组或者函数,函数返回值为`{ data: BaseData[], total: number }`类型
*/
data: {
type: [Array, Function] as PropType<BaseData[] | DataFn>,
},
/**
* 表格列设置
*/
columns: {
type: Array as PropType<BaseColumn[]>,
default: () => [],
},
/**
* 分页参数配置
*/
pagination: {
type: Object as PropType<any>,
default: () => reactive(config.pagination),
},
/**
* 搜索表单配置
*/
search: {
type: Object as PropType<FormProps>,
},
/**
* 新建弹窗配置
*/
create: {
type: Object as PropType<FormModalProps>,
},
/**
* 修改弹窗配置
*/
modify: {
type: Object as PropType<FormModalProps>,
},
/**
* 详情弹窗配置
*/
detail: {
type: Object as PropType<any>,
},
/**
* 传递给 Table 组件的属性
*/
tableProps: {
type: Object as PropType<InstanceType<typeof BaseTable>["$props"]>,
},
},
setup(props) {
const loading = ref(false);
const tableRef = ref<InstanceType<typeof BaseTable>>()
const searchRef = ref<FormInstance>();
const createRef = ref<FormModalInstance>();
const modifyRef = ref<FormModalInstance>();
const renderData = ref<BaseData[]>([]);
const inlined = computed(() => (props.search?.items?.length ?? 0) <= config.searchInlineCount);
const reloadData = () => loadData({ current: 1, pageSize: 10 });
const openModifyModal = (data: any) => modifyRef.value?.open(data);
const loadData = async (pagination: Partial<any> = {}) => {
const merged = { ...props.pagination, ...pagination };
const paging = { page: merged.current, size: merged.pageSize };
const model = searchRef.value?.getModel() ?? {};
if (Array.isArray(props.data)) {
const filters = Object.entries(model);
const data = props.data.filter((item) => {
return filters.every(([key, value]) => {
if (typeof value === "string") {
return item[key].includes(value);
}
return item[key] === value;
});
});
renderData.value = data;
props.pagination.total = renderData.value.length;
props.pagination.current = 1;
}
if (typeof props.data === "function") {
try {
loading.value = true;
const resData = await props.data(model, paging);
const { data = [], total = 0 } = resData?.data || {};
renderData.value = data;
props.pagination.total = total;
props.pagination.current = paging.page;
} catch (error) {
const message = config.getApiErrorMessage(error);
if (message) {
Message.error(`提示:${message}`);
}
} finally {
loading.value = false;
}
}
};
watch(
() => props.data,
(data) => {
if (Array.isArray(data)) {
renderData.value = data;
props.pagination.total = data.length;
props.pagination.current = 1;
}
},
{
immediate: true,
}
);
onMounted(() => {
loadData();
});
if (props.search) {
merge(props.search, { formProps: { layout: "inline" } });
}
const state = {
loading,
inlined,
tableRef,
searchRef,
createRef,
modifyRef,
renderData,
loadData,
reloadData,
openModifyModal,
};
provide("ref:table", { ...state, ...props });
return state;
},
render() {
(this.columns as any).instance = this;
return (
<div class="table w-full">
{!this.inlined && (
<div class="border-b pb-2 border-slate-200 mb-5">
<Form ref="searchRef" class="!grid grid-cols-4 gap-x-6" {...this.search}></Form>
</div>
)}
<div class={`mb-3 flex justify-between ${!this.inlined && "mt-2"}`}>
<div class={`${this.create || this.$slots.action ? null : "!hidden"} flex-1 flex gap-2 `}>
{this.create && (
<FormModal {...(this.create as any)} ref="createRef" onSubmited={this.reloadData}></FormModal>
)}
{this.modify && (
<FormModal
{...(this.modify as any)}
ref="modifyRef"
onSubmited={this.reloadData}
trigger={false}
></FormModal>
)}
{this.$slots.action?.()}
</div>
<div>{this.inlined && <Form ref="searchRef" {...this.search}></Form>}</div>
</div>
<BaseTable
ref="tableRef"
row-key="id"
bordered={false}
{...this.$attrs}
{...this.tableProps}
loading={this.loading}
pagination={this.pagination}
data={this.renderData}
columns={this.columns}
onPageChange={(current: number) => this.loadData({ current })}
></BaseTable>
</div>
);
},
});
/**
* 表格组件实例
*/
export type TableInstance = InstanceType<typeof Table>;
/**
* 表格组件参数
*/
export type TableProps = TableInstance["$props"];