diff --git a/packages/components/select/_example/remote-search.tsx b/packages/components/select/_example/remote-search.tsx
index 9d911cba02..dfb8db0313 100644
--- a/packages/components/select/_example/remote-search.tsx
+++ b/packages/components/select/_example/remote-search.tsx
@@ -1,53 +1,73 @@
-import React, { useState } from 'react';
-import { Select } from 'tdesign-react';
+import React, { useEffect, useState } from 'react';
+import { Select, Space } from 'tdesign-react';
+
+const OPTIONS = Array.from({ length: 20 }).map((_, i) => ({
+ value: `t${i + 1}`,
+ label: `Tencent_${i + 1}`,
+}));
const RemoteSearchSelect = () => {
- const [value, setValue] = useState('');
+ const [singleLoading, setSingleLoading] = useState(false);
+ const [singleOptions, setSingleOptions] = useState([]);
+ const [singleValue, setSingleValue] = useState('');
- const [loading, setLoading] = useState(false);
- const [options, setOptions] = useState([]);
+ const [multipleLoading, setMultipleLoading] = useState(false);
+ const [multipleOptions, setMultipleOptions] = useState([]);
+ const [multipleValue, setMultipleValue] = useState([]);
- const onChange = (value: string) => {
- setValue(value);
- };
+ useEffect(() => {
+ setSingleLoading(true);
+ setMultipleLoading(true);
+
+ setTimeout(() => {
+ setSingleOptions(OPTIONS);
+ setSingleLoading(false);
+ setSingleValue(OPTIONS[0].value);
+ setMultipleOptions(OPTIONS);
+ setMultipleLoading(false);
+ setMultipleValue([OPTIONS[0].value, OPTIONS[1].value]);
+ }, 500);
+ }, []);
- const handleRemoteSearch = (search: string) => {
- setLoading(true);
+ const handleSingle = (search: string) => {
+ setSingleLoading(true);
+ setTimeout(() => {
+ const filtered = OPTIONS.filter((item) => item.label.includes(search));
+ setSingleOptions(filtered);
+ setSingleLoading(false);
+ }, 500);
+ };
+ const handleMultiple = (search: string) => {
+ setMultipleLoading(true);
setTimeout(() => {
- setLoading(false);
- let options = [];
- if (search) {
- options = [
- {
- value: `腾讯_test1`,
- label: `腾讯_test1`,
- },
- {
- value: `腾讯_test2`,
- label: `腾讯_test2`,
- },
- {
- value: `腾讯_test3`,
- label: `腾讯_test3`,
- },
- ].filter((item) => item.label.includes(search));
- }
-
- setOptions(options);
+ const filtered = OPTIONS.filter((item) => item.label.includes(search));
+ setMultipleOptions(filtered);
+ setMultipleLoading(false);
}, 500);
};
return (
-
+
+
+
+
);
};
diff --git a/packages/components/select/base/Select.tsx b/packages/components/select/base/Select.tsx
index 35d43aaa35..84c6b58edc 100644
--- a/packages/components/select/base/Select.tsx
+++ b/packages/components/select/base/Select.tsx
@@ -1,3 +1,5 @@
+import classNames from 'classnames';
+import { debounce, get, isFunction } from 'lodash-es';
import React, {
Children,
cloneElement,
@@ -8,8 +10,6 @@ import React, {
useRef,
useState,
} from 'react';
-import classNames from 'classnames';
-import { debounce, get, isFunction } from 'lodash-es';
import composeRefs from '../../_util/composeRefs';
import forwardRefWithStatics from '../../_util/forwardRefWithStatics';
import { getOffsetTopToContainer } from '../../_util/helper';
@@ -117,6 +117,12 @@ const Select = forwardRefWithStatics(
if (disabled) return;
visible ? toggleIsScrolling(false) : onInputChange('', { trigger: 'blur' });
setInnerPopupVisible(visible, ctx);
+
+ if (visible && isFunction(onSearch) && !inputValue) {
+ // @ts-ignore
+ // 实际是由 click 触发而非键盘事件,待补充类型
+ onSearch('', { e: ctx.e });
+ }
};
const { currentOptions, setCurrentOptions, tmpPropOptions, valueToOption, selectedOptions, flattenedOptions } =
diff --git a/packages/components/select/hooks/useOptions.ts b/packages/components/select/hooks/useOptions.ts
index 6b276c1332..dc690cd693 100644
--- a/packages/components/select/hooks/useOptions.ts
+++ b/packages/components/select/hooks/useOptions.ts
@@ -7,11 +7,17 @@ import { getKeyMapping, getValueToOption, type ValueToOption } from '../util/hel
import type { SelectKeysType, SelectOption, SelectOptionGroup, SelectValue, TdOptionProps } from '../type';
+type OptionValueType = SelectValue;
+
// 针对分组的相关判断和扁平处理
export function isSelectOptionGroup(option: SelectOption): option is SelectOptionGroup {
return !!option && 'group' in option && 'children' in option;
}
+export function isValueSelected(v: SelectValue, key: string, valueType: string, valueKey: string) {
+ return valueType === 'value' ? String(v) === String(key) : get(v, valueKey) === key;
+}
+
export const flattenOptions = (options: SelectOption[] = []) => {
const flattened = [];
options.forEach((option) => {
@@ -26,8 +32,6 @@ export const flattenOptions = (options: SelectOption[] = []) => {
return flattened;
};
-type OptionValueType = SelectValue;
-
// 处理 options 的逻辑
function useOptions(
keys: SelectKeysType,
@@ -48,6 +52,7 @@ function useOptions(
useEffect(() => {
setFlattenedOptions(flattenOptions(currentOptions));
}, [currentOptions]);
+
// 处理设置 option 的逻辑
useEffect(() => {
let transformedOptions = options;
@@ -76,6 +81,7 @@ function useOptions(
};
transformedOptions = arrayChildren?.map((v) => handlerOptionElement(v));
}
+
if (keys) {
// 如果有定制 keys 先做转换
transformedOptions = transformedOptions?.map((option) => ({
@@ -84,10 +90,28 @@ function useOptions(
label: get(option, labelKey),
}));
}
+
setCurrentOptions(transformedOptions);
setTmpPropOptions(transformedOptions);
- setValueToOption(getValueToOption(children as ReactElement, options as TdOptionProps[], keys) || {});
+ setValueToOption((prevValueToOption) => {
+ const newValueToOption = getValueToOption(children as ReactElement, options as TdOptionProps[], keys) || {};
+ const mergedValueToOption = { ...newValueToOption };
+
+ // 保持之前选中的 option 在映射中,避免远程搜索时,状态丢失
+ Object.keys(prevValueToOption).forEach((key) => {
+ if (mergedValueToOption[key]) return;
+ const isSelected = Array.isArray(value)
+ ? value.some((v) => isValueSelected(v, key, valueType, valueKey))
+ : isValueSelected(value, key, valueType, valueKey);
+
+ if (isSelected) {
+ mergedValueToOption[key] = prevValueToOption[key];
+ }
+ });
+
+ return mergedValueToOption;
+ });
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [options, keys, children, reserveKeyword]);
diff --git a/test/snap/__snapshots__/csr.test.jsx.snap b/test/snap/__snapshots__/csr.test.jsx.snap
index 38ee7e82d8..be019843ca 100644
--- a/test/snap/__snapshots__/csr.test.jsx.snap
+++ b/test/snap/__snapshots__/csr.test.jsx.snap
@@ -88870,44 +88870,124 @@ exports[`csr snapshot test > csr test packages/components/select/_example/prefix
exports[`csr snapshot test > csr test packages/components/select/_example/remote-search.tsx 1`] = `
@@ -150616,7 +150696,7 @@ exports[`ssr snapshot test > ssr test packages/components/select/_example/popup-
exports[`ssr snapshot test > ssr test packages/components/select/_example/prefix.tsx 1`] = `""`;
-exports[`ssr snapshot test > ssr test packages/components/select/_example/remote-search.tsx 1`] = `""`;
+exports[`ssr snapshot test > ssr test packages/components/select/_example/remote-search.tsx 1`] = `""`;
exports[`ssr snapshot test > ssr test packages/components/select/_example/scroll-bottom.tsx 1`] = `""`;
diff --git a/test/snap/__snapshots__/ssr.test.jsx.snap b/test/snap/__snapshots__/ssr.test.jsx.snap
index 485ee63d37..2a987d1477 100644
--- a/test/snap/__snapshots__/ssr.test.jsx.snap
+++ b/test/snap/__snapshots__/ssr.test.jsx.snap
@@ -844,7 +844,7 @@ exports[`ssr snapshot test > ssr test packages/components/select/_example/popup-
exports[`ssr snapshot test > ssr test packages/components/select/_example/prefix.tsx 1`] = `""`;
-exports[`ssr snapshot test > ssr test packages/components/select/_example/remote-search.tsx 1`] = `""`;
+exports[`ssr snapshot test > ssr test packages/components/select/_example/remote-search.tsx 1`] = `""`;
exports[`ssr snapshot test > ssr test packages/components/select/_example/scroll-bottom.tsx 1`] = `""`;