Kaynağa Gözat

feat: 自定义权限测试用例

lanjianrong 5 yıl önce
ebeveyn
işleme
a1e4863df4

+ 6 - 6
config/routes.js

@@ -64,13 +64,13 @@ export default [
         component: './Customer/Business',
         access: 'authRouteFilter',
         validatePerm: true
+      },
+      {
+        path: '/customer/test',
+        name: 'test',
+        icon: 'icon-usd-circle',
+        component: './Customer/Test'
       }
-      // {
-      //   path: '/customer/test',
-      //   name: 'test',
-      //   icon: 'icon-usd-circle',
-      //   component: './Customer/Test'
-      // }
     ]
   },
   {

+ 1 - 1
src/pages/Customer/Company/components/AddCompany/index.jsx

@@ -72,7 +72,7 @@ const AddCompanyModal = ({ dataId, dataType, onConfirm, dispatch, natures }) =>
       />
       {showDataItem ? <ProFormText name={`${dataType}Id`} hidden /> : null}
       <Form.Item
-        name="district"
+        name="districtIds"
         label="客户地区"
         rules={[{ required: true, message: '请选择地区' }]}>
         <LazyCascader showFooter={true} />

+ 5 - 0
src/pages/Customer/Company/components/ConnectCompany/index.less

@@ -41,3 +41,8 @@
     padding: 1.5rem;
   }
 }
+
+.customTree {
+  height: calc(63vh - 100px - 3rem);
+  overflow-y: auto;
+}

+ 220 - 0
src/pages/Customer/Company/components/CustomPerm.jsx

@@ -0,0 +1,220 @@
+/* eslint-disable consistent-return */
+import { Button, Transfer, Tree } from 'antd'
+import React, { useState, useEffect } from 'react'
+import styles from './ConnectCompany/index.less'
+import { CloseOutlined } from '@ant-design/icons'
+import consts from '@/basic/consts'
+import { useDispatch } from 'umi'
+import { queryDepartmentStaffList } from '@/services/department'
+import { useWindowSizeFn } from '@/hooks/event/useWindomSizeFn'
+
+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 = () => {
+  const [state, setState] = useState({
+    dataSource: [],
+    treeKeys: [],
+    targetKeys: [],
+    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
+        })
+        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) => {
+    console.log(direction, value)
+  }
+
+  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] })
+    }
+  }
+
+  const handleUnCheckItem = (checkedKeys, key) => {
+    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,
+        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,
+          treeKeys: state.treeKeys.filter(item => item !== pNode.key && item !== key)
+        })
+      } else {
+        setState({
+          ...state,
+          treeKeys: state.treeKeys.filter(item => item !== key)
+        })
+      }
+    }
+  }
+  return (
+    <div className={['ant-modal-content', styles.modalContainer].join(' ')}>
+      <div className={styles.modalHeader}>
+        <span>自定义展示数据</span>
+        <span className={styles.closeIcon}>
+          <CloseOutlined onClick={closeModal} />
+        </span>
+      </div>
+      <div className={styles.modalContent}>
+        <Transfer
+          showSearch
+          oneWay={true}
+          className="h-full"
+          render={item => item.title}
+          onSearch={handleOnSearch}
+          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
+                  checkable
+                  defaultExpandAll
+                  height={state.height}
+                  checkedKeys={checkedKeys}
+                  treeData={generateTree(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={(_, { checkedNodes, node: { isDepartment, key } }) => {
+                    if (isDepartment) {
+                      itemIsChecked(onItemSelectAll)(checkedNodes, checkedKeys)
+                    } else {
+                      onItemSelect(key, !isChecked(checkedKeys, key))
+                    }
+                  }}
+                />
+              ) : null
+            }
+          }}
+        </Transfer>
+      </div>
+      <div className={[styles.modalFooter, 'text-right'].join(' ')}>
+        <Button className="mr-4" onClick={closeModal}>
+          取消
+        </Button>
+        <Button type="primary">确定</Button>
+      </div>
+    </div>
+  )
+}
+
+export default CustomPerm

+ 4 - 1
src/pages/Customer/Company/index.jsx

@@ -488,9 +488,12 @@ const Company = props => {
             defaultValue="oneself"
             dropdownMatchSelectWidth={false}
             onChange={e => {
+              // if ( e === 'custom' ) {
+
+              // }
               setState({ ...state, params: { ...state.params, dataPermission: e } })
             }}
-            options={permData?.customer || []}
+            options={[...(permData?.customer || []), { label: '自定义', value: 'custom' }]}
           />
         }
         rowSelection={{

+ 9 - 9
src/pages/Customer/Test/index.jsx

@@ -1,10 +1,10 @@
-// import React from 'react'
+import React from 'react'
+import CustomPerm from '../Company/components/CustomPerm'
 
-// export default function Index() {
-
-//   return (
-//     <div>
-
-//     </div>
-//   )
-// }
+export default function Index() {
+  return (
+    <div>
+      <CustomPerm />
+    </div>
+  )
+}

+ 2 - 2
src/pages/Hr/Employee/components/RoleMenu/index.jsx

@@ -85,7 +85,7 @@ const RoleMenu = props => {
         </ModalForm>
       </div>
       <div className="p-4 bg-white" style={{ height: 'calc(100% - 1rem*2 - 20px)' }}>
-        {departments.length && (
+        {departments.length ? (
           <DirectoryTree
             multiple
             defaultExpandAll
@@ -95,7 +95,7 @@ const RoleMenu = props => {
             showIcon={false}
             treeData={newList}
           />
-        )}
+        ) : null}
       </div>
     </div>
   )

+ 2 - 2
src/pages/workbench/Dashboard/index.jsx

@@ -26,7 +26,7 @@ const Dashboard = () => {
     serviceLogChainRatio: {},
     aggregation: {},
     businessChar: {},
-    params: { businessGroupId: '', dataPermission: 'oneself', cyclical: 'month' }
+    params: { businessGroupId: '', dataPermission: 'oneself', cyclical: 'week' }
   })
   const { toggleModal, setModalProps } = useModal()
   // 展示服务弹窗详情
@@ -200,7 +200,7 @@ const Dashboard = () => {
             loading={state.loading}
             options={cyclicalOp}
             dropdownMatchSelectWidth={false}
-            defaultValue="month"
+            defaultValue="week"
             onChange={e =>
               setState({ ...state, immediate: true, params: { ...state.params, cyclical: e } })
             }

+ 8 - 3
src/services/department.js

@@ -3,7 +3,7 @@ import request from '@/basic/utils/request'
 /**
  * @description 获取部门列表
  */
-export function getDepartMentList(parentId) {
+export async function getDepartMentList(parentId) {
   return request('/department/list', {
     params: { parentId }
   })
@@ -12,13 +12,18 @@ export function getDepartMentList(parentId) {
 /**
  * @description 新增部门
  */
-export function apiaddDepartment(payload) {
+export async function apiaddDepartment(payload) {
   return request.post('/department/add', { data: payload })
 }
 
 /**
  * @description 删除部门
  */
-export function delDepartment(id) {
+export async function delDepartment(id) {
   return request.post('/department/delete', { data: { id } })
 }
+
+/** 获取部门-员工列表 */
+export async function queryDepartmentStaffList() {
+  return request.get('/department/staff')
+}