Add per-column filterable flag with persistence and settings toggle

ColumnConfig gains filterable (default false), persisted to localStorage
with backward-compat for configs saved before this field existed. The
column-settings modal now shows a funnel toggle per row alongside the
visibility checkbox.

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Misaka_Company
2026-06-18 09:47:46 +08:00
parent f78918c3b0
commit f8914fb287

View File

@@ -14,7 +14,14 @@ import {
Checkbox,
AutoComplete,
} from "antd";
import { SearchOutlined, SettingOutlined, HolderOutlined, ClockCircleOutlined, DownloadOutlined } from "@ant-design/icons";
import {
SearchOutlined,
SettingOutlined,
HolderOutlined,
ClockCircleOutlined,
DownloadOutlined,
FilterOutlined,
} from "@ant-design/icons";
import type { ColumnsType } from "antd/es/table";
import {
DndContext,
@@ -38,6 +45,7 @@ const { Title, Text } = Typography;
interface ColumnConfig {
key: string;
visible: boolean;
filterable: boolean;
}
const COLUMN_DEFS: { key: string; width: number }[] = [
@@ -102,6 +110,7 @@ const COLUMN_DEFS: { key: string; width: number }[] = [
const DEFAULT_CONFIG: ColumnConfig[] = COLUMN_DEFS.map((d) => ({
key: d.key,
visible: true,
filterable: false,
}));
const STORAGE_KEY = "web-table-column-config";
@@ -110,9 +119,15 @@ function loadConfig(): ColumnConfig[] {
try {
const saved = localStorage.getItem(STORAGE_KEY);
if (saved) {
const parsed: ColumnConfig[] = JSON.parse(saved);
const parsed = JSON.parse(saved) as Partial<ColumnConfig>[];
const savedKeys = new Set(parsed.map((c) => c.key));
if (COLUMN_DEFS.every((d) => savedKeys.has(d.key))) return parsed;
if (COLUMN_DEFS.every((d) => savedKeys.has(d.key))) {
return parsed.map((c) => ({
key: c.key!,
visible: !!c.visible,
filterable: !!c.filterable, // 旧配置缺该字段时按 false
}));
}
}
} catch {
/* ignore */
@@ -151,12 +166,16 @@ function SortableRow({
id,
label,
visible,
filterable,
onToggle,
onToggleFilter,
}: {
id: string;
label: string;
visible: boolean;
filterable: boolean;
onToggle: () => void;
onToggleFilter: () => void;
}) {
const {
attributes,
@@ -188,6 +207,14 @@ function SortableRow({
<HolderOutlined />
</span>
<Checkbox checked={visible} onChange={onToggle} style={{ flexShrink: 0 }} />
<Button
size="small"
type={filterable ? "primary" : "default"}
icon={<FilterOutlined />}
onClick={onToggleFilter}
title={filterable ? "已设为可筛选,点击取消" : "设为可筛选"}
style={{ flexShrink: 0, padding: "0 6px" }}
/>
<span style={{ userSelect: "none" }}>{label}</span>
</div>
);
@@ -240,6 +267,12 @@ function ColumnSettingsModal({
);
};
const toggleFilter = (key: string) => {
setLocal((prev) =>
prev.map((c) => (c.key === key ? { ...c, filterable: !c.filterable } : c))
);
};
const selectAll = () => setLocal((prev) => prev.map((c) => ({ ...c, visible: true })));
const deselectAll = () => setLocal((prev) => prev.map((c) => ({ ...c, visible: false })));
const resetDefault = () => setLocal(DEFAULT_CONFIG.map((c) => ({ ...c })));
@@ -288,7 +321,9 @@ function ColumnSettingsModal({
id={c.key}
label={c.key}
visible={c.visible}
filterable={c.filterable}
onToggle={() => toggle(c.key)}
onToggleFilter={() => toggleFilter(c.key)}
/>
))}
</SortableContext>