@@ -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