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.

213 lines
8.2 KiB

1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
  1. import { Button, Input, Modal, ModalProps, Select, SelectProps, Spin, Tag } from 'antd'
  2. import { memo, ReactNode, useEffect, useRef, useState } from 'react'
  3. import { Flexbox } from 'react-layout-kit'
  4. import { useStyle } from './style.ts'
  5. import { List, ListViewItem } from './List.tsx'
  6. import { useTranslation } from '@/i18n.ts'
  7. import DepartmentTree from '@/components/department-tree/DepartmentTree.tsx'
  8. import { DraggablePanel } from '@/components/draggable-panel'
  9. import { useAtom, useAtomValue } from 'jotai'
  10. import { userListAtom, userSearchAtom, } from '@/store/system/user.ts'
  11. import { IUser } from '@/types'
  12. import EmptyWrap from '@/components/empty/EmptyWrap.tsx'
  13. export interface UserSelectProps extends SelectProps {
  14. value?: any[]
  15. onChange?: (value: any[]) => void
  16. //多选
  17. multiple?: boolean,
  18. renderValue?: (value: any[], def: ReactNode) => ReactNode
  19. }
  20. export interface UserModelProps extends Pick<ModalProps, 'open'> {
  21. value?: any[]
  22. onChange?: (value?: any[]) => void
  23. children?: ReactNode
  24. //多选
  25. multiple?: boolean
  26. renderValue?: (value: any[], def: ReactNode) => ReactNode
  27. }
  28. export type UserPickerProps =
  29. | {
  30. type?: 'modal';
  31. /** Props for the modal component */
  32. } & UserModelProps
  33. | {
  34. type: 'select';
  35. /** Props for the select component */
  36. } & UserSelectProps
  37. const UserSelect = memo((props: UserSelectProps) => {
  38. return (
  39. <Select {...props}>
  40. </Select>
  41. )
  42. })
  43. const UserModel = memo(({ multiple, children, value, onChange, ...props }: UserModelProps) => {
  44. const { styles, cx } = useStyle()
  45. const { t } = useTranslation()
  46. const [ innerValue, setValue ] = useState(() => {
  47. if (!multiple && !Array.isArray(value)) {
  48. return [ value ]
  49. }
  50. })
  51. const [ , setSearch ] = useAtom(userSearchAtom)
  52. const { data: users, isPending } = useAtomValue(userListAtom)
  53. const [ open, setOpen ] = useState(false)
  54. const selectUserRef = useRef<IUser[]>([])
  55. const [ , update ] = useState({})
  56. useEffect(() => {
  57. if (value === undefined) return
  58. setValue(Array.isArray(value) ? value : [ value ])
  59. }, [ value ])
  60. useEffect(() => {
  61. selectUserRef.current = []
  62. innerValue?.forEach(id => {
  63. const item = users?.rows?.find(user => user.id === id)
  64. if (item) {
  65. selectUserRef.current = [ ...selectUserRef.current, item ]
  66. }
  67. })
  68. update({})
  69. }, [ innerValue ])
  70. useEffect(() => {
  71. return () => {
  72. selectUserRef.current = []
  73. }
  74. }, [])
  75. const renderTarget = () => {
  76. return <span onClick={() => setOpen(true)}>
  77. {children ?? <Button type={'primary'} >{t('component.UserPicker.targetText', '选择成员')}</Button>}
  78. </span>
  79. }
  80. const renderValue = () => {
  81. const dom = selectUserRef.current?.map(user => {
  82. return <Tag key={user.id} color={'blue'} closable onClose={() => {
  83. const newVal = innerValue?.filter(val => val !== user.id)
  84. setValue(newVal)
  85. onChange?.(newVal)
  86. }}>{user.name}</Tag>
  87. })
  88. if (props.renderValue) {
  89. return props.renderValue(selectUserRef.current, dom)
  90. }
  91. return dom
  92. }
  93. return (
  94. <>
  95. <div>
  96. {renderTarget()}
  97. </div>
  98. <div className={styles.values}>
  99. {renderValue()}
  100. </div>
  101. <Modal
  102. className={styles.modal}
  103. title={t('component.UserPicker.title', '成员选择')}
  104. width={800}
  105. {...props}
  106. open={open}
  107. onOk={() => {
  108. onChange?.(multiple ? innerValue : innerValue?.[0])
  109. setOpen(false)
  110. }}
  111. onCancel={() => {
  112. setOpen(false)
  113. }}
  114. >
  115. <Flexbox horizontal={true} className={styles.container} gap={8} height={400}>
  116. <Flexbox flex={5} gap={10}>
  117. <Input.Search
  118. allowClear={true}
  119. onSearch={value => {
  120. setSearch({
  121. key: value,
  122. })
  123. }}
  124. placeholder={t('component.UserPicker.placeholder', '输入成员姓名,回车查询')}/>
  125. <Flexbox flex={1} horizontal={true} className={styles.bordered}>
  126. <Flexbox flex={1} padding={4}>
  127. <DepartmentTree
  128. root={true}
  129. fieldNames={{
  130. title: 'name',
  131. key: 'id'
  132. }}
  133. autoExpandParent={true}
  134. onSelect={(keys) => {
  135. setSearch({
  136. dept_id: keys?.[0]
  137. })
  138. }}
  139. />
  140. </Flexbox>
  141. <Flexbox flex={1} className={styles.usersPanel}>
  142. <Spin spinning={isPending} delay={200}>
  143. <EmptyWrap isEmpty={!users?.rows || users?.rows.length === 0}>
  144. <List rowKey={'id'}
  145. multiple={multiple}
  146. value={innerValue}
  147. onChange={(val) => {
  148. setValue(val)
  149. }}
  150. dataSource={users?.rows ?? []}
  151. />
  152. </EmptyWrap>
  153. </Spin>
  154. </Flexbox>
  155. </Flexbox>
  156. </Flexbox>
  157. <DraggablePanel expandable={false}
  158. placement="right"
  159. maxWidth={300}
  160. minWidth={280}
  161. className={cx(styles.bordered, styles.draggablePanel)}
  162. >
  163. <Flexbox flex={6 / 2}>
  164. <div className={styles.selected}>{t('component.UserPicker.selected', '已选({{count}})', { count: selectUserRef.current?.length ?? 0 })}</div>
  165. <div>
  166. {
  167. selectUserRef.current?.map(user => {
  168. return (<ListViewItem
  169. onDel={user => {
  170. setValue(innerValue?.filter(id => id !== user.id))
  171. }}
  172. key={user.id}
  173. user={user!}/>)
  174. })
  175. }
  176. </div>
  177. </Flexbox>
  178. </DraggablePanel>
  179. </Flexbox>
  180. </Modal>
  181. </>
  182. )
  183. })
  184. const UserPicker = memo(({ type = 'modal', ...props }: UserPickerProps) => {
  185. return type === 'modal' ? <UserModel {...props as UserModelProps} /> : <UserSelect {...props as UserSelectProps} />
  186. })
  187. export default UserPicker