-
Notifications
You must be signed in to change notification settings - Fork 336
处理搜索交互逻辑 #1023
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
处理搜索交互逻辑 #1023
Changes from all commits
8722072
04253ff
bc0a86f
babec4a
b90e951
631e086
354dd0b
aeb461f
5701f8a
09be758
a829565
9949cc2
b9c60b0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,50 @@ | ||
| import type { SearchType } from "@App/app/service/service_worker/types"; | ||
| import { requestFilterResult } from "@App/pages/store/features/script"; | ||
|
|
||
| export type SearchFilterKeyEntry = { type: SearchType; keyword: string }; | ||
| export type SearchFilterRequest = { type: SearchType; keyword: string; bySelect?: boolean }; // 两个Type日后可能会不同。先分开写。 | ||
|
|
||
| // 静态变量不随重绘重置 | ||
| let lastReqType: SearchType | undefined = undefined; | ||
| let lastKeyword: string = ""; | ||
| type SearchFilterCacheEntry = { code: boolean; name: boolean; auto: boolean }; | ||
| const searchFilterCache: Map<string, SearchFilterCacheEntry> = new Map(); | ||
|
|
||
| export class SearchFilter { | ||
| static async requestFilterResult(req: SearchFilterRequest) { | ||
| if (req.keyword === lastKeyword) { | ||
| lastReqType = req.type; | ||
| return Promise.resolve(this); | ||
| } else { | ||
| const res = await requestFilterResult({ value: req.keyword }); | ||
| lastReqType = req.type; | ||
| lastKeyword = req.keyword; | ||
| searchFilterCache.clear(); | ||
| if (res && Array.isArray(res)) { | ||
| for (const entry of res) { | ||
| searchFilterCache.set(entry.uuid, { | ||
| code: entry.code, | ||
| name: entry.name, | ||
| auto: entry.auto, | ||
| }); | ||
| } | ||
| } | ||
| return this; | ||
| } | ||
| } | ||
|
|
||
| static checkByUUID(uuid: string): boolean { | ||
| const result = searchFilterCache.get(uuid); | ||
| if (!result) return false; | ||
| switch (lastReqType) { | ||
| case "auto": | ||
| return result.auto; | ||
| case "script_code": | ||
| return result.code; | ||
| case "name": | ||
| return result.name; | ||
| default: | ||
| return false; | ||
| } | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -11,7 +11,7 @@ import { ListHomeRender } from "../utils"; | |||||||||||||||||||||||
| import { IconEdit, IconLink, IconUserAdd } from "@arco-design/web-react/icon"; | ||||||||||||||||||||||||
| import type { SearchType } from "@App/app/service/service_worker/types"; | ||||||||||||||||||||||||
| import type { TFunction } from "i18next"; | ||||||||||||||||||||||||
| import type { RefInputType } from "@arco-design/web-react/es/Input"; | ||||||||||||||||||||||||
| import type { SearchFilterKeyEntry, SearchFilterRequest } from "./SearchFilter"; | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| export const EnableSwitch = React.memo( | ||||||||||||||||||||||||
| ({ | ||||||||||||||||||||||||
|
|
@@ -184,50 +184,52 @@ UpdateTimeCell.displayName = "UpdateTimeCell"; | |||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| interface ScriptSearchFieldProps { | ||||||||||||||||||||||||
| t: TFunction<"translation", undefined>; | ||||||||||||||||||||||||
| defaultValue: { keyword: string; type: SearchType }; | ||||||||||||||||||||||||
| onSearch?: (req: { keyword: string; type: SearchType }) => void; | ||||||||||||||||||||||||
| inputRef?: React.RefObject<RefInputType>; | ||||||||||||||||||||||||
| defaultValue?: SearchFilterKeyEntry; | ||||||||||||||||||||||||
| onChange?: (req: SearchFilterRequest) => void; | ||||||||||||||||||||||||
| onSearch?: (req: SearchFilterRequest) => void; | ||||||||||||||||||||||||
| autoFocus?: boolean; | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| export const ScriptSearchField = React.memo( | ||||||||||||||||||||||||
| ({ t, defaultValue, onSearch, inputRef }: ScriptSearchFieldProps) => { | ||||||||||||||||||||||||
| const [keyword, setKeyword] = React.useState(defaultValue?.keyword || ""); | ||||||||||||||||||||||||
| const [type, setType] = React.useState<SearchType>(defaultValue?.type || "auto"); | ||||||||||||||||||||||||
| return ( | ||||||||||||||||||||||||
| <Space direction="horizontal"> | ||||||||||||||||||||||||
| <Select | ||||||||||||||||||||||||
| className="flex-1" | ||||||||||||||||||||||||
| triggerProps={{ autoAlignPopupWidth: false, autoAlignPopupMinWidth: true, position: "bl" }} | ||||||||||||||||||||||||
| size="small" | ||||||||||||||||||||||||
| value={type} | ||||||||||||||||||||||||
| onChange={(value) => { | ||||||||||||||||||||||||
| setType(value as SearchType); | ||||||||||||||||||||||||
| onSearch?.({ keyword, type: value as SearchType }); | ||||||||||||||||||||||||
| }} | ||||||||||||||||||||||||
| > | ||||||||||||||||||||||||
| <Select.Option value="auto">{t("auto")}</Select.Option> | ||||||||||||||||||||||||
| <Select.Option value="name">{t("name")}</Select.Option> | ||||||||||||||||||||||||
| <Select.Option value="script_code">{t("script_code")}</Select.Option> | ||||||||||||||||||||||||
| </Select> | ||||||||||||||||||||||||
| <Input.Search | ||||||||||||||||||||||||
| ref={inputRef} | ||||||||||||||||||||||||
| size="small" | ||||||||||||||||||||||||
| searchButton | ||||||||||||||||||||||||
| style={{ width: 280 }} | ||||||||||||||||||||||||
| value={keyword} | ||||||||||||||||||||||||
| placeholder={t("enter_search_value", { search: `${t("name")}/${t("script_code")}` })!} | ||||||||||||||||||||||||
| onChange={(value) => { | ||||||||||||||||||||||||
| setKeyword(value); | ||||||||||||||||||||||||
| }} | ||||||||||||||||||||||||
| onSearch={(value) => { | ||||||||||||||||||||||||
| onSearch?.({ keyword: value, type }); | ||||||||||||||||||||||||
| }} | ||||||||||||||||||||||||
| /> | ||||||||||||||||||||||||
| </Space> | ||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||
| (prevProps, nextProps) => { | ||||||||||||||||||||||||
| return prevProps.t === nextProps.t && prevProps.defaultValue === nextProps.defaultValue; | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||
| ScriptSearchField.displayName = "ScriptSearchField"; | ||||||||||||||||||||||||
| export const ScriptSearchField = ({ t, defaultValue, onChange, onSearch, autoFocus }: ScriptSearchFieldProps) => { | ||||||||||||||||||||||||
| const [keyword, setKeyword] = React.useState(defaultValue?.keyword || ""); | ||||||||||||||||||||||||
| const [type, setType] = React.useState<SearchType>(defaultValue?.type || "auto"); | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
| const [type, setType] = React.useState<SearchType>(defaultValue?.type || "auto"); | |
| const [type, setType] = React.useState<SearchType>(defaultValue?.type || "auto"); | |
| React.useEffect(() => { | |
| if (defaultValue?.keyword !== undefined) { | |
| setKeyword(defaultValue.keyword); | |
| } | |
| if (defaultValue?.type !== undefined) { | |
| setType(defaultValue.type); | |
| } | |
| }, [defaultValue]); |
Copilot
AI
Nov 28, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
移除了 React.memo 优化可能会导致性能问题。ScriptSearchField 组件会在 filterDropdown 中被渲染(在 ScriptTable.tsx 中),而 filterDropdown 每次表格重新渲染时都会被调用。没有 React.memo,即使 props 没有变化,组件也会重新渲染。
建议恢复 React.memo 或使用 useCallback 优化回调函数,以避免不必要的重新渲染:
export const ScriptSearchField = React.memo(({ t, defaultValue, onChange, onSearch, autoFocus }: ScriptSearchFieldProps) => {
// ... 组件实现
});
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
新增的
SearchFilter类缺少类和方法的文档注释。建议添加 JSDoc 注释来说明:onResponse方法的回调时机例如: