Parcourir la source

Merge branch 'dev' of http://192.168.1.41:3000/outaozhen/cldV2react into dev

outaozhen il y a 4 ans
Parent
commit
c16a035527

+ 1 - 1
config/proxy.js

@@ -8,7 +8,7 @@
 export default {
 export default {
   dev: {
   dev: {
     '/api/': {
     '/api/': {
-      target: 'http://cld2qa.com',
+      target: 'http://cld2.com',
       changeOrigin: true,
       changeOrigin: true,
       pathRewrite: {
       pathRewrite: {
         '^': ''
         '^': ''

+ 46 - 36
src/app.tsx

@@ -1,4 +1,6 @@
 import { queryCurrentUser, queryPermData } from './services/user'
 import { queryCurrentUser, queryPermData } from './services/user'
+import type { RequestConfig } from 'umi'
+import { ErrorShowType } from 'umi'
 import { history } from 'umi'
 import { history } from 'umi'
 import RightContent from '@/components/RightContent'
 import RightContent from '@/components/RightContent'
 import { message, notification } from 'antd'
 import { message, notification } from 'antd'
@@ -9,6 +11,7 @@ import { getAuthCache, setAuthCache } from './utils/auth'
 import { LAYOUT_COLLAPSED_KEY } from './utils/cache/cacheEnum'
 import { LAYOUT_COLLAPSED_KEY } from './utils/cache/cacheEnum'
 import consts from './consts'
 import consts from './consts'
 import ModalStore from './components/ModalStore'
 import ModalStore from './components/ModalStore'
+import { stringify } from 'qs'
 
 
 const loginPath = '/user/login'
 const loginPath = '/user/login'
 
 
@@ -81,7 +84,12 @@ export const layout = ({ initialState, setInitialState }) => {
     onPageChange: async location => {
     onPageChange: async location => {
       // 如果没有登录,重定向到 login
       // 如果没有登录,重定向到 login
       if (!initialState?.currentUser?.staffId && location.pathname !== loginPath) {
       if (!initialState?.currentUser?.staffId && location.pathname !== loginPath) {
-        history.replace('/user/login')
+        history.replace({
+          pathname: loginPath,
+          search: stringify({
+            redirect: history.location.pathname
+          })
+        })
       } else {
       } else {
         location.pathname !== loginPath &&
         location.pathname !== loginPath &&
           tryChangeWorkStatus(initialState?.currentUser?.staffId, location.pathname)
           tryChangeWorkStatus(initialState?.currentUser?.staffId, location.pathname)
@@ -95,23 +103,38 @@ export const layout = ({ initialState, setInitialState }) => {
 }
 }
 
 
 const errorHandler = error => {
 const errorHandler = error => {
-  const { response } = error
-
-  if (response && response.status) {
-    const errorText = codeMessage[response.status] || response.statusText
-    const { status, url } = response
-    notification.error({
-      message: `请求错误 ${status}: ${url}`,
-      description: errorText
-    })
-  } else if (!response) {
+  const { data, info } = error
+  if (info) {
+    const { errorMessage = '请求失败', showType, errorCode } = info
+    if (consts.TOKEN_INVALID_CODE.includes(errorCode)) {
+      history.replace({
+        pathname: loginPath,
+        search: stringify({
+          redirect: window.location.pathname
+        })
+      })
+    }
+    switch (showType) {
+      case ErrorShowType.ERROR_MESSAGE:
+        message.error(errorMessage)
+      case ErrorShowType.WARN_MESSAGE:
+        message.warn(errorMessage)
+      case ErrorShowType.NOTIFICATION:
+        const title = consts.TOKEN_INVALID_CODE.includes(errorCode) ? '用户信息过期' : '请求失败'
+        notification.error({
+          message: title,
+          description: errorMessage
+        })
+      default:
+        break
+    }
+  } else {
     notification.error({
     notification.error({
       description: '您的网络发生异常,无法连接服务器',
       description: '您的网络发生异常,无法连接服务器',
       message: '网络异常'
       message: '网络异常'
     })
     })
   }
   }
-
-  return response
+  return data
 }
 }
 
 
 const authHeaderInterceptor = (url, options) => {
 const authHeaderInterceptor = (url, options) => {
@@ -128,28 +151,6 @@ const authHeaderInterceptor = (url, options) => {
   return { url, options }
   return { url, options }
 }
 }
 
 
-const refreshTokenIfNeed = async (response, config) => {
-  const res = await response.clone().json()
-  if (
-    res &&
-    res.code !== consts.RET_CODE.SUCCESS &&
-    !consts.TOKEN_INVALID_CODE.includes(res.code) &&
-    config.url !== '/api/login'
-  ) {
-    message.error((res && res.msg) || '请求错误')
-    // return Promise.reject(response)
-    return response
-  }
-  if (res && res.code && consts.TOKEN_INVALID_CODE.includes(res.code)) {
-    history.replace(consts.LOGIN_PATH)
-  }
-  return response
-}
-
-/**
- * 配置request请求时的默认参数
- */
-
 export const request: RequestConfig = {
 export const request: RequestConfig = {
   errorHandler,
   errorHandler,
   prefix: '/api',
   prefix: '/api',
@@ -157,6 +158,15 @@ export const request: RequestConfig = {
   credentials: 'include', // 默认请求是否带上cookie
   credentials: 'include', // 默认请求是否带上cookie
   prefix: consts.PREFIX_URL,
   prefix: consts.PREFIX_URL,
   cache: 'no-cache',
   cache: 'no-cache',
-  responseInterceptors: [(response, options) => refreshTokenIfNeed(response, options)],
+  errorConfig: {
+    adaptor: resData => {
+      return {
+        success: resData.code === consts.RET_CODE.SUCCESS,
+        errorMessage: resData.msg,
+        errorCode: resData.code,
+        showType: resData.showType
+      }
+    }
+  },
   requestInterceptors: [authHeaderInterceptor]
   requestInterceptors: [authHeaderInterceptor]
 }
 }

+ 1 - 1
src/basic/consts.js

@@ -7,5 +7,5 @@ export default {
   LOGIN_PATH: '/user/login', //
   LOGIN_PATH: '/user/login', //
   TOKEN_INVALID_CODE: [2, 3], // 接口返回码如果是2, 3 则表明token过期或无效 需要自动刷新token
   TOKEN_INVALID_CODE: [2, 3], // 接口返回码如果是2, 3 则表明token过期或无效 需要自动刷新token
   TOKEN_WHITE_LIST: ['/api/login'], // 不需要设置token的白名单
   TOKEN_WHITE_LIST: ['/api/login'], // 不需要设置token的白名单
-  RET_CODE: { SUCCESS: 0, FAIL: 1, TOKEN_UNDEFINED: 19, TOKEN_EXPIRED: 2 } // 返回RET状态码解析
+  RET_CODE: { SUCCESS: 0, FAIL: -1, TOKEN_UNDEFINED: 19, TOKEN_EXPIRED: 2 } // 返回RET状态码解析
 }
 }

+ 0 - 12
src/basic/utils.js

@@ -1,12 +0,0 @@
-export const importAll = context => {
-  const map = {}
-  // eslint-disable-next-line no-restricted-syntax
-  for (const key of context.keys()) {
-    const keyArr = key.split('/')
-    keyArr.shift() // 移除 ./
-    map[keyArr.join('.').replace(/\.js$/g, '')] = context(key)
-  }
-  return map
-}
-
-export default importAll(require.context('./utils', true, /\.js$/))

+ 0 - 100
src/basic/utils/common.js

@@ -1,100 +0,0 @@
-import { history } from 'umi'
-import consts from '../consts'
-
-const USER_INFO = 'USER_INFO' // 用户个人信息
-const USER_ACCOUNT = 'USER_ACCOUNT' // 用户账号
-const TOKEN_ID = 'TOKEN_ID' // TOKEN令牌ID
-
-// 本地存储封装
-const storage = {
-  get(key) {
-    const val = localStorage.getItem(key)
-    if (val) {
-      return JSON.parse(val)
-    }
-    return null
-  },
-  set(key, value) {
-    localStorage.setItem(key, JSON.stringify(value))
-  },
-  del(key) {
-    localStorage.removeItem(key)
-  },
-  clear() {
-    localStorage.clear()
-  }
-}
-
-/**
- * 保存用户信息到本地存储中
- * @param {*} user 用户信息
- */
-export const saveUserInfo = user => {
-  storage.set(USER_INFO, user)
-}
-
-/**
- * 获取用户信息
- */
-export const getUserInfo = () => {
-  const user = storage.get(USER_INFO)
-  return user
-}
-
-/**
- * 移除用户信息
- */
-export const removeUserInfo = () => {
-  return storage.del(USER_INFO)
-}
-
-/**
- * 保存用户账号信息
- * @param {*} account 账号
- */
-export const saveUserAccount = account => {
-  storage.set(USER_ACCOUNT, account)
-}
-
-/**
- * 获取用户账号信息
- */
-export const getUserAccount = () => {
-  return storage.get(USER_ACCOUNT)
-}
-
-/**
- * 移除用户账号信息
- */
-export const removeUserAccount = () => {
-  return storage.del(USER_ACCOUNT)
-}
-/**
- * 保存令牌ID到本地存储中
- * @param {*} id token令牌id
- */
-export const saveTokenId = id => {
-  return storage.set(TOKEN_ID, id)
-}
-
-/**
- * 移除令牌ID
- */
-export const removeTokenId = () => {
-  return storage.del(TOKEN_ID)
-}
-
-/**
- * 从本地存储中获取令牌ID
- */
-export const getTokenid = () => {
-  return storage.get(TOKEN_ID) || ''
-}
-
-/**
- * 重定向到登录页
- */
-export const redirectToLogin = url => {
-  // 重定向登录页面
-  history.replace(url || consts.LOGIN_PATH)
-}

+ 0 - 117
src/basic/utils/request.js

@@ -1,117 +0,0 @@
-/**
- * request 网络请求工具
- * 更详细的 api 文档: https://github.com/umijs/umi-request
- */
-import { extend } from 'umi'
-import { message, notification } from 'antd'
-import { redirectToLogin } from './common'
-import consts from '@/consts'
-import { getToken } from '@/utils/auth'
-
-const codeMessage = {
-  200: '服务器成功返回请求的数据。',
-  201: '新建或修改数据成功。',
-  202: '一个请求已经进入后台排队(异步任务)。',
-  204: '删除数据成功。',
-  400: '发出的请求有错误,服务器没有进行新建或修改数据的操作。',
-  401: '用户没有权限(令牌、用户名、密码错误)。',
-  403: '用户得到授权,但是访问是被禁止的。',
-  404: '发出的请求针对的是不存在的记录,服务器没有进行操作。',
-  406: '请求的格式不可得。',
-  410: '请求的资源被永久删除,且不会再得到的。',
-  422: '当创建一个对象时,发生一个验证错误。',
-  500: '服务器发生错误,请检查服务器。',
-  502: '网关错误。',
-  503: '服务不可用,服务器暂时过载或维护。',
-  504: '网关超时。'
-}
-/**
- * 异常处理程序
- */
-
-const errorHandler = error => {
-  const { response } = error
-
-  if (response && response.status) {
-    const errorText = codeMessage[response.status] || response.statusText
-    const { status, url } = response
-    notification.error({
-      message: `请求错误 ${status}: ${url}`,
-      description: errorText
-    })
-  } else if (!response) {
-    notification.error({
-      description: '您的网络发生异常,无法连接服务器',
-      message: '网络异常'
-    })
-  }
-
-  return response
-}
-
-/**
- * 配置request请求时的默认参数
- */
-
-const request = extend({
-  errorHandler,
-  // 默认错误处理
-  credentials: 'include', // 默认请求是否带上cookie
-  prefix: consts.PREFIX_URL,
-  cache: 'no-cache'
-})
-
-// 请求拦截器
-request.interceptors.request.use((url, options) => {
-  const token = getToken()
-  if (token) {
-    // 在白名单里的请求放过
-    if (consts.TOKEN_WHITE_LIST.includes(url)) {
-      return { url, options }
-    }
-    // eslint-disable-next-line no-param-reassign
-    options.headers[consts.TOKEN_HEADER] = `bearer ${token}`
-    return { url, options }
-  }
-  return { url, options }
-})
-const refreshTokenIfNeed = async (response, config) => {
-  const res = await response.clone().json()
-  if (
-    res &&
-    res.code !== consts.RET_CODE.SUCCESS &&
-    !consts.TOKEN_INVALID_CODE.includes(res.code) &&
-    config.url !== '/api/login'
-  ) {
-    message.error((res && res.msg) || '请求错误')
-    // return Promise.reject(response)
-    return response
-  }
-  if (res && res.code && consts.TOKEN_INVALID_CODE.includes(res.code)) {
-    redirectToLogin()
-    // const account = getUserAccount()
-    // if (!account || !currentTokenRetry) {
-    //   return response
-    // }
-    // currentTokenRetry -= 1
-    // // token过期或者没有传token
-    // const data = await apiGetToken(account)
-    // if (data && data.data && data.data.code === consts.RET_CODE.SUCCESS) {
-    //   currentTokenRetry = RETRY_TOKEN_TIME // 重置token重试次数
-    //   // 刷新token成功,保存token
-    //   const token = data.data && data.data.token
-    //   setAuthCache(TOKEN_KEY, token)
-    //   // 重新发起请求
-    //   const newResponse = await request(response.url, config)
-    //   return newResponse
-    // }
-  }
-  return response
-}
-
-// 响应拦截器
-request.interceptors.response.use((response, options) => {
-  return refreshTokenIfNeed(response, options)
-})
-
-export default request

+ 1 - 0
src/locales/zh-CN/pages.js

@@ -1,5 +1,6 @@
 export default {
 export default {
   'pages.layouts.userLayout.title': '纵横办公系统',
   'pages.layouts.userLayout.title': '纵横办公系统',
+  'pages.login.failure': '登录失败',
   'pages.login.accountLogin.tab': '账户密码登录',
   'pages.login.accountLogin.tab': '账户密码登录',
   'pages.login.accountLogin.errorMessage': '错误的用户名或密码',
   'pages.login.accountLogin.errorMessage': '错误的用户名或密码',
   'pages.login.username.placeholder': '用户名: admin or user',
   'pages.login.username.placeholder': '用户名: admin or user',

+ 5 - 3
src/pages/Product/Lock/LockStore/components/ReceiveNewLock.jsx

@@ -56,7 +56,7 @@ const ReceiveNewLock = ({ refresh }) => {
     <CustomModal
     <CustomModal
       title={
       title={
         <div className="flex flex-nowrap items-center justify-between">
         <div className="flex flex-nowrap items-center justify-between">
-          "新系统生成的锁,将在列表中按“本部门”展示。"
+          &quot;新系统生成的锁,将在列表中按“本部门”展示。&quot;
           <Input.Search
           <Input.Search
             style={{ width: '300px' }}
             style={{ width: '300px' }}
             placeholder="输入锁号/产品进行搜索"
             placeholder="输入锁号/产品进行搜索"
@@ -77,8 +77,10 @@ const ReceiveNewLock = ({ refresh }) => {
           bordered={true}
           bordered={true}
           scroll={{ y: 460 }}
           scroll={{ y: 460 }}
           request={async (params, _, filter) => {
           request={async (params, _, filter) => {
-            const { data: { longle, total } = { longle: [], total: 0 }, code = -1 } =
-              await queryReceiveList({ ...params, ...filter })
+            const { data: { longle, total } = { longle: [], total: 0 }, code = -1 } = await queryReceiveList({
+              ...params,
+              ...filter
+            })
             return {
             return {
               data: longle,
               data: longle,
               success: code === consts.RET_CODE.SUCCESS,
               success: code === consts.RET_CODE.SUCCESS,

+ 33 - 59
src/pages/User/Login/index.jsx

@@ -9,6 +9,7 @@ import { apiLogin } from '@/services/login'
 import { setAuthCache } from '@/utils/auth'
 import { setAuthCache } from '@/utils/auth'
 import { TOKEN_KEY } from '@/utils/cache/cacheEnum'
 import { TOKEN_KEY } from '@/utils/cache/cacheEnum'
 import { DEFAULT_CACHE_TIME } from '@/settings/encryptionSetting'
 import { DEFAULT_CACHE_TIME } from '@/settings/encryptionSetting'
+import consts from '@/consts'
 
 
 const LoginMessage = ({ content }) => (
 const LoginMessage = ({ content }) => (
   <Alert
   <Alert
@@ -23,8 +24,11 @@ const LoginMessage = ({ content }) => (
 
 
 const Login = () => {
 const Login = () => {
   const [submitting, setSubmitting] = useState(false)
   const [submitting, setSubmitting] = useState(false)
-  const [userLoginState, setUserLoginState] = useState({})
-  const [type, setType] = useState('account')
+  const [userLoginState, setUserLoginState] = useState({
+    submitting: false,
+    status: '',
+    type: 'account'
+  })
   const { initialState, setInitialState } = useModel('@@initialState')
   const { initialState, setInitialState } = useModel('@@initialState')
 
 
   const intl = useIntl()
   const intl = useIntl()
@@ -34,10 +38,6 @@ const Login = () => {
     const permData = await initialState?.fetchPermData?.()
     const permData = await initialState?.fetchPermData?.()
     if (userInfo) {
     if (userInfo) {
       setInitialState({ ...initialState, ...userInfo, permData })
       setInitialState({ ...initialState, ...userInfo, permData })
-      // const { username, staffId: id, wsToken: token } = userInfo?.currentUser
-      // ws.init(`ws://cld2qa.com/summon/v1/chat/link?username=${username}&id=${id}&token=${token}`, {
-      //   onMessage
-      // })
     }
     }
     return userInfo
     return userInfo
   }
   }
@@ -48,7 +48,7 @@ const Login = () => {
     setTimeout(() => {
     setTimeout(() => {
       const { query } = history.location
       const { query } = history.location
       const { redirect } = query
       const { redirect } = query
-      history.push(redirect || '/')
+      history.replace(redirect || '/')
       return notification.success({
       return notification.success({
         message: intl.formatMessage({ id: 'pages.login.success.title' }),
         message: intl.formatMessage({ id: 'pages.login.success.title' }),
         description: redirect && `${intl.formatMessage({ id: 'pages.login.success.desc' })}: ${username}`,
         description: redirect && `${intl.formatMessage({ id: 'pages.login.success.desc' })}: ${username}`,
@@ -58,33 +58,16 @@ const Login = () => {
   }
   }
 
 
   const handleSubmit = async values => {
   const handleSubmit = async values => {
-    setSubmitting(true)
-    try {
-      // 登录
-      const res = await apiLogin({ ...values })
-      if (res.code === 0) {
-        setAuthCache(TOKEN_KEY, res.data.token, DEFAULT_CACHE_TIME)
-
-        // const defaultLoginSuccessMessage = intl.formatMessage({
-        //   id: 'pages.login.success',
-        //   defaultMessage: '登录成功!'
-        // })
-        // message.success(defaultLoginSuccessMessage)
-        const userinfo = await fetchUserInfo()
-        /** 此方法会跳转到 redirect 参数所在的位置 */
-        return goto(userinfo?.currentUser?.username)
-      }
-      // 如果失败去设置用户错误信息
-      setUserLoginState(res.msg)
-    } catch (error) {
-      const defaultLoginFailureMessage = intl.formatMessage({
-        id: 'pages.login.failure',
-        defaultMessage: '登录失败,请重试!'
-      })
-
-      return message.error(defaultLoginFailureMessage)
+    setUserLoginState({ ...userLoginState, submitting: true })
+    const { code = -1, data: { token = '' } = {} } = await apiLogin({ ...values })
+    if (code === consts.RET_CODE.SUCCESS) {
+      setAuthCache(TOKEN_KEY, token, DEFAULT_CACHE_TIME)
+      const userinfo = await fetchUserInfo()
+      /** 此方法会跳转到 redirect 参数所在的位置 */
+      return goto(userinfo?.currentUser?.username)
     }
     }
-    return setSubmitting(false)
+    // 如果失败去设置用户错误信息
+    setUserLoginState({ ...userLoginState, status: 'error', submitting: false })
   }
   }
   const { status, type: loginType } = userLoginState
   const { status, type: loginType } = userLoginState
 
 
@@ -119,6 +102,7 @@ const Login = () => {
               submitButtonProps: {
               submitButtonProps: {
                 loading: submitting,
                 loading: submitting,
                 size: 'large',
                 size: 'large',
+                className: 'enter-x',
                 style: {
                 style: {
                   width: '100%'
                   width: '100%'
                 }
                 }
@@ -128,7 +112,7 @@ const Login = () => {
               handleSubmit(values)
               handleSubmit(values)
             }}
             }}
           >
           >
-            <Tabs activeKey={type} onChange={setType}>
+            <Tabs activeKey={loginType} className="enter-x">
               <Tabs.TabPane
               <Tabs.TabPane
                 key="account"
                 key="account"
                 tab={intl.formatMessage({
                 tab={intl.formatMessage({
@@ -136,25 +120,20 @@ const Login = () => {
                   defaultMessage: '账户密码登录'
                   defaultMessage: '账户密码登录'
                 })}
                 })}
               />
               />
-              {/* <Tabs.TabPane
-                key="mobile"
-                tab={intl.formatMessage({
-                  id: 'pages.login.phoneLogin.tab',
-                  defaultMessage: '手机号登录',
-                })}
-              /> */}
             </Tabs>
             </Tabs>
 
 
             {status === 'error' && loginType === 'account' && (
             {status === 'error' && loginType === 'account' && (
-              <LoginMessage
-                content={intl.formatMessage({
-                  id: 'pages.login.accountLogin.errorMessage',
-                  defaultMessage: '账户或密码错误'
-                })}
-              />
+              <div className="enter-x">
+                <LoginMessage
+                  content={intl.formatMessage({
+                    id: 'pages.login.accountLogin.errorMessage',
+                    defaultMessage: '账户或密码错误'
+                  })}
+                />
+              </div>
             )}
             )}
-            {type === 'account' && (
-              <>
+            {loginType === 'account' && (
+              <div className="enter-x">
                 <ProFormText
                 <ProFormText
                   name="username"
                   name="username"
                   fieldProps={{
                   fieldProps={{
@@ -187,23 +166,18 @@ const Login = () => {
                     }
                     }
                   ]}
                   ]}
                 />
                 />
-              </>
+              </div>
             )}
             )}
-            <div
-              style={{
-                marginBottom: 24
-              }}
-            >
+            <div className="enter-x mb-6">
               <ProFormCheckbox noStyle name="autoLogin">
               <ProFormCheckbox noStyle name="autoLogin">
                 <FormattedMessage id="pages.login.rememberMe" defaultMessage="自动登录" />
                 <FormattedMessage id="pages.login.rememberMe" defaultMessage="自动登录" />
               </ProFormCheckbox>
               </ProFormCheckbox>
-              <a
+              {/* <a
                 style={{
                 style={{
                   float: 'right'
                   float: 'right'
-                }}
-              >
+                }}>
                 <FormattedMessage id="pages.login.forgotPassword" defaultMessage="忘记密码" />
                 <FormattedMessage id="pages.login.forgotPassword" defaultMessage="忘记密码" />
-              </a>
+              </a> */}
             </div>
             </div>
           </ProForm>
           </ProForm>
         </div>
         </div>

+ 124 - 132
src/pages/Workbench/Dashboard/index.jsx

@@ -17,6 +17,7 @@ import styles from './index.less'
 import { isDevMode } from '@/utils/env'
 import { isDevMode } from '@/utils/env'
 import { isMobile } from '@/utils/is'
 import { isMobile } from '@/utils/is'
 import { useModal } from '@/components/ModalStore'
 import { useModal } from '@/components/ModalStore'
+import classNames from 'classnames'
 
 
 const Dashboard = ({ dispatch, departments = [] }) => {
 const Dashboard = ({ dispatch, departments = [] }) => {
   const { initialState: { currentUser } = { currentUser: {} } } = useModel('@@initialState')
   const { initialState: { currentUser } = { currentUser: {} } } = useModel('@@initialState')
@@ -76,7 +77,6 @@ const Dashboard = ({ dispatch, departments = [] }) => {
       pageSize: 100
       pageSize: 100
     })
     })
     if (code === consts.RET_CODE.SUCCESS && canSetState) {
     if (code === consts.RET_CODE.SUCCESS && canSetState) {
-      // list存起来
       setState({
       setState({
         ...state,
         ...state,
         staffList: list
         staffList: list
@@ -357,8 +357,8 @@ const Dashboard = ({ dispatch, departments = [] }) => {
         </Row> */}
         </Row> */}
       </div>
       </div>
 
 
-      <div className="mt-4 flex flex-row ">
-        <span className="mx-4 my-4">
+      <div className="flex flex-wrap">
+        <div className="mr-4 my-4 flex">
           <Select
           <Select
             disabled={state.disabled}
             disabled={state.disabled}
             loading={state.loading}
             loading={state.loading}
@@ -367,14 +367,14 @@ const Dashboard = ({ dispatch, departments = [] }) => {
             defaultValue={defaultCyclicalValue}
             defaultValue={defaultCyclicalValue}
             onChange={handleCyclicalChange}
             onChange={handleCyclicalChange}
           />
           />
-          <span className="mx-4">
+          <div className="mx-2">
             <PermSelect
             <PermSelect
               loading={state.loading}
               loading={state.loading}
               disabled={state.disabled}
               disabled={state.disabled}
               onConfirm={handlePermChange}
               onConfirm={handlePermChange}
               dataType="workbench"
               dataType="workbench"
             />
             />
-          </span>
+          </div>
           {state.params.dataPermission === 'all' ? (
           {state.params.dataPermission === 'all' ? (
             <TreeSelect
             <TreeSelect
               size="middle"
               size="middle"
@@ -398,106 +398,99 @@ const Dashboard = ({ dispatch, departments = [] }) => {
               }}
               }}
             />
             />
           ) : null}
           ) : null}
-        </span>
+        </div>
 
 
         <div className="flex items-center flex-row ml-2">
         <div className="flex items-center flex-row ml-2">
           {['all', 'department'].includes(state.params.dataPermission) &&
           {['all', 'department'].includes(state.params.dataPermission) &&
             state.staffList.map(item => (
             state.staffList.map(item => (
-              <div key={item.id} onClick={() => handleStaffClick(item.id)}>
-                <div
-                  className={[
-                    'relative',
-                    'cursor-pointer',
-                    state.selectId === item.id ? styles.imgBg : styles.paddingBg
-                  ].join(' ')}
-                >
-                  <img
-                    className="w-full max-w-30px h-30px border rounded-30px"
-                    src={
-                      (isDevMode() ? 'http://cld2qa.com' : 'http://zhcld.com') +
-                      (item?.avatar ?? consts.DEFAULT_AVATAR)
-                    }
-                  />
-                  <span className={['absolute', styles.avtraName].join(' ')}>
-                    <span className={styles.bb}>{item.username}</span>
-                  </span>
-                </div>
+              <div
+                className={classNames('relative cursor-pointer', styles.staffItem, {
+                  [styles.active]: state.selectId === item.id
+                })}
+                key={item.id}
+                onClick={() => handleStaffClick(item.id)}
+              >
+                <img
+                  className="w-full max-w-30px h-30px border rounded-30px"
+                  src={
+                    (isDevMode() ? 'http://cld2qa.com' : 'http://zhcld.com') +
+                    (item?.avatar ?? consts.DEFAULT_AVATAR)
+                  }
+                />
+                <span className={styles.extra}>{item.username}</span>
               </div>
               </div>
             ))}
             ))}
         </div>
         </div>
       </div>
       </div>
-      <div className="mt-4">
-        <Row gutter={[8, 8]}>
-          <Col xs={24} sm={24} md={24} lg={8} xl={8}>
-            <div className="p-4 bg-white border rounded-2px border-hex-f0f0f0 shadow-card">
-              {state.loading ? (
-                <Skeleton active avatar />
-              ) : (
-                <Row className="cursor-pointer" onClick={() => handleRatioCard(cardTypeMap.CLIENT)}>
-                  <Col span={6}>
-                    <div className="w-full max-w-48px h-48px border rounded-48px bg-[#0c7cd5] flex items-center justify-center">
-                      <AddressBook size="26" fill="#fff" />
-                    </div>
-                  </Col>
-                  <Col span={18} flex="wrap">
-                    <div className="flex items-center justify-between ">
-                      <div className="text-2xl">{state.clientChainRatio.count}</div>
-                      <span
-                        className={[
-                          'text-xl',
-                          state.clientChainRatio.percentage.startsWith('-')
-                            ? 'text-green-500'
-                            : 'text-red-500'
-                        ].join(' ')}
-                      >
-                        {state.clientChainRatio.percentage}
-                      </span>
-                    </div>
-                    <div className="flex justify-between items-center">
-                      <div>新增客户</div>
-                      <span>{cyclicalOp.find(item => item.value === state.params.cyclical)?.title}</span>
-                    </div>
-                  </Col>
-                </Row>
-              )}
-            </div>
-          </Col>
-          <Col xs={24} sm={24} md={24} lg={8} xl={8}>
-            <div className="p-4 bg-white border rounded-2px border-hex-f0f0f0 shadow-card">
-              {state.loading ? (
-                <Skeleton active avatar />
-              ) : (
-                <Row className="cursor-pointer" onClick={() => handleRatioCard(cardTypeMap.COMPANY)}>
-                  <Col span={6}>
-                    <div className="w-full max-w-48px h-48px border rounded-48px bg-[#0c7cd5] flex items-center justify-center">
-                      <City size="26" fill="#fff" />
-                    </div>
-                  </Col>
-                  <Col span={18} flex="wrap">
-                    <div className="flex items-center justify-between ">
-                      <div className="text-2xl">{state.customerChainRatio.count}</div>
-                      <span
-                        className={[
-                          'text-xl',
-                          state.customerChainRatio.percentage.startsWith('-')
-                            ? 'text-green-500'
-                            : 'text-red-500'
-                        ].join(' ')}
-                      >
-                        {state.customerChainRatio.percentage}
-                      </span>
-                    </div>
+      <Row gutter={[8, 8]}>
+        <Col xs={24} sm={24} md={24} lg={8} xl={8}>
+          <div className="p-4 bg-white border rounded-2px border-hex-f0f0f0 shadow-card">
+            {state.loading ? (
+              <Skeleton active avatar />
+            ) : (
+              <Row className="cursor-pointer" onClick={() => handleRatioCard(cardTypeMap.CLIENT)}>
+                <Col span={6}>
+                  <div className="w-full max-w-48px h-48px border rounded-48px bg-[#0c7cd5] flex items-center justify-center">
+                    <AddressBook size="26" fill="#fff" />
+                  </div>
+                </Col>
+                <Col span={18} flex="wrap">
+                  <div className="flex items-center justify-between ">
+                    <div className="text-2xl">{state.clientChainRatio.count}</div>
+                    <span
+                      className={[
+                        'text-xl',
+                        state.clientChainRatio.percentage.startsWith('-') ? 'text-green-500' : 'text-red-500'
+                      ].join(' ')}
+                    >
+                      {state.clientChainRatio.percentage}
+                    </span>
+                  </div>
+                  <div className="flex justify-between items-center">
+                    <div>新增客户</div>
+                    <span>{cyclicalOp.find(item => item.value === state.params.cyclical)?.title}</span>
+                  </div>
+                </Col>
+              </Row>
+            )}
+          </div>
+        </Col>
+        <Col xs={24} sm={24} md={24} lg={8} xl={8}>
+          <div className="p-4 bg-white border rounded-2px border-hex-f0f0f0 shadow-card">
+            {state.loading ? (
+              <Skeleton active avatar />
+            ) : (
+              <Row className="cursor-pointer" onClick={() => handleRatioCard(cardTypeMap.COMPANY)}>
+                <Col span={6}>
+                  <div className="w-full max-w-48px h-48px border rounded-48px bg-[#0c7cd5] flex items-center justify-center">
+                    <City size="26" fill="#fff" />
+                  </div>
+                </Col>
+                <Col span={18} flex="wrap">
+                  <div className="flex items-center justify-between ">
+                    <div className="text-2xl">{state.customerChainRatio.count}</div>
+                    <span
+                      className={[
+                        'text-xl',
+                        state.customerChainRatio.percentage.startsWith('-')
+                          ? 'text-green-500'
+                          : 'text-red-500'
+                      ].join(' ')}
+                    >
+                      {state.customerChainRatio.percentage}
+                    </span>
+                  </div>
 
 
-                    <div className="flex justify-between items-center">
-                      <div>新增单位</div>
-                      <span>{cyclicalOp.find(item => item.value === state.params.cyclical)?.title}</span>
-                    </div>
-                  </Col>
-                </Row>
-              )}
-            </div>
-          </Col>
-          {/* <Col xs={24} sm={24} md={24} lg={6} xl={6}>
+                  <div className="flex justify-between items-center">
+                    <div>新增单位</div>
+                    <span>{cyclicalOp.find(item => item.value === state.params.cyclical)?.title}</span>
+                  </div>
+                </Col>
+              </Row>
+            )}
+          </div>
+        </Col>
+        {/* <Col xs={24} sm={24} md={24} lg={6} xl={6}>
             <div className="p-4 bg-white border rounded-2px border-hex-f0f0f0 shadow-card">
             <div className="p-4 bg-white border rounded-2px border-hex-f0f0f0 shadow-card">
               {state.loading ? (
               {state.loading ? (
                 <Skeleton active avatar />
                 <Skeleton active avatar />
@@ -535,43 +528,42 @@ const Dashboard = ({ dispatch, departments = [] }) => {
               )}
               )}
             </div>
             </div>
           </Col> */}
           </Col> */}
-          <Col xs={24} sm={24} md={24} lg={8} xl={8}>
-            <div className="p-4 bg-white border rounded-2px border-hex-f0f0f0 shadow-card">
-              {state.loading ? (
-                <Skeleton active avatar />
-              ) : (
-                <Row onClick={() => handleRatioCard(cardTypeMap.SERVICE)} className="cursor-pointer">
-                  <Col span={6}>
-                    <div className="w-full max-w-48px h-48px border rounded-48px bg-[#0c7cd5] flex items-center justify-center">
-                      <Comment size="26" fill="#fff" />
-                    </div>
-                  </Col>
-                  <Col span={18} flex="wrap">
-                    <div className="flex items-center justify-between ">
-                      <div className="text-2xl">{state.serviceLogChainRatio.count}</div>
-                      <span
-                        className={[
-                          'text-xl',
-                          state.serviceLogChainRatio.percentage.startsWith('-')
-                            ? 'text-green-500'
-                            : 'text-red-500'
-                        ].join(' ')}
-                      >
-                        {state.serviceLogChainRatio.percentage}
-                      </span>
-                    </div>
+        <Col xs={24} sm={24} md={24} lg={8} xl={8}>
+          <div className="p-4 bg-white border rounded-2px border-hex-f0f0f0 shadow-card">
+            {state.loading ? (
+              <Skeleton active avatar />
+            ) : (
+              <Row onClick={() => handleRatioCard(cardTypeMap.SERVICE)} className="cursor-pointer">
+                <Col span={6}>
+                  <div className="w-full max-w-48px h-48px border rounded-48px bg-[#0c7cd5] flex items-center justify-center">
+                    <Comment size="26" fill="#fff" />
+                  </div>
+                </Col>
+                <Col span={18} flex="wrap">
+                  <div className="flex items-center justify-between ">
+                    <div className="text-2xl">{state.serviceLogChainRatio.count}</div>
+                    <span
+                      className={[
+                        'text-xl',
+                        state.serviceLogChainRatio.percentage.startsWith('-')
+                          ? 'text-green-500'
+                          : 'text-red-500'
+                      ].join(' ')}
+                    >
+                      {state.serviceLogChainRatio.percentage}
+                    </span>
+                  </div>
 
 
-                    <div className="flex justify-between items-center">
-                      <div>服务记录</div>
-                      <span>{cyclicalOp.find(item => item.value === state.params.cyclical)?.title}</span>
-                    </div>
-                  </Col>
-                </Row>
-              )}
-            </div>
-          </Col>
-        </Row>
-      </div>
+                  <div className="flex justify-between items-center">
+                    <div>服务记录</div>
+                    <span>{cyclicalOp.find(item => item.value === state.params.cyclical)?.title}</span>
+                  </div>
+                </Col>
+              </Row>
+            )}
+          </div>
+        </Col>
+      </Row>
       <div className="mt-4">
       <div className="mt-4">
         <Row gutter={[8, 8]}>
         <Row gutter={[8, 8]}>
           {/* <Col xs={24} sm={24} md={24} lg={12} xl={12}>
           {/* <Col xs={24} sm={24} md={24} lg={12} xl={12}>

+ 19 - 26
src/pages/Workbench/Dashboard/index.less

@@ -14,31 +14,24 @@
   &::-webkit-scrollbar {
   &::-webkit-scrollbar {
     display: none;
     display: none;
   }
   }
-  .avtraName {
-    position: absolute;
-    bottom: 0;
-    left: 8px;
-    width: 30px;
-    margin: 0 auto;
-    white-space: nowrap;
-    transform: scale(0.8);
-  }
-  .bb {
-    display: flex;
-    align-items: center;
-    justify-content: center;
-    width: 100%;
-  }
-}
-
-.paddingBg {
-  padding: 5px 8px 18px 8px;
-  background: #f0f2f5;
-}
-.imgBg {
-  padding: 5px 8px 18px 8px;
-  background: #886ab5;
-  :global(.absolute) {
-    color: white;
+  .staffItem {
+    padding: 5px 8px 18px 8px;
+    transition: all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
+    &.active {
+      background: #886ab5;
+      color: white;
+    }
+    .extra {
+      position: absolute;
+      bottom: -2px;
+      left: 8px;
+      width: 30px;
+      margin: 0 auto;
+      white-space: nowrap;
+      transform: scale(0.8);
+      display: flex;
+      align-items: center;
+      justify-content: center;
+    }
   }
   }
 }
 }

+ 0 - 6
src/utils.js

@@ -1,6 +0,0 @@
-import utils, { importAll } from '@/src/basic/utils'
-import merge from 'lodash-es/merge'
-
-const baseUtils = importAll(require.context('./utils', true, /\.js$/))
-
-export default merge(utils, baseUtils)