|  | @@ -1,8 +1,9 @@
 | 
	
		
			
				|  |  |  import React, { useState, useRef, useEffect } from 'react'
 | 
	
		
			
				|  |  |  import { useRequest, request } from 'umi'
 | 
	
		
			
				|  |  | -import { Button, Input, message, Modal } from 'antd'
 | 
	
		
			
				|  |  | +import { Button, Input, message, Modal, Spin } from 'antd'
 | 
	
		
			
				|  |  |  import { fetchStaffList } from '@/services/user/api'
 | 
	
		
			
				|  |  | -import { LoadingOutlined, MoreOutlined } from '@ant-design/icons'
 | 
	
		
			
				|  |  | +import { useDebounceFn } from 'ahooks'
 | 
	
		
			
				|  |  | +import consts from '@/utils/consts'
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  interface ConnectModalProps {
 | 
	
		
			
				|  |  |    postUrl: string
 | 
	
	
		
			
				|  | @@ -11,6 +12,7 @@ interface ConnectModalProps {
 | 
	
		
			
				|  |  |    dataId?: string
 | 
	
		
			
				|  |  |    onReload?: () => void
 | 
	
		
			
				|  |  |    show?: boolean
 | 
	
		
			
				|  |  | +  closeAfterSelect?: boolean
 | 
	
		
			
				|  |  |    onShowChange?: (isShow: boolean) => void
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  const ConnectModal: React.FC<ConnectModalProps> = ({
 | 
	
	
		
			
				|  | @@ -20,11 +22,20 @@ const ConnectModal: React.FC<ConnectModalProps> = ({
 | 
	
		
			
				|  |  |    show,
 | 
	
		
			
				|  |  |    onShowChange,
 | 
	
		
			
				|  |  |    postUrl,
 | 
	
		
			
				|  |  | +  closeAfterSelect = true,
 | 
	
		
			
				|  |  |    showButton = true
 | 
	
		
			
				|  |  |  }) => {
 | 
	
		
			
				|  |  | -  const containerRef = useRef<HTMLDivElement>(null)
 | 
	
		
			
				|  |  | +  const scrollRef = useRef<HTMLDivElement>(null)
 | 
	
		
			
				|  |  |    const [visible, setVisible] = useState(false)
 | 
	
		
			
				|  |  | -  const [searchVal, setSearchVal] = useState('')
 | 
	
		
			
				|  |  | +  const [state, setState] = useState({
 | 
	
		
			
				|  |  | +    laoding: true,
 | 
	
		
			
				|  |  | +    list: [],
 | 
	
		
			
				|  |  | +    searchVal: '',
 | 
	
		
			
				|  |  | +    current: 1,
 | 
	
		
			
				|  |  | +    pageSize: 10,
 | 
	
		
			
				|  |  | +    total: 0,
 | 
	
		
			
				|  |  | +    noMore: false
 | 
	
		
			
				|  |  | +  })
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |    const { run: tryConnectStaff } = useRequest(
 | 
	
		
			
				|  |  |      (params: API.AddRoleStaffCommonParams) =>
 | 
	
	
		
			
				|  | @@ -36,9 +47,6 @@ const ConnectModal: React.FC<ConnectModalProps> = ({
 | 
	
		
			
				|  |  |        manual: true,
 | 
	
		
			
				|  |  |        onSuccess: async () => {
 | 
	
		
			
				|  |  |          message.success('关联成功')
 | 
	
		
			
				|  |  | -        if (!show) {
 | 
	
		
			
				|  |  | -          setVisible(false)
 | 
	
		
			
				|  |  | -        }
 | 
	
		
			
				|  |  |          await onReload()
 | 
	
		
			
				|  |  |        },
 | 
	
		
			
				|  |  |        onError: e => {
 | 
	
	
		
			
				|  | @@ -47,48 +55,48 @@ const ConnectModal: React.FC<ConnectModalProps> = ({
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |    )
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  const {
 | 
	
		
			
				|  |  | -    run: tryQueryStaffList,
 | 
	
		
			
				|  |  | -    data,
 | 
	
		
			
				|  |  | -    noMore,
 | 
	
		
			
				|  |  | -    loadMore,
 | 
	
		
			
				|  |  | -    loadingMore
 | 
	
		
			
				|  |  | -  } = useRequest(
 | 
	
		
			
				|  |  | -    result =>
 | 
	
		
			
				|  |  | -      fetchStaffList({
 | 
	
		
			
				|  |  | -        current: result?.list.length / 10 + 1 || 1,
 | 
	
		
			
				|  |  | -        pageSize: 10,
 | 
	
		
			
				|  |  | -        search: searchVal
 | 
	
		
			
				|  |  | -      }),
 | 
	
		
			
				|  |  | -    {
 | 
	
		
			
				|  |  | -      manual: true,
 | 
	
		
			
				|  |  | -      loadMore: true,
 | 
	
		
			
				|  |  | -      ref: containerRef,
 | 
	
		
			
				|  |  | -      isNoMore: (res: API.BasicFetchResult<API.StaffItem>) => res.list.length >= res.total,
 | 
	
		
			
				|  |  | -      refreshDeps: [searchVal]
 | 
	
		
			
				|  |  | +  const initData = async params => {
 | 
	
		
			
				|  |  | +    const { data: { list, total } = { list: [], total: 0 }, code = -1 } = await fetchStaffList(
 | 
	
		
			
				|  |  | +      params
 | 
	
		
			
				|  |  | +    )
 | 
	
		
			
				|  |  | +    if (code === consts.RET_CODE.SUCCESS) {
 | 
	
		
			
				|  |  | +      if (params?.current === 1) {
 | 
	
		
			
				|  |  | +        scrollRef.current?.scrollTo({ top: 0 })
 | 
	
		
			
				|  |  | +        // 请求的是第一页或者搜索框
 | 
	
		
			
				|  |  | +        setState({ ...state, list, total, laoding: false, searchVal: params?.search })
 | 
	
		
			
				|  |  | +      } else {
 | 
	
		
			
				|  |  | +        setState({
 | 
	
		
			
				|  |  | +          ...state,
 | 
	
		
			
				|  |  | +          list: state.list.concat(list),
 | 
	
		
			
				|  |  | +          total,
 | 
	
		
			
				|  |  | +          searchVal: params?.search,
 | 
	
		
			
				|  |  | +          laoding: false,
 | 
	
		
			
				|  |  | +          current: params?.current || 1
 | 
	
		
			
				|  |  | +        })
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  | -  )
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  const handleSearch = (value: string) => {
 | 
	
		
			
				|  |  | -    setSearchVal(value)
 | 
	
		
			
				|  |  | -    setTimeout(() => {
 | 
	
		
			
				|  |  | -      tryQueryStaffList()
 | 
	
		
			
				|  |  | -    }, 250)
 | 
	
		
			
				|  |  | +  const onChange = e => {
 | 
	
		
			
				|  |  | +    // const { value = '' } = e.target || e.currentTarget
 | 
	
		
			
				|  |  | +    initData({ search: e, current: 1, pageSize: state.pageSize })
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  const { list = [] }: { list: API.StaffItem[] } = data || {}
 | 
	
		
			
				|  |  | +  const { run: handleInputChange } = useDebounceFn(onChange)
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |    useEffect(() => {
 | 
	
		
			
				|  |  |      if (show || visible) {
 | 
	
		
			
				|  |  | -      tryQueryStaffList()
 | 
	
		
			
				|  |  | +      initData({ current: 1, pageSize: 10 })
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |    }, [show, visible])
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |    const handleOnCancel = () => {
 | 
	
		
			
				|  |  | -    if (onShowChange !== undefined) {
 | 
	
		
			
				|  |  | +    // 不可关闭
 | 
	
		
			
				|  |  | +    if (onShowChange) {
 | 
	
		
			
				|  |  |        onShowChange(false)
 | 
	
		
			
				|  |  | -    } else {
 | 
	
		
			
				|  |  | -      setVisible(false)
 | 
	
		
			
				|  |  | +      return
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  | +    setVisible(false)
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |    const itemSelectHandler = async (staffId: string) => {
 | 
	
	
		
			
				|  | @@ -97,8 +105,36 @@ const ConnectModal: React.FC<ConnectModalProps> = ({
 | 
	
		
			
				|  |  |        params.id = dataId
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |      await tryConnectStaff(params)
 | 
	
		
			
				|  |  | +    if (!closeAfterSelect) {
 | 
	
		
			
				|  |  | +      const nList = state.list.map(item => {
 | 
	
		
			
				|  |  | +        const nItem = { ...item, disable: false }
 | 
	
		
			
				|  |  | +        if (item.id === staffId) {
 | 
	
		
			
				|  |  | +          nItem.disable = true
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +        return nItem
 | 
	
		
			
				|  |  | +      })
 | 
	
		
			
				|  |  | +      setState({ ...state, list: nList })
 | 
	
		
			
				|  |  | +      return
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |      handleOnCancel()
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  const handleListScroll = () => {
 | 
	
		
			
				|  |  | +    // 未滚动到底部
 | 
	
		
			
				|  |  | +    if (
 | 
	
		
			
				|  |  | +      scrollRef.current.scrollHeight - scrollRef.current.clientHeight <=
 | 
	
		
			
				|  |  | +      scrollRef.current.scrollTop
 | 
	
		
			
				|  |  | +    ) {
 | 
	
		
			
				|  |  | +      // 已到底部
 | 
	
		
			
				|  |  | +      if (state.current * state.pageSize > state.total) {
 | 
	
		
			
				|  |  | +        setState({ ...state, noMore: true })
 | 
	
		
			
				|  |  | +        return
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +      initData({ current: state.current + 1, pageSize: state.pageSize, search: state.searchVal })
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |    return (
 | 
	
		
			
				|  |  |      <>
 | 
	
		
			
				|  |  |        {showButton && (
 | 
	
	
		
			
				|  | @@ -111,32 +147,44 @@ const ConnectModal: React.FC<ConnectModalProps> = ({
 | 
	
		
			
				|  |  |          onCancel={handleOnCancel}
 | 
	
		
			
				|  |  |          getContainer={false}
 | 
	
		
			
				|  |  |          width="60vw"
 | 
	
		
			
				|  |  | +        footer={null}
 | 
	
		
			
				|  |  |          title={
 | 
	
		
			
				|  |  |            <Input.Search
 | 
	
		
			
				|  |  |              placeholder="搜索员工(姓名)"
 | 
	
		
			
				|  |  | -            onSearch={value => handleSearch(value)}
 | 
	
		
			
				|  |  | -            onPressEnter={e => handleSearch(e.currentTarget.value)}
 | 
	
		
			
				|  |  | +            onSearch={value => handleInputChange(value)}
 | 
	
		
			
				|  |  | +            onPressEnter={e => handleInputChange(e.currentTarget.value)}
 | 
	
		
			
				|  |  |              style={{ width: '95%' }}
 | 
	
		
			
				|  |  |              allowClear={true}
 | 
	
		
			
				|  |  |            />
 | 
	
		
			
				|  |  |          }>
 | 
	
		
			
				|  |  | -        <div ref={containerRef} className="h-60vh overflow-y-auto overflow-x-hidden">
 | 
	
		
			
				|  |  | -          {list.map(item => (
 | 
	
		
			
				|  |  | -            <div className="group-item-card" key={item.id}>
 | 
	
		
			
				|  |  | -              <div className="w-4/3 flex justify-between">
 | 
	
		
			
				|  |  | -                <span className="w-1/5">{item.username}</span>
 | 
	
		
			
				|  |  | -                <span className="w-2/5">{item.phone}</span>
 | 
	
		
			
				|  |  | -                <span className="w-1/5">{item.position}</span>
 | 
	
		
			
				|  |  | -                <span className="w-1/5">{item.category}</span>
 | 
	
		
			
				|  |  | -              </div>
 | 
	
		
			
				|  |  | -              <div className="w-1/4 flex justify-end">
 | 
	
		
			
				|  |  | -                <span className="btn-outline" onClick={() => itemSelectHandler(item.id)}>
 | 
	
		
			
				|  |  | -                  选择ta
 | 
	
		
			
				|  |  | -                </span>
 | 
	
		
			
				|  |  | +        <div
 | 
	
		
			
				|  |  | +          ref={scrollRef}
 | 
	
		
			
				|  |  | +          onScroll={handleListScroll}
 | 
	
		
			
				|  |  | +          className="h-60vh overflow-y-auto overflow-x-hidden">
 | 
	
		
			
				|  |  | +          <Spin spinning={state.laoding}>
 | 
	
		
			
				|  |  | +            {state.list.map(item => (
 | 
	
		
			
				|  |  | +              <div className="group-item-card" key={item.id}>
 | 
	
		
			
				|  |  | +                <div className="w-4/3 flex justify-between">
 | 
	
		
			
				|  |  | +                  <span className="w-1/5">{item.username}</span>
 | 
	
		
			
				|  |  | +                  <span className="w-2/5">{item.phone}</span>
 | 
	
		
			
				|  |  | +                  <span className="w-1/5">{item.position}</span>
 | 
	
		
			
				|  |  | +                  <span className="w-1/5">{item.category}</span>
 | 
	
		
			
				|  |  | +                </div>
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +                <div className="w-1/4 flex justify-end">
 | 
	
		
			
				|  |  | +                  {item.disable ? (
 | 
	
		
			
				|  |  | +                    <span className="text-xs">已选择</span>
 | 
	
		
			
				|  |  | +                  ) : (
 | 
	
		
			
				|  |  | +                    <span className="btn-outline" onClick={() => itemSelectHandler(item.id)}>
 | 
	
		
			
				|  |  | +                      选择ta
 | 
	
		
			
				|  |  | +                    </span>
 | 
	
		
			
				|  |  | +                  )}
 | 
	
		
			
				|  |  | +                </div>
 | 
	
		
			
				|  |  |                </div>
 | 
	
		
			
				|  |  | -            </div>
 | 
	
		
			
				|  |  | -          ))}
 | 
	
		
			
				|  |  | -          {noMore && <div className="text-center text-gray-400">已到底部</div>}
 | 
	
		
			
				|  |  | +            ))}
 | 
	
		
			
				|  |  | +          </Spin>
 | 
	
		
			
				|  |  | +          {state.noMore && <div className="text-center text-gray-400 mt-2">已到底部</div>}
 | 
	
		
			
				|  |  | +          {/* {noMore && <div className="text-center text-gray-400">已到底部</div>}
 | 
	
		
			
				|  |  |            {!noMore && (
 | 
	
		
			
				|  |  |              <div className="flex justify-center mt-3 cursor-pointer">
 | 
	
		
			
				|  |  |                {loadingMore ? (
 | 
	
	
		
			
				|  | @@ -145,7 +193,7 @@ const ConnectModal: React.FC<ConnectModalProps> = ({
 | 
	
		
			
				|  |  |                  <MoreOutlined rotate={90} style={{ fontSize: 24 }} onClick={loadMore} />
 | 
	
		
			
				|  |  |                )}
 | 
	
		
			
				|  |  |              </div>
 | 
	
		
			
				|  |  | -          )}
 | 
	
		
			
				|  |  | +          )} */}
 | 
	
		
			
				|  |  |          </div>
 | 
	
		
			
				|  |  |        </Modal>
 | 
	
		
			
				|  |  |      </>
 |