| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362 |
- /* eslint-disable no-underscore-dangle */
- /* eslint-disable consistent-return */
- import { Button, message, Transfer, Tree } from 'antd'
- import React, { useMemo, useState, useEffect } from 'react'
- import styles from './ConnectCompany/index.less'
- import { 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'
- import CustomModal from '@/components/Modal/src/components/CustomModal'
- const isChecked = (selectedKeys, eventKey) => selectedKeys.includes(eventKey)
- const itemIsChecked = onItemSelectAll => (selectedKeys, checkedKeys, checked, nodeList) => {
- if (!checked) {
- onItemSelectAll(nodeList, checked)
- } else {
- const keys = selectedKeys
- .filter(({ isDepartment }) => !isDepartment)
- .filter(({ key }) => !isChecked(checkedKeys, key))
- .map(({ key }) => key)
- onItemSelectAll(keys, checked)
- }
- }
- const generateTree = (treeNodes = [], checkedKeys = []) =>
- treeNodes.map(({ children, ...props }) => {
- const node = {
- ...props,
- disabled: checkedKeys.length && checkedKeys.includes(props.key),
- children: generateTree(children, checkedKeys)
- }
- if (props.isDepartment) {
- node.disabled = children?.every(
- item => checkedKeys.includes(item.key) || item.children?.every(u => !u.children?.length)
- )
- }
- return node
- })
- const CustomPerm = ({ onConfirm, defaultValues = [] }) => {
- const [state, setState] = useState({
- dataSource: [],
- leftTargetId: null,
- treeKeys: [],
- targetKeys: defaultValues,
- rightTargetId: null,
- transferDataSource: [],
- height: 0
- })
- function calcTreeHeight() {
- const node = document.querySelector('.ant-transfer-list-body')
- if (node) {
- return node.clientHeight - 56
- }
- return null
- }
- useEffect(() => {
- const transferDataSource = []
- function flatten(list = []) {
- list.forEach(item => {
- transferDataSource.push({
- key: item.key,
- title: item.title,
- parentId: item.parentId,
- children: item.children,
- isDepartment: item.isDepartment
- })
- if (item.children?.length) flatten(item.children)
- })
- }
- async function fetchList() {
- const { code = -1, data = [] } = await queryDepartmentStaffList()
- if (code === consts.RET_CODE.SUCCESS) {
- const height = calcTreeHeight()
- flatten(data)
- setState({ ...state, dataSource: data, transferDataSource, height })
- }
- }
- fetchList()
- }, [])
- useWindowSizeFn(() => {
- const height = calcTreeHeight()
- setState({ ...state, height })
- })
- const dispatch = useDispatch()
- const closeModal = () => {
- dispatch({
- type: 'single/changeModal'
- })
- }
- const handleOnSearch = (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
- }
- if (isEmpty(value)) setState({ ...state, leftTargetId: null })
- } else {
- if (!isEmpty(value) && !isNullOrUnDef(targetNode)) {
- setState({ ...state, rightTargetId: targetNode.key })
- return
- }
- if (isEmpty(value)) setState({ ...state, rightTargetId: null })
- }
- }
- const onChange = (keys, direction, moveKeys) => {
- if (direction === 'left') {
- const currKeys = state.targetKeys.filter(key => !moveKeys.includes(key))
- setState({
- ...state,
- targetKeys: currKeys,
- treeKeys: state.treeKeys.filter(item => {
- const node = state.transferDataSource.find(i => i.key === item)
- if (node?.isDepartment) {
- const childrenKey = node?.children.map(c => c.key).filter(c => !currKeys.includes(c))
- if (childrenKey?.every(w => !currKeys.includes(w))) {
- return !moveKeys.includes(item) && item !== node.key
- }
- }
- return !moveKeys.includes(item)
- })
- })
- } 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, options = {}) => {
- // options-用于处理右侧自定义删除逻辑中,防止两个setState合并从而导致更新失败
- const node = state.transferDataSource.find(item => item.key === key)
- if (node?.isDepartment) {
- const filterKeys = state.transferDataSource
- .filter(item => item.parentId === node.key)
- .map(item => item.key)
- setState({
- ...state,
- ...options,
- treeKeys: state.treeKeys.filter(item => ![key, ...filterKeys].includes(item))
- })
- } else {
- const pNode = state.transferDataSource.find(item => item.key === node.parentId)
- if (checkedKeys.includes(pNode.key)) {
- // 说明 父节点已经被选中
- setState({
- ...state,
- ...options,
- treeKeys: state.treeKeys.filter(item => item !== pNode.key && item !== key)
- })
- } else {
- setState({
- ...state,
- ...options,
- treeKeys: state.treeKeys.filter(item => item !== key)
- })
- }
- }
- }
- // 根据计算返回tree选中的节点
- 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 state.treeKeys.filter(item => item === targetId)
- }
- return []
- }, [state.leftTargetId, state.treeKeys.length, state.targetKeys.length])
- return (
- <CustomModal title="自定义展示数据">
- <div className={styles.modalContent}>
- <Transfer
- showSearch
- oneWay={true}
- titles={['部门栏', '目标栏']}
- operations={['点击添加']}
- className="h-full"
- render={item => item.title}
- onSearch={handleOnSearch}
- dataSource={state.transferDataSource}
- targetKeys={state.targetKeys}
- onChange={onChange}
- showSelectAll={false}>
- {({ direction, onItemSelectAll, onItemSelect }) => {
- if (direction === 'left') {
- return state.dataSource?.length ? (
- <Tree
- blockNode
- checkable
- defaultExpandAll
- height={state.height}
- checkedKeys={[...checkedKeys, ...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)
- } else {
- setState({
- ...state,
- treeKeys: checkedNodes.map(item => item.key)
- })
- }
- if (isDepartment) {
- itemIsChecked(onItemSelectAll)(
- checkedNodes,
- checkedKeys,
- checked,
- state.transferDataSource
- .filter(item => item.parentId === key)
- .map(item => item.key)
- )
- } else {
- onItemSelect(key, !isChecked(checkedKeys, key))
- }
- }}
- 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.parentId === parentId)
- .map(item => item.key)
- const count = checkedKeys.reduce((prev, curr) => {
- if (samePidArr.includes(curr)) {
- return prev + 1
- }
- return prev
- }, 0)
- 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,
- treeKeys
- })
- }
- if (isDepartment) {
- itemIsChecked(onItemSelectAll)(
- state.transferDataSource.filter(item => item.parentId === key),
- checkedKeys,
- checked,
- state.transferDataSource
- .filter(item => item.parentId === key)
- .map(item => item.key)
- )
- } else {
- onItemSelect(key, !isChecked(checkedKeys, key))
- }
- }}
- />
- ) : null
- }
- if (direction === 'right') {
- return (
- <div className={styles.removeTreeSwitcher}>
- <Tree
- blockNode={true}
- height={state.height}
- treeData={generateTree(
- state.rightTargetId
- ? state.transferDataSource
- .filter(item => state.targetKeys?.includes(item.key))
- .filter(item => item === state.rightTargetId)
- : state.transferDataSource.filter(item =>
- state.targetKeys?.includes(item.key)
- )
- )}
- titleRender={item => (
- <li className="ant-transfer-list-content-item">
- <span className="ant-transfer-list-content-item-text">{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.key)
- handleUnCheckItem(state.treeKeys, item.key, { targetKeys: currKeys })
- }}>
- <DeleteOutlined />
- </span>
- </li>
- )}
- />
- </div>
- )
- }
- }}
- </Transfer>
- </div>
- <div className={[styles.modalFooter, 'text-right'].join(' ')}>
- <Button className="mr-4" onClick={closeModal}>
- 取消
- </Button>
- <Button
- type="primary"
- onClick={() => {
- if (!state.targetKeys.length) {
- return message.warning('目标栏尚未有员工,操作无效')
- }
- if (onConfirm) {
- onConfirm({ staffIds: state.targetKeys, dataPermission: 'custom' })
- }
- closeModal()
- }}>
- 确定
- </Button>
- </div>
- </CustomModal>
- )
- }
- export default CustomPerm
|