|
|
@@ -1,12 +1,14 @@
|
|
|
+/* eslint-disable no-underscore-dangle */
|
|
|
/* eslint-disable consistent-return */
|
|
|
import { Button, Transfer, Tree } from 'antd'
|
|
|
-import React, { useState, useEffect } from 'react'
|
|
|
+import React, { useMemo, useState, useEffect } from 'react'
|
|
|
import styles from './ConnectCompany/index.less'
|
|
|
-import { CloseOutlined } from '@ant-design/icons'
|
|
|
+import { CloseOutlined, DeleteOutlined } from '@ant-design/icons'
|
|
|
import consts from '@/basic/consts'
|
|
|
import { useDispatch } from 'umi'
|
|
|
import { queryDepartmentStaffList } from '@/services/department'
|
|
|
import { useWindowSizeFn } from '@/hooks/event/useWindomSizeFn'
|
|
|
+import { isEmpty, isNull, isNullOrUnDef } from '@/utils/is'
|
|
|
|
|
|
const isChecked = (selectedKeys, eventKey) => selectedKeys.includes(eventKey)
|
|
|
|
|
|
@@ -18,6 +20,7 @@ const itemIsChecked = onItemSelectAll => (selectedKeys, checkedKeys, checked, no
|
|
|
.filter(({ isDepartment }) => !isDepartment)
|
|
|
.filter(({ key }) => !isChecked(checkedKeys, key))
|
|
|
.map(({ key }) => key)
|
|
|
+
|
|
|
onItemSelectAll(keys, checked)
|
|
|
}
|
|
|
}
|
|
|
@@ -40,8 +43,10 @@ const generateTree = (treeNodes = [], checkedKeys = []) =>
|
|
|
const CustomPerm = () => {
|
|
|
const [state, setState] = useState({
|
|
|
dataSource: [],
|
|
|
+ leftTargetId: null,
|
|
|
treeKeys: [],
|
|
|
targetKeys: [],
|
|
|
+ rightTargetId: null,
|
|
|
transferDataSource: [],
|
|
|
height: 0
|
|
|
})
|
|
|
@@ -91,7 +96,20 @@ const CustomPerm = () => {
|
|
|
}
|
|
|
|
|
|
const handleOnSearch = (direction, value) => {
|
|
|
- console.log(direction, value)
|
|
|
+ const targetNode = state.transferDataSource.find(item => item.title.indexOf(value) > -1)
|
|
|
+ if (direction === 'left') {
|
|
|
+ if (!isEmpty(value) && !isNullOrUnDef(targetNode)) {
|
|
|
+ setState({ ...state, leftTargetId: targetNode.key })
|
|
|
+ return
|
|
|
+ }
|
|
|
+ isEmpty(value) && setState({ ...state, leftTargetId: null })
|
|
|
+ } else {
|
|
|
+ if (!isEmpty(value) && !isNullOrUnDef(targetNode)) {
|
|
|
+ setState({ ...state, rightTargetId: targetNode.key })
|
|
|
+ return
|
|
|
+ }
|
|
|
+ isEmpty(value) && setState({ ...state, rightTargetId: null })
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
const onChange = (keys, direction, moveKeys) => {
|
|
|
@@ -114,9 +132,14 @@ const CustomPerm = () => {
|
|
|
} else {
|
|
|
setState({ ...state, targetKeys: [...state.targetKeys, ...keys] })
|
|
|
}
|
|
|
+ if (direction === 'right') {
|
|
|
+ const newTargetKeys = moveKeys.filter(item => !state.targetKeys.includes(item))
|
|
|
+ setState({ ...state, targetKeys: [...state.targetKeys, ...newTargetKeys] })
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- const handleUnCheckItem = (checkedKeys, key) => {
|
|
|
+ const handleUnCheckItem = (checkedKeys, key, options = {}) => {
|
|
|
+ // options-用于处理右侧自定义删除逻辑中,防止两个setState合并从而导致更新失败
|
|
|
const node = state.transferDataSource.find(item => item.key === key)
|
|
|
if (node?.isDepartment) {
|
|
|
const filterKeys = state.transferDataSource
|
|
|
@@ -124,6 +147,7 @@ const CustomPerm = () => {
|
|
|
.map(item => item.key)
|
|
|
setState({
|
|
|
...state,
|
|
|
+ ...options,
|
|
|
treeKeys: state.treeKeys.filter(item => ![key, ...filterKeys].includes(item))
|
|
|
})
|
|
|
} else {
|
|
|
@@ -132,16 +156,48 @@ const CustomPerm = () => {
|
|
|
// 说明 父节点已经被选中
|
|
|
setState({
|
|
|
...state,
|
|
|
+ ...options,
|
|
|
treeKeys: state.treeKeys.filter(item => item !== pNode.key && item !== key)
|
|
|
})
|
|
|
} else {
|
|
|
setState({
|
|
|
...state,
|
|
|
+ ...options,
|
|
|
treeKeys: state.treeKeys.filter(item => item !== key)
|
|
|
})
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
+ const checkedKeys = useMemo(() => {
|
|
|
+ const targetId = state.leftTargetId
|
|
|
+ if (isNull(targetId)) {
|
|
|
+ return [...state.treeKeys]
|
|
|
+ }
|
|
|
+ const node = state.transferDataSource.find(item => item.key === targetId)
|
|
|
+ if (!isNullOrUnDef(node)) {
|
|
|
+ // 搜索的节点存在
|
|
|
+ if (node.isDepartment) {
|
|
|
+ // 节点是树节点
|
|
|
+ if (state.treeKeys.findIndex(item => item === targetId) !== -1) {
|
|
|
+ // treeKeys中有这个树节点
|
|
|
+ return [
|
|
|
+ ...state.transferDataSource
|
|
|
+ .filter(item => item.parentId === targetId)
|
|
|
+ .map(item => item.key),
|
|
|
+ node.key
|
|
|
+ ]
|
|
|
+ }
|
|
|
+ // 过滤出当前树节点的子元素中所有处于target状态的叶子结点
|
|
|
+ return state.transferDataSource
|
|
|
+ .filter(item => item.parentId === targetId)
|
|
|
+ .filter(item => state.treeKeys.includes(item.key))
|
|
|
+ .map(item => item.key)
|
|
|
+ }
|
|
|
+ // 直接返回当前节点
|
|
|
+ return [targetId]
|
|
|
+ }
|
|
|
+ return []
|
|
|
+ }, [state.leftTargetId, state.treeKeys.length, state.targetKeys.length])
|
|
|
return (
|
|
|
<div className={['ant-modal-content', styles.modalContainer].join(' ')}>
|
|
|
<div className={styles.modalHeader}>
|
|
|
@@ -157,13 +213,12 @@ const CustomPerm = () => {
|
|
|
className="h-full"
|
|
|
render={item => item.title}
|
|
|
onSearch={handleOnSearch}
|
|
|
+ dataSource={state.transferDataSource}
|
|
|
targetKeys={state.targetKeys}
|
|
|
onChange={onChange}
|
|
|
- dataSource={state.transferDataSource}
|
|
|
showSelectAll={false}>
|
|
|
{({ direction, onItemSelectAll, onItemSelect }) => {
|
|
|
if (direction === 'left') {
|
|
|
- const checkedKeys = [...state.targetKeys, ...state.treeKeys]
|
|
|
return state.dataSource?.length ? (
|
|
|
<Tree
|
|
|
blockNode
|
|
|
@@ -171,7 +226,12 @@ const CustomPerm = () => {
|
|
|
defaultExpandAll
|
|
|
height={state.height}
|
|
|
checkedKeys={checkedKeys}
|
|
|
- treeData={generateTree(state.dataSource, state.targetKeys)}
|
|
|
+ treeData={generateTree(
|
|
|
+ state.leftTargetId
|
|
|
+ ? state.transferDataSource.filter(item => item.key === state.leftTargetId)
|
|
|
+ : state.dataSource,
|
|
|
+ state.targetKeys
|
|
|
+ )}
|
|
|
onCheck={(_, { checked, checkedNodes, node: { isDepartment, key } }) => {
|
|
|
if (!checked) {
|
|
|
handleUnCheckItem(checkedKeys, key)
|
|
|
@@ -194,15 +254,16 @@ const CustomPerm = () => {
|
|
|
onItemSelect(key, !isChecked(checkedKeys, key))
|
|
|
}
|
|
|
}}
|
|
|
- onSelect={(_, { selected: checked, node: { isDepartment, key, parentId } }) => {
|
|
|
+ onSelect={(_, { node: { isDepartment, key, parentId } }) => {
|
|
|
+ const checked = !state.treeKeys.includes(key)
|
|
|
if (!checked) {
|
|
|
handleUnCheckItem(checkedKeys, key)
|
|
|
} else {
|
|
|
const treeKeys = [...state.treeKeys]
|
|
|
if (!isDepartment) {
|
|
|
- const samePidArr = state.transferDataSource.filter(
|
|
|
- item => item.key === parentId
|
|
|
- )
|
|
|
+ const samePidArr = state.transferDataSource
|
|
|
+ .filter(item => item.parentId === parentId)
|
|
|
+ .map(item => item.key)
|
|
|
const count = checkedKeys.reduce((prev, curr) => {
|
|
|
if (samePidArr.includes(curr)) {
|
|
|
return prev + 1
|
|
|
@@ -212,6 +273,13 @@ const CustomPerm = () => {
|
|
|
if (samePidArr.length - count === 1) {
|
|
|
treeKeys.push(parentId)
|
|
|
}
|
|
|
+ treeKeys.push(key)
|
|
|
+ } else {
|
|
|
+ const samePidArr = state.transferDataSource
|
|
|
+ .filter(item => item.parentId === key)
|
|
|
+ .filter(item => !checkedKeys.includes(item.key))
|
|
|
+ .map(item => item.key)
|
|
|
+ treeKeys.push(...samePidArr, key)
|
|
|
}
|
|
|
setState({
|
|
|
...state,
|
|
|
@@ -220,9 +288,7 @@ const CustomPerm = () => {
|
|
|
}
|
|
|
isDepartment &&
|
|
|
itemIsChecked(onItemSelectAll)(
|
|
|
- state.transferDataSource.filter(
|
|
|
- item => item.key === key || item.parentId === key
|
|
|
- ),
|
|
|
+ state.transferDataSource.filter(item => item.parentId === key),
|
|
|
checkedKeys,
|
|
|
checked,
|
|
|
state.transferDataSource
|
|
|
@@ -234,6 +300,31 @@ const CustomPerm = () => {
|
|
|
/>
|
|
|
) : null
|
|
|
}
|
|
|
+
|
|
|
+ if (direction === 'right') {
|
|
|
+ return (
|
|
|
+ <ul className="ant-transfer-list-content ant-transfer-list-content-show-remove">
|
|
|
+ {(state.rightTargetId
|
|
|
+ ? state.targetKeys.filter(item => item === state.rightTargetId)
|
|
|
+ : state.targetKeys
|
|
|
+ ).map(item => (
|
|
|
+ <li className="ant-transfer-list-content-item" key={item}>
|
|
|
+ <span className="ant-transfer-list-content-item-text">
|
|
|
+ {state.transferDataSource.find(i => i.key === item)?.title}
|
|
|
+ </span>
|
|
|
+ <span
|
|
|
+ className="text-hex-d9d9d9 hover:text-hex-886ab5 transition-colors duration-300"
|
|
|
+ onClick={() => {
|
|
|
+ const currKeys = state.targetKeys.filter(i => i !== item)
|
|
|
+ handleUnCheckItem(state.treeKeys, item, { targetKeys: currKeys })
|
|
|
+ }}>
|
|
|
+ <DeleteOutlined />
|
|
|
+ </span>
|
|
|
+ </li>
|
|
|
+ ))}
|
|
|
+ </ul>
|
|
|
+ )
|
|
|
+ }
|
|
|
}}
|
|
|
</Transfer>
|
|
|
</div>
|