Skip to content

Commit 856d828

Browse files
committed
fix: 搜索防抖优化 & 单选恢复三栏布局
- 解耦 searchInputValue(即时显示) 与 searchKeyword(防抖查询),修复搜索卡顿 - PC/移动端搜索框绑定 searchInputValue,按键即时响应 - PC单选恢复三栏布局(宽度1200),右侧已选面板始终显示 - 移除单选模式重复的'当前选中'指示条 - 组件卸载时清理搜索/部门搜索防抖定时器 - .gitignore 排除 .recovered-sessions/
1 parent 1f6f8ff commit 856d828

2 files changed

Lines changed: 21 additions & 39 deletions

File tree

packages/@steedos-widgets/amis-object/src/components/MobileDrawerContent.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,7 @@ interface MobileDrawerProps {
149149
expandedKeys: React.Key[];
150150
users: any[];
151151
searchKeyword: string;
152+
searchInputValue: string;
152153
tempSelectedUsers: any[];
153154
mobileActiveTab: 'dept' | 'users' | 'selected';
154155
clearable: boolean;
@@ -171,7 +172,7 @@ interface MobileDrawerProps {
171172
export const MobileDrawerContent: React.FC<MobileDrawerProps> = (props) => {
172173
const {
173174
visible, multiple, loading, deptTree, treeKey, deptSearchKeyword,
174-
selectedDept, selectedDeptName, expandedKeys, users, searchKeyword,
175+
selectedDept, selectedDeptName, expandedKeys, users, searchKeyword, searchInputValue,
175176
tempSelectedUsers, mobileActiveTab, clearable,
176177
onSelectDept, onLoadData, onExpandKeys, onDeptSearch, onUserSearch,
177178
onAddUser, onRemoveUser, onToggleUser, onToggleSelectAll,
@@ -209,7 +210,7 @@ export const MobileDrawerContent: React.FC<MobileDrawerProps> = (props) => {
209210
)}
210211
{/* 搜索框 + 全选同行(参考钉钉/飞书规范) */}
211212
<div style={{ padding: '8px 16px', display: 'flex', alignItems: 'center', gap: 8 }}>
212-
<Input placeholder="搜索姓名、邮箱或用户名" prefix={<SearchOutlined />} value={searchKeyword} onChange={(e) => onUserSearch(e.target.value)} allowClear size="large" style={{ flex: 1 }} />
213+
<Input placeholder="搜索姓名、邮箱或用户名" prefix={<SearchOutlined />} value={searchInputValue} onChange={(e) => onUserSearch(e.target.value)} allowClear size="large" style={{ flex: 1 }} />
213214
{multiple && users.length > 0 && (
214215
<Button size="small" onClick={onToggleSelectAll} style={{ flexShrink: 0 }}>{isAllSelected ? '取消全选' : '全选'}</Button>
215216
)}

packages/@steedos-widgets/amis-object/src/components/SteedosUserSelector.tsx

Lines changed: 18 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,7 @@ export const SteedosUserSelector: React.FC<UserSelectorProps> = (props) => {
181181
const [tempSelectedUsers, setTempSelectedUsers] = useState<any[]>([]);
182182
const [loading, setLoading] = useState(false);
183183
const [searchKeyword, setSearchKeyword] = useState('');
184+
const [searchInputValue, setSearchInputValue] = useState(''); // 搜索框即时值(解耦输入与防抖查询)
184185
const [deptSearchKeyword, setDeptSearchKeyword] = useState('');
185186
const [draggedIndex, setDraggedIndex] = useState<number | null>(null);
186187
const [expandedKeys, setExpandedKeys] = useState<React.Key[]>([]);
@@ -241,6 +242,14 @@ export const SteedosUserSelector: React.FC<UserSelectorProps> = (props) => {
241242
}
242243
}, [value]);
243244

245+
// 组件卸载时清理防抖定时器,避免 setState on unmounted component
246+
useEffect(() => {
247+
return () => {
248+
if (searchTimeoutRef.current) clearTimeout(searchTimeoutRef.current);
249+
if (deptSearchTimeoutRef.current) clearTimeout(deptSearchTimeoutRef.current);
250+
};
251+
}, []);
252+
244253
// 加载部门树
245254
useEffect(() => {
246255
if (visible) {
@@ -253,6 +262,7 @@ export const SteedosUserSelector: React.FC<UserSelectorProps> = (props) => {
253262
setSelectedDept(null);
254263
setDeptSearchKeyword('');
255264
setSearchKeyword('');
265+
setSearchInputValue(''); // 同步清空搜索输入框
256266

257267
fetchDeptTree()
258268
.then(data => {
@@ -332,6 +342,7 @@ export const SteedosUserSelector: React.FC<UserSelectorProps> = (props) => {
332342
if (selectedKeys.length > 0) {
333343
setSelectedDept(String(selectedKeys[0]));
334344
setSearchKeyword(''); // 互斥规则:切换部门时清空搜索关键字
345+
setSearchInputValue(''); // 同步清空搜索输入框
335346
// 移动端:记住部门名称,自动跳转到人员Tab
336347
if (isMobile && info?.node) {
337348
setSelectedDeptName(String((info.node as any).title || ''));
@@ -385,6 +396,7 @@ export const SteedosUserSelector: React.FC<UserSelectorProps> = (props) => {
385396

386397
// 处理用户搜索
387398
const handleSearch = (searchValue: string) => {
399+
setSearchInputValue(searchValue); // 立即更新输入框显示
388400
if (searchTimeoutRef.current) {
389401
clearTimeout(searchTimeoutRef.current);
390402
}
@@ -590,7 +602,7 @@ export const SteedosUserSelector: React.FC<UserSelectorProps> = (props) => {
590602
okText="确定"
591603
cancelText="取消"
592604
footer={multiple ? undefined : null}
593-
width={multiple ? 1200 : 850}
605+
width={1200}
594606
destroyOnClose
595607
bodyStyle={{ height: 600, overflow: 'hidden', padding: 0 }}
596608
>
@@ -636,7 +648,7 @@ export const SteedosUserSelector: React.FC<UserSelectorProps> = (props) => {
636648
<Input
637649
placeholder="搜索姓名、邮箱或用户名"
638650
prefix={<SearchOutlined />}
639-
value={searchKeyword}
651+
value={searchInputValue}
640652
onChange={(e) => handleSearch(e.target.value)}
641653
allowClear
642654
style={{ flex: 1 }}
@@ -647,38 +659,6 @@ export const SteedosUserSelector: React.FC<UserSelectorProps> = (props) => {
647659
</Button>
648660
)}
649661
</div>
650-
{/* 单选模式:当前选中指示条 */}
651-
{!multiple && tempSelectedUsers.length > 0 && (
652-
<div style={{
653-
display: 'flex', alignItems: 'center', gap: 8, padding: '6px 12px',
654-
marginBottom: 8, background: '#f6f8fa', borderRadius: 6, border: '1px solid #e8e8e8'
655-
}}>
656-
<span style={{ fontSize: 12, color: '#999', flexShrink: 0 }}>当前选中</span>
657-
<Avatar
658-
src={tempSelectedUsers[0].avatar ? `/api/v6/users/${tempSelectedUsers[0].user}/avatar` : undefined}
659-
size={24}
660-
style={{ backgroundColor: tempSelectedUsers[0].avatar ? undefined : '#1890ff', flexShrink: 0 }}
661-
>
662-
{!tempSelectedUsers[0].avatar && tempSelectedUsers[0].name?.charAt(0)}
663-
</Avatar>
664-
<span style={{ fontSize: 14, fontWeight: 500, flex: 1, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
665-
{tempSelectedUsers[0].name}
666-
</span>
667-
{tempSelectedUsers[0].email && (
668-
<span style={{ fontSize: 12, color: '#999', flexShrink: 0 }}>{tempSelectedUsers[0].email}</span>
669-
)}
670-
{clearable && (
671-
<CloseOutlined
672-
onClick={(e) => {
673-
e.stopPropagation();
674-
setTempSelectedUsers([]);
675-
handleOkWithUsers([]);
676-
}}
677-
style={{ fontSize: 12, color: '#999', cursor: 'pointer', flexShrink: 0, padding: 4 }}
678-
/>
679-
)}
680-
</div>
681-
)}
682662
<div style={{ flex: 1, overflowY: 'auto', border: '1px solid #f0f0f0', borderRadius: 4, position: 'relative' }}>
683663
<Spin spinning={loading}>
684664
{users.length > 0 ? (
@@ -750,8 +730,8 @@ export const SteedosUserSelector: React.FC<UserSelectorProps> = (props) => {
750730
</div>
751731
</div>
752732

753-
{/* 右侧:已选中(仅多选模式显示) */}
754-
{multiple && <div style={{ width: 240, borderLeft: '1px solid #f0f0f0', padding: 16, display: 'flex', flexDirection: 'column' }}>
733+
{/* 右侧:已选中 */}
734+
<div style={{ width: 240, borderLeft: '1px solid #f0f0f0', padding: 16, display: 'flex', flexDirection: 'column' }}>
755735
<div style={{ marginBottom: 12, fontSize: 14 }}>
756736
<span style={{ fontWeight: 500 }}>已选中</span>
757737
<span style={{ marginLeft: 8, color: '#999' }}>({tempSelectedUsers.length})</span>
@@ -818,7 +798,7 @@ export const SteedosUserSelector: React.FC<UserSelectorProps> = (props) => {
818798
清空全部
819799
</Button>
820800
)}
821-
</div>}
801+
</div>
822802
</div>
823803
</Modal>}
824804

@@ -836,6 +816,7 @@ export const SteedosUserSelector: React.FC<UserSelectorProps> = (props) => {
836816
expandedKeys={expandedKeys}
837817
users={users}
838818
searchKeyword={searchKeyword}
819+
searchInputValue={searchInputValue}
839820
tempSelectedUsers={tempSelectedUsers}
840821
mobileActiveTab={mobileActiveTab}
841822
clearable={clearable}

0 commit comments

Comments
 (0)