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:
@@ -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>
|
||||||
|
|||||||
Reference in New Issue
Block a user