optimization: (fix: the crash when inputting special characters in the search box (#4026)) (#4029)

* add a test rule

* add translation support
This commit is contained in:
Dyna
2025-07-10 21:09:02 +08:00
committed by GitHub
parent 1d49d79af2
commit 660c03a564
3 changed files with 39 additions and 17 deletions

View File

@@ -34,11 +34,6 @@ const StyledTextField = styled(TextField)(({ theme }) => ({
}, },
})); }));
// 转义正则表达式特殊字符的函数
const escapeRegExp = (string: string) => {
return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
};
export const BaseSearchBox = (props: SearchProps) => { export const BaseSearchBox = (props: SearchProps) => {
const { t } = useTranslation(); const { t } = useTranslation();
const inputRef = useRef<HTMLInputElement>(null); const inputRef = useRef<HTMLInputElement>(null);
@@ -60,9 +55,28 @@ export const BaseSearchBox = (props: SearchProps) => {
inheritViewBox: true, inheritViewBox: true,
}; };
// 验证正则表达式的辅助函数
const validateRegex = (pattern: string) => {
if (!pattern) return true;
try {
new RegExp(pattern);
return true;
} catch (e) {
return false;
}
};
const createMatcher = useMemo(() => { const createMatcher = useMemo(() => {
return (searchText: string) => { return (searchText: string) => {
try { try {
// 当启用正则表达式验证是否合规
if (useRegularExpression && searchText) {
const isValid = validateRegex(searchText);
if (!isValid) {
throw new Error(t("Invalid regular expression"));
}
}
return (content: string) => { return (content: string) => {
if (!searchText) return true; if (!searchText) return true;
@@ -70,29 +84,25 @@ export const BaseSearchBox = (props: SearchProps) => {
let searchItem = !matchCase ? searchText.toLowerCase() : searchText; let searchItem = !matchCase ? searchText.toLowerCase() : searchText;
if (useRegularExpression) { if (useRegularExpression) {
const pattern = matchWholeWord return new RegExp(searchItem).test(item);
? `\\b${escapeRegExp(searchItem)}\\b`
: escapeRegExp(searchItem);
return new RegExp(pattern).test(item);
} }
if (matchWholeWord) { if (matchWholeWord) {
return new RegExp(`\\b${escapeRegExp(searchItem)}\\b`).test(item); return new RegExp(`\\b${searchItem}\\b`).test(item);
} }
return item.includes(searchItem); return item.includes(searchItem);
}; };
} catch (err) { } catch (err) {
setErrorMessage(`${err}`); setErrorMessage(err instanceof Error ? err.message : `${err}`);
return () => true; return () => false; // 无效正则规则 不匹配值
} }
}; };
}, [matchCase, matchWholeWord, useRegularExpression]); }, [matchCase, matchWholeWord, useRegularExpression, t]);
useEffect(() => { useEffect(() => {
if (!inputRef.current) return; if (!inputRef.current) return;
const value = inputRef.current.value; const value = inputRef.current.value;
setErrorMessage("");
props.onSearch(createMatcher(value), { props.onSearch(createMatcher(value), {
text: value, text: value,
matchCase, matchCase,
@@ -104,6 +114,15 @@ export const BaseSearchBox = (props: SearchProps) => {
const onChange = (e: ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => { const onChange = (e: ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
const value = e.target?.value ?? ""; const value = e.target?.value ?? "";
setErrorMessage(""); setErrorMessage("");
// 验证正则表达式
if (useRegularExpression && value) {
const isValid = validateRegex(value);
if (!isValid) {
setErrorMessage(t("Invalid regular expression"));
}
}
props.onSearch(createMatcher(value), { props.onSearch(createMatcher(value), {
text: value, text: value,
matchCase, matchCase,
@@ -113,7 +132,7 @@ export const BaseSearchBox = (props: SearchProps) => {
}; };
return ( return (
<Tooltip title={errorMessage} placement="bottom-start"> <Tooltip title={errorMessage || ""} placement="bottom-start">
<StyledTextField <StyledTextField
autoComplete="new-password" autoComplete="new-password"
inputRef={inputRef} inputRef={inputRef}
@@ -125,6 +144,7 @@ export const BaseSearchBox = (props: SearchProps) => {
placeholder={props.placeholder ?? t("Filter conditions")} placeholder={props.placeholder ?? t("Filter conditions")}
sx={{ input: { py: 0.65, px: 1.25 } }} sx={{ input: { py: 0.65, px: 1.25 } }}
onChange={onChange} onChange={onChange}
error={!!errorMessage}
slotProps={{ slotProps={{
input: { input: {
sx: { pr: 1 }, sx: { pr: 1 },

View File

@@ -647,5 +647,6 @@
"Allowed Origins": "Allowed Origins", "Allowed Origins": "Allowed Origins",
"Please enter a valid url": "Please enter a valid url", "Please enter a valid url": "Please enter a valid url",
"Add": "Add", "Add": "Add",
"Development mode: Automatically includes Tauri and localhost origins": "Development mode: Automatically includes Tauri and localhost origins" "Development mode: Automatically includes Tauri and localhost origins": "Development mode: Automatically includes Tauri and localhost origins",
"Invalid regular expression": "Invalid regular expression"
} }

View File

@@ -647,5 +647,6 @@
"Allowed Origins": "允许的来源", "Allowed Origins": "允许的来源",
"Please enter a valid url": "请输入有效的网址", "Please enter a valid url": "请输入有效的网址",
"Add": "添加", "Add": "添加",
"Development mode: Automatically includes Tauri and localhost origins": "开发模式:自动包含 Tauri 和 localhost 来源" "Development mode: Automatically includes Tauri and localhost origins": "开发模式:自动包含 Tauri 和 localhost 来源",
"Invalid regular expression": "无效的正则表达式"
} }