Add search history with AutoComplete dropdown

- Show last 10 search records on input focus with clock icon
- Filter history as user types
- Click history item to search directly
- Deduplicate and persist to localStorage

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Misaka_Company
2026-06-10 12:19:24 +08:00
parent aac2e9ceca
commit 2c66faf207

View File

@@ -12,8 +12,9 @@ import {
Empty, Empty,
Modal, Modal,
Checkbox, Checkbox,
AutoComplete,
} from "antd"; } from "antd";
import { SearchOutlined, SettingOutlined, HolderOutlined } from "@ant-design/icons"; import { SearchOutlined, SettingOutlined, HolderOutlined, ClockCircleOutlined } from "@ant-design/icons";
import type { ColumnsType } from "antd/es/table"; import type { ColumnsType } from "antd/es/table";
import { import {
DndContext, DndContext,
@@ -300,6 +301,29 @@ function ColumnSettingsModal({
); );
} }
/* ─── Search History ─── */
const HISTORY_KEY = "web-table-search-history";
const MAX_HISTORY = 10;
function loadHistory(): string[] {
try {
const saved = localStorage.getItem(HISTORY_KEY);
return saved ? JSON.parse(saved) : [];
} catch {
return [];
}
}
function saveHistoryToStorage(history: string[]) {
localStorage.setItem(HISTORY_KEY, JSON.stringify(history));
}
function addToHistory(value: string, history: string[]): string[] {
const filtered = history.filter((h) => h !== value);
return [value, ...filtered].slice(0, MAX_HISTORY);
}
/* ─── Search Bar (isolated) ─── */ /* ─── Search Bar (isolated) ─── */
function SearchBar({ function SearchBar({
@@ -313,17 +337,47 @@ function SearchBar({
loading: boolean; loading: boolean;
resultCount: number; resultCount: number;
searched: boolean; searched: boolean;
onSearch: (workshopNo: string) => void; onSearch: (workshopNo: string) => Promise<void>;
onClear: () => void; onClear: () => void;
onOpenColumnSettings: () => void; onOpenColumnSettings: () => void;
}) { }) {
const [inputValue, setInputValue] = useState(""); const [inputValue, setInputValue] = useState("");
const [history, setHistory] = useState<string[]>(() => loadHistory());
const [open, setOpen] = useState(false);
const filteredHistory = inputValue
? history.filter((h) => h.toLowerCase().includes(inputValue.toLowerCase()))
: history;
const options = filteredHistory.map((h) => ({
value: h,
label: (
<span style={{ display: "flex", alignItems: "center", gap: 8 }}>
<ClockCircleOutlined style={{ color: "#bbb" }} />
{h}
</span>
),
}));
const handleSearch = useCallback(() => { const handleSearch = useCallback(() => {
if (inputValue.trim()) { const val = inputValue.trim();
onSearch(inputValue.trim()); if (!val) return;
} const updated = addToHistory(val, history);
}, [inputValue, onSearch]); setHistory(updated);
saveHistoryToStorage(updated);
onSearch(val);
}, [inputValue, history, onSearch]);
const handleSelect = useCallback(
(val: string) => {
setInputValue(val);
const updated = addToHistory(val, history);
setHistory(updated);
saveHistoryToStorage(updated);
onSearch(val);
},
[history, onSearch]
);
return ( return (
<div <div
@@ -339,15 +393,23 @@ function SearchBar({
</Title> </Title>
<Space> <Space>
<Space.Compact> <Space.Compact>
<Input <AutoComplete
prefix={<SearchOutlined />} open={open && filteredHistory.length > 0}
placeholder="请输入车间号,如 R05697" options={options}
value={inputValue} value={inputValue}
onChange={(e) => setInputValue(e.target.value)} onChange={(val) => setInputValue(val)}
onPressEnter={handleSearch} onSelect={handleSelect}
allowClear onFocus={() => setOpen(true)}
onBlur={() => setOpen(false)}
style={{ width: 320 }} style={{ width: 320 }}
/> >
<Input
prefix={<SearchOutlined />}
placeholder="请输入车间号,如 R05697"
onPressEnter={handleSearch}
allowClear
/>
</AutoComplete>
<Button type="primary" onClick={handleSearch} loading={loading}> <Button type="primary" onClick={handleSearch} loading={loading}>
</Button> </Button>