You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 

214 lines
8.2 KiB

import { Button, Input, Modal, ModalProps, Select, SelectProps, Spin, Tag } from 'antd'
import { memo, ReactNode, useEffect, useRef, useState } from 'react'
import { Flexbox } from 'react-layout-kit'
import { useStyle } from './style.ts'
import { List, ListViewItem } from './List.tsx'
import { useTranslation } from '@/i18n.ts'
import DepartmentTree from '@/components/department-tree/DepartmentTree.tsx'
import { DraggablePanel } from '@/components/draggable-panel'
import { useAtom, useAtomValue } from 'jotai'
import { userListAtom, userSearchAtom, } from '@/store/user.ts'
import { IUser } from '@/types'
import EmptyWrap from '@/components/empty/EmptyWrap.tsx'
export interface UserSelectProps extends SelectProps {
value?: any[]
onChange?: (value: any[]) => void
//多选
multiple?: boolean,
renderValue?: (value: any[], def: ReactNode) => ReactNode
}
export interface UserModelProps extends Pick<ModalProps, 'open'> {
value?: any[]
onChange?: (value?: any[]) => void
children?: ReactNode
//多选
multiple?: boolean
renderValue?: (value: any[], def: ReactNode) => ReactNode
}
export type UserPickerProps =
| {
type?: 'modal';
/** Props for the modal component */
} & UserModelProps
| {
type: 'select';
/** Props for the select component */
} & UserSelectProps
const UserSelect = memo((props: UserSelectProps) => {
return (
<Select {...props}>
</Select>
)
})
const UserModel = memo(({ multiple, children, value, onChange, ...props }: UserModelProps) => {
const { styles, cx } = useStyle()
const { t } = useTranslation()
const [ innerValue, setValue ] = useState(() => {
if (!multiple && !Array.isArray(value)) {
return [ value ]
}
})
const [ , setSearch ] = useAtom(userSearchAtom)
const { data: users, isPending } = useAtomValue(userListAtom)
const [ open, setOpen ] = useState(false)
const selectUserRef = useRef<IUser[]>([])
const [ , update ] = useState({})
useEffect(() => {
if (value === undefined) return
setValue(Array.isArray(value) ? value : [ value ])
}, [ value ])
useEffect(() => {
selectUserRef.current = []
innerValue?.forEach(id => {
const item = users?.rows?.find(user => user.id === id)
if (item) {
selectUserRef.current = [ ...selectUserRef.current, item ]
}
})
update({})
}, [ innerValue ])
useEffect(() => {
return () => {
selectUserRef.current = []
}
}, [])
const renderTarget = () => {
return <span onClick={() => setOpen(true)}>
{children ?? <Button type={'primary'} >{t('component.UserPicker.targetText', '选择成员')}</Button>}
</span>
}
const renderValue = () => {
const dom = selectUserRef.current?.map(user => {
return <Tag key={user.id} color={'blue'} closable onClose={() => {
const newVal = innerValue?.filter(val => val !== user.id)
setValue(newVal)
onChange?.(newVal)
}}>{user.name}</Tag>
})
if (props.renderValue) {
return props.renderValue(selectUserRef.current, dom)
}
return dom
}
return (
<>
<div>
{renderTarget()}
</div>
<div className={styles.values}>
{renderValue()}
</div>
<Modal
className={styles.modal}
title={t('component.UserPicker.title', '成员选择')}
width={800}
{...props}
open={open}
onOk={() => {
onChange?.(multiple ? innerValue : innerValue?.[0])
setOpen(false)
}}
onCancel={() => {
setOpen(false)
}}
>
<Flexbox horizontal={true} className={styles.container} gap={8} height={400}>
<Flexbox flex={5} gap={10}>
<Input.Search
allowClear={true}
onSearch={value => {
setSearch({
key: value,
})
}}
placeholder={t('component.UserPicker.placeholder', '输入成员姓名,回车查询')}/>
<Flexbox flex={1} horizontal={true} className={styles.bordered}>
<Flexbox flex={1} padding={4}>
<DepartmentTree
root={true}
fieldNames={{
title: 'name',
key: 'id'
}}
autoExpandParent={true}
onSelect={(keys) => {
setSearch({
dept_id: keys?.[0]
})
}}
/>
</Flexbox>
<Flexbox flex={1} className={styles.usersPanel}>
<Spin spinning={isPending} delay={200}>
<EmptyWrap isEmpty={!users?.rows || users?.rows.length === 0}>
<List rowKey={'id'}
multiple={multiple}
value={innerValue}
onChange={(val) => {
setValue(val)
}}
dataSource={users?.rows ?? []}
/>
</EmptyWrap>
</Spin>
</Flexbox>
</Flexbox>
</Flexbox>
<DraggablePanel expandable={false}
placement="right"
maxWidth={300}
minWidth={280}
className={cx(styles.bordered, styles.draggablePanel)}
>
<Flexbox flex={6 / 2}>
<div className={styles.selected}>{t('component.UserPicker.selected', '已选({{count}})', { count: selectUserRef.current?.length ?? 0 })}</div>
<div>
{
selectUserRef.current?.map(user => {
return (<ListViewItem
onDel={user => {
setValue(innerValue?.filter(id => id !== user.id))
}}
key={user.id}
user={user!}/>)
})
}
</div>
</Flexbox>
</DraggablePanel>
</Flexbox>
</Modal>
</>
)
})
const UserPicker = memo(({ type = 'modal', ...props }: UserPickerProps) => {
return type === 'modal' ? <UserModel {...props as UserModelProps} /> : <UserSelect {...props as UserSelectProps} />
})
export default UserPicker