|
@@ -0,0 +1,357 @@
|
|
|
|
|
+import { Avatar, Button, DatePicker, Drawer, Input, Modal, Select, Space, Table } from "antd";
|
|
|
|
|
+import React, { useEffect, useState } from "react";
|
|
|
|
|
+import style from './index.less';
|
|
|
|
|
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
|
|
|
|
+import { faUserFriends } from "@fortawesome/free-solid-svg-icons";
|
|
|
|
|
+import { useAjax } from "@/Hook/useAjax";
|
|
|
|
|
+import { getPullGroupListApi, GetPullGroupListProps } from "../../API/logs";
|
|
|
|
|
+import SearchBox from "../../components/searchBox";
|
|
|
|
|
+import { ExclamationCircleOutlined, SearchOutlined } from "@ant-design/icons";
|
|
|
|
|
+import dayJs from "dayjs";
|
|
|
|
|
+import { getProjectGroupsAllListApi } from "../../API/groupManage";
|
|
|
|
|
+import SelectGroupLeader from "../groupLeaderManage/selectGroupLeader";
|
|
|
|
|
+import { copy } from "@/utils/utils";
|
|
|
|
|
+import { TaskDetails } from "../groupChat/taskList/details";
|
|
|
|
|
+
|
|
|
|
|
+interface Props {
|
|
|
|
|
+ pullGroupCount: number,
|
|
|
|
|
+ pullSuccessCount: number,
|
|
|
|
|
+ pullSuccessRate: number,
|
|
|
|
|
+ pullFailCount: number
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/**
|
|
|
|
|
+ * 群聊创建
|
|
|
|
|
+ * @param param0
|
|
|
|
|
+ * @returns
|
|
|
|
|
+ */
|
|
|
|
|
+const PullGroup: React.FC<Props> = ({ pullFailCount, pullGroupCount, pullSuccessCount, pullSuccessRate }) => {
|
|
|
|
|
+
|
|
|
|
|
+ /**************************************/
|
|
|
|
|
+ const [visible, setVisible] = useState<boolean>(false)
|
|
|
|
|
+ const [queryParams, setQueryParams] = useState<GetPullGroupListProps>({ pageNum: 1, pageSize: 20 })
|
|
|
|
|
+ const [oldQueryParams, setOldQueryParams] = useState<GetPullGroupListProps>({ pageNum: 1, pageSize: 20 })
|
|
|
|
|
+ const [groupList, setGroupList] = useState<{ label: string, value: number }[]>([])
|
|
|
|
|
+ const [detalisData, setDetalisData] = useState<{ visible?: boolean, taskLogId?: number }>()
|
|
|
|
|
+ const [modal, contextHolder] = Modal.useModal();
|
|
|
|
|
+
|
|
|
|
|
+ const getPullGroupList = useAjax((params) => getPullGroupListApi(params))
|
|
|
|
|
+ const getProjectGroupsAllList = useAjax(() => getProjectGroupsAllListApi())
|
|
|
|
|
+ /**************************************/
|
|
|
|
|
+
|
|
|
|
|
+ useEffect(() => {
|
|
|
|
|
+ if (visible) {
|
|
|
|
|
+ const { corpChatUserIds, ...p } = queryParams
|
|
|
|
|
+ let params: any = { ...p }
|
|
|
|
|
+ if (corpChatUserIds?.length) {
|
|
|
|
|
+ params.corpChatUserIds = corpChatUserIds.map(item => item.value)
|
|
|
|
|
+ } else {
|
|
|
|
|
+ delete params?.corpChatUserIds
|
|
|
|
|
+ }
|
|
|
|
|
+ getPullGroupList.run(params)
|
|
|
|
|
+ }
|
|
|
|
|
+ }, [queryParams, visible])
|
|
|
|
|
+
|
|
|
|
|
+ useEffect(() => {
|
|
|
|
|
+ getProjectGroupsAllList.run().then(res => {
|
|
|
|
|
+ setGroupList([{ label: '空项目组', value: 0 }, ...res?.data?.map(item => ({ label: item.name, value: item.id })) || []])
|
|
|
|
|
+ })
|
|
|
|
|
+ }, [])
|
|
|
|
|
+
|
|
|
|
|
+ return <>
|
|
|
|
|
+ {contextHolder}
|
|
|
|
|
+ <div className={style.moduleCard} onClick={() => setVisible(true)}>
|
|
|
|
|
+ <div className={style.moduleCard_header}>
|
|
|
|
|
+ <Avatar style={{ backgroundColor: '#f6ffed', color: '#73d13d' }} size={30}>
|
|
|
|
|
+ <FontAwesomeIcon icon={faUserFriends} />
|
|
|
|
|
+ </Avatar>
|
|
|
|
|
+ 群聊创建-拉群
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div className={style.moduleCard_center}>
|
|
|
|
|
+ 预计拉群数:{pullGroupCount || 0} | 拉群成功数:{pullSuccessCount || 0}
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div className={style.moduleCard_footer}>
|
|
|
|
|
+ <span>今日拉群成功率:{((pullSuccessRate || 0) * 100).toFixed(2)} %</span>
|
|
|
|
|
+ <span className={style.error}>今日拉群失败数:{pullFailCount}</span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <Drawer
|
|
|
|
|
+ title="群聊创建"
|
|
|
|
|
+ onClose={() => setVisible(false)}
|
|
|
|
|
+ open={visible}
|
|
|
|
|
+ width={'70%'}
|
|
|
|
|
+ >
|
|
|
|
|
+ <SearchBox
|
|
|
|
|
+ bodyPadding={`0 0 10px`}
|
|
|
|
|
+ buttons={<>
|
|
|
|
|
+ <Button type="primary" onClick={() => {
|
|
|
|
|
+ setQueryParams({ ...oldQueryParams, pageNum: 1, pageSize: queryParams.pageSize })
|
|
|
|
|
+ }} loading={getPullGroupList.loading} icon={<SearchOutlined />}>搜索</Button>
|
|
|
|
|
+ </>}
|
|
|
|
|
+ >
|
|
|
|
|
+ <>
|
|
|
|
|
+ <Input
|
|
|
|
|
+ placeholder='任务名称'
|
|
|
|
|
+ style={{ width: 150 }}
|
|
|
|
|
+ allowClear
|
|
|
|
|
+ value={oldQueryParams?.projectName}
|
|
|
|
|
+ onChange={(e) => setOldQueryParams({ ...oldQueryParams, projectName: e.target.value })}
|
|
|
|
|
+ />
|
|
|
|
|
+ <Input
|
|
|
|
|
+ placeholder='任务ID'
|
|
|
|
|
+ style={{ width: 150 }}
|
|
|
|
|
+ allowClear
|
|
|
|
|
+ value={oldQueryParams?.taskId}
|
|
|
|
|
+ onChange={(e) => setOldQueryParams({ ...oldQueryParams, taskId: e.target.value })}
|
|
|
|
|
+ />
|
|
|
|
|
+ <Input
|
|
|
|
|
+ placeholder='群名称'
|
|
|
|
|
+ style={{ width: 150 }}
|
|
|
|
|
+ allowClear
|
|
|
|
|
+ value={oldQueryParams?.groupName}
|
|
|
|
|
+ onChange={(e) => setOldQueryParams({ ...oldQueryParams, groupName: e.target.value })}
|
|
|
|
|
+ />
|
|
|
|
|
+ <SelectGroupLeader
|
|
|
|
|
+ value={oldQueryParams?.corpChatUserIds}
|
|
|
|
|
+ onChange={(corpChatUserIds) => {
|
|
|
|
|
+ setOldQueryParams({ ...oldQueryParams, corpChatUserIds })
|
|
|
|
|
+ }}
|
|
|
|
|
+ />
|
|
|
|
|
+ <Select
|
|
|
|
|
+ placeholder='请选择项目组'
|
|
|
|
|
+ allowClear
|
|
|
|
|
+ options={groupList}
|
|
|
|
|
+ showSearch
|
|
|
|
|
+ style={{ width: 150 }}
|
|
|
|
|
+ filterOption={(input, option) =>
|
|
|
|
|
+ (option?.label ?? '').toLowerCase().includes(input.toLowerCase())
|
|
|
|
|
+ }
|
|
|
|
|
+ mode="multiple"
|
|
|
|
|
+ value={oldQueryParams?.projectGroupIds}
|
|
|
|
|
+ onChange={(value) => setOldQueryParams({ ...oldQueryParams, projectGroupIds: value })}
|
|
|
|
|
+ />
|
|
|
|
|
+ <DatePicker.RangePicker
|
|
|
|
|
+ placeholder={['执行开始日期', '执行结束日期']}
|
|
|
|
|
+ allowClear
|
|
|
|
|
+ value={oldQueryParams?.startDay ? [dayJs(oldQueryParams?.startDay), dayJs(oldQueryParams?.endDay)] : undefined}
|
|
|
|
|
+ onChange={(_, options) => setOldQueryParams({ ...oldQueryParams, startDay: options?.[0], endDay: options?.[1] })}
|
|
|
|
|
+ />
|
|
|
|
|
+ </>
|
|
|
|
|
+ </SearchBox>
|
|
|
|
|
+
|
|
|
|
|
+ <Table
|
|
|
|
|
+ dataSource={getPullGroupList?.data?.data?.records}
|
|
|
|
|
+ columns={[
|
|
|
|
|
+ {
|
|
|
|
|
+ title: '操作',
|
|
|
|
|
+ dataIndex: 'cz',
|
|
|
|
|
+ key: 'cz',
|
|
|
|
|
+ width: 100,
|
|
|
|
|
+ align: 'center',
|
|
|
|
|
+ render: (_, record: any) => {
|
|
|
|
|
+ return <Space>
|
|
|
|
|
+ <a onClick={() => setDetalisData({ visible: true, taskLogId: record.id })}>详情</a>
|
|
|
|
|
+ <a onClick={() => {
|
|
|
|
|
+ modal.confirm({
|
|
|
|
|
+ title: '确认',
|
|
|
|
|
+ icon: <ExclamationCircleOutlined />,
|
|
|
|
|
+ content: '是否跳转到日志详情?',
|
|
|
|
|
+ okText: '确认',
|
|
|
|
|
+ cancelText: '复制',
|
|
|
|
|
+ onCancel: () => {
|
|
|
|
|
+ copy(record.projectName + '任务Id:' + record.taskId)
|
|
|
|
|
+ },
|
|
|
|
|
+ onOk: () => {
|
|
|
|
|
+ localStorage.setItem('OPENTASK', JSON.stringify({
|
|
|
|
|
+ id: record.projectId,
|
|
|
|
|
+ projectName: record.projectName,
|
|
|
|
|
+ taskType: 'TASK_STAT_PULL_GROUP',
|
|
|
|
|
+ taskId: record.taskId,
|
|
|
|
|
+ msg: '跳转日志详情'
|
|
|
|
|
+ }))
|
|
|
|
|
+ window.open(`/weComTask#/weComTask/groupChat/taskList`)
|
|
|
|
|
+ },
|
|
|
|
|
+ });
|
|
|
|
|
+ }}>任务跳转</a>
|
|
|
|
|
+ </Space>
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ title: '任务名称',
|
|
|
|
|
+ dataIndex: 'projectName',
|
|
|
|
|
+ key: 'projectName',
|
|
|
|
|
+ width: 180,
|
|
|
|
|
+ ellipsis: true,
|
|
|
|
|
+ render: (text) => <a onClick={() => copy(text)}>{text}</a>
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ title: '子任务ID',
|
|
|
|
|
+ dataIndex: 'taskId',
|
|
|
|
|
+ key: 'taskId',
|
|
|
|
|
+ width: 70,
|
|
|
|
|
+ align: 'center'
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ title: '项目组名称',
|
|
|
|
|
+ dataIndex: 'projectGroupName',
|
|
|
|
|
+ key: 'projectGroupName',
|
|
|
|
|
+ width: 100,
|
|
|
|
|
+ ellipsis: true,
|
|
|
|
|
+ render: (text) => text ? <a onClick={() => copy(text)}>{text}</a> : '--'
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ title: '项目组ID',
|
|
|
|
|
+ dataIndex: 'projectGroupId',
|
|
|
|
|
+ key: 'projectGroupId',
|
|
|
|
|
+ width: 70,
|
|
|
|
|
+ align: 'center'
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ title: '群聊名称',
|
|
|
|
|
+ dataIndex: 'groupName',
|
|
|
|
|
+ key: 'groupName',
|
|
|
|
|
+ width: 150,
|
|
|
|
|
+ ellipsis: true,
|
|
|
|
|
+ render: (text) => <a onClick={() => copy(text)}>{text}</a>
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ title: '群主号',
|
|
|
|
|
+ dataIndex: 'corpChatUserList',
|
|
|
|
|
+ key: 'corpChatUserList',
|
|
|
|
|
+ width: 150,
|
|
|
|
|
+ ellipsis: true,
|
|
|
|
|
+ align: 'center',
|
|
|
|
|
+ render: (value) => value?.map((item) => `${item.corpUserName}(${item.corpName})`).join(',')
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ title: '企微号',
|
|
|
|
|
+ dataIndex: 'corpUserList',
|
|
|
|
|
+ key: 'corpUserList',
|
|
|
|
|
+ width: 200,
|
|
|
|
|
+ ellipsis: true,
|
|
|
|
|
+ render: (value) => value?.map((item) => `${item.corpUserName}(${item.corpName})`).join(',')
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ title: '机器人号',
|
|
|
|
|
+ dataIndex: 'corpRobotList',
|
|
|
|
|
+ key: 'corpRobotList',
|
|
|
|
|
+ width: 120,
|
|
|
|
|
+ align: 'center',
|
|
|
|
|
+ ellipsis: true,
|
|
|
|
|
+ render: (value) => value?.map((item) => `${item.corpUserName}(${item.corpName})`).join(',')
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ title: '公众号',
|
|
|
|
|
+ dataIndex: 'mpAccountInfo',
|
|
|
|
|
+ key: 'mpAccountInfo',
|
|
|
|
|
+ width: 100,
|
|
|
|
|
+ align: 'center',
|
|
|
|
|
+ ellipsis: true,
|
|
|
|
|
+ render: (value) => value?.name || '--'
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ title: '执行时间',
|
|
|
|
|
+ dataIndex: 'createTime',
|
|
|
|
|
+ key: 'createTime',
|
|
|
|
|
+ width: 130,
|
|
|
|
|
+ ellipsis: true,
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ title: '预计拉群数',
|
|
|
|
|
+ dataIndex: 'pullGroupCount',
|
|
|
|
|
+ key: 'pullGroupCount',
|
|
|
|
|
+ width: 90,
|
|
|
|
|
+ align: 'center',
|
|
|
|
|
+ render: (value) => value || 0
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ title: '实际拉群数',
|
|
|
|
|
+ dataIndex: 'pullSuccessCount',
|
|
|
|
|
+ key: 'pullSuccessCount',
|
|
|
|
|
+ width: 90,
|
|
|
|
|
+ align: 'center',
|
|
|
|
|
+ render: (value) => value || 0
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ title: '预计拉群客户数',
|
|
|
|
|
+ dataIndex: 'pullGroupUserCount',
|
|
|
|
|
+ key: 'pullGroupUserCount',
|
|
|
|
|
+ width: 90,
|
|
|
|
|
+ align: 'center',
|
|
|
|
|
+ render: (value) => value || 0
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ title: '实际拉群客户数',
|
|
|
|
|
+ dataIndex: 'pullSuccessUserCount',
|
|
|
|
|
+ key: 'pullSuccessUserCount',
|
|
|
|
|
+ width: 90,
|
|
|
|
|
+ align: 'center',
|
|
|
|
|
+ render: (value) => value || 0
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ title: '上次失败补拉数量',
|
|
|
|
|
+ dataIndex: 'lastFailedRetryCount',
|
|
|
|
|
+ key: 'lastFailedRetryCount',
|
|
|
|
|
+ width: 90,
|
|
|
|
|
+ align: 'center',
|
|
|
|
|
+ render: (value) => value || 0
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ title: '失败客户数',
|
|
|
|
|
+ dataIndex: 'pullFailedUserCount',
|
|
|
|
|
+ key: 'pullFailedUserCount',
|
|
|
|
|
+ width: 90,
|
|
|
|
|
+ align: 'center',
|
|
|
|
|
+ render: (value) => value || 0
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ title: '删除或拉黑客户数',
|
|
|
|
|
+ dataIndex: 'pullBlackUserCount',
|
|
|
|
|
+ key: 'pullBlackUserCount',
|
|
|
|
|
+ width: 90,
|
|
|
|
|
+ align: 'center',
|
|
|
|
|
+ render: (value) => value || 0
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ title: '发送邀请客户数',
|
|
|
|
|
+ dataIndex: 'sendInviteUserCount',
|
|
|
|
|
+ key: 'sendInviteUserCount',
|
|
|
|
|
+ width: 90,
|
|
|
|
|
+ align: 'center',
|
|
|
|
|
+ render: (value) => value || 0
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ title: '退群客户数',
|
|
|
|
|
+ dataIndex: 'quitUserCount',
|
|
|
|
|
+ key: 'quitUserCount',
|
|
|
|
|
+ width: 90,
|
|
|
|
|
+ align: 'center',
|
|
|
|
|
+ render: (value) => value || 0
|
|
|
|
|
+ },
|
|
|
|
|
+ ]}
|
|
|
|
|
+ rowKey={'id'}
|
|
|
|
|
+ bordered={true}
|
|
|
|
|
+ size='small'
|
|
|
|
|
+ scroll={{ x: 1200 }}
|
|
|
|
|
+ loading={getPullGroupList?.loading}
|
|
|
|
|
+ pagination={{
|
|
|
|
|
+ current: getPullGroupList?.data?.data?.current,
|
|
|
|
|
+ pageSize: getPullGroupList?.data?.data?.size,
|
|
|
|
|
+ total: getPullGroupList?.data?.data?.total,
|
|
|
|
|
+ showSizeChanger: true,
|
|
|
|
|
+ onChange: (page, pageSize) => {
|
|
|
|
|
+ setQueryParams({ ...queryParams, pageNum: page, pageSize })
|
|
|
|
|
+ setOldQueryParams({ ...oldQueryParams, pageNum: page, pageSize })
|
|
|
|
|
+ }
|
|
|
|
|
+ }}
|
|
|
|
|
+ />
|
|
|
|
|
+ </Drawer>
|
|
|
|
|
+ {/* 详情 */}
|
|
|
|
|
+ {detalisData?.visible && <TaskDetails
|
|
|
|
|
+ {...detalisData}
|
|
|
|
|
+ onClose={() => setDetalisData(undefined)}
|
|
|
|
|
+ />}
|
|
|
|
|
+ </>
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+export default React.memo(PullGroup);
|