开发: 添加及修改路由
This commit is contained in:
		@@ -29,58 +29,69 @@ export default [
 | 
			
		||||
  //   component: './Welcome',
 | 
			
		||||
  // },
 | 
			
		||||
  {
 | 
			
		||||
    path: '/departments',
 | 
			
		||||
    path: '/scrm',
 | 
			
		||||
    name: 'scrm',
 | 
			
		||||
    icon: 'crown',
 | 
			
		||||
    // access: 'canAdmin',
 | 
			
		||||
    routes: [
 | 
			
		||||
      {
 | 
			
		||||
        path: '/departments',
 | 
			
		||||
        redirect: '/departments/page/list',
 | 
			
		||||
        path: '/scrm',
 | 
			
		||||
        redirect: '/scrm/custom/workbench',
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        path: '/departments/page',
 | 
			
		||||
        name: '部门管理',
 | 
			
		||||
        // hideInBreadcrumb: true,
 | 
			
		||||
        // component: './Admin',
 | 
			
		||||
        path: '/scrm/custom',
 | 
			
		||||
        name: '客户管理',
 | 
			
		||||
        routes: [
 | 
			
		||||
          {
 | 
			
		||||
            path: '/departments/page',
 | 
			
		||||
            redirect: '/departments/page/list',
 | 
			
		||||
            path: '/scrm/custom',
 | 
			
		||||
            redirect: '/scrm/custom/workbench',
 | 
			
		||||
          },
 | 
			
		||||
          {
 | 
			
		||||
            name: '工作台',
 | 
			
		||||
            icon: 'table',
 | 
			
		||||
            path: '/scrm/custom/workbench',
 | 
			
		||||
            component: './Workbench',
 | 
			
		||||
          },
 | 
			
		||||
          {
 | 
			
		||||
            name: '客户列表',
 | 
			
		||||
            icon: 'table',
 | 
			
		||||
            path: '/scrm/custom/list',
 | 
			
		||||
            component: './CustomList',
 | 
			
		||||
          },
 | 
			
		||||
        ],
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        path: '/scrm/dep',
 | 
			
		||||
        name: '部门管理',
 | 
			
		||||
        routes: [
 | 
			
		||||
          {
 | 
			
		||||
            path: '/scrm/dep',
 | 
			
		||||
            redirect: '/scrm/dep/list',
 | 
			
		||||
          },
 | 
			
		||||
          {
 | 
			
		||||
            name: '部门员工',
 | 
			
		||||
            icon: 'table',
 | 
			
		||||
            path: '/departments/page/list',
 | 
			
		||||
            path: '/scrm/dep/list',
 | 
			
		||||
            component: './DepartmentsList',
 | 
			
		||||
          },
 | 
			
		||||
        ],
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        path: '/scrm/chat',
 | 
			
		||||
        name: '聊天记录',
 | 
			
		||||
        routes: [
 | 
			
		||||
          {
 | 
			
		||||
            path: '/scrm/chat',
 | 
			
		||||
            redirect: '/scrm/chat/list',
 | 
			
		||||
          },
 | 
			
		||||
          {
 | 
			
		||||
            name: '成员聊天',
 | 
			
		||||
            icon: 'table',
 | 
			
		||||
            path: '/departments/page/list2',
 | 
			
		||||
            component: './ChatLogs',
 | 
			
		||||
          },
 | 
			
		||||
          {
 | 
			
		||||
            name: '客户聊天',
 | 
			
		||||
            icon: 'table',
 | 
			
		||||
            path: '/departments/page/list3',
 | 
			
		||||
            path: '/scrm/chat/list',
 | 
			
		||||
            component: './ChatLogs',
 | 
			
		||||
          },
 | 
			
		||||
        ],
 | 
			
		||||
      },
 | 
			
		||||
      // {
 | 
			
		||||
      //   path: '/departments/sub-page2',
 | 
			
		||||
      //   name: '商品',
 | 
			
		||||
      //   // component: './TableList',
 | 
			
		||||
      //   routes: [
 | 
			
		||||
      //     {
 | 
			
		||||
      //       name: '列表',
 | 
			
		||||
      //       icon: 'table',
 | 
			
		||||
      //       path: '/departments/sub-page2/list',
 | 
			
		||||
      //       component: './TableList',
 | 
			
		||||
      //     },
 | 
			
		||||
      //   ],
 | 
			
		||||
      // },
 | 
			
		||||
    ],
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
@@ -92,7 +103,7 @@ export default [
 | 
			
		||||
  // },
 | 
			
		||||
  {
 | 
			
		||||
    path: '/',
 | 
			
		||||
    redirect: '/departments',
 | 
			
		||||
    redirect: '/scrm',
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    path: '*',
 | 
			
		||||
 
 | 
			
		||||
@@ -45,6 +45,7 @@
 | 
			
		||||
    "not ie <= 10"
 | 
			
		||||
  ],
 | 
			
		||||
  "dependencies": {
 | 
			
		||||
    "@ant-design/charts": "^1.4.2",
 | 
			
		||||
    "@ant-design/icons": "^5.0.1",
 | 
			
		||||
    "@ant-design/pro-components": "^2.3.57",
 | 
			
		||||
    "@ant-design/use-emotion-css": "1.0.4",
 | 
			
		||||
@@ -57,6 +58,7 @@
 | 
			
		||||
    "rc-menu": "^9.8.2",
 | 
			
		||||
    "rc-util": "^5.27.2",
 | 
			
		||||
    "react": "^18.2.0",
 | 
			
		||||
    "react-countup": "^6.4.2",
 | 
			
		||||
    "react-dev-inspector": "^1.8.4",
 | 
			
		||||
    "react-dom": "^18.2.0",
 | 
			
		||||
    "react-helmet-async": "^1.3.0"
 | 
			
		||||
 
 | 
			
		||||
@@ -106,7 +106,7 @@ export interface IChat {
 | 
			
		||||
}
 | 
			
		||||
export interface IChatItem {
 | 
			
		||||
  from?: IStaffsItem;
 | 
			
		||||
  to?: ICustFollow;
 | 
			
		||||
  to?: ICustFollow | IStaffsItem | IGroup;
 | 
			
		||||
  group?: any;
 | 
			
		||||
  chat?: IChat;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -5,6 +5,8 @@
 | 
			
		||||
 * @returns
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
import React, { useEffect, useState } from 'react';
 | 
			
		||||
 | 
			
		||||
export const groupMembersCount = (data: any[], state: any) => {
 | 
			
		||||
  let count = 0;
 | 
			
		||||
  data.forEach((item) => {
 | 
			
		||||
@@ -86,3 +88,35 @@ export const formatTags = (data: any) => {
 | 
			
		||||
  }
 | 
			
		||||
  return <></>;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
type IGroupIcon = {
 | 
			
		||||
  groupList: any[];
 | 
			
		||||
};
 | 
			
		||||
/**
 | 
			
		||||
 * todo 群头像拼接
 | 
			
		||||
 * @param props
 | 
			
		||||
 * @returns
 | 
			
		||||
 */
 | 
			
		||||
export const GroupIcon: React.FC<IGroupIcon> = (props) => {
 | 
			
		||||
  const [list, setList] = useState<any>([[], [], []]);
 | 
			
		||||
 | 
			
		||||
  useEffect(() => {
 | 
			
		||||
    let temp: any = [[], [], []];
 | 
			
		||||
    const { groupList } = props;
 | 
			
		||||
    for (let index = 0; index < 9; index++) {
 | 
			
		||||
      const element = groupList[index];
 | 
			
		||||
 | 
			
		||||
      if (index < 3) {
 | 
			
		||||
        temp[0].push(element);
 | 
			
		||||
      } else if (index < 6) {
 | 
			
		||||
        temp[1].push(element);
 | 
			
		||||
      } else {
 | 
			
		||||
        temp[2].push(element);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    console.log(temp);
 | 
			
		||||
  }, [props.groupList]);
 | 
			
		||||
 | 
			
		||||
  // <div className={styles.avatar}>{item.name ? item.name[0] : '群'}</div>;
 | 
			
		||||
  return <div></div>;
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -12,11 +12,7 @@ import { ChatBar } from './components/ChatBar';
 | 
			
		||||
import { ChatTime } from './components/ChatTime';
 | 
			
		||||
import { Gender } from './components/Gender';
 | 
			
		||||
import styles from './index.module.scss';
 | 
			
		||||
interface ISearchWord {
 | 
			
		||||
  '0': string;
 | 
			
		||||
  '1': string;
 | 
			
		||||
  '2': string;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const ChatLogs: React.FC = () => {
 | 
			
		||||
  const [param] = useState({
 | 
			
		||||
    curr_page: 1,
 | 
			
		||||
@@ -80,7 +76,7 @@ const ChatLogs: React.FC = () => {
 | 
			
		||||
    '3': '离职继承完成',
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  const [searchWord, setSearchWord] = useState<ISearchWord>({
 | 
			
		||||
  const [searchWord, setSearchWord] = useState<any>({
 | 
			
		||||
    '0': '',
 | 
			
		||||
    '1': '',
 | 
			
		||||
    '2': '',
 | 
			
		||||
@@ -100,72 +96,6 @@ const ChatLogs: React.FC = () => {
 | 
			
		||||
    setFlolowsBox(false);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // 监听DOM变动
 | 
			
		||||
  const callback = function (mutationsList: any) {
 | 
			
		||||
    // Use traditional 'for loops' for IE 11
 | 
			
		||||
    for (let mutation of mutationsList) {
 | 
			
		||||
      if (mutation.type === 'childList') {
 | 
			
		||||
        document.querySelector('.curr_page' + param.curr_page)?.scrollIntoView(true);
 | 
			
		||||
      } else if (mutation.type === 'attributes') {
 | 
			
		||||
        console.log('The ' + mutation.attributeName + ' attribute was modified.');
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  };
 | 
			
		||||
  const observer = new MutationObserver(callback);
 | 
			
		||||
 | 
			
		||||
  useEffect(() => {
 | 
			
		||||
    document.addEventListener('click', show, false);
 | 
			
		||||
    getStaffsList();
 | 
			
		||||
 | 
			
		||||
    observer.observe(chatBoxRef.current, { childList: true });
 | 
			
		||||
    const myScript = document.createElement('script');
 | 
			
		||||
    myScript.src = '/public/scripts/amrnb.js';
 | 
			
		||||
    myScript.async = false;
 | 
			
		||||
    document.body.appendChild(myScript);
 | 
			
		||||
 | 
			
		||||
    return () => {
 | 
			
		||||
      document.removeEventListener('click', show, false);
 | 
			
		||||
      observer.disconnect();
 | 
			
		||||
      document.body.removeChild(myScript);
 | 
			
		||||
    };
 | 
			
		||||
  }, []);
 | 
			
		||||
 | 
			
		||||
  // 获取员工
 | 
			
		||||
  const getStaffsList = () => {
 | 
			
		||||
    post({ url: '/Staffs/Data' }).then((res) => {
 | 
			
		||||
      if (res.err_code == 0) {
 | 
			
		||||
        if (Array.isArray(res.data) && res.data.length) {
 | 
			
		||||
          // setSelectStaff(res.data[0]);
 | 
			
		||||
          // selectStaffRef.current = res.data[0];
 | 
			
		||||
          res.data.forEach((element: IStaffsItem) => {
 | 
			
		||||
            if (element.user_id == 'yangxb') {
 | 
			
		||||
              setSelectStaff(element);
 | 
			
		||||
              selectStaffRef.current = element;
 | 
			
		||||
            }
 | 
			
		||||
          });
 | 
			
		||||
          setStaffsList(res.data);
 | 
			
		||||
          setInnerStaffsList(res.data);
 | 
			
		||||
          getCustFollowsList();
 | 
			
		||||
          getGroupList();
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  const page = (curr: number) => {
 | 
			
		||||
    param.curr_page = curr;
 | 
			
		||||
    param.msg_from = selectStaffRef.current?.user_id + '';
 | 
			
		||||
    if (tabKey == '0') {
 | 
			
		||||
      param.msg_to_list = selectInnerStaffRef.current?.user_id + '';
 | 
			
		||||
    } else if (tabKey == '1') {
 | 
			
		||||
      param.msg_to_list = selectCustFollowRef.current?.cust_id + '';
 | 
			
		||||
    } else {
 | 
			
		||||
      param.room_id = selectGroupRef.current?.group_id + '';
 | 
			
		||||
    }
 | 
			
		||||
    timeShowRef.current = false;
 | 
			
		||||
    getChatLogsList();
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  const getChatLogsList = () => {
 | 
			
		||||
    chatLogLoadingRef.current = true;
 | 
			
		||||
    setChatLogLoading(true);
 | 
			
		||||
@@ -216,6 +146,20 @@ const ChatLogs: React.FC = () => {
 | 
			
		||||
    });
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  const page = (curr: number) => {
 | 
			
		||||
    param.curr_page = curr;
 | 
			
		||||
    param.msg_from = selectStaffRef.current?.user_id + '';
 | 
			
		||||
    if (tabKey == '0') {
 | 
			
		||||
      param.msg_to_list = selectInnerStaffRef.current?.user_id + '';
 | 
			
		||||
    } else if (tabKey == '1') {
 | 
			
		||||
      param.msg_to_list = selectCustFollowRef.current?.cust_id + '';
 | 
			
		||||
    } else {
 | 
			
		||||
      param.room_id = selectGroupRef.current?.group_id + '';
 | 
			
		||||
    }
 | 
			
		||||
    timeShowRef.current = false;
 | 
			
		||||
    getChatLogsList();
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  const getGroupList = () => {
 | 
			
		||||
    post({
 | 
			
		||||
      url: '/Groups/GroupsList',
 | 
			
		||||
@@ -271,6 +215,58 @@ const ChatLogs: React.FC = () => {
 | 
			
		||||
    });
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  // 获取员工
 | 
			
		||||
  const getStaffsList = () => {
 | 
			
		||||
    post({ url: '/Staffs/Data' }).then((res) => {
 | 
			
		||||
      if (res.err_code == 0) {
 | 
			
		||||
        if (Array.isArray(res.data) && res.data.length) {
 | 
			
		||||
          setSelectStaff(res.data[0]);
 | 
			
		||||
          selectStaffRef.current = res.data[0];
 | 
			
		||||
          // res.data.forEach((element: IStaffsItem) => {
 | 
			
		||||
          //   if (element.user_id == 'yangxb') {
 | 
			
		||||
          //     setSelectStaff(element);
 | 
			
		||||
          //     selectStaffRef.current = element;
 | 
			
		||||
          //   }
 | 
			
		||||
          // });
 | 
			
		||||
          setStaffsList(res.data);
 | 
			
		||||
          setInnerStaffsList(res.data);
 | 
			
		||||
          getCustFollowsList();
 | 
			
		||||
          getGroupList();
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  // 监听DOM变动
 | 
			
		||||
  const callback = function (mutationsList: any) {
 | 
			
		||||
    // Use traditional 'for loops' for IE 11
 | 
			
		||||
    for (let mutation of mutationsList) {
 | 
			
		||||
      if (mutation.type === 'childList') {
 | 
			
		||||
        document.querySelector('.curr_page' + param.curr_page)?.scrollIntoView(true);
 | 
			
		||||
      } else if (mutation.type === 'attributes') {
 | 
			
		||||
        console.log('The ' + mutation.attributeName + ' attribute was modified.');
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  };
 | 
			
		||||
  const observer = new MutationObserver(callback);
 | 
			
		||||
 | 
			
		||||
  useEffect(() => {
 | 
			
		||||
    document.addEventListener('click', show, false);
 | 
			
		||||
    getStaffsList();
 | 
			
		||||
 | 
			
		||||
    observer.observe(chatBoxRef.current, { childList: true });
 | 
			
		||||
    const myScript = document.createElement('script');
 | 
			
		||||
    myScript.src = '/public/scripts/amrnb.js';
 | 
			
		||||
    myScript.async = false;
 | 
			
		||||
    document.body.appendChild(myScript);
 | 
			
		||||
 | 
			
		||||
    return () => {
 | 
			
		||||
      document.removeEventListener('click', show, false);
 | 
			
		||||
      observer.disconnect();
 | 
			
		||||
      document.body.removeChild(myScript);
 | 
			
		||||
    };
 | 
			
		||||
  }, []);
 | 
			
		||||
 | 
			
		||||
  // const { notification } = App.useApp();
 | 
			
		||||
  const tabContent = () => {
 | 
			
		||||
    if (tabKey == '0') {
 | 
			
		||||
@@ -549,7 +545,7 @@ const ChatLogs: React.FC = () => {
 | 
			
		||||
                <div
 | 
			
		||||
                  key={item.user_id}
 | 
			
		||||
                  className={styles.chatB}
 | 
			
		||||
                  onClick={(e) => {
 | 
			
		||||
                  onClick={() => {
 | 
			
		||||
                    setSelectCustFollow(undefined);
 | 
			
		||||
                    selectCustFollowRef.current = undefined;
 | 
			
		||||
                    setSelectInnerStaff(undefined);
 | 
			
		||||
@@ -637,7 +633,7 @@ const ChatLogs: React.FC = () => {
 | 
			
		||||
                setSearchWord({ ...searchWord });
 | 
			
		||||
              }}
 | 
			
		||||
              allowClear
 | 
			
		||||
            ></Input>
 | 
			
		||||
            />
 | 
			
		||||
          </Form>
 | 
			
		||||
          <Tabs
 | 
			
		||||
            items={tabs}
 | 
			
		||||
@@ -647,7 +643,7 @@ const ChatLogs: React.FC = () => {
 | 
			
		||||
            onChange={(val) => {
 | 
			
		||||
              setTabKey(val);
 | 
			
		||||
            }}
 | 
			
		||||
          ></Tabs>
 | 
			
		||||
          />
 | 
			
		||||
          <div className={styles.chatBBox}>{tabContent()}</div>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div style={{ flex: 1 }}>
 | 
			
		||||
@@ -688,9 +684,7 @@ const ChatLogs: React.FC = () => {
 | 
			
		||||
              footer={false}
 | 
			
		||||
            >
 | 
			
		||||
              {tabKeyRef.current == '0' ? (
 | 
			
		||||
                <DepartmentMembersDetail
 | 
			
		||||
                  record={selectInnerStaff as IStaffsItem}
 | 
			
		||||
                ></DepartmentMembersDetail>
 | 
			
		||||
                <DepartmentMembersDetail record={selectInnerStaff as IStaffsItem} />
 | 
			
		||||
              ) : tabKeyRef.current == '1' ? (
 | 
			
		||||
                <div>
 | 
			
		||||
                  <div
 | 
			
		||||
@@ -707,7 +701,7 @@ const ChatLogs: React.FC = () => {
 | 
			
		||||
                    <div style={{ display: 'flex', flexDirection: 'column' }}>
 | 
			
		||||
                      <div style={{ fontSize: 16 }}>
 | 
			
		||||
                        <span style={{ marginRight: 8 }}>{selectCustFollow?.name}</span>
 | 
			
		||||
                        <Gender gender={selectCustFollow?.gender}></Gender>
 | 
			
		||||
                        <Gender gender={selectCustFollow?.gender} />
 | 
			
		||||
                      </div>
 | 
			
		||||
                      <div style={{ color: '#666' }}>{selectCustFollow?.description}</div>
 | 
			
		||||
                    </div>
 | 
			
		||||
@@ -718,7 +712,8 @@ const ChatLogs: React.FC = () => {
 | 
			
		||||
              ) : (
 | 
			
		||||
                <div>
 | 
			
		||||
                  <div style={{ marginBottom: 8, textIndent: '2em' }}>
 | 
			
		||||
                    群主:{groupMembersObjRef.current[selectGroupRef.current?.owner]?.name}
 | 
			
		||||
                    群主:
 | 
			
		||||
                    {groupMembersObjRef.current[selectGroupRef.current?.owner as string]?.name}
 | 
			
		||||
                  </div>
 | 
			
		||||
                  <div style={{ marginBottom: 8 }}>
 | 
			
		||||
                    {adminList(selectGroupRef.current?.admin_list, groupMembersObjRef.current)}
 | 
			
		||||
@@ -937,26 +932,26 @@ const ChatLogs: React.FC = () => {
 | 
			
		||||
                  没有更多聊天记录了
 | 
			
		||||
                </div>
 | 
			
		||||
              ) : null}
 | 
			
		||||
              {chatLogs.map((item, i) => {
 | 
			
		||||
              {chatLogs.map((item) => {
 | 
			
		||||
                if (item.curr_page) {
 | 
			
		||||
                  return (
 | 
			
		||||
                    <div
 | 
			
		||||
                      key={item.curr_page}
 | 
			
		||||
                      className={`curr_page${param.curr_page}`}
 | 
			
		||||
                      style={{ height: 0 }}
 | 
			
		||||
                    ></div>
 | 
			
		||||
                    />
 | 
			
		||||
                  );
 | 
			
		||||
                } else {
 | 
			
		||||
                  return (
 | 
			
		||||
                    <div key={item.msg_id}>
 | 
			
		||||
                      {/* {item.show_time ? <ChatTime msgtime={item.msg_time}></ChatTime> : null} */}
 | 
			
		||||
                      <ChatTime msgtime={item.msg_time}></ChatTime>
 | 
			
		||||
                      <ChatTime msgtime={item.msg_time} />
 | 
			
		||||
                      {tabKey == '2' ? (
 | 
			
		||||
                        <ChatBar
 | 
			
		||||
                          from={selectStaff}
 | 
			
		||||
                          to={groupMembersObjRef.current[item.msg_from]}
 | 
			
		||||
                          chat={item}
 | 
			
		||||
                        ></ChatBar>
 | 
			
		||||
                        />
 | 
			
		||||
                      ) : (
 | 
			
		||||
                        <ChatBar
 | 
			
		||||
                          from={selectStaff}
 | 
			
		||||
@@ -966,7 +961,7 @@ const ChatLogs: React.FC = () => {
 | 
			
		||||
                              : selectCustFollowRef.current
 | 
			
		||||
                          }
 | 
			
		||||
                          chat={item}
 | 
			
		||||
                        ></ChatBar>
 | 
			
		||||
                        />
 | 
			
		||||
                      )}
 | 
			
		||||
                    </div>
 | 
			
		||||
                  );
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										0
									
								
								src/pages/CustomList/index.module.scss
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								src/pages/CustomList/index.module.scss
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										252
									
								
								src/pages/CustomList/index.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										252
									
								
								src/pages/CustomList/index.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,252 @@
 | 
			
		||||
import { SearchBarPlugin, SearchBottonsCardPlugin } from '@/components/SearchBarPlugin';
 | 
			
		||||
import { post } from '@/services/ajax';
 | 
			
		||||
import { PageContainer } from '@ant-design/pro-components';
 | 
			
		||||
import { Button, Col, DatePicker, Drawer, Form, Input, Pagination, Row, Table } from 'antd';
 | 
			
		||||
import { stringify } from 'qs';
 | 
			
		||||
import React, { useEffect, useState } from 'react';
 | 
			
		||||
import { IStaffsItem } from '../ChatLogs/ChatLogsType';
 | 
			
		||||
 | 
			
		||||
interface IDepartment {
 | 
			
		||||
  children: null | IDepartment[];
 | 
			
		||||
  department_leader: string;
 | 
			
		||||
  id: number;
 | 
			
		||||
  name: string;
 | 
			
		||||
  parent_id: number;
 | 
			
		||||
  sort: number;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
interface IStaffsData {
 | 
			
		||||
  count: number;
 | 
			
		||||
  data?: IStaffsItem[];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type Param = {
 | 
			
		||||
  curr_page: number;
 | 
			
		||||
  page_count: number;
 | 
			
		||||
  dep_id: number;
 | 
			
		||||
  name?: string;
 | 
			
		||||
  position?: string;
 | 
			
		||||
  telephone?: string;
 | 
			
		||||
  mobile?: string;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const CustomList: React.FC = () => {
 | 
			
		||||
  const [param] = useState<Param>({
 | 
			
		||||
    curr_page: 1,
 | 
			
		||||
    page_count: 20,
 | 
			
		||||
    dep_id: 0,
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  const [departmentID, setDepartmentsID] = useState<number>(0);
 | 
			
		||||
  const [departmentsList, setDepartmentsList] = useState<IDepartment[]>([]);
 | 
			
		||||
  const [staffsData, setStaffsData] = useState<IStaffsData>({ count: 0, data: [] });
 | 
			
		||||
  const [loadingL, setLoadingL] = useState(false);
 | 
			
		||||
  const [loading, setLoading] = useState(false);
 | 
			
		||||
  const [open, setOpen] = useState(false);
 | 
			
		||||
  const [record, setRecord] = useState<IStaffsItem>();
 | 
			
		||||
 | 
			
		||||
  const getStaffsList = () => {
 | 
			
		||||
    setLoading(true);
 | 
			
		||||
    post({ url: '/Staffs/List', data: stringify(param) }).then((res) => {
 | 
			
		||||
      setLoading(false);
 | 
			
		||||
      if (res.err_code == 0) {
 | 
			
		||||
        if (!Array.isArray(res.data)) {
 | 
			
		||||
          res.data = [];
 | 
			
		||||
        }
 | 
			
		||||
        setStaffsData(res as IStaffsData);
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  const getDepartmentsList = () => {
 | 
			
		||||
    setLoadingL(true);
 | 
			
		||||
    post({ url: '/Departments/List' }).then((res) => {
 | 
			
		||||
      setLoadingL(false);
 | 
			
		||||
      if (res.err_code == 0) {
 | 
			
		||||
        if (Array.isArray(res.data) && res.data.length) {
 | 
			
		||||
          param.dep_id = res.data[0].id;
 | 
			
		||||
          setDepartmentsID(param.dep_id);
 | 
			
		||||
          setDepartmentsList(res.data);
 | 
			
		||||
          getStaffsList();
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  const page = (page: number) => {
 | 
			
		||||
    param.curr_page = page;
 | 
			
		||||
    getStaffsList();
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  useEffect(() => {
 | 
			
		||||
    getDepartmentsList();
 | 
			
		||||
  }, []);
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <PageContainer>
 | 
			
		||||
      <div style={{ flexGrow: 1, minWidth: 0 }}>
 | 
			
		||||
        <SearchBarPlugin>
 | 
			
		||||
          <Form autoComplete="off">
 | 
			
		||||
            <Row gutter={{ xs: 0, sm: 16 }}>
 | 
			
		||||
              <Col xs={24} sm={12} md={8}>
 | 
			
		||||
                <Form.Item label="客户名称">
 | 
			
		||||
                  <Input
 | 
			
		||||
                    autoComplete="off"
 | 
			
		||||
                    onChange={(e) => {
 | 
			
		||||
                      param.name = e.target.value.trim();
 | 
			
		||||
                    }}
 | 
			
		||||
                    allowClear
 | 
			
		||||
                    onPressEnter={() => page(1)}
 | 
			
		||||
                  />
 | 
			
		||||
                </Form.Item>
 | 
			
		||||
              </Col>
 | 
			
		||||
              <Col xs={24} sm={12} md={8}>
 | 
			
		||||
                <Form.Item label={<span style={{ textIndent: '2em' }}>职务</span>}>
 | 
			
		||||
                  <Input
 | 
			
		||||
                    autoComplete="off"
 | 
			
		||||
                    onChange={(e) => {
 | 
			
		||||
                      param.position = e.target.value.trim();
 | 
			
		||||
                    }}
 | 
			
		||||
                    allowClear
 | 
			
		||||
                    onPressEnter={() => page(1)}
 | 
			
		||||
                  />
 | 
			
		||||
                </Form.Item>
 | 
			
		||||
              </Col>
 | 
			
		||||
              <Col xs={24} sm={12} md={8}>
 | 
			
		||||
                <Form.Item label={<span style={{ textIndent: '1em' }}>手机号</span>}>
 | 
			
		||||
                  <Input
 | 
			
		||||
                    autoComplete="off"
 | 
			
		||||
                    onChange={(e) => {
 | 
			
		||||
                      param.mobile = e.target.value.trim();
 | 
			
		||||
                    }}
 | 
			
		||||
                    allowClear
 | 
			
		||||
                    onPressEnter={() => page(1)}
 | 
			
		||||
                  />
 | 
			
		||||
                </Form.Item>
 | 
			
		||||
              </Col>
 | 
			
		||||
              <Col xs={24} sm={12} md={8}>
 | 
			
		||||
                <Form.Item label={<span style={{ textIndent: '1em' }}>手机号</span>}>
 | 
			
		||||
                  <DatePicker.RangePicker
 | 
			
		||||
                    style={{ width: '100%' }}
 | 
			
		||||
                    onChange={(dates, dateStrings) => {
 | 
			
		||||
                      console.log(dates, dateStrings);
 | 
			
		||||
                    }}
 | 
			
		||||
                  />
 | 
			
		||||
                </Form.Item>
 | 
			
		||||
              </Col>
 | 
			
		||||
              {/* <Col xs={24} sm={12} md={6}>
 | 
			
		||||
                  <Form.Item label="用户名">
 | 
			
		||||
                    <Select
 | 
			
		||||
                      defaultValue="lucy"
 | 
			
		||||
                      style={{ width: '100%' }}
 | 
			
		||||
                      onChange={() => {}}
 | 
			
		||||
                      options={[
 | 
			
		||||
                        { value: 'jack', label: 'Jack' },
 | 
			
		||||
                        { value: 'lucy', label: 'Lucy' },
 | 
			
		||||
                        { value: 'Yiminghe', label: 'yiminghe' },
 | 
			
		||||
                      ]}
 | 
			
		||||
                    />
 | 
			
		||||
                  </Form.Item>
 | 
			
		||||
                </Col> */}
 | 
			
		||||
            </Row>
 | 
			
		||||
          </Form>
 | 
			
		||||
        </SearchBarPlugin>
 | 
			
		||||
        <SearchBottonsCardPlugin>
 | 
			
		||||
          <Row justify={'center'}>
 | 
			
		||||
            <Button
 | 
			
		||||
              type="primary"
 | 
			
		||||
              onClick={() => {
 | 
			
		||||
                page(1);
 | 
			
		||||
              }}
 | 
			
		||||
              style={{ marginRight: 12 }}
 | 
			
		||||
            >
 | 
			
		||||
              搜索
 | 
			
		||||
            </Button>
 | 
			
		||||
          </Row>
 | 
			
		||||
        </SearchBottonsCardPlugin>
 | 
			
		||||
        <Table
 | 
			
		||||
          tableLayout="fixed"
 | 
			
		||||
          size="middle"
 | 
			
		||||
          dataSource={staffsData.data}
 | 
			
		||||
          style={{ padding: 16, borderRadius: 6, background: '#fff', marginTop: 16 }}
 | 
			
		||||
          pagination={false}
 | 
			
		||||
          bordered={true}
 | 
			
		||||
          rowKey={'user_id'}
 | 
			
		||||
          loading={loading}
 | 
			
		||||
        >
 | 
			
		||||
          <Table.Column
 | 
			
		||||
            title="客户"
 | 
			
		||||
            dataIndex={'name'}
 | 
			
		||||
            render={(value, record: IStaffsItem) => {
 | 
			
		||||
              return (
 | 
			
		||||
                <div
 | 
			
		||||
                  onClick={() => {
 | 
			
		||||
                    setRecord(record);
 | 
			
		||||
                    setOpen(true);
 | 
			
		||||
                  }}
 | 
			
		||||
                  style={{ color: '#1890ff', cursor: 'pointer' }}
 | 
			
		||||
                >
 | 
			
		||||
                  <span
 | 
			
		||||
                    style={{
 | 
			
		||||
                      display: 'inline-block',
 | 
			
		||||
                      marginRight: record.isleader == 1 ? 4 : 0,
 | 
			
		||||
                    }}
 | 
			
		||||
                  >
 | 
			
		||||
                    {value}
 | 
			
		||||
                  </span>
 | 
			
		||||
                  {record?.isleader == 1 ? (
 | 
			
		||||
                    <span
 | 
			
		||||
                      style={{
 | 
			
		||||
                        color: '#999',
 | 
			
		||||
                        fontSize: 12,
 | 
			
		||||
                        border: '1px solid #ddd',
 | 
			
		||||
                        borderRadius: 4,
 | 
			
		||||
                        padding: '2px 4px',
 | 
			
		||||
                      }}
 | 
			
		||||
                    >
 | 
			
		||||
                      负责人
 | 
			
		||||
                    </span>
 | 
			
		||||
                  ) : null}
 | 
			
		||||
                </div>
 | 
			
		||||
              );
 | 
			
		||||
            }}
 | 
			
		||||
          />
 | 
			
		||||
          <Table.Column title="客户标签" width={160} dataIndex={'position'} />
 | 
			
		||||
          <Table.Column title="跟进员工" width={160} dataIndex={'position'} />
 | 
			
		||||
          <Table.Column title="商机阶段" width={160} dataIndex={'position'} />
 | 
			
		||||
          <Table.Column title="添加方式" width={160} dataIndex={'position'} />
 | 
			
		||||
          <Table.Column title="添加时间" width={160} dataIndex={'position'} />
 | 
			
		||||
          <Table.Column title="操作" width={160} dataIndex={'position'} />
 | 
			
		||||
        </Table>
 | 
			
		||||
        <Drawer title="成员详情" open={open} onClose={() => setOpen(false)} width={800} />
 | 
			
		||||
        <Pagination
 | 
			
		||||
          style={{
 | 
			
		||||
            background: '#fff',
 | 
			
		||||
            borderRadius: 6,
 | 
			
		||||
            marginTop: 16,
 | 
			
		||||
            padding: 16,
 | 
			
		||||
            display: 'flex',
 | 
			
		||||
            justifyContent: 'center',
 | 
			
		||||
            alignItems: 'center',
 | 
			
		||||
          }}
 | 
			
		||||
          current={param.curr_page}
 | 
			
		||||
          pageSize={param.page_count}
 | 
			
		||||
          total={staffsData.count}
 | 
			
		||||
          pageSizeOptions={[10, 20, 50, 100]}
 | 
			
		||||
          onShowSizeChange={(current, size) => {
 | 
			
		||||
            param.page_count = size;
 | 
			
		||||
            page(1);
 | 
			
		||||
          }}
 | 
			
		||||
          showTotal={(total) => {
 | 
			
		||||
            return <span style={{ lineHeight: 1 }}>共{total}条</span>;
 | 
			
		||||
          }}
 | 
			
		||||
          onChange={(curr) => {
 | 
			
		||||
            page(curr);
 | 
			
		||||
          }}
 | 
			
		||||
        />
 | 
			
		||||
      </div>
 | 
			
		||||
    </PageContainer>
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export default CustomList;
 | 
			
		||||
@@ -51,7 +51,7 @@ const DepartmentsList: React.FC = () => {
 | 
			
		||||
    page_count: 20,
 | 
			
		||||
    dep_id: 0,
 | 
			
		||||
  });
 | 
			
		||||
  const { notification, modal } = App.useApp();
 | 
			
		||||
  const { notification } = App.useApp();
 | 
			
		||||
  const [departmentID, setDepartmentsID] = useState<number>(0);
 | 
			
		||||
  const [departmentsList, setDepartmentsList] = useState<IDepartment[]>([]);
 | 
			
		||||
  const [staffsData, setStaffsData] = useState<IStaffsData>({ count: 0, data: [] });
 | 
			
		||||
@@ -60,6 +60,19 @@ const DepartmentsList: React.FC = () => {
 | 
			
		||||
  const [open, setOpen] = useState(false);
 | 
			
		||||
  const [record, setRecord] = useState<IStaffsItem>();
 | 
			
		||||
 | 
			
		||||
  const getStaffsList = () => {
 | 
			
		||||
    setLoading(true);
 | 
			
		||||
    post({ url: '/Staffs/List', data: stringify(param) }).then((res) => {
 | 
			
		||||
      setLoading(false);
 | 
			
		||||
      if (res.err_code == 0) {
 | 
			
		||||
        if (!Array.isArray(res.data)) {
 | 
			
		||||
          res.data = [];
 | 
			
		||||
        }
 | 
			
		||||
        setStaffsData(res as IStaffsData);
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  const getDepartmentsList = () => {
 | 
			
		||||
    setLoadingL(true);
 | 
			
		||||
    post({ url: '/Departments/List' }).then((res) => {
 | 
			
		||||
@@ -74,18 +87,6 @@ const DepartmentsList: React.FC = () => {
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
  };
 | 
			
		||||
  const getStaffsList = () => {
 | 
			
		||||
    setLoading(true);
 | 
			
		||||
    post({ url: '/Staffs/List', data: stringify(param) }).then((res) => {
 | 
			
		||||
      setLoading(false);
 | 
			
		||||
      if (res.err_code == 0) {
 | 
			
		||||
        if (!Array.isArray(res.data)) {
 | 
			
		||||
          res.data = [];
 | 
			
		||||
        }
 | 
			
		||||
        setStaffsData(res as IStaffsData);
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  const page = (page: number) => {
 | 
			
		||||
    param.curr_page = page;
 | 
			
		||||
@@ -162,7 +163,7 @@ const DepartmentsList: React.FC = () => {
 | 
			
		||||
                blockNode
 | 
			
		||||
                selectedKeys={[departmentID]}
 | 
			
		||||
                defaultExpandAll
 | 
			
		||||
                treeData={departmentsList}
 | 
			
		||||
                treeData={departmentsList as any}
 | 
			
		||||
                fieldNames={{ title: 'name', key: 'id' }}
 | 
			
		||||
                onSelect={(selectedKeys) => {
 | 
			
		||||
                  if (selectedKeys.length) {
 | 
			
		||||
@@ -212,7 +213,7 @@ const DepartmentsList: React.FC = () => {
 | 
			
		||||
                    </div>
 | 
			
		||||
                  );
 | 
			
		||||
                }}
 | 
			
		||||
              ></Tree>
 | 
			
		||||
              />
 | 
			
		||||
            ) : null}
 | 
			
		||||
          </Spin>
 | 
			
		||||
        </div>
 | 
			
		||||
@@ -246,7 +247,7 @@ const DepartmentsList: React.FC = () => {
 | 
			
		||||
            <Form autoComplete="off">
 | 
			
		||||
              <Row gutter={{ xs: 0, sm: 16 }}>
 | 
			
		||||
                <Col xs={24} sm={12} md={8}>
 | 
			
		||||
                  <Form.Item label="姓名">
 | 
			
		||||
                  <Form.Item label={<span style={{ textIndent: '1em' }}>姓名</span>}>
 | 
			
		||||
                    <Input
 | 
			
		||||
                      autoComplete="off"
 | 
			
		||||
                      onChange={(e) => {
 | 
			
		||||
@@ -254,11 +255,11 @@ const DepartmentsList: React.FC = () => {
 | 
			
		||||
                      }}
 | 
			
		||||
                      allowClear
 | 
			
		||||
                      onPressEnter={() => page(1)}
 | 
			
		||||
                    ></Input>
 | 
			
		||||
                    />
 | 
			
		||||
                  </Form.Item>
 | 
			
		||||
                </Col>
 | 
			
		||||
                <Col xs={24} sm={12} md={8}>
 | 
			
		||||
                  <Form.Item label="职务">
 | 
			
		||||
                  <Form.Item label={<span style={{ textIndent: '1em' }}>职务</span>}>
 | 
			
		||||
                    <Input
 | 
			
		||||
                      autoComplete="off"
 | 
			
		||||
                      onChange={(e) => {
 | 
			
		||||
@@ -266,7 +267,7 @@ const DepartmentsList: React.FC = () => {
 | 
			
		||||
                      }}
 | 
			
		||||
                      allowClear
 | 
			
		||||
                      onPressEnter={() => page(1)}
 | 
			
		||||
                    ></Input>
 | 
			
		||||
                    />
 | 
			
		||||
                  </Form.Item>
 | 
			
		||||
                </Col>
 | 
			
		||||
                <Col xs={24} sm={12} md={8}>
 | 
			
		||||
@@ -278,7 +279,7 @@ const DepartmentsList: React.FC = () => {
 | 
			
		||||
                      }}
 | 
			
		||||
                      allowClear
 | 
			
		||||
                      onPressEnter={() => page(1)}
 | 
			
		||||
                    ></Input>
 | 
			
		||||
                    />
 | 
			
		||||
                  </Form.Item>
 | 
			
		||||
                </Col>
 | 
			
		||||
                {/* <Col xs={24} sm={12} md={6}>
 | 
			
		||||
@@ -374,20 +375,20 @@ const DepartmentsList: React.FC = () => {
 | 
			
		||||
                  </div>
 | 
			
		||||
                );
 | 
			
		||||
              }}
 | 
			
		||||
            ></Table.Column>
 | 
			
		||||
            <Table.Column title="职务" width={160} dataIndex={'position'}></Table.Column>
 | 
			
		||||
            />
 | 
			
		||||
            <Table.Column title="职务" width={160} dataIndex={'position'} />
 | 
			
		||||
            <Table.Column
 | 
			
		||||
              title="部门"
 | 
			
		||||
              dataIndex={'dep_name'}
 | 
			
		||||
              render={(val) => {
 | 
			
		||||
                return <>{val.join(',')}</>;
 | 
			
		||||
              }}
 | 
			
		||||
            ></Table.Column>
 | 
			
		||||
            <Table.Column title="手机号" width={160} dataIndex={'mobile'}></Table.Column>
 | 
			
		||||
            <Table.Column title="企业邮箱" dataIndex={'biz_mail'}></Table.Column>
 | 
			
		||||
            />
 | 
			
		||||
            <Table.Column title="手机号" width={160} dataIndex={'mobile'} />
 | 
			
		||||
            <Table.Column title="企业邮箱" dataIndex={'biz_mail'} />
 | 
			
		||||
          </Table>
 | 
			
		||||
          <Drawer title="成员详情" open={open} onClose={() => setOpen(false)} width={800}>
 | 
			
		||||
            <DepartmentMembersDetail record={record as IStaffsItem}></DepartmentMembersDetail>
 | 
			
		||||
            <DepartmentMembersDetail record={record as IStaffsItem} />
 | 
			
		||||
          </Drawer>
 | 
			
		||||
          <Pagination
 | 
			
		||||
            style={{
 | 
			
		||||
@@ -408,10 +409,10 @@ const DepartmentsList: React.FC = () => {
 | 
			
		||||
              // setParam({ ...param });
 | 
			
		||||
              page(1);
 | 
			
		||||
            }}
 | 
			
		||||
            showTotal={(total, range) => {
 | 
			
		||||
            showTotal={(total) => {
 | 
			
		||||
              return <span style={{ lineHeight: 1 }}>共{total}条</span>;
 | 
			
		||||
            }}
 | 
			
		||||
            onChange={(curr, pageSize) => {
 | 
			
		||||
            onChange={(curr) => {
 | 
			
		||||
              page(curr);
 | 
			
		||||
            }}
 | 
			
		||||
          />
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										27
									
								
								src/pages/Workbench/components/DataItemCard.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								src/pages/Workbench/components/DataItemCard.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,27 @@
 | 
			
		||||
import { ExclamationCircleOutlined } from '@ant-design/icons';
 | 
			
		||||
import { Popover } from 'antd';
 | 
			
		||||
import React from 'react';
 | 
			
		||||
import CountUp from 'react-countup';
 | 
			
		||||
import styles from '../index.module.scss';
 | 
			
		||||
 | 
			
		||||
type IProps = {
 | 
			
		||||
  title: string;
 | 
			
		||||
  content: string;
 | 
			
		||||
  count: number;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const DataItemCard: React.FC<IProps> = (props) => {
 | 
			
		||||
  return (
 | 
			
		||||
    <div className={styles.dataCard}>
 | 
			
		||||
      <div>
 | 
			
		||||
        <span style={{ marginRight: 8 }}>{props.title}</span>
 | 
			
		||||
        <Popover placement="topLeft" content={props.content}>
 | 
			
		||||
          <ExclamationCircleOutlined style={{ cursor: 'pointer', color: '#999' }} />
 | 
			
		||||
        </Popover>
 | 
			
		||||
      </div>
 | 
			
		||||
      <div className={styles.dataCount}>
 | 
			
		||||
        <CountUp end={props.count} duration={2}></CountUp>
 | 
			
		||||
      </div>
 | 
			
		||||
    </div>
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										30
									
								
								src/pages/Workbench/components/DataSumItemCard.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								src/pages/Workbench/components/DataSumItemCard.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,30 @@
 | 
			
		||||
import { ExclamationCircleOutlined } from '@ant-design/icons';
 | 
			
		||||
import { Popover } from 'antd';
 | 
			
		||||
import React from 'react';
 | 
			
		||||
import CountUp from 'react-countup';
 | 
			
		||||
import styles from '../index.module.scss';
 | 
			
		||||
type IProps = {
 | 
			
		||||
  title: string;
 | 
			
		||||
  content: string;
 | 
			
		||||
  count: number;
 | 
			
		||||
  icon: React.ReactNode;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const DataSumItemCard: React.FC<IProps> = (props) => {
 | 
			
		||||
  return (
 | 
			
		||||
    <div className={styles.dataItem}>
 | 
			
		||||
      <div className={styles.dataIconBox}>{props.icon}</div>
 | 
			
		||||
      <div>
 | 
			
		||||
        <div>
 | 
			
		||||
          <span style={{ marginRight: 8 }}>{props.title}</span>
 | 
			
		||||
          <Popover placement="topLeft" content={props.content}>
 | 
			
		||||
            <ExclamationCircleOutlined style={{ cursor: 'pointer', color: '#999' }} />
 | 
			
		||||
          </Popover>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div className={styles.dataCount}>
 | 
			
		||||
          <CountUp end={props.count} duration={2} />
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
    </div>
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										48
									
								
								src/pages/Workbench/index.module.scss
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								src/pages/Workbench/index.module.scss
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,48 @@
 | 
			
		||||
.dataSum {
 | 
			
		||||
  margin-bottom: 24px;
 | 
			
		||||
  padding: 24px;
 | 
			
		||||
  color: #000;
 | 
			
		||||
  background-color: #fff;
 | 
			
		||||
  border-radius: 8px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.dataItem {
 | 
			
		||||
  display: inline-flex;
 | 
			
		||||
  flex: 1;
 | 
			
		||||
  margin-bottom: 12px;
 | 
			
		||||
  border-right: 1px solid #ddd;
 | 
			
		||||
  padding-left: 24px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.dataIconBox {
 | 
			
		||||
  display: flex;
 | 
			
		||||
  align-items: center;
 | 
			
		||||
  justify-content: center;
 | 
			
		||||
  width: 64px;
 | 
			
		||||
  height: 64px;
 | 
			
		||||
  margin-right: 16px;
 | 
			
		||||
  background-color: #edf2f9;
 | 
			
		||||
  border-radius: 12px;
 | 
			
		||||
 | 
			
		||||
  .icon {
 | 
			
		||||
    color: #1890ff;
 | 
			
		||||
    font-size: 32px;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.dataCount {
 | 
			
		||||
  font-size: 24px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.dataCardBox {
 | 
			
		||||
  display: flex;
 | 
			
		||||
  margin-top: 16px;
 | 
			
		||||
  margin-bottom: 16px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.dataCard {
 | 
			
		||||
  padding: 16px;
 | 
			
		||||
  border: 1px solid #ddd;
 | 
			
		||||
  border-radius: 12px;
 | 
			
		||||
  flex: 1;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										166
									
								
								src/pages/Workbench/index.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										166
									
								
								src/pages/Workbench/index.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,166 @@
 | 
			
		||||
import { Area, Line } from '@ant-design/charts';
 | 
			
		||||
import { BarChartOutlined, CommentOutlined, TeamOutlined } from '@ant-design/icons';
 | 
			
		||||
import { PageContainer } from '@ant-design/pro-components';
 | 
			
		||||
import React, { useEffect, useState } from 'react';
 | 
			
		||||
import { DataItemCard } from './components/DataItemCard';
 | 
			
		||||
import { DataSumItemCard } from './components/DataSumItemCard';
 | 
			
		||||
import styles from './index.module.scss';
 | 
			
		||||
 | 
			
		||||
const Workbench: React.FC = () => {
 | 
			
		||||
  const [data, setData] = useState([]);
 | 
			
		||||
 | 
			
		||||
  const asyncFetch = () => {
 | 
			
		||||
    fetch('https://gw.alipayobjects.com/os/bmw-prod/e00d52f4-2fa6-47ee-a0d7-105dd95bde20.json')
 | 
			
		||||
      .then((response) => response.json())
 | 
			
		||||
      .then((json) => setData(json))
 | 
			
		||||
      .catch((error) => {
 | 
			
		||||
        console.log('fetch data failed', error);
 | 
			
		||||
      });
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  useEffect(() => {
 | 
			
		||||
    asyncFetch();
 | 
			
		||||
  }, []);
 | 
			
		||||
 | 
			
		||||
  const [dataArea, setDataArea] = useState([
 | 
			
		||||
    { type: '订单数', date: '2021-02-01', value: 400 },
 | 
			
		||||
    { type: '订单数', date: '2021-02-05', value: 900 },
 | 
			
		||||
    { type: '订单数', date: '2021-02-08', value: 300 },
 | 
			
		||||
    { type: '收衣数', date: '2021-02-01', value: 700 },
 | 
			
		||||
    { type: '收衣数', date: '2021-02-05', value: 500 },
 | 
			
		||||
    { type: '收衣数', date: '2021-02-08', value: 1000 },
 | 
			
		||||
  ]);
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <PageContainer>
 | 
			
		||||
      <div className={styles.dataSum} style={{ padding: '24px 0' }}>
 | 
			
		||||
        <div
 | 
			
		||||
          style={{
 | 
			
		||||
            display: 'flex',
 | 
			
		||||
            justifyContent: 'space-between',
 | 
			
		||||
            marginBottom: 24,
 | 
			
		||||
            padding: '0 24px',
 | 
			
		||||
          }}
 | 
			
		||||
        >
 | 
			
		||||
          <span style={{ fontSize: 16 }}>数据总览</span>
 | 
			
		||||
          <span style={{ color: '#999' }}>更新时间:2022-12-12 23:30:30</span>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div style={{ display: 'flex' }}>
 | 
			
		||||
          <DataSumItemCard
 | 
			
		||||
            title="客户数量"
 | 
			
		||||
            content="当前员工权限范围内的全部客户数量(含重复)"
 | 
			
		||||
            icon={<TeamOutlined className={styles.icon} />}
 | 
			
		||||
            count={111}
 | 
			
		||||
          />
 | 
			
		||||
          <DataSumItemCard
 | 
			
		||||
            title="客群数量"
 | 
			
		||||
            content="当前员工权限范围内的全部客群数量"
 | 
			
		||||
            icon={<CommentOutlined className={styles.icon} />}
 | 
			
		||||
            count={111}
 | 
			
		||||
          />
 | 
			
		||||
          <DataSumItemCard
 | 
			
		||||
            title="客群成员总数"
 | 
			
		||||
            content="当前员工权限范围内客群成员的总数(含员工)(含重复)"
 | 
			
		||||
            icon={<BarChartOutlined className={styles.icon} />}
 | 
			
		||||
            count={111}
 | 
			
		||||
          />
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
      <div className={styles.dataSum}>
 | 
			
		||||
        <div style={{ fontSize: 16 }}>客户数据</div>
 | 
			
		||||
        <div className={styles.dataCardBox}>
 | 
			
		||||
          <DataItemCard
 | 
			
		||||
            title="今日新增客户"
 | 
			
		||||
            content="当前员工权限范围内今日添加的客户数(含重复及流失)"
 | 
			
		||||
            count={11}
 | 
			
		||||
          />
 | 
			
		||||
          <span style={{ width: 16 }} />
 | 
			
		||||
          <DataItemCard
 | 
			
		||||
            title="今日跟进客户"
 | 
			
		||||
            content="当前员工权限范围内今日处于跟进中状态的客户数(含重复)"
 | 
			
		||||
            count={11}
 | 
			
		||||
          />
 | 
			
		||||
          <span style={{ width: 16 }} />
 | 
			
		||||
          <DataItemCard
 | 
			
		||||
            title="今日净增客户"
 | 
			
		||||
            content="当前员工权限范围内今日添加的客户数(不含重复及流失)"
 | 
			
		||||
            count={11}
 | 
			
		||||
          />
 | 
			
		||||
          <span style={{ width: 16 }} />
 | 
			
		||||
          <DataItemCard
 | 
			
		||||
            title="今日流失客户"
 | 
			
		||||
            content="当前员工权限范围内的流失的全部客户数量"
 | 
			
		||||
            count={11}
 | 
			
		||||
          />
 | 
			
		||||
          <span style={{ width: 16 }} />
 | 
			
		||||
          <DataItemCard
 | 
			
		||||
            title="昨日发送申请"
 | 
			
		||||
            content="当前员工数权限范围内主动向客户发起的申请数"
 | 
			
		||||
            count={11}
 | 
			
		||||
          />
 | 
			
		||||
        </div>
 | 
			
		||||
        <Line
 | 
			
		||||
          data={data}
 | 
			
		||||
          xField="year"
 | 
			
		||||
          yField="gdp"
 | 
			
		||||
          seriesField="name"
 | 
			
		||||
          // yAxis= {{
 | 
			
		||||
          //   label: {
 | 
			
		||||
          //     formatter: (v: any) => `${(v / 10e8).toFixed(1)} B`,
 | 
			
		||||
          //   },
 | 
			
		||||
          // },}
 | 
			
		||||
          legend={{
 | 
			
		||||
            position: 'top',
 | 
			
		||||
          }}
 | 
			
		||||
          smooth={true}
 | 
			
		||||
          // @TODO 后续会换一种动画方式
 | 
			
		||||
          animation={{
 | 
			
		||||
            appear: {
 | 
			
		||||
              animation: 'path-in',
 | 
			
		||||
              duration: 3000,
 | 
			
		||||
            },
 | 
			
		||||
          }}
 | 
			
		||||
        />
 | 
			
		||||
      </div>
 | 
			
		||||
      <div className={styles.dataSum}>
 | 
			
		||||
        <div style={{ fontSize: 16 }}>客群数据</div>
 | 
			
		||||
        <div className={styles.dataCardBox}>
 | 
			
		||||
          <DataItemCard
 | 
			
		||||
            title="今日新增客群"
 | 
			
		||||
            content="当前员工权限范围内今日创建的客群数"
 | 
			
		||||
            count={11}
 | 
			
		||||
          />
 | 
			
		||||
          <span style={{ width: 16 }} />
 | 
			
		||||
          <DataItemCard
 | 
			
		||||
            title="今日解散客群"
 | 
			
		||||
            content="当前员工权限范围内今日解散的客群数"
 | 
			
		||||
            count={11}
 | 
			
		||||
          />
 | 
			
		||||
          <span style={{ width: 16 }} />
 | 
			
		||||
          <DataItemCard
 | 
			
		||||
            title="今日新增成员"
 | 
			
		||||
            content="当前员工权限范围内今日新增客群成员数(含员工)"
 | 
			
		||||
            count={11}
 | 
			
		||||
          />
 | 
			
		||||
          <span style={{ width: 16 }} />
 | 
			
		||||
          <DataItemCard
 | 
			
		||||
            title="今日退出成员"
 | 
			
		||||
            content="当前员工权限范围内今日退出客群成员数(含员工)"
 | 
			
		||||
            count={11}
 | 
			
		||||
          />
 | 
			
		||||
        </div>
 | 
			
		||||
        <Area
 | 
			
		||||
          data={dataArea}
 | 
			
		||||
          xField="date"
 | 
			
		||||
          yField="value"
 | 
			
		||||
          seriesField="type"
 | 
			
		||||
          smooth={true}
 | 
			
		||||
          legend={{ position: 'top' }}
 | 
			
		||||
          isStack={false}
 | 
			
		||||
        />
 | 
			
		||||
      </div>
 | 
			
		||||
    </PageContainer>
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export default Workbench;
 | 
			
		||||
		Reference in New Issue
	
	Block a user