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, Checkbox,
AutoComplete, AutoComplete,
} from "antd"; } 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 type { ColumnsType } from "antd/es/table";
import { import {
DndContext, DndContext,
@@ -38,6 +45,7 @@ const { Title, Text } = Typography;
interface ColumnConfig { interface ColumnConfig {
key: string; key: string;
visible: boolean; visible: boolean;
filterable: boolean;
} }
const COLUMN_DEFS: { key: string; width: number }[] = [ 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) => ({ const DEFAULT_CONFIG: ColumnConfig[] = COLUMN_DEFS.map((d) => ({
key: d.key, key: d.key,
visible: true, visible: true,
filterable: false,
})); }));
const STORAGE_KEY = "web-table-column-config"; const STORAGE_KEY = "web-table-column-config";
@@ -110,9 +119,15 @@ function loadConfig(): ColumnConfig[] {
try { try {
const saved = localStorage.getItem(STORAGE_KEY); const saved = localStorage.getItem(STORAGE_KEY);
if (saved) { 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)); 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 { } catch {
/* ignore */ /* ignore */
@@ -151,12 +166,16 @@ function SortableRow({
id, id,
label, label,
visible, visible,
filterable,
onToggle, onToggle,
onToggleFilter,
}: { }: {
id: string; id: string;
label: string; label: string;
visible: boolean; visible: boolean;
filterable: boolean;
onToggle: () => void; onToggle: () => void;
onToggleFilter: () => void;
}) { }) {
const { const {
attributes, attributes,
@@ -188,6 +207,14 @@ function SortableRow({
<HolderOutlined /> <HolderOutlined />
</span> </span>
<Checkbox checked={visible} onChange={onToggle} style={{ flexShrink: 0 }} /> <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> <span style={{ userSelect: "none" }}>{label}</span>
</div> </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 selectAll = () => setLocal((prev) => prev.map((c) => ({ ...c, visible: true })));
const deselectAll = () => setLocal((prev) => prev.map((c) => ({ ...c, visible: false }))); const deselectAll = () => setLocal((prev) => prev.map((c) => ({ ...c, visible: false })));
const resetDefault = () => setLocal(DEFAULT_CONFIG.map((c) => ({ ...c }))); const resetDefault = () => setLocal(DEFAULT_CONFIG.map((c) => ({ ...c })));
@@ -288,7 +321,9 @@ function ColumnSettingsModal({
id={c.key} id={c.key}
label={c.key} label={c.key}
visible={c.visible} visible={c.visible}
filterable={c.filterable}
onToggle={() => toggle(c.key)} onToggle={() => toggle(c.key)}
onToggleFilter={() => toggleFilter(c.key)}
/> />
))} ))}
</SortableContext> </SortableContext>