details.tsx 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509
  1. import { useAjax } from "@/Hook/useAjax"
  2. import { getProjectLogDetailsListApi, getProjectLogListApi, getProjectTaskLogCountApi, getProjectTaskLogListApi } from "@/pages/weComTask/API/groupChat"
  3. import FilterUserTooltip from "@/pages/weComTask/components/filterUser/filterUserTooltip";
  4. import { Badge, Button, Drawer, Flex, Modal, Popover, Space, Table, Typography } from "antd"
  5. import React, { useEffect, useState } from "react"
  6. import { QuestionCircleFilled } from "@ant-design/icons"
  7. import { businessPlanData, LQTaskStatus, TIME_TYPE_ZJ } from "../../businessPlan/create/const";
  8. import PreviewTime from "@/pages/weComTask/components/previewTime";
  9. const { Text, Paragraph, Title } = Typography;
  10. interface Props {
  11. data: any,
  12. bookPlatForm: TASK_CREATE.BookPlatFormProps[]
  13. bookList: TASK_CREATE.BookListProps[]
  14. visible?: boolean,
  15. onClose?: () => void,
  16. }
  17. const Details: React.FC<Props> = ({ data, bookPlatForm, bookList, visible, onClose }) => {
  18. /*******************************************/
  19. const getProjectLogList = useAjax((params) => getProjectLogListApi(params))
  20. /*******************************************/
  21. useEffect(() => {
  22. getProjectLogList.run(data.id)
  23. }, [])
  24. return <Drawer
  25. title={<Space>
  26. <strong>{data.taskName} 运营计划</strong>
  27. <Button type="link" onClick={() => getProjectLogList.refresh()}>刷新</Button>
  28. </Space>}
  29. onClose={onClose}
  30. open={visible}
  31. width={1400}
  32. styles={{ body: { paddingTop: 5 } }}
  33. >
  34. <Table
  35. size='small'
  36. bordered
  37. rowKey={'id'}
  38. dataSource={getProjectLogList?.data?.data || []}
  39. scroll={{ y: 1000 }}
  40. columns={[
  41. {
  42. title: '任务名称',
  43. dataIndex: 'taskName',
  44. key: 'taskName',
  45. width: 180,
  46. ellipsis: true,
  47. fixed: 'left'
  48. },
  49. {
  50. title: '群名称',
  51. dataIndex: 'groupName',
  52. key: 'groupName',
  53. width: 120,
  54. ellipsis: true,
  55. fixed: 'left'
  56. },
  57. {
  58. title: '计划状态',
  59. dataIndex: 'taskStatus',
  60. key: 'taskStatus',
  61. width: 90,
  62. align: 'center',
  63. fixed: 'left',
  64. render(value) {
  65. return LQTaskStatus[value] || '--'
  66. },
  67. },
  68. {
  69. title: '执行时间',
  70. dataIndex: 'timeRepeatType',
  71. key: 'timeRepeatType',
  72. width: 100,
  73. ellipsis: true,
  74. align: 'center',
  75. render(value, records: any) {
  76. return <>
  77. {TIME_TYPE_ZJ[value] || '--'}
  78. {value !== 'TIME_TYPE_SINGLE_TIMELY' && <Popover
  79. placement="left"
  80. content={<div>
  81. <PreviewTime
  82. {...records}
  83. sendTime={records?.sendTime || records?.repeatSendTime}
  84. />
  85. </div>}
  86. styles={{ body: { width: 300, overflow: 'hidden', overflowY: 'auto', maxHeight: 400 } }}
  87. >
  88. <a style={{ color: '#000' }}><QuestionCircleFilled /></a>
  89. </Popover>}
  90. </>
  91. },
  92. },
  93. {
  94. title: '是否开启已有旧群聊补缺',
  95. dataIndex: 'isRepair',
  96. key: 'isRepair',
  97. width: 150,
  98. ellipsis: true,
  99. render(value, record: any) {
  100. return value ? <Space>
  101. <Badge status="success" text="开启" />
  102. <span>近{record.repairTimes}天</span>
  103. </Space> : <Badge status="error" text="关闭" />
  104. },
  105. },
  106. {
  107. title: '进群对象',
  108. dataIndex: 'corpCondition',
  109. key: 'corpCondition',
  110. width: 90,
  111. align: 'center',
  112. render(value) {
  113. return !value?.[0]?.allCorpUser ? <div style={{ display: 'flex', alignItems: 'center' }}>
  114. <div style={{ flex: '1 0', overflow: 'hidden' }}>
  115. <Text ellipsis>指定</Text>
  116. </div>
  117. <Popover
  118. placement="right"
  119. styles={{ body: { maxWidth: 350, maxHeight: 350, overflow: 'hidden', overflowY: 'auto' } }}
  120. mouseEnterDelay={0.5}
  121. content={<FilterUserTooltip
  122. bookCityList={bookPlatForm?.map(item => ({ label: item.platformName, value: item.platformKey }))}
  123. configName={'进群对象'}
  124. data={value?.[0]}
  125. />}
  126. >
  127. <a style={{ color: '#000' }}><QuestionCircleFilled /></a>
  128. </Popover>
  129. </div> : '全部'
  130. },
  131. },
  132. {
  133. title: '群递增起始编号',
  134. dataIndex: 'groupIndex',
  135. key: 'groupIndex',
  136. width: 60,
  137. align: 'center'
  138. },
  139. {
  140. title: '群固定人数',
  141. dataIndex: 'groupUserCount',
  142. key: 'groupUserCount',
  143. width: 50,
  144. align: 'center'
  145. },
  146. {
  147. title: '邀请客户进群完毕后客服号是否退群',
  148. dataIndex: 'autoOutGroup',
  149. key: 'autoOutGroup',
  150. width: 85,
  151. align: 'center',
  152. render(value) {
  153. return value ? <span style={{ color: '#1890ff' }}>是</span> : <span style={{ color: 'red' }}>否</span>
  154. },
  155. },
  156. {
  157. title: '是否排除已在群的客户',
  158. dataIndex: 'excludeInGroup',
  159. key: 'excludeInGroup',
  160. width: 60,
  161. align: 'center',
  162. render(value) {
  163. return value ? <span style={{ color: '#1890ff' }}>是</span> : <span style={{ color: 'red' }}>否</span>
  164. },
  165. },
  166. {
  167. title: '拉群完成后自动删除拉群标签',
  168. dataIndex: 'excludeInGroup',
  169. key: 'excludeInGroup',
  170. width: 70,
  171. align: 'center',
  172. render(value) {
  173. return value ? <span style={{ color: '#1890ff' }}>是</span> : <span style={{ color: 'red' }}>否</span>
  174. },
  175. },
  176. {
  177. title: '群聊关联公众号',
  178. dataIndex: 'mpAccountInfo',
  179. key: 'mpAccountInfo',
  180. width: 100,
  181. align: 'center',
  182. ellipsis: true,
  183. render(value) {
  184. return value?.name || '--'
  185. },
  186. },
  187. {
  188. title: '拉群完成后群聊备注',
  189. dataIndex: 'remark',
  190. key: 'remark',
  191. width: 140,
  192. ellipsis: true,
  193. render(value) {
  194. return value || '--'
  195. },
  196. },
  197. {
  198. title: '拉群完成后群聊智能标签',
  199. dataIndex: 'msgTagDTO',
  200. key: 'msgTagDTO',
  201. width: 200,
  202. ellipsis: true,
  203. render(value) {
  204. return value && Object.keys(value)?.length > 0 ?
  205. <Paragraph ellipsis style={{ margin: 0 }}>{Object.keys(value).map(key => {
  206. if (key === 'business' && value[key]) {
  207. return `业务(来源渠道):${businessPlanData.find(i => i.value === value.business)?.label || '<空>'}`
  208. } else if (key === 'bookCity' && value[key]) {
  209. return `书城(来源渠道):${bookPlatForm.find(i => i.id === value.bookCity)?.platformName || '<空>'}`
  210. } else if (key === 'product' && value[key]) {
  211. return `产品(来源渠道):${bookList.find(i => i.id === value.product)?.bookName || '<空>'}`
  212. }
  213. return ''
  214. }).join('、')}</Paragraph> : '--'
  215. },
  216. },
  217. {
  218. title: '建群成功发送内容',
  219. dataIndex: 'groupSendMsg',
  220. key: 'groupSendMsg',
  221. width: 140,
  222. ellipsis: true,
  223. render(value) {
  224. return value ? value : '--'
  225. },
  226. },
  227. {
  228. title: '创建人',
  229. dataIndex: 'createBy',
  230. key: 'createBy',
  231. width: 80,
  232. align: 'center',
  233. ellipsis: true,
  234. render(value) {
  235. return value?.nickname || '--'
  236. }
  237. }
  238. ]}
  239. expandable={{
  240. fixed: 'left',
  241. expandRowByClick: true,
  242. expandedRowRender: (record) => <ExpandedRow record={record} />
  243. }}
  244. />
  245. </Drawer>
  246. }
  247. const ExpandedRow: React.FC<{ record: any }> = ({ record }) => {
  248. /*****************************************/
  249. const [detalisData, setDetalisData] = useState<{ visible?: boolean, taskLogId?: number }>()
  250. const [queryForm, setQueryForm] = useState<{ pageNum: number, pageSize: number }>({ pageNum: 1, pageSize: 20 })
  251. const [failedUserDetails, setFailedUserDetails] = useState<{ visible?: boolean, list: any[] }>({ visible: false, list: [] })
  252. const [total, setTotal] = useState<{ pullGroupCount?: number, pullGroupUserCount?: number, pullGroupUserSuccessCount?: number, pullSuccessCount?: number }>({})
  253. const getProjectTaskLogList = useAjax((params) => getProjectTaskLogListApi(params))
  254. const getProjectTaskLogCount = useAjax((params) => getProjectTaskLogCountApi(params))
  255. /*****************************************/
  256. useEffect(() => {
  257. getProjectTaskLogList.run({ ...queryForm, taskId: record.id })
  258. }, [record.id, queryForm])
  259. useEffect(() => {
  260. getProjectTaskLogCount.run(record.id).then(res => {
  261. setTotal(res?.data || {})
  262. })
  263. }, [record.id, queryForm])
  264. return <div style={{ maxHeight: 450 }}>
  265. <Flex gap={20} align="center" style={{ marginBottom: 10, marginTop: 5 }}>
  266. <Title level={4} style={{ margin: 0 }}>{record?.taskName} 任务列表</Title>
  267. <Button type="link" onClick={() => getProjectTaskLogList.refresh()}>刷新</Button>
  268. <Text strong>预计拉群数:{total?.pullGroupCount || 0}</Text>
  269. <Text strong>预计拉群客户数:{total?.pullGroupUserCount || 0}</Text>
  270. <Text strong>拉群成功总客户数:{total?.pullGroupUserSuccessCount || 0}</Text>
  271. <Text strong>拉群成功总数数:{total?.pullSuccessCount || 0}</Text>
  272. </Flex>
  273. <Table
  274. size='small'
  275. bordered
  276. rowKey={'id'}
  277. dataSource={getProjectTaskLogList?.data?.data?.records}
  278. loading={getProjectTaskLogList.loading}
  279. scroll={{ x: 400, y: 350 }}
  280. columns={[
  281. {
  282. title: '操作',
  283. dataIndex: 'cz',
  284. key: 'cz',
  285. width: 80,
  286. align: 'center',
  287. render(_, record: any) {
  288. return <a onClick={() => setDetalisData({ visible: true, taskLogId: record.id })}>详情</a>
  289. }
  290. },
  291. {
  292. title: '预计拉群数',
  293. dataIndex: 'pullGroupCount',
  294. key: 'pullGroupCount',
  295. width: 120
  296. },
  297. {
  298. title: '预计拉群客户数',
  299. dataIndex: 'pullGroupUserCount',
  300. key: 'pullGroupUserCount',
  301. width: 120
  302. },
  303. {
  304. title: '上次失败补拉数量',
  305. dataIndex: 'lastFailedRetryCount',
  306. key: 'lastFailedRetryCount',
  307. width: 120
  308. },
  309. {
  310. title: '实际拉群客户数',
  311. dataIndex: 'pullSuccessUserCount',
  312. key: 'pullSuccessUserCount',
  313. width: 120
  314. },
  315. {
  316. title: '实际拉群数',
  317. dataIndex: 'pullSuccessCount',
  318. key: 'pullSuccessCount',
  319. width: 120
  320. },
  321. {
  322. title: '失败客户数',
  323. dataIndex: 'pullFailedUserCount',
  324. key: 'pullFailedUserCount',
  325. width: 120,
  326. render: (a, b) => {
  327. return <a onClick={() => setFailedUserDetails({ visible: true, list: b?.pullFailedUserDetail || [] })}>{a}</a>
  328. }
  329. },
  330. {
  331. title: '执行时间',
  332. dataIndex: 'createTime',
  333. key: 'createTime'
  334. }
  335. ]}
  336. pagination={{
  337. total: getProjectTaskLogList?.data?.data?.total || 0,
  338. current: queryForm.pageNum,
  339. pageSize: queryForm.pageSize,
  340. showTotal: (total) => `共 ${total} 条数据`,
  341. onChange: (pageNum, pageSize) => setQueryForm({ pageNum, pageSize })
  342. }}
  343. />
  344. {/* 详情 */}
  345. {detalisData?.visible && <TaskDetails
  346. {...detalisData}
  347. onClose={() => setDetalisData(undefined)}
  348. />}
  349. {/* 失败客户详情 */}
  350. {failedUserDetails?.visible && <FailedUserDetails
  351. {...failedUserDetails}
  352. onClose={() => setFailedUserDetails({ visible: false, list: [] })}
  353. />}
  354. </div>
  355. }
  356. const TaskDetails: React.FC<{
  357. visible?: boolean,
  358. taskLogId?: number,
  359. onClose?: () => void
  360. }> = ({ visible, taskLogId, onClose }) => {
  361. /*******************************************/
  362. const getProjectLogDetailsList = useAjax((params) => getProjectLogDetailsListApi(params))
  363. /*******************************************/
  364. useEffect(() => {
  365. getProjectLogDetailsList.run(taskLogId)
  366. }, [taskLogId])
  367. return <Modal
  368. title={<strong>任务详情</strong>}
  369. open={visible}
  370. onCancel={onClose}
  371. width={900}
  372. footer={null}
  373. >
  374. <Table
  375. size='small'
  376. bordered
  377. rowKey={'id'}
  378. dataSource={getProjectLogDetailsList?.data?.data || []}
  379. scroll={{ y: 1000 }}
  380. columns={[
  381. {
  382. title: '群名称',
  383. dataIndex: 'chatName',
  384. key: 'chatName',
  385. width: 120,
  386. ellipsis: true,
  387. },
  388. {
  389. title: '群主号名称',
  390. dataIndex: 'corpUserName',
  391. key: 'corpUserName',
  392. width: 120,
  393. ellipsis: true,
  394. },
  395. {
  396. title: '预计拉群客户数',
  397. dataIndex: 'pullGroupUserCount',
  398. key: 'pullGroupUserCount',
  399. width: 80,
  400. align: 'center'
  401. },
  402. {
  403. title: '实际拉群客户数',
  404. dataIndex: 'actualPullGroupUserCount',
  405. key: 'actualPullGroupUserCount',
  406. width: 80,
  407. align: 'center'
  408. },
  409. {
  410. title: '删除或拉黑客户数',
  411. dataIndex: 'deleteOrBlockUserCount',
  412. key: 'deleteOrBlockUserCount',
  413. width: 80,
  414. align: 'center'
  415. }
  416. ]}
  417. />
  418. </Modal>
  419. }
  420. const FailedUserDetails: React.FC<{
  421. visible?: boolean,
  422. list?: any[],
  423. onClose?: () => void
  424. }> = ({ visible, list, onClose }) => {
  425. return <Modal
  426. title={<strong>任务详情</strong>}
  427. open={visible}
  428. onCancel={onClose}
  429. width={900}
  430. footer={null}
  431. >
  432. <Table
  433. size='small'
  434. bordered
  435. rowKey={'externalUserId'}
  436. dataSource={list || []}
  437. scroll={{ y: 1000 }}
  438. columns={[
  439. {
  440. title: '企业名称',
  441. dataIndex: 'corpName',
  442. key: 'corpName',
  443. width: 120,
  444. ellipsis: true,
  445. },
  446. {
  447. title: '企业ID',
  448. dataIndex: 'corpId',
  449. key: 'corpId',
  450. width: 120,
  451. ellipsis: true,
  452. },
  453. {
  454. title: '客服名称',
  455. dataIndex: 'corpUserName',
  456. key: 'corpUserName',
  457. width: 120,
  458. ellipsis: true,
  459. },
  460. {
  461. title: '客服ID',
  462. dataIndex: 'corpUserId',
  463. key: 'corpUserId',
  464. width: 120,
  465. ellipsis: true,
  466. },
  467. {
  468. title: '客服名称',
  469. dataIndex: 'name',
  470. key: 'name',
  471. width: 120,
  472. ellipsis: true,
  473. },
  474. {
  475. title: '客户ID',
  476. dataIndex: 'externalUserId',
  477. key: 'externalUserId',
  478. width: 120,
  479. ellipsis: true,
  480. }
  481. ]}
  482. />
  483. </Modal>
  484. }
  485. export default React.memo(Details)