Wire controlled column filters and clear-filters button
buildTableColumns now attaches antd controlled filter props (via buildColumnFilterProps) to columns marked filterable; filters state is controlled via Table onChange. A 'clear filters' button in the search bar resets all filters and is disabled when none are active. Filters and row lock reset on search/clear. Also renames the component module table-filters.tsx -> table-filter-ui.tsx to avoid a same-base-name resolution collision with the logic module table-filters.ts. Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -39,6 +39,7 @@ import {
|
|||||||
} from "@dnd-kit/sortable";
|
} from "@dnd-kit/sortable";
|
||||||
import { CSS } from "@dnd-kit/utilities";
|
import { CSS } from "@dnd-kit/utilities";
|
||||||
import { nextLockedRow, type DataRow } from "./table-filters";
|
import { nextLockedRow, type DataRow } from "./table-filters";
|
||||||
|
import { buildColumnFilterProps } from "./table-filter-ui";
|
||||||
|
|
||||||
const { Title, Text } = Typography;
|
const { Title, Text } = Typography;
|
||||||
|
|
||||||
@@ -141,14 +142,16 @@ function saveConfig(config: ColumnConfig[]) {
|
|||||||
|
|
||||||
function buildTableColumns(
|
function buildTableColumns(
|
||||||
config: ColumnConfig[],
|
config: ColumnConfig[],
|
||||||
dataColumns: string[]
|
dataColumns: string[],
|
||||||
|
data: DataRow[],
|
||||||
|
filters: Record<string, React.Key[]>
|
||||||
): ColumnsType<DataRow> {
|
): ColumnsType<DataRow> {
|
||||||
const available = new Set(dataColumns);
|
const available = new Set(dataColumns);
|
||||||
return config
|
return config
|
||||||
.filter((c) => c.visible && available.has(c.key))
|
.filter((c) => c.visible && available.has(c.key))
|
||||||
.map((c) => {
|
.map((c) => {
|
||||||
const def = COLUMN_DEFS.find((d) => d.key === c.key);
|
const def = COLUMN_DEFS.find((d) => d.key === c.key);
|
||||||
return {
|
const base = {
|
||||||
title: c.key,
|
title: c.key,
|
||||||
dataIndex: c.key,
|
dataIndex: c.key,
|
||||||
key: c.key,
|
key: c.key,
|
||||||
@@ -157,6 +160,9 @@ function buildTableColumns(
|
|||||||
render: (val: string | number | null) =>
|
render: (val: string | number | null) =>
|
||||||
val === null || val === undefined ? "" : String(val),
|
val === null || val === undefined ? "" : String(val),
|
||||||
};
|
};
|
||||||
|
return c.filterable
|
||||||
|
? { ...base, ...buildColumnFilterProps(c.key, data, filters[c.key] ?? null) }
|
||||||
|
: base;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -367,6 +373,8 @@ function SearchBar({
|
|||||||
onOpenColumnSettings,
|
onOpenColumnSettings,
|
||||||
exporting,
|
exporting,
|
||||||
onExport,
|
onExport,
|
||||||
|
hasActiveFilters,
|
||||||
|
onClearFilters,
|
||||||
}: {
|
}: {
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
resultCount: number;
|
resultCount: number;
|
||||||
@@ -376,6 +384,8 @@ function SearchBar({
|
|||||||
onOpenColumnSettings: () => void;
|
onOpenColumnSettings: () => void;
|
||||||
exporting: boolean;
|
exporting: boolean;
|
||||||
onExport: () => void;
|
onExport: () => void;
|
||||||
|
hasActiveFilters: boolean;
|
||||||
|
onClearFilters: () => void;
|
||||||
}) {
|
}) {
|
||||||
const [inputValue, setInputValue] = useState("");
|
const [inputValue, setInputValue] = useState("");
|
||||||
const [history, setHistory] = useState<string[]>(() => loadHistory());
|
const [history, setHistory] = useState<string[]>(() => loadHistory());
|
||||||
@@ -461,6 +471,13 @@ function SearchBar({
|
|||||||
>
|
>
|
||||||
导出全部
|
导出全部
|
||||||
</Button>
|
</Button>
|
||||||
|
<Button
|
||||||
|
icon={<FilterOutlined />}
|
||||||
|
onClick={onClearFilters}
|
||||||
|
disabled={!hasActiveFilters}
|
||||||
|
>
|
||||||
|
清除筛选
|
||||||
|
</Button>
|
||||||
</Space>
|
</Space>
|
||||||
{searched && !loading && (
|
{searched && !loading && (
|
||||||
<Text type="secondary" style={{ marginLeft: 16 }}>
|
<Text type="secondary" style={{ marginLeft: 16 }}>
|
||||||
@@ -483,6 +500,7 @@ export default function Home() {
|
|||||||
const [modalOpen, setModalOpen] = useState(false);
|
const [modalOpen, setModalOpen] = useState(false);
|
||||||
const [exporting, setExporting] = useState(false);
|
const [exporting, setExporting] = useState(false);
|
||||||
const [lockedRowKey, setLockedRowKey] = useState<string | null>(null);
|
const [lockedRowKey, setLockedRowKey] = useState<string | null>(null);
|
||||||
|
const [filters, setFilters] = useState<Record<string, React.Key[]>>({});
|
||||||
|
|
||||||
// Persist config
|
// Persist config
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -505,6 +523,7 @@ export default function Home() {
|
|||||||
setData(json.data);
|
setData(json.data);
|
||||||
setSearched(true);
|
setSearched(true);
|
||||||
setLockedRowKey(null);
|
setLockedRowKey(null);
|
||||||
|
setFilters({});
|
||||||
} catch {
|
} catch {
|
||||||
setError("网络请求失败");
|
setError("网络请求失败");
|
||||||
} finally {
|
} finally {
|
||||||
@@ -518,6 +537,7 @@ export default function Home() {
|
|||||||
setSearched(false);
|
setSearched(false);
|
||||||
setError(null);
|
setError(null);
|
||||||
setLockedRowKey(null);
|
setLockedRowKey(null);
|
||||||
|
setFilters({});
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const handleExport = useCallback(async () => {
|
const handleExport = useCallback(async () => {
|
||||||
@@ -551,9 +571,14 @@ export default function Home() {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const tableColumns = useMemo(
|
const tableColumns = useMemo(
|
||||||
() => buildTableColumns(columnConfig, columns),
|
() => buildTableColumns(columnConfig, columns, data, filters),
|
||||||
[columnConfig, columns]
|
[columnConfig, columns, data, filters]
|
||||||
);
|
);
|
||||||
|
const hasActiveFilters = useMemo(
|
||||||
|
() => Object.values(filters).some((arr) => arr && arr.length > 0),
|
||||||
|
[filters]
|
||||||
|
);
|
||||||
|
const clearFilters = useCallback(() => setFilters({}), []);
|
||||||
const totalWidth = useMemo(
|
const totalWidth = useMemo(
|
||||||
() => tableColumns.reduce((sum, col) => sum + (col.width as number), 0),
|
() => tableColumns.reduce((sum, col) => sum + (col.width as number), 0),
|
||||||
[tableColumns]
|
[tableColumns]
|
||||||
@@ -574,6 +599,8 @@ export default function Home() {
|
|||||||
onOpenColumnSettings={() => setModalOpen(true)}
|
onOpenColumnSettings={() => setModalOpen(true)}
|
||||||
exporting={exporting}
|
exporting={exporting}
|
||||||
onExport={handleExport}
|
onExport={handleExport}
|
||||||
|
hasActiveFilters={hasActiveFilters}
|
||||||
|
onClearFilters={clearFilters}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div style={{ flex: 1, minHeight: 0 }}>
|
<div style={{ flex: 1, minHeight: 0 }}>
|
||||||
@@ -624,6 +651,9 @@ export default function Home() {
|
|||||||
rowClassName={(record) =>
|
rowClassName={(record) =>
|
||||||
String(record.ID) === lockedRowKey ? "row-locked" : ""
|
String(record.ID) === lockedRowKey ? "row-locked" : ""
|
||||||
}
|
}
|
||||||
|
onChange={(_pagination, tableFilters) =>
|
||||||
|
setFilters(tableFilters as Record<string, React.Key[]>)
|
||||||
|
}
|
||||||
bordered
|
bordered
|
||||||
size="small"
|
size="small"
|
||||||
pagination={false}
|
pagination={false}
|
||||||
|
|||||||
Reference in New Issue
Block a user