momentItem.tsx 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558
  1. import React, { useEffect, useState } from "react"
  2. import style from './index.less';
  3. import { Avatar, Badge, Button, DatePicker, Drawer, Input, Modal, Select, Space, Table, Tabs, Tag } from "antd";
  4. import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
  5. import { faCommentDots } from "@fortawesome/free-solid-svg-icons";
  6. import { useAjax } from "@/Hook/useAjax";
  7. import { getProjectGroupsAllListApi } from "../../API/groupManage";
  8. import { ExclamationCircleOutlined, SearchOutlined } from "@ant-design/icons";
  9. import { getMomentCorpUserLogListApi, getMomentJobLogListApi, GetSendMomentLogListProps } from "../../API/logs";
  10. import useNewToken from "@/Hook/useNewToken";
  11. import SearchBox from "../../components/searchBox";
  12. import dayJs from "dayjs";
  13. import { copy } from "@/utils/utils";
  14. interface Props {
  15. taskTotal: number
  16. todayAddTask: number
  17. todayFinishTaskRate: number
  18. todayFailTask: number
  19. }
  20. /**
  21. * 朋友圈
  22. * @returns
  23. */
  24. const MomentItem: React.FC<Props> = ({ taskTotal, todayAddTask, todayFinishTaskRate, todayFailTask }) => {
  25. /*************************************************/
  26. const [visible, setVisible] = useState<boolean>(false)
  27. const [activeKey, setActiveKey] = useState<string>('2')
  28. const [groupList, setGroupList] = useState<{ label: string, value: number }[]>([]);
  29. const getProjectGroupsAllList = useAjax(() => getProjectGroupsAllListApi())
  30. /*************************************************/
  31. useEffect(() => {
  32. if (visible) {
  33. getProjectGroupsAllList.run().then(res => {
  34. setGroupList([{ label: '空项目组', value: 0 }, ...res?.data?.map(item => ({ label: item.name, value: item.id })) || []])
  35. })
  36. }
  37. }, [visible])
  38. return <>
  39. <div className={style.moduleCard} onClick={() => setVisible(true)}>
  40. <div className={style.moduleCard_header}>
  41. <Avatar style={{ backgroundColor: '#dcfce7', color: '#16a34a' }} size={30}>
  42. <FontAwesomeIcon icon={faCommentDots} />
  43. </Avatar>
  44. 朋友圈
  45. </div>
  46. <div className={style.moduleCard_center}>
  47. 总任务数:{taskTotal || 0} | 今日新增:{todayAddTask || 0}
  48. </div>
  49. <div className={style.moduleCard_footer}>
  50. <span>今日完成率:{((todayFinishTaskRate || 0) * 100).toFixed(2)} %</span>
  51. <span className={style.error}>今日异常:{todayFailTask}</span>
  52. </div>
  53. </div>
  54. <Drawer
  55. title={'朋友圈'}
  56. onClose={() => setVisible(false)}
  57. open={visible}
  58. width={'70%'}
  59. >
  60. <Tabs
  61. destroyOnHidden={false}
  62. onChange={(key) => {
  63. setActiveKey(key)
  64. }}
  65. activeKey={activeKey}
  66. type="card"
  67. items={[
  68. {
  69. key: '2',
  70. label: '下发企微号',
  71. children: <>
  72. {activeKey == '2' && <GroupXfCorpTabls groupList={groupList} />}
  73. </>
  74. },
  75. {
  76. key: '4',
  77. label: '群发记录',
  78. children: <>
  79. {activeKey == '4' && <GroupTaskNotes groupList={groupList} />}
  80. </>
  81. }
  82. ]}
  83. />
  84. </Drawer>
  85. </>
  86. }
  87. /**
  88. * 下发企微号
  89. * @param param0
  90. * @returns
  91. */
  92. const GroupXfCorpTabls: React.FC<{ groupList: { label: string, value: number }[] }> = ({ groupList }) => {
  93. /***********************************/
  94. const [queryParams, setQueryParams] = useState<GetSendMomentLogListProps>({ pageNum: 1, pageSize: 20 })
  95. const [oldQueryParams, setOldQueryParams] = useState<GetSendMomentLogListProps>({ pageNum: 1, pageSize: 20 })
  96. const { token } = useNewToken()
  97. const [modal, contextHolder] = Modal.useModal();
  98. const getMomentCorpUserLogList = useAjax((params) => getMomentCorpUserLogListApi(params))
  99. /***********************************/
  100. useEffect(() => {
  101. getMomentCorpUserLogList.run(queryParams)
  102. }, [queryParams])
  103. return <div>
  104. {contextHolder}
  105. <SearchBox
  106. bodyPadding={`0 0 10px`}
  107. buttons={<>
  108. <Button type="primary" onClick={() => {
  109. setQueryParams({ ...oldQueryParams, pageNum: 1, pageSize: queryParams.pageSize })
  110. }} loading={getMomentCorpUserLogList.loading} icon={<SearchOutlined />}>搜索</Button>
  111. </>}
  112. >
  113. <>
  114. <Input
  115. placeholder="任务名称"
  116. style={{ width: 120 }}
  117. allowClear
  118. onChange={(e) => setOldQueryParams({ ...oldQueryParams, projectName: e.target.value })}
  119. value={oldQueryParams?.projectName}
  120. />
  121. <Input
  122. placeholder="子任务ID"
  123. style={{ width: 120 }}
  124. allowClear
  125. onChange={(e) => setOldQueryParams({ ...oldQueryParams, taskId: e.target.value })}
  126. value={oldQueryParams?.taskId}
  127. />
  128. <Select
  129. placeholder='请选择项目组'
  130. allowClear
  131. options={groupList}
  132. showSearch
  133. maxTagCount={1}
  134. style={{ minWidth: 150 }}
  135. filterOption={(input, option) =>
  136. (option?.label ?? '').toLowerCase().includes(input.toLowerCase())
  137. }
  138. mode="multiple"
  139. value={oldQueryParams?.projectGroupIds}
  140. onChange={(value) => setOldQueryParams({ ...oldQueryParams, projectGroupIds: value })}
  141. />
  142. <DatePicker
  143. placeholder="发送开始时间"
  144. onChange={(e, date) => { setOldQueryParams({ ...oldQueryParams, startDay: date as string }) }}
  145. allowClear
  146. value={oldQueryParams?.startDay ? dayJs(oldQueryParams?.startDay) : undefined}
  147. />
  148. <DatePicker
  149. placeholder="发送结束时间"
  150. onChange={(e, date) => { setOldQueryParams({ ...oldQueryParams, endDay: date as string }) }}
  151. allowClear
  152. value={oldQueryParams?.endDay ? dayJs(oldQueryParams?.endDay) : undefined}
  153. />
  154. </>
  155. </SearchBox>
  156. {/* 表 */}
  157. <Table
  158. style={{ marginTop: 10 }}
  159. dataSource={getMomentCorpUserLogList?.data?.data?.records}
  160. loading={getMomentCorpUserLogList?.loading}
  161. bordered
  162. columns={[
  163. {
  164. title: '操作',
  165. dataIndex: 'cz',
  166. key: 'cz',
  167. width: 100,
  168. align: 'center',
  169. render: (_, record: any) => {
  170. return <Space>
  171. <a onClick={() => {
  172. modal.confirm({
  173. title: '确认',
  174. icon: <ExclamationCircleOutlined />,
  175. content: '是否跳转到日志详情?',
  176. okText: '确认',
  177. cancelText: '复制',
  178. onCancel: () => {
  179. copy(record.projectName + '任务Id:' + record.taskId)
  180. },
  181. onOk: () => {
  182. localStorage.setItem('OPENTASK', JSON.stringify({
  183. id: record.projectId,
  184. projectName: record.projectName,
  185. taskType: 'TASK_STAT_MOMENT',
  186. taskId: record.taskId,
  187. msg: '跳转日志详情'
  188. }))
  189. window.open(`/weComTask#/weComTask/moments/taskList`)
  190. },
  191. });
  192. }}>任务跳转</a>
  193. </Space>
  194. }
  195. },
  196. {
  197. title: '任务名称',
  198. dataIndex: 'projectName',
  199. key: 'projectName',
  200. width: 180,
  201. ellipsis: true,
  202. render: (text) => <a onClick={() => copy(text)}>{text}</a>
  203. },
  204. {
  205. title: '子任务ID',
  206. dataIndex: 'taskId',
  207. key: 'taskId',
  208. width: 70,
  209. align: 'center'
  210. },
  211. {
  212. title: '项目组名称',
  213. dataIndex: 'projectGroupName',
  214. key: 'projectGroupName',
  215. width: 100,
  216. ellipsis: true,
  217. align: 'center',
  218. render: (text) => text ? <a onClick={() => copy(text)}>{text}</a> : '--'
  219. },
  220. {
  221. title: '项目组ID',
  222. dataIndex: 'projectGroupId',
  223. key: 'projectGroupId',
  224. width: 70,
  225. align: 'center'
  226. },
  227. {
  228. title: '所属企业',
  229. dataIndex: 'corpName',
  230. key: 'corpName',
  231. align: 'center',
  232. width: 120,
  233. ellipsis: true
  234. },
  235. {
  236. title: '企微号',
  237. dataIndex: 'corpUserName',
  238. key: 'corpUserName',
  239. align: 'center',
  240. width: 120,
  241. ellipsis: true,
  242. render: (a: string, b: any) => {
  243. return a + `(${b?.corpUserId})`
  244. }
  245. },
  246. {
  247. title: '发送状态',
  248. dataIndex: 'sendStatus',
  249. key: 'sendStatus',
  250. align: 'center',
  251. width: 80,
  252. render: (a) => {
  253. return <Badge text={a === 1 ? '已确认发送' : '未确认发送'} status={a === 1 ? 'success' : 'error'} />
  254. }
  255. },
  256. {
  257. title: '预计送达数',
  258. dataIndex: 'sendCount',
  259. key: 'sendCount',
  260. align: 'center',
  261. width: 75,
  262. ellipsis: true,
  263. render: (a) => {
  264. return a > 0 ? a : 0
  265. }
  266. },
  267. {
  268. title: '消息ID',
  269. dataIndex: 'jobId',
  270. key: 'jobId',
  271. align: 'center',
  272. width: 65,
  273. ellipsis: true,
  274. },
  275. {
  276. title: '发送成功',
  277. dataIndex: 'sendSuccessCount',
  278. key: 'sendSuccessCount',
  279. align: 'center',
  280. width: 75,
  281. render: (a) => {
  282. return a > 0 ? a : 0
  283. }
  284. },
  285. {
  286. title: '发送失败',
  287. dataIndex: 'sendFailedCount',
  288. key: 'sendFailedCount',
  289. align: 'center',
  290. width: 75,
  291. render: (a) => {
  292. return a > 0 ? a : 0
  293. }
  294. },
  295. {
  296. title: '评论数',
  297. dataIndex: 'commentCount',
  298. key: 'commentCount',
  299. align: 'center',
  300. width: 65,
  301. ellipsis: true,
  302. },
  303. {
  304. title: '点赞数',
  305. dataIndex: 'likeCount',
  306. key: 'likeCount',
  307. align: 'center',
  308. width: 65,
  309. ellipsis: true,
  310. },
  311. {
  312. title: '发送时间',
  313. dataIndex: 'sendTime',
  314. key: 'sendTime',
  315. align: 'center',
  316. width: 135,
  317. ellipsis: true
  318. }
  319. ]}
  320. scroll={{ x: 1000 }}
  321. rowKey={'id'}
  322. size='small'
  323. onRow={(row: any) => {
  324. return !row?.status ? {
  325. style: { background: token.colorPrimaryBgHover }
  326. } : {}
  327. }}
  328. pagination={{
  329. total: getMomentCorpUserLogList?.data?.data?.total,
  330. showTotal: (total) => <Tag color="cyan">总共{total}数据</Tag>,
  331. showSizeChanger: true,
  332. showLessItems: true,
  333. defaultCurrent: 1,
  334. defaultPageSize: 200,//默认初始的每页条数
  335. current: getMomentCorpUserLogList?.data?.data?.current || 1,
  336. pageSize: getMomentCorpUserLogList?.data?.data?.size || 20,
  337. onChange: (page, pageSize) => {
  338. setQueryParams({ ...queryParams, pageNum: page, pageSize })
  339. setOldQueryParams({ ...oldQueryParams, pageNum: page, pageSize })
  340. }
  341. }}
  342. />
  343. </div>
  344. }
  345. /**
  346. * 群发记录
  347. * @param param0
  348. * @returns
  349. */
  350. const GroupTaskNotes: React.FC<{ groupList: { label: string, value: number }[] }> = ({ groupList }) => {
  351. /***********************************/
  352. const [queryParams, setQueryParams] = useState<GetSendMomentLogListProps>({ pageNum: 1, pageSize: 20 })
  353. const [oldQueryParams, setOldQueryParams] = useState<GetSendMomentLogListProps>({ pageNum: 1, pageSize: 20 })
  354. const { token } = useNewToken()
  355. const [modal, contextHolder] = Modal.useModal();
  356. const getMomentJobLogList = useAjax((params) => getMomentJobLogListApi(params))
  357. /***********************************/
  358. useEffect(() => {
  359. getMomentJobLogList.run(queryParams)
  360. }, [queryParams])
  361. return <div>
  362. {contextHolder}
  363. <SearchBox
  364. bodyPadding={`0 0 10px`}
  365. buttons={<>
  366. <Button type="primary" onClick={() => {
  367. setQueryParams({ ...oldQueryParams, pageNum: 1, pageSize: queryParams.pageSize })
  368. }} loading={getMomentJobLogList.loading} icon={<SearchOutlined />}>搜索</Button>
  369. </>}
  370. >
  371. <>
  372. <Input
  373. placeholder="任务名称"
  374. style={{ width: 120 }}
  375. allowClear
  376. onChange={(e) => setOldQueryParams({ ...oldQueryParams, projectName: e.target.value })}
  377. value={oldQueryParams?.projectName}
  378. />
  379. <Input
  380. placeholder="子任务ID"
  381. style={{ width: 120 }}
  382. allowClear
  383. onChange={(e) => setOldQueryParams({ ...oldQueryParams, taskId: e.target.value })}
  384. value={oldQueryParams?.taskId}
  385. />
  386. <Select
  387. placeholder='请选择项目组'
  388. allowClear
  389. options={groupList}
  390. showSearch
  391. maxTagCount={1}
  392. style={{ minWidth: 150 }}
  393. filterOption={(input, option) =>
  394. (option?.label ?? '').toLowerCase().includes(input.toLowerCase())
  395. }
  396. mode="multiple"
  397. value={oldQueryParams?.projectGroupIds}
  398. onChange={(value) => setOldQueryParams({ ...oldQueryParams, projectGroupIds: value })}
  399. />
  400. <DatePicker
  401. placeholder="发送开始时间"
  402. onChange={(e, date) => { setOldQueryParams({ ...oldQueryParams, startDay: date as string }) }}
  403. allowClear
  404. value={oldQueryParams?.startDay ? dayJs(oldQueryParams?.startDay) : undefined}
  405. />
  406. <DatePicker
  407. placeholder="发送结束时间"
  408. onChange={(e, date) => { setOldQueryParams({ ...oldQueryParams, endDay: date as string }) }}
  409. allowClear
  410. value={oldQueryParams?.endDay ? dayJs(oldQueryParams?.endDay) : undefined}
  411. />
  412. </>
  413. </SearchBox>
  414. <Table
  415. style={{ marginTop: 10 }}
  416. dataSource={getMomentJobLogList?.data?.data?.records}
  417. loading={getMomentJobLogList?.loading}
  418. bordered
  419. columns={[
  420. {
  421. title: '操作',
  422. dataIndex: 'cz',
  423. key: 'cz',
  424. width: 100,
  425. align: 'center',
  426. render: (_, record: any) => {
  427. return <Space>
  428. <a onClick={() => {
  429. modal.confirm({
  430. title: '确认',
  431. icon: <ExclamationCircleOutlined />,
  432. content: '是否跳转到日志详情?',
  433. okText: '确认',
  434. cancelText: '复制',
  435. onCancel: () => {
  436. copy(record.projectName + '任务Id:' + record.taskId)
  437. },
  438. onOk: () => {
  439. localStorage.setItem('OPENTASK', JSON.stringify({
  440. id: record.projectId,
  441. projectName: record.projectName,
  442. taskType: 'TASK_STAT_MOMENT',
  443. taskId: record.taskId,
  444. msg: '跳转日志详情'
  445. }))
  446. window.open(`/weComTask#/weComTask/moments/taskList`)
  447. },
  448. });
  449. }}>任务跳转</a>
  450. </Space>
  451. }
  452. },
  453. {
  454. title: '任务名称',
  455. dataIndex: 'projectName',
  456. key: 'projectName',
  457. width: 180,
  458. ellipsis: true,
  459. render: (text) => <a onClick={() => copy(text)}>{text}</a>
  460. },
  461. {
  462. title: '子任务ID',
  463. dataIndex: 'taskId',
  464. key: 'taskId',
  465. width: 70,
  466. align: 'center'
  467. },
  468. {
  469. title: '项目组名称',
  470. dataIndex: 'projectGroupName',
  471. key: 'projectGroupName',
  472. width: 100,
  473. ellipsis: true,
  474. align: 'center',
  475. render: (text) => text ? <a onClick={() => copy(text)}>{text}</a> : '--'
  476. },
  477. {
  478. title: '项目组ID',
  479. dataIndex: 'projectGroupId',
  480. key: 'projectGroupId',
  481. width: 70,
  482. align: 'center'
  483. },
  484. {
  485. title: '消息ID',
  486. dataIndex: 'jobId',
  487. key: 'jobId',
  488. width: 70,
  489. ellipsis: true
  490. },
  491. {
  492. title: '发送时间',
  493. dataIndex: 'createTime',
  494. key: 'createTime',
  495. width: 110
  496. },
  497. {
  498. title: '任务日志',
  499. dataIndex: 'jobLog',
  500. key: 'jobLog',
  501. width: 250,
  502. ellipsis: true
  503. },
  504. {
  505. title: '任务状态',
  506. dataIndex: 'jobStatus',
  507. key: 'jobStatus',
  508. width: 110,
  509. ellipsis: true
  510. }
  511. ]}
  512. scroll={{ x: 1000 }}
  513. rowKey={(s) => {
  514. return s.id
  515. }}
  516. size='small'
  517. pagination={{
  518. total: getMomentJobLogList?.data?.data?.total,
  519. showTotal: (total) => <Tag color="cyan">总共{total}数据</Tag>,
  520. showSizeChanger: true,
  521. showLessItems: true,
  522. defaultCurrent: 1,
  523. defaultPageSize: 200,//默认初始的每页条数
  524. current: getMomentJobLogList?.data?.data?.current || 1,
  525. pageSize: getMomentJobLogList?.data?.data?.size || 20,
  526. onChange: (page, pageSize) => {
  527. setQueryParams({ ...queryParams, pageNum: page, pageSize })
  528. setOldQueryParams({ ...oldQueryParams, pageNum: page, pageSize })
  529. }
  530. }}
  531. />
  532. </div>
  533. }
  534. export default React.memo(MomentItem)