Explorar el Código

feat: 员工在线列表

lanjianrong hace 4 años
padre
commit
0730aed520

+ 1 - 1
config/routes.js

@@ -116,7 +116,7 @@ export default [
           },
           },
           {
           {
             path: '/product/cloud/building',
             path: '/product/cloud/building',
-            name: 'build',
+            name: 'building',
             component: './Product/Cloud'
             component: './Product/Cloud'
           },
           },
           {
           {

+ 9 - 9
src/app.tsx

@@ -4,8 +4,8 @@ import RightContent from '@/components/RightContent'
 import { message } from 'antd'
 import { message } from 'antd'
 import logo from '../public/logo.svg'
 import logo from '../public/logo.svg'
 import BasicModal, { BasicDrawer } from '@/components/Modal'
 import BasicModal, { BasicDrawer } from '@/components/Modal'
-import ws, { onMessage } from '@/utils/ws'
-import Book from './components/RightContent/Book'
+// import ws, { onMessage } from '@/utils/ws'
+import { tryChangeWorkStatus } from './components/RightContent/Book'
 
 
 const loginPath = '/user/login'
 const loginPath = '/user/login'
 
 
@@ -35,12 +35,12 @@ export async function getInitialState() {
     const userInfo = (await fetchUserInfo()) || {}
     const userInfo = (await fetchUserInfo()) || {}
     const permData = (await fetchPermData()) || {}
     const permData = (await fetchPermData()) || {}
 
 
-    if (userInfo?.currentUser?.staffId) {
-      const { username, staffId: id, wsToken: token } = userInfo?.currentUser
-      ws.init(`ws://cld2qa.com/summon/v1/chat/link?username=${username}&id=${id}&token=${token}`, {
-        onMessage
-      })
-    }
+    // if (userInfo?.currentUser?.staffId) {
+    //   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 {
     return {
       fetchUserInfo,
       fetchUserInfo,
@@ -70,7 +70,7 @@ export const layout = ({ initialState }) => {
       if (!initialState?.currentUser?.staffId && location.pathname !== loginPath) {
       if (!initialState?.currentUser?.staffId && location.pathname !== loginPath) {
         history.replcae('/user/login')
         history.replcae('/user/login')
       } else {
       } else {
-        Book.tryChangeWorkStatus(initialState?.currentUser?.staffId, location.pathname)
+        tryChangeWorkStatus(initialState?.currentUser?.staffId, location.pathname)
       }
       }
 
 
       // if (
       // if (

+ 117 - 0
src/components/RightContent/Book/components/UserList/index.less

@@ -0,0 +1,117 @@
+.custom-scroll,
+.custom-scrollbar {
+  overflow: hidden;
+  overflow-y: scroll;
+  -webkit-overflow-scrolling: touch;
+}
+.custom-scroll::-webkit-scrollbar-track-piece,
+.custom-scrollbar::-webkit-scrollbar-track-piece {
+  background-color: transparent;
+}
+.custom-scroll::-webkit-scrollbar-thumb:vertical,
+.custom-scrollbar::-webkit-scrollbar-thumb:vertical {
+  background-color: #666666;
+}
+.custom-scroll::-webkit-scrollbar,
+.custom-scrollbar::-webkit-scrollbar {
+  width: 4px;
+  height: 4px;
+}
+.custom-scroll::-webkit-scrollbar-corner,
+.custom-scrollbar::-webkit-scrollbar-corner {
+  width: 40px;
+}
+
+.status {
+  position: relative;
+  &::before {
+    position: absolute;
+    top: -2px;
+    right: -2px;
+    display: block;
+    width: 15px;
+    height: 15px;
+    background: #505050;
+    border: 2px solid white;
+    border-radius: 50%;
+    content: ' ';
+  }
+  &.status-sm::before {
+    top: 0;
+    right: 0;
+    width: 10px;
+    height: 10px;
+    border-width: 1px;
+  }
+  &.status-success::before {
+    background: #1dc9b7;
+  }
+  &.status-danger::before {
+    background: #fd3995;
+  }
+  &.status-warning::before {
+    background: #ffc241;
+  }
+}
+
+.book-list {
+  z-index: 101;
+  // position: absolute;
+  // right: -14.5rem;
+  width: 0;
+  height: calc(100vh - 50px);
+  transition: all 0.47s cubic-bezier(0.34, 1.25, 0.3, 1);
+  transition-delay: 0.1s;
+  .postion {
+    transform: translateY(-50%);
+  }
+  &.show-short {
+    width: 3rem;
+    &:hover {
+      width: 14.563rem;
+      border-left-color: rgba(0, 0, 0, 0.1);
+    }
+  }
+}
+
+/* Rectangle Out */
+.hvr-rectangle-out {
+  position: relative;
+  display: inline-block;
+  vertical-align: middle;
+  // background: #e1e1e1;
+  // background-color: unset;
+  box-shadow: 0 0 1px rgba(0, 0, 0, 0);
+  -webkit-transform: perspective(1px) translateZ(0);
+  transform: perspective(1px) translateZ(0);
+  -webkit-transition-duration: 0.3s;
+  transition-duration: 0.3s;
+  -webkit-transition-property: color;
+  transition-property: color;
+  &::before {
+    position: absolute;
+    top: 0;
+    right: 0;
+    bottom: 0;
+    left: 0;
+    z-index: -1;
+    background: linear-gradient(rgba(29, 33, 41, 0.03), rgba(29, 33, 41, 0.04));
+    -webkit-transform: scale(0);
+    transform: scale(0);
+    -webkit-transition-timing-function: ease-out;
+    transition-timing-function: ease-out;
+    -webkit-transition-duration: 0.3s;
+    transition-duration: 0.3s;
+    -webkit-transition-property: transform;
+    transition-property: transform;
+    content: '';
+  }
+  &:hover {
+    // background: linear-gradient(rgba(29, 33, 41, 0.03), rgba(29, 33, 41, 0.04));
+    cursor: pointer;
+  }
+  &:hover::before {
+    -webkit-transform: scale(1);
+    transform: scale(1);
+  }
+}

+ 79 - 0
src/components/RightContent/Book/components/UserList/index.tsx

@@ -0,0 +1,79 @@
+import { useState } from 'react'
+import { Button } from 'antd'
+import { useIntl } from 'umi'
+import './index.less'
+
+interface StaffItem {
+  avatar: string
+  id: string
+  username: string
+  workStatus: string
+  wsKey: string
+  department: string
+}
+
+type UserListProps = {
+  onlineList: StaffItem[]
+}
+
+const UesrList: React.FC<UserListProps> = ({ onlineList }) => {
+  const [visible, setVisible] = useState(false)
+  const intl = useIntl()
+  return (
+    <>
+      <Button onClick={() => setVisible(!visible)}>固钉</Button>
+      <div
+        className={[
+          'book-list border border-solid border-hex-1d1d1d border-opacity-7 bg-hex-f7f9fa flex flex-col bg-hex-f9f7fa text-hex-666',
+          visible ? 'show-short' : ''
+        ].join(' ')}
+      >
+        <div className="flex-1 h-full custom-scroll">
+          <div className="w-full">
+            <ul className="list-none m-0">
+              {onlineList.map(item => {
+                return (
+                  <li
+                    key={item.id}
+                    className="table px-2 py-2 text-hex-505050 hover-white hvr-rectangle-out w-full"
+                  >
+                    <div className="table-cell align-middle status status-success status-sm ">
+                      <span
+                        className="w-8 h-8 rounded-1/2 block"
+                        style={{
+                          backgroundImage: `url(http://cld.smartcost.com.cn${item.avatar}_2.jpg)`,
+                          backgroundSize: 'cover'
+                        }}
+                      />
+                    </div>
+                    <div className="table-cell w-full align-middle pl-2 pr-2">
+                      <div className="max-w-160px overflow-hidden whitespace-nowrap overflow-ellipsis text-sm relative">
+                        <span>{item.username}</span>
+                        {/* <small className="block italic text-hex-1dc9b7 text-xs">Online</small> */}
+                        <small className="block text-xs">
+                          {/* <span className="mr-1">正在</span> */}
+                          <span className="italic">
+                            {intl.formatMessage({
+                              id: item.workStatus,
+                              defaultMessage: ''
+                            })}
+                          </span>
+                          <span className="ml-1">工作中</span>
+                        </small>
+                        <span className="text-xs text-gray-500 absolute right-0 top-1/2 postion">
+                          {item.department}
+                        </span>
+                      </div>
+                    </div>
+                  </li>
+                )
+              })}
+            </ul>
+          </div>
+        </div>
+      </div>
+    </>
+  )
+}
+
+export default UesrList

src/components/RightContent/Book/wsNotice.tsx → src/components/RightContent/Book/components/wsNotice.tsx


+ 33 - 5
src/components/RightContent/Book/index.tsx

@@ -1,9 +1,38 @@
-import ws, { CmdType } from '@/utils/ws'
-// import menuLocal from '@/locales/zh-CN/menu'
+import { useState, useEffect } from 'react'
+import ws, { CmdType, onMessage } from '@/utils/ws'
 import { debounce } from 'lodash'
 import { debounce } from 'lodash'
+import UesrList from './components/UserList'
+import { useModel } from 'umi'
 
 
 const Book = () => {
 const Book = () => {
-  return <></>
+  const [onlineList, setOnlineList] = useState([])
+  const { initialState } = useModel('@@initialState')
+
+  const { username, staffId: id, wsToken: token } = initialState?.currentUser
+  // if (!currentUser) return null
+  // eslint-disable-next-line react-hooks/rules-of-hooks
+  useEffect(() => {
+    id &&
+      ws.init(`ws://cld2qa.com/summon/v1/chat/link?username=${username}&id=${id}&token=${token}`, {
+        onMessage: msg => {
+          onMessage(msg)
+          if (msg?.cmd === CmdType.OnlineBook) {
+            setOnlineList(msg.onlineStaff)
+          }
+        }
+      })
+
+    return () => {
+      ws.disconnect()
+    }
+  }, [])
+  return (
+    <div className="fixed top-12 right-0">
+      <div className="relative flex flex-row items-center">
+        <UesrList onlineList={onlineList} />
+      </div>
+    </div>
+  )
 }
 }
 
 
 // 员工工作状态
 // 员工工作状态
@@ -18,7 +47,6 @@ function changeWorkStatus(userid: string, pathname: string) {
       memo: targetMenuLocal
       memo: targetMenuLocal
     })
     })
 }
 }
-const tryChangeWorkStatus = debounce(changeWorkStatus, 1500)
-Book.tryChangeWorkStatus = tryChangeWorkStatus
+export const tryChangeWorkStatus = debounce(changeWorkStatus, 1500)
 
 
 export default Book
 export default Book

+ 2 - 1
src/components/RightContent/index.tsx

@@ -7,7 +7,7 @@ import Avatar from './AvatarDropdown'
 import Notify from './NoticeIcon'
 import Notify from './NoticeIcon'
 // import HeaderSearch from '../HeaderSearch'
 // import HeaderSearch from '../HeaderSearch'
 import styles from './index.less'
 import styles from './index.less'
-
+import Book from './Book'
 const ENVTagColor = {
 const ENVTagColor = {
   dev: 'orange',
   dev: 'orange',
   test: 'green',
   test: 'green',
@@ -60,6 +60,7 @@ const GlobalHeaderRight: React.FC = () => {
       >
       >
         <QuestionCircleOutlined />
         <QuestionCircleOutlined />
       </span> */}
       </span> */}
+      <Book />
       <Notify />
       <Notify />
       <Avatar />
       <Avatar />
       {REACT_APP_ENV && REACT_APP_ENV !== 'prod' && (
       {REACT_APP_ENV && REACT_APP_ENV !== 'prod' && (

+ 1 - 1
src/locales/zh-CN/menu.js

@@ -13,7 +13,7 @@ export default {
   'menu.product.lock.lockcount': '使用统计',
   'menu.product.lock.lockcount': '使用统计',
   'menu.product.cloud': '云版管理',
   'menu.product.cloud': '云版管理',
   'menu.product.cloud.curing': '养护云造价',
   'menu.product.cloud.curing': '养护云造价',
-  'menu.product.cloud.build': '大司空云计价',
+  'menu.product.cloud.building': '大司空云计价',
   'menu.product.cloud.highway': '公路云计价',
   'menu.product.cloud.highway': '公路云计价',
   'menu.product.cloud.gs': '大司空概算',
   'menu.product.cloud.gs': '大司空概算',
   'menu.hr': '人资',
   'menu.hr': '人资',

+ 1 - 1
src/utils/ws.ts

@@ -1,7 +1,7 @@
 import { isDevMode } from '@/utils/env'
 import { isDevMode } from '@/utils/env'
 import { message } from 'antd'
 import { message } from 'antd'
 import type { MessageType } from '@/types/typing'
 import type { MessageType } from '@/types/typing'
-import wsNotice from '@/components/RightContent/Book/wsNotice'
+import wsNotice from '@/components/RightContent/Book/components/wsNotice'
 
 
 enum ReadyState {
 enum ReadyState {
   Connecting = 0,
   Connecting = 0,