瀏覽代碼

feat: ConnectModal添加控制弹窗是否关闭的字段

lanjianrong 3 年之前
父節點
當前提交
fe8944bab3

+ 2 - 2
config/config.ts

@@ -52,7 +52,7 @@ export default defineConfig({
   },
   chainWebpack(config) {
     config.plugin('windicss').use(WindiCSSPlugin)
-  }
+  },
   // Fast Refresh 热更新
-  // fastRefresh: {}
+  fastRefresh: {}
 })

+ 5 - 5
package.json

@@ -56,7 +56,7 @@
     "@icon-park/react": "^1.3.3",
     "@umijs/plugin-openapi": "^1.2.0",
     "@umijs/route-utils": "^1.0.36",
-    "ahooks": "^2.10.4",
+    "ahooks": "2.10.9",
     "antd": "^4.14.0",
     "classnames": "^2.2.6",
     "lodash": "^4.17.11",
@@ -67,7 +67,7 @@
     "react-dev-inspector": "^1.1.1",
     "react-dom": "^17.0.0",
     "react-helmet-async": "^1.0.4",
-    "umi": "^3.5.0"
+    "umi": "3.5.3"
   },
   "devDependencies": {
     "@ant-design/pro-cli": "^2.0.2",
@@ -87,7 +87,7 @@
     "@umijs/plugin-esbuild": "^1.0.1",
     "@umijs/preset-ant-design-pro": "^1.2.0",
     "@umijs/preset-dumi": "^1.1.7",
-    "@umijs/preset-react": "1.8.11",
+    "@umijs/preset-react": "1.8.22",
     "@umijs/yorkie": "^2.0.3",
     "carlo": "^0.9.46",
     "cross-env": "^7.0.0",
@@ -107,8 +107,8 @@
     "puppeteer-core": "^8.0.0",
     "stylelint": "^13.0.0",
     "typescript": "^4.2.2",
-    "windicss": "3.1.3",
-    "windicss-webpack-plugin": "1.2.1"
+    "windicss": "3.1.7",
+    "windicss-webpack-plugin": "1.4.6"
   },
   "engines": {
     "node": ">=10.0.0"

+ 1 - 0
src/pages/Role/Customer/index.tsx

@@ -175,6 +175,7 @@ const Customer = () => {
                 title="关联员工"
                 dataId={state.id}
                 postUrl="/role/staff/add"
+                closeAfterSelect={false}
                 onReload={() => tryGetRoleStaffList(state.id)}
               />
             )}

+ 1 - 0
src/pages/Role/Hr/index.tsx

@@ -170,6 +170,7 @@ const Hr = () => {
                 title="关联员工"
                 dataId={state.id}
                 postUrl="/role/staff/add"
+                closeAfterSelect={false}
                 onReload={() => tryGetRoleStaffList(state.id)}
               />
             )}

+ 1 - 0
src/pages/Role/Product/index.tsx

@@ -175,6 +175,7 @@ const Product = () => {
                 title="关联员工"
                 dataId={state.id}
                 postUrl="/role/staff/add"
+                closeAfterSelect={false}
                 onReload={() => tryGetRoleStaffList(state.id)}
               />
             )}

+ 104 - 56
src/pages/Role/System/components/ConnectModal/index.tsx

@@ -1,8 +1,9 @@
 import React, { useState, useRef, useEffect } from 'react'
 import { useRequest, request } from 'umi'
-import { Button, Input, message, Modal } from 'antd'
+import { Button, Input, message, Modal, Spin } from 'antd'
 import { fetchStaffList } from '@/services/user/api'
-import { LoadingOutlined, MoreOutlined } from '@ant-design/icons'
+import { useDebounceFn } from 'ahooks'
+import consts from '@/utils/consts'
 
 interface ConnectModalProps {
   postUrl: string
@@ -11,6 +12,7 @@ interface ConnectModalProps {
   dataId?: string
   onReload?: () => void
   show?: boolean
+  closeAfterSelect?: boolean
   onShowChange?: (isShow: boolean) => void
 }
 const ConnectModal: React.FC<ConnectModalProps> = ({
@@ -20,11 +22,20 @@ const ConnectModal: React.FC<ConnectModalProps> = ({
   show,
   onShowChange,
   postUrl,
+  closeAfterSelect = true,
   showButton = true
 }) => {
-  const containerRef = useRef<HTMLDivElement>(null)
+  const scrollRef = useRef<HTMLDivElement>(null)
   const [visible, setVisible] = useState(false)
-  const [searchVal, setSearchVal] = useState('')
+  const [state, setState] = useState({
+    laoding: true,
+    list: [],
+    searchVal: '',
+    current: 1,
+    pageSize: 10,
+    total: 0,
+    noMore: false
+  })
 
   const { run: tryConnectStaff } = useRequest(
     (params: API.AddRoleStaffCommonParams) =>
@@ -36,9 +47,6 @@ const ConnectModal: React.FC<ConnectModalProps> = ({
       manual: true,
       onSuccess: async () => {
         message.success('关联成功')
-        if (!show) {
-          setVisible(false)
-        }
         await onReload()
       },
       onError: e => {
@@ -47,48 +55,48 @@ const ConnectModal: React.FC<ConnectModalProps> = ({
     }
   )
 
-  const {
-    run: tryQueryStaffList,
-    data,
-    noMore,
-    loadMore,
-    loadingMore
-  } = useRequest(
-    result =>
-      fetchStaffList({
-        current: result?.list.length / 10 + 1 || 1,
-        pageSize: 10,
-        search: searchVal
-      }),
-    {
-      manual: true,
-      loadMore: true,
-      ref: containerRef,
-      isNoMore: (res: API.BasicFetchResult<API.StaffItem>) => res.list.length >= res.total,
-      refreshDeps: [searchVal]
+  const initData = async params => {
+    const { data: { list, total } = { list: [], total: 0 }, code = -1 } = await fetchStaffList(
+      params
+    )
+    if (code === consts.RET_CODE.SUCCESS) {
+      if (params?.current === 1) {
+        scrollRef.current?.scrollTo({ top: 0 })
+        // 请求的是第一页或者搜索框
+        setState({ ...state, list, total, laoding: false, searchVal: params?.search })
+      } else {
+        setState({
+          ...state,
+          list: state.list.concat(list),
+          total,
+          searchVal: params?.search,
+          laoding: false,
+          current: params?.current || 1
+        })
+      }
     }
-  )
+  }
 
-  const handleSearch = (value: string) => {
-    setSearchVal(value)
-    setTimeout(() => {
-      tryQueryStaffList()
-    }, 250)
+  const onChange = e => {
+    // const { value = '' } = e.target || e.currentTarget
+    initData({ search: e, current: 1, pageSize: state.pageSize })
   }
 
-  const { list = [] }: { list: API.StaffItem[] } = data || {}
+  const { run: handleInputChange } = useDebounceFn(onChange)
+
   useEffect(() => {
     if (show || visible) {
-      tryQueryStaffList()
+      initData({ current: 1, pageSize: 10 })
     }
   }, [show, visible])
 
   const handleOnCancel = () => {
-    if (onShowChange !== undefined) {
+    // 不可关闭
+    if (onShowChange) {
       onShowChange(false)
-    } else {
-      setVisible(false)
+      return
     }
+    setVisible(false)
   }
 
   const itemSelectHandler = async (staffId: string) => {
@@ -97,8 +105,36 @@ const ConnectModal: React.FC<ConnectModalProps> = ({
       params.id = dataId
     }
     await tryConnectStaff(params)
+    if (!closeAfterSelect) {
+      const nList = state.list.map(item => {
+        const nItem = { ...item, disable: false }
+        if (item.id === staffId) {
+          nItem.disable = true
+        }
+        return nItem
+      })
+      setState({ ...state, list: nList })
+      return
+    }
+
     handleOnCancel()
   }
+
+  const handleListScroll = () => {
+    // 未滚动到底部
+    if (
+      scrollRef.current.scrollHeight - scrollRef.current.clientHeight <=
+      scrollRef.current.scrollTop
+    ) {
+      // 已到底部
+      if (state.current * state.pageSize > state.total) {
+        setState({ ...state, noMore: true })
+        return
+      }
+      initData({ current: state.current + 1, pageSize: state.pageSize, search: state.searchVal })
+    }
+  }
+
   return (
     <>
       {showButton && (
@@ -111,32 +147,44 @@ const ConnectModal: React.FC<ConnectModalProps> = ({
         onCancel={handleOnCancel}
         getContainer={false}
         width="60vw"
+        footer={null}
         title={
           <Input.Search
             placeholder="搜索员工(姓名)"
-            onSearch={value => handleSearch(value)}
-            onPressEnter={e => handleSearch(e.currentTarget.value)}
+            onSearch={value => handleInputChange(value)}
+            onPressEnter={e => handleInputChange(e.currentTarget.value)}
             style={{ width: '95%' }}
             allowClear={true}
           />
         }>
-        <div ref={containerRef} className="h-60vh overflow-y-auto overflow-x-hidden">
-          {list.map(item => (
-            <div className="group-item-card" key={item.id}>
-              <div className="w-4/3 flex justify-between">
-                <span className="w-1/5">{item.username}</span>
-                <span className="w-2/5">{item.phone}</span>
-                <span className="w-1/5">{item.position}</span>
-                <span className="w-1/5">{item.category}</span>
-              </div>
-              <div className="w-1/4 flex justify-end">
-                <span className="btn-outline" onClick={() => itemSelectHandler(item.id)}>
-                  选择ta
-                </span>
+        <div
+          ref={scrollRef}
+          onScroll={handleListScroll}
+          className="h-60vh overflow-y-auto overflow-x-hidden">
+          <Spin spinning={state.laoding}>
+            {state.list.map(item => (
+              <div className="group-item-card" key={item.id}>
+                <div className="w-4/3 flex justify-between">
+                  <span className="w-1/5">{item.username}</span>
+                  <span className="w-2/5">{item.phone}</span>
+                  <span className="w-1/5">{item.position}</span>
+                  <span className="w-1/5">{item.category}</span>
+                </div>
+
+                <div className="w-1/4 flex justify-end">
+                  {item.disable ? (
+                    <span className="text-xs">已选择</span>
+                  ) : (
+                    <span className="btn-outline" onClick={() => itemSelectHandler(item.id)}>
+                      选择ta
+                    </span>
+                  )}
+                </div>
               </div>
-            </div>
-          ))}
-          {noMore && <div className="text-center text-gray-400">已到底部</div>}
+            ))}
+          </Spin>
+          {state.noMore && <div className="text-center text-gray-400 mt-2">已到底部</div>}
+          {/* {noMore && <div className="text-center text-gray-400">已到底部</div>}
           {!noMore && (
             <div className="flex justify-center mt-3 cursor-pointer">
               {loadingMore ? (
@@ -145,7 +193,7 @@ const ConnectModal: React.FC<ConnectModalProps> = ({
                 <MoreOutlined rotate={90} style={{ fontSize: 24 }} onClick={loadMore} />
               )}
             </div>
-          )}
+          )} */}
         </div>
       </Modal>
     </>

+ 1 - 0
src/pages/Role/System/index.tsx

@@ -138,6 +138,7 @@ const System = () => {
               <ConnectModal
                 title="关联员工"
                 dataId={state.id}
+                closeAfterSelect={false}
                 onReload={() => tryGetRoleStaffList(state.id)}
                 postUrl="/role/staff/add"
               />

+ 1 - 0
src/pages/Role/Workbench/index.tsx

@@ -172,6 +172,7 @@ const Workbench = () => {
               <ConnectModal
                 title="关联员工"
                 dataId={state.id}
+                closeAfterSelect={false}
                 onReload={() => tryGetRoleStaffList(state.id)}
                 postUrl="/role/staff/add"
               />

+ 53 - 0
src/utils/is.ts

@@ -0,0 +1,53 @@
+const { toString } = Object.prototype
+
+export function is(val: unknown, type: string) {
+  return toString.call(val) === `[object ${type}]`
+}
+
+export function isDef<T = unknown>(val?: T): val is T {
+  return typeof val !== 'undefined'
+}
+
+export function isUnDef<T = unknown>(val?: T): val is T {
+  return !isDef(val)
+}
+
+export function isNull(val: unknown): val is null {
+  return val === null
+}
+
+export function isObject(val: any): val is Record<any, any> {
+  return val !== null && is(val, 'Object')
+}
+
+export function isBoolean(val: unknown): val is boolean {
+  return is(val, 'Boolean')
+}
+
+export function isString(val: unknown): val is string {
+  return is(val, 'String')
+}
+
+export function isArray(val: any): val is any[] {
+  return val && Array.isArray(val)
+}
+
+export function isNullOrUnDef(val: unknown): val is null | undefined {
+  return isUnDef(val) || isNull(val)
+}
+
+export function isEmpty<T = unknown>(val: T): val is T {
+  if (isArray(val) || isString(val)) {
+    return val.length === 0
+  }
+
+  if (val instanceof Map || val instanceof Set) {
+    return val.size === 0
+  }
+
+  if (isObject(val)) {
+    return Object.keys(val).length === 0
+  }
+
+  return false
+}