table-reuse
Version:
A reusable table built on top of Antd ProTable
273 lines (263 loc) • 11.4 kB
JavaScript
'use strict';
var jsxRuntime = require('react/jsx-runtime');
var React = require('react');
var proComponents = require('@ant-design/pro-components');
var antd = require('antd');
var actionView = require('action-view');
var icons = require('@ant-design/icons');
var dayjs = require('dayjs');
const SELECT_BOX_PLACEHOLDER = "Select...";
const getSelectFilter = (filter) => {
const { label, fieldName, options, filterProps } = filter;
return (jsxRuntime.jsx(proComponents.ProFormSelect, { name: fieldName, label: label, placeholder: SELECT_BOX_PLACEHOLDER, fieldProps: {
showSearch: true,
allowClear: true,
options,
optionFilterProp: "label",
...filterProps,
} }, fieldName));
};
const getRadioFilter = (filter) => {
const { label, fieldName, options } = filter;
return jsxRuntime.jsx(proComponents.ProFormRadio.Group, { name: fieldName, label: label, options: options }, fieldName);
};
const getDatePickerFilter = (filter) => {
const { label, fieldName } = filter;
return jsxRuntime.jsx(proComponents.ProFormDatePicker, { name: fieldName, label: label }, fieldName);
};
const getDateRangePickerFilter = (filter) => {
const { label, fieldName } = filter;
return (jsxRuntime.jsx(proComponents.ProFormDateTimeRangePicker, { name: fieldName, label: label, transform: (value, namePath, allValues) => {
if (Array.isArray(value) && typeof value[0] === "string" && typeof value[1] === "string") {
return {
[namePath]: [dayjs(value[0]), dayjs(value[1])],
};
}
return allValues;
}, fieldProps: {
placeholder: ["Start date", "End date"],
separator: jsxRuntime.jsx(icons.ArrowRightOutlined, {}),
} }, fieldName));
};
const getSearchAreaFilter = (config, onSearch) => {
const { searchType = "options", searchField = "searchValue" } = config;
if (searchType === "single" && onSearch) {
return (jsxRuntime.jsx(proComponents.ProFormItem, { name: searchField, children: jsxRuntime.jsx(antd.Input.Search, {}) }));
}
if (searchType === "options") {
return (jsxRuntime.jsxs(proComponents.ProFormGroup, { children: [jsxRuntime.jsx(proComponents.ProFormSelect, { name: "searchField", placeholder: SELECT_BOX_PLACEHOLDER, fieldProps: {
showSearch: true,
allowClear: true,
options: config.searchOptions,
} }), jsxRuntime.jsx(proComponents.ProFormText, { name: "searchValue", placeholder: "Enter here", fieldProps: {
allowClear: true,
suffix: jsxRuntime.jsx(icons.SearchOutlined, {}),
} })] }));
}
return (jsxRuntime.jsx(proComponents.ProFormText, { name: searchField, placeholder: config.inputPlaceholder, fieldProps: {
allowClear: true,
suffix: jsxRuntime.jsx(icons.SearchOutlined, {}),
} }));
};
function SearchForm({ form, filters = [], onSearch, onReset, searchConfig, }) {
const renderFilter = (filter) => {
switch (filter.filterType) {
case "SELECT":
return getSelectFilter(filter);
case "RADIO":
return getRadioFilter(filter);
case "DATE":
return getDatePickerFilter(filter);
case "DATE_RANGE":
return getDateRangePickerFilter(filter);
default:
return null;
}
};
const onFinish = async () => {
const values = form.getFieldsValue();
console.log("onFinish", values);
return onSearch(values);
};
return (jsxRuntime.jsxs(proComponents.QueryFilter, { form: form, onFinish: onFinish, onReset: onReset, labelWidth: "auto", defaultCollapsed: false, span: 8, submitter: {
resetButtonProps: {
children: "Reset",
},
submitButtonProps: {
children: "Search",
type: "primary",
},
}, children: [searchConfig && (jsxRuntime.jsx(React.Fragment, { children: getSearchAreaFilter(searchConfig) }, "search-field")), filters.map((filter) => (jsxRuntime.jsx(React.Fragment, { children: renderFilter(filter) }, filter.fieldName)))] }));
}
function createActionColumn(buildRowActions, maxVisibleRowActions = 1) {
if (!buildRowActions)
return undefined;
return {
title: "Actions",
valueType: "option",
// fixed: "right",
render: (_, record) => {
const actions = buildRowActions(record);
return jsxRuntime.jsx(actionView.ActionView, { actions: actions, maxVisible: maxVisibleRowActions });
},
};
}
/**
* 创建 ProTable 的 request 方法
* 在标准化 filter 后合并 tabFilter
*/
function createRequestFunction(onList, searchParams, tabFilter, buildApiPayload) {
const requestFunc = (params, sort) => {
const normalizedFilter = buildApiPayload
? buildApiPayload(searchParams)
: searchParams;
const mergedFilter = { ...normalizedFilter, ...tabFilter };
const apiPayload = {
...params,
sort: sort ?? {},
...mergedFilter,
};
console.log("normalizedFilter", normalizedFilter);
console.log("mergedFilter", mergedFilter);
console.log("apiPayload", apiPayload);
return onList(apiPayload);
};
return requestFunc;
}
// Used as sample
function createProTableTabs({ tabs, activeKey, onTabChange, }) {
return {
menu: {
type: "tab",
activeKey: String(activeKey),
items: tabs.map((tab) => ({
key: String(tab.key),
label: tab.label,
})),
onChange: (key) => {
// TS now knows key is string | undefined
if (key !== undefined) {
console.log("切换到", key);
onTabChange(key);
}
},
},
};
}
function SearchableTable({ columns, onList, tableId, tableTitle, pageActions, buildRowActions, searchConfig, filters, tabs = [], defaultTabKey, buildApiPayload, rowKey = "id", pullInterval = 0, maxVisibleRowActions = 2, reloadOnDataChange = false, }) {
const [form] = antd.Form.useForm();
const actionRef = React.useRef();
const [activeTabKey, setActiveTabKey] = React.useState(defaultTabKey);
const [searchParams, setSearchParams] = React.useState();
const tabFilter = React.useMemo(() => {
const current = tabs.find((tab) => tab.key === activeTabKey);
return (current?.filter ?? {});
}, [activeTabKey, tabs]);
const request = React.useMemo(() => createRequestFunction(onList, searchParams, tabFilter, buildApiPayload), [onList, searchParams, tabFilter, buildApiPayload]);
React.useEffect(() => {
if (pullInterval > 0) {
const timer = setInterval(() => {
actionRef.current?.reload();
}, pullInterval);
return () => clearInterval(timer);
}
}, [pullInterval]);
React.useEffect(() => {
if (reloadOnDataChange) {
actionRef.current?.reload();
}
}, [searchParams, activeTabKey, reloadOnDataChange]);
const onSearch = async () => {
const values = await form.validateFields();
console.log("onSearch", values);
setSearchParams(values);
actionRef.current?.reload();
};
const handleReset = () => {
form.resetFields();
setSearchParams({});
actionRef.current?.reload();
};
const allColumns = React.useMemo(() => {
const actionColumn = createActionColumn(buildRowActions, maxVisibleRowActions);
return actionColumn ? [...columns, actionColumn] : columns;
}, [columns, buildRowActions, maxVisibleRowActions]);
const onTabChange = (newKey) => {
console.log("onTabChange:", newKey);
setActiveTabKey(newKey);
onSearch();
};
const toolbar = createProTableTabs({
tabs,
activeKey: activeTabKey,
onTabChange,
});
return (jsxRuntime.jsxs("div", { children: [tableTitle && jsxRuntime.jsx("h3", { style: { marginBottom: 16 }, children: tableTitle }), filters && (jsxRuntime.jsx(SearchForm, { form: form, filters: filters, onSearch: onSearch, onReset: handleReset, searchConfig: searchConfig })), jsxRuntime.jsx(proComponents.ProTable, { request: request, columns: allColumns, rowKey: rowKey, actionRef: actionRef, toolBarRender: () => actionView.createButtons(pageActions || []), toolbar: toolbar, columnsState: {
persistenceKey: `${tableId || tableTitle}-column-state`,
persistenceType: "localStorage",
}, search: false, pagination: { showSizeChanger: true }, scroll: { x: "max-content" } })] }));
}
function getCrudActions({ basePath, navigate, labels, rowKey, apiDelete, }) {
// Top-level page actions
const pageActions = [];
if (labels.createLabel) {
pageActions.push({
label: labels.createLabel,
type: "primary",
onClick: () => navigate(`/${basePath}/create`),
});
}
// Row-level actions
const buildRowActions = (row) => {
const id = row[rowKey];
if (id == null)
return [];
const actions = [];
if (labels.viewLabel) {
actions.push({
label: labels.viewLabel,
type: "text",
onClick: () => navigate(`/${basePath}/view/${id}`),
});
}
if (labels.editLabel) {
actions.push({
label: labels.editLabel,
type: "text",
onClick: () => navigate(`/${basePath}/edit/${id}`),
});
}
if (labels.cloneLabel) {
actions.push({
label: labels.cloneLabel,
type: "text",
onClick: () => navigate(`/${basePath}/create`, { state: { cloneId: id } }),
});
}
if (labels.deleteLabel && apiDelete) {
actions.push({
label: labels.deleteLabel,
type: "text",
danger: true,
onClick: async () => {
try {
await apiDelete(row[rowKey]);
}
catch (err) {
console.error("Delete failed:", err);
}
},
});
}
return actions;
};
return { pageActions, buildRowActions };
}
exports.SearchableTable = SearchableTable;
exports.getCrudActions = getCrudActions;
exports.getDatePickerFilter = getDatePickerFilter;
exports.getDateRangePickerFilter = getDateRangePickerFilter;
exports.getRadioFilter = getRadioFilter;
exports.getSearchAreaFilter = getSearchAreaFilter;
exports.getSelectFilter = getSelectFilter;
//# sourceMappingURL=index.cjs.js.map