|  | @@ -0,0 +1,145 @@
 | 
	
		
			
				|  |  | +import { useRef, useState, useMemo, useContext } from 'react'
 | 
	
		
			
				|  |  | +import { Button, Drawer, Radio, Select, Spin, Tag } from 'antd'
 | 
	
		
			
				|  |  | +import { Actions, FlowContext } from '../../context'
 | 
	
		
			
				|  |  | +import debounce from 'lodash/debounce'
 | 
	
		
			
				|  |  | +import type { RadioChangeEvent } from 'antd'
 | 
	
		
			
				|  |  | +import { queryAcountList } from '@/services/api/institution'
 | 
	
		
			
				|  |  | +import consts from '@/utils/consts'
 | 
	
		
			
				|  |  | +const debounceTimeout = 800
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +export enum ApprovalType {
 | 
	
		
			
				|  |  | +  TARGET = '1', // 指定成员
 | 
	
		
			
				|  |  | +  SUPERIOR = '2', // 上级
 | 
	
		
			
				|  |  | +  ROLE = '3', // 角色
 | 
	
		
			
				|  |  | +  INITIATOR = '4', // 发起人自选
 | 
	
		
			
				|  |  | +  MULTISTAGE = '5' // 连续多级上级
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +const FlowDrawer = () => {
 | 
	
		
			
				|  |  | +  const { flowState, dispatch } = useContext(FlowContext)
 | 
	
		
			
				|  |  | +  const { drawerConfig } = flowState
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  const fetchRef = useRef(0)
 | 
	
		
			
				|  |  | +  const [state, setState] = useState({
 | 
	
		
			
				|  |  | +    approvalType: ApprovalType.TARGET,
 | 
	
		
			
				|  |  | +    fetching: false,
 | 
	
		
			
				|  |  | +    options: [],
 | 
	
		
			
				|  |  | +    staffOptions: []
 | 
	
		
			
				|  |  | +  })
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  const handleCancel = () => dispatch({ type: Actions.CLOSE_MODAL })
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  const plainOptions = [
 | 
	
		
			
				|  |  | +    { label: '指定成员', value: ApprovalType.TARGET },
 | 
	
		
			
				|  |  | +    { label: '上级', value: ApprovalType.SUPERIOR },
 | 
	
		
			
				|  |  | +    { label: '角色', value: ApprovalType.ROLE },
 | 
	
		
			
				|  |  | +    { label: '发起人自选', value: ApprovalType.INITIATOR },
 | 
	
		
			
				|  |  | +    { label: '连续多级上级', value: ApprovalType.MULTISTAGE }
 | 
	
		
			
				|  |  | +  ]
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  const approvalTypeChange = (e: RadioChangeEvent) => {
 | 
	
		
			
				|  |  | +    const { target } = e
 | 
	
		
			
				|  |  | +    if (target?.value) {
 | 
	
		
			
				|  |  | +      setState({ ...state, approvalType: target.value })
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  const fetchOptions = async params => {
 | 
	
		
			
				|  |  | +    const { code = -1, data = {} } = await queryAcountList({
 | 
	
		
			
				|  |  | +      ...params,
 | 
	
		
			
				|  |  | +      current: 1,
 | 
	
		
			
				|  |  | +      pageSize: 100,
 | 
	
		
			
				|  |  | +      dataID: 'e01b8695-c43b-4d2d-8169-a021aa6b69ae'
 | 
	
		
			
				|  |  | +    })
 | 
	
		
			
				|  |  | +    if (code === consts.RET_CODE.SUCCESS) {
 | 
	
		
			
				|  |  | +      return data.items.map(item => ({ label: item.name, value: item.ID }))
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    return []
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  const debounceFetcher = useMemo(() => {
 | 
	
		
			
				|  |  | +    const loadOptions = params => {
 | 
	
		
			
				|  |  | +      fetchRef.current += 1
 | 
	
		
			
				|  |  | +      const fetchId = fetchRef.current
 | 
	
		
			
				|  |  | +      setState({ ...state, options: [], fetching: true })
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +      fetchOptions(params).then(newOptions => {
 | 
	
		
			
				|  |  | +        if (fetchId !== fetchRef.current) {
 | 
	
		
			
				|  |  | +          // for fetch callback order
 | 
	
		
			
				|  |  | +          return
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        setState({ ...state, options: newOptions, fetching: false })
 | 
	
		
			
				|  |  | +      })
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    return debounce(loadOptions, debounceTimeout)
 | 
	
		
			
				|  |  | +  }, [fetchOptions, debounceTimeout])
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  const staffIds = state.staffOptions.map(i => i.value)
 | 
	
		
			
				|  |  | +  const triggerChange = (val, option) => {
 | 
	
		
			
				|  |  | +    if (!staffIds.includes(val)) {
 | 
	
		
			
				|  |  | +      setState({ ...state, staffOptions: [...state.staffOptions, option] })
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  const handleOnOk = () => {
 | 
	
		
			
				|  |  | +    const payload = {
 | 
	
		
			
				|  |  | +      id: drawerConfig.nodeId,
 | 
	
		
			
				|  |  | +      node: {
 | 
	
		
			
				|  |  | +        approvalType: state.approvalType,
 | 
	
		
			
				|  |  | +        staffOptions: state.staffOptions
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    dispatch({
 | 
	
		
			
				|  |  | +      type: Actions.SET_FLOW_NODE,
 | 
	
		
			
				|  |  | +      payload
 | 
	
		
			
				|  |  | +    })
 | 
	
		
			
				|  |  | +    handleCancel()
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  return (
 | 
	
		
			
				|  |  | +    <Drawer
 | 
	
		
			
				|  |  | +      visible={drawerConfig.visible}
 | 
	
		
			
				|  |  | +      onClose={handleCancel}
 | 
	
		
			
				|  |  | +      footer={
 | 
	
		
			
				|  |  | +        <div className="flex justify-end">
 | 
	
		
			
				|  |  | +          <Button onClick={handleCancel}>取消</Button>
 | 
	
		
			
				|  |  | +          <Button className="ml-8px" type="primary" onClick={handleOnOk}>
 | 
	
		
			
				|  |  | +            确认
 | 
	
		
			
				|  |  | +          </Button>
 | 
	
		
			
				|  |  | +        </div>
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +      title={<span className="font-medium">设置审批人</span>}
 | 
	
		
			
				|  |  | +      width="30%">
 | 
	
		
			
				|  |  | +      <div>
 | 
	
		
			
				|  |  | +        <Radio.Group
 | 
	
		
			
				|  |  | +          options={plainOptions}
 | 
	
		
			
				|  |  | +          onChange={approvalTypeChange}
 | 
	
		
			
				|  |  | +          value={state.approvalType}
 | 
	
		
			
				|  |  | +        />
 | 
	
		
			
				|  |  | +        <div className="mt-40px flex flex-col">
 | 
	
		
			
				|  |  | +          <div className="text-sm mb-4">
 | 
	
		
			
				|  |  | +            <span className="font-medium mr-1 text-14px">添加员工</span>
 | 
	
		
			
				|  |  | +            <span className="text-hex-000000 text-opacity-45">不得超过20人</span>
 | 
	
		
			
				|  |  | +          </div>
 | 
	
		
			
				|  |  | +          <Select
 | 
	
		
			
				|  |  | +            showSearch
 | 
	
		
			
				|  |  | +            value={null}
 | 
	
		
			
				|  |  | +            onChange={triggerChange}
 | 
	
		
			
				|  |  | +            filterOption={false}
 | 
	
		
			
				|  |  | +            onSearch={v => debounceFetcher({ search: v })}
 | 
	
		
			
				|  |  | +            notFoundContent={state.fetching ? <Spin size="small" /> : null}
 | 
	
		
			
				|  |  | +            options={state.options?.filter(item => !staffIds.includes(item.value))}
 | 
	
		
			
				|  |  | +          />
 | 
	
		
			
				|  |  | +          <div className="mt-4">
 | 
	
		
			
				|  |  | +            {state.staffOptions.map(item => (
 | 
	
		
			
				|  |  | +              <Tag key={item.value}>{item.label}</Tag>
 | 
	
		
			
				|  |  | +            ))}
 | 
	
		
			
				|  |  | +          </div>
 | 
	
		
			
				|  |  | +        </div>
 | 
	
		
			
				|  |  | +      </div>
 | 
	
		
			
				|  |  | +    </Drawer>
 | 
	
		
			
				|  |  | +  )
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +export default FlowDrawer
 |