import React, {
useEffect,
useCallback,
useState,
useRef,
Fragment
} from 'react';
import {Form, Modal, Transfer, Checkbox, Empty, Button, message } from 'antd';
import { getSysUserByPage } from '@/services';
import empty from '@/Images/empty.png';
import './index.less';
const lodash = require('lodash');
const { isEmpty } = lodash;
// 组件接收数据
interface PropsType {
// 选择用户的弹窗标题
title: string;
// 取消事件
onCancel: () => void;
// 最大用户,如果不限制最大用户,可以不传该字段
maxCount?: number;
// 点击确定,返回选择的用户信息
onConfirm: (userIdArr: Array<UserType>) => void;
}
const SelectUser = (props: PropsType) => {
const { title = intl.formatMessage({ id: 'Select Person' }), onCancel, maxCount, onConfirm } = props;
const [formInstance] = Form.useForm();
// 查询的分页信息
const [pageInfo, setPageInfo] = useState({ pageNum: 1, pageSize: 100 });
// 模糊搜索关键字
const [searchKey, setSearchKey] = useState('');
// 是否loading
const [isLoading, setIsLoading] = useState(false);
// 用户列表
const [userList, setUserList] = useState<Array<UserType>>([]);
// 当前选中的用户
const [targetKeys, setTargetKeys] = useState<string[]>([]);
const [selectedKeys, setSelectedKeys] = useState<any>([]);
const userRef = useRef();
/**
* 分页获取用户列表I
* pageNum: 页码
* pageSize: 每页条数
* key: 模糊搜索关键字
*/
const getUsers = useCallback(
async (pageNum: number, pageSize: number, key: string) => {
setIsLoading(true);
const res = await getSysUserByPage({
pageNum,
pageSize,
syncStatus: true,
nameSearchKey: key || undefined
});
const { code, data, msg } = res || {};
if (code === 0) {
const { current, size, records } = data || {};
if (Array.isArray(records)) {
if (records.length > 0) {
setPageInfo({ pageNum: current, pageSize: size });
const newRecord = records.map((item: UserType) => ({
...item,
userId: `${item.userId}`
}));
if (pageNum === 1) {
// const tempSelectedKeys = selectedKeys.filter(key => newRecord.some(item => item.userId === key));
// setTargetKeys(selectedKeys);
setUserList(newRecord);
// setSelectedKeys(selectedKeys);
} else {
// const tempSelectedKeys = selectedKeys.filter(key => [...userList, ...newRecord].some(item => item.userId === key));
// setTargetKeys(selectedKeys);
setUserList([...userList, ...newRecord]);
// setSelectedKeys(selectedKeys);
}
} else if (records.length === 0) {
if (pageNum > 1) {
getUsers(pageNum - 1, pageSize, key);
} else {
setPageInfo({ pageNum: 1, pageSize: size });
// setTargetKeys(selectedKeys);
setUserList([]);
// setTargetKeys([]);
// setSelectedKeys(selectedKeys);
}
}
}
} else {
message.error(msg);
}
setIsLoading(false);
},
[userList]
);
useEffect(() => {
getUsers(1, pageInfo.pageSize, searchKey);
}, []);
/**
* 滑动自动加载下一页
*/
const _onScroll = () => {
if (userRef.current) {
const scrollTop = userRef.current.scrollTop;
const clientHeight = userRef.current.clientHeight;
const scrollHeight = userRef.current.scrollHeight;
if (scrollHeight - clientHeight - scrollTop === 0 && !isLoading) {
getUsers(pageInfo.pageNum + 1, pageInfo.pageSize, searchKey);
}
}
};
/**
* 点击确定
*/
const buttonClick = () => {
formInstance.validateFields().then((res) => {
const { userIdArr } = res;
const result: Array<UserType> = userIdArr.map((id: string) => {
return userList.find((item: UserType) => id === item.userId);
});
onConfirm &&
onConfirm(
result.map((item: UserType) => ({
...item,
userId: Number(item.userId)
}))
);
});
};
/**
* 点击右侧清空,清空选择项,重置表单
*/
const _handleClear = () => {
setTargetKeys([]);
formInstance.resetFields();
};
/**
* 点击穿梭框中间的按钮
* @param keys 选择用户的userId
* @returns
*/
const _handleTransferChange = (keys: Array<string>) => {
if (maxCount && keys.length > maxCount) {
message.warn(intl.formatMessage({ id: 'Up to x persons can be selected. Please check and try again' }, { count: maxCount }));
return;
}
console.log('keys', keys);
setTargetKeys(keys);
setSelectedKeys(keys);
};
/**
* 点击左侧,模糊搜索按钮
* @param value 模糊搜索关键字
*/
const _handleSearch = (value: string) => {
// _handleClear();
setSearchKey(value);
getUsers(1, pageInfo.pageSize, value);
};
return (
<Modal
visible
centered
canDragm
title={title}
footer={[
<Button
key="1"
btnType="primary"
style={{ marginRight: '10px' }}
onClick={buttonClick}
>
{intl.formatMessage({ id: 'OK' })}
</Button>,
<Button key="2" onClick={onCancel}>
{intl.formatMessage({ id: 'Cancel' })}
</Button>
]}
onCancel={onCancel}
destroyOnClose
className="component_selectUser_modal"
>
<Loading loading={isLoading}>
<Form form={formInstance}>
<Form.Item
className="component_selectUser_formItem"
label={intl.formatMessage({ id: 'Select Person' })}
name="userIdArr"
rules={[{ required: true, message: `${title}` }]}
>
<Transfer
oneWay
selectAllLabels={['', intl.formatMessage({ id: 'Selected' })]}
showSelectAll={false}
titles={[
'',
<Icon icon="deleteIcon" onClick={_handleClear} />
]}
rowKey={(item: UserType) => item.userId}
dataSource={userList}
targetKeys={targetKeys}
// selectedKeys={targetKeys}
render={(item: UserType) => `${item.name}(${item.username})`}
onChange={_handleTransferChange}
>
{({ direction, onItemSelect, selectedKeys }) => {
if (direction === 'left') {
const checkedKeys: Array<string> = [
...selectedKeys,
...targetKeys
];
return (
<Fragment>
<input className="component_selectUser_hiddenInput" />
<Input
className="component_selectUser_input"
allowClear
placeholder={intl.formatMessage({ id: 'Please enter search criteria' })}
onSearch={_handleSearch}
onEnterPress={_handleSearch}
onChange={(value: string) => setSearchKey(value)}
value={searchKey}
/>
<Divider className="component_selectUser_divider" />
{isEmpty(userList) ? (
<Empty
className="component_selectUser_empty"
image={empty}
description={intl.formatMessage({ id: 'No data' })}
/>
) : (
<div
ref={userRef}
onScrollCapture={_onScroll}
className="component_selectUser_container"
>
<Checkbox.Group
value={checkedKeys}
className="component_selectUser_checkboxGroup"
>
{userList.map((item: UserType) => {
const { userId, name, username } = item;
const title = `${name || intl.formatMessage({ id: 'N/A' })}(${username || intl.formatMessage({ id: 'N/A' })})`;
return (
<Checkbox
key={userId}
value={userId}
onChange={(e) => {
const { checked, value } = e.target;
if (
(maxCount &&
checkedKeys.length < maxCount) ||
checkedKeys.includes(value) ||
!maxCount
) {
onItemSelect(value, checked);
} else if (maxCount) {
message.warn(intl.formatMessage({ id: 'Up to x persons can be selected. Please check and try again' }, { count: maxCount }));
}
}}
className="component_selectUser_checkbox"
>
<span title={title}>{title}</span>
</Checkbox>
);
})}
</Checkbox.Group>
</div>
)}
</Fragment>
);
}
}}
</Transfer>
</Form.Item>
</Form>
</Loading>
</Modal>
);
};
export default SelectUser;
样式
.component_selectUser_modal {
width: 700px !important;
.component_selectUser_formItem {
flex-wrap: nowrap;
>.ant4-form-item-control {
width: calc(100% - 107px);
}
.component_selectUser_hiddenInput {
display: none;
}
.component_selectUser_input {
width: 100%;
}
.component_selectUser_divider {
margin: 10px 0;
}
.component_selectUser_empty {
margin-top: 144px;
text-align: center;
}
.ant-transfer {
.component_selectUser_container {
width: 100%;
height: calc(100% - 51px);
overflow: auto;
.component_selectUser_checkboxGroup {
width: 100%;
.component_selectUser_checkbox {
width: 100%;
height: 30px;
display: flex;
align-items: center;
&:hover {
background-color: #edf8ff;
}
>span {
&:first-child {
top: 0;
}
&:last-child {
display: inline-block;
width: calc(100% - 32px);
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
}
}
}
}
}
.ant-transfer-list {
width: calc(50% - 33px);
.ant-transfer-list-header {
background-color: #f2f3f5;
}
.ant-transfer-list-body {
height: 440px;
padding: 10px;
.ant-transfer-list-body-customize-wrapper {
height: 100%;
}
.ant-empty-image {
margin-bottom: 6px;
}
.ant-empty-description {
color: rgba(0, 0, 0, 0.7);
}
.ant-checkbox-wrapper {
margin-left: 0;
}
.ant-transfer-list-content-item {
&:hover {
background-color: #edf8ff;
.ant-transfer-list-content-item-remove {
visibility: visible;
}
}
.ant-transfer-list-content-item-remove {
visibility: hidden;
.anticon-delete {
svg {
color: #999;
border-radius: 50%;
path {
d: path('M513.43007 1019.262092c-280.20375 0-507.388982-227.207745-507.388982-507.410472 0-280.224216 227.185232-507.409448 507.388982-507.409448 280.247752 0 507.391029 227.185232 507.391029 507.409448C1020.821099 792.054347 793.678846 1019.262092 513.43007 1019.262092zM746.107387 363.903034c9.540284-9.53926 9.540284-25.021883 0-34.539654l-51.822272-51.800783c-9.535167-9.558703-24.977881-9.558703-34.518165 0L512.976746 424.334381 366.184495 277.562597c-9.53619-9.558703-24.977881-9.558703-34.518165 0l-51.822272 51.800783c-9.538237 9.517771-9.538237 25.001417 0 34.539654l146.793274 146.770761-146.793274 146.790204c-9.538237 9.518794-9.538237 25.004487 0 34.540677l51.822272 51.79976c9.540284 9.538237 24.981974 9.538237 34.518165 0L512.976746 597.014232l146.790204 146.790204c9.540284 9.538237 24.982998 9.538237 34.518165 0l51.822272-51.79976c9.540284-9.53619 9.540284-25.021883 0-34.540677L599.317183 510.674818 746.107387 363.903034z'
);
}
}
}
}
}
}
&:first-child {
.ant-transfer-list-header {
display: none;
}
}
&:last-child {
.ant-transfer-list-body-not-found {
display: none;
}
}
}
.ant-transfer-operation {
width: 50px;
height: 50px;
button {
margin-bottom: 0;
width: 100%;
height: 100%;
border-radius: 50%;
color: #0183cc;
background-color: #fff;
border-color: #2993ce;
.anticon-right {
svg {
font-size: 22px;
path {
d: path('M885.113 489.373L628.338 232.599c-12.496-12.497-32.758-12.497-45.254 0-12.497 12.497-12.497 32.758 0 45.255l203.3 203.3H158.025c-17.036 0-30.846 13.811-30.846 30.846 0 17.036 13.811 30.846 30.846 30.846h628.36L583.084 746.147c-12.497 12.496-12.497 32.758 0 45.255 6.248 6.248 14.438 9.372 22.627 9.372s16.379-3.124 22.627-9.372l256.775-256.775a31.999 31.999 0 0 0 0-45.254z'
);
}
}
}
&:hover {
color: #2993ce !important;
background-color: #eef9ff !important;
border-color: #1394dd !important;
}
&:active {
color: #287bab !important;
background-color: #f7faff !important;
border-color: #287bab !important;
}
&[disabled] {
color: rgba(0, 0, 0, 0.25) !important;
border-color: #d9d9d9 !important;
background-color: #f5f5f5 !important;
}
}
}
}
}
}