TaskList.tsx 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400
  1. import PageContent from '@/components/PageContent';
  2. import PageTitle from '@/components/PageTitle';
  3. import {
  4. IPropsType,
  5. ITopFilter,
  6. IUserType,
  7. } from '@/pages/TaskManage/Detail/TaskList/taskList.types';
  8. import {
  9. MandateClass,
  10. MandateStatus,
  11. MandateType,
  12. OrderStatus,
  13. OrderType,
  14. } from '@/pages/TaskManage/constent';
  15. import { connect, useLocation, useRequest } from '@umijs/max';
  16. import TopFilter from '@/pages/TaskManage/components/TopFilter';
  17. import { IMandateType } from '@/pages/TaskManage/index.types';
  18. import { useNavigate } from '@@/exports';
  19. import { DownOutlined } from '@ant-design/icons';
  20. import {
  21. Col,
  22. Collapse,
  23. CollapseProps,
  24. Divider,
  25. Form,
  26. Input,
  27. List,
  28. Modal,
  29. Row,
  30. message,
  31. } from 'antd';
  32. import ScrollLoading from '@/components/ScrollLoading';
  33. import { getMandateList, withdrawOrderRequest } from '@/services/TaskManage';
  34. import dayjs from 'dayjs';
  35. import { useEffect, useState } from 'react';
  36. import styles from './taskList.less';
  37. const TaskList: React.FC<IPropsType> = (props) => {
  38. const { userList, loading, dispatch } = props;
  39. const location = useLocation();
  40. const queryParams = new URLSearchParams(location.search);
  41. const project_id = Number(queryParams.get('project_id'));
  42. const mandateType = Number(queryParams.get('mandateType'));
  43. const navigate = useNavigate();
  44. // 顶部选择器配置
  45. const [topFiltersConfig, setTopFiltersConfig] = useState<ITopFilter[]>([]);
  46. const [mandateList, setMandateList] = useState<IMandateType[]>([]);
  47. const [currentParams, setCurrentParams] = useState({
  48. project_id,
  49. mandate_type: mandateType,
  50. pageSize: 20,
  51. currentPage: 1,
  52. });
  53. const [pagination, setPagination] = useState({
  54. current: 1,
  55. total: 0,
  56. pageSize: 20,
  57. });
  58. const [withdrawOrderOpen, setWithdrawOrderOpen] = useState(false);
  59. const [clickedOrder, setClickedOrder] = useState({});
  60. const [withdrawReason, setWithdrawReason] = useState('');
  61. const { run: getList, loading: loadData } = useRequest(getMandateList, {
  62. defaultParams: [currentParams],
  63. formatResult: (result) => {
  64. const pageInfo = result.data.pagination;
  65. if (result.data.pagination.current === 1) {
  66. setMandateList(result.data.list);
  67. } else {
  68. if (mandateList.length < pageInfo.total) {
  69. setMandateList([...mandateList, ...result.data.list]);
  70. }
  71. }
  72. setPagination(pageInfo);
  73. },
  74. });
  75. // 获取用户
  76. useEffect(() => {
  77. if (userList.length === 0) {
  78. dispatch({
  79. type: 'taskUser/fetchUserList',
  80. payload: { project_id },
  81. });
  82. }
  83. }, []);
  84. // 配置顶部下拉过滤器
  85. useEffect(() => {
  86. const filters: ITopFilter[] = [];
  87. filters.push({
  88. key: 'mandate_class',
  89. placeholder: '任务类别',
  90. // @ts-ignore
  91. options: MandateClass.map((item) => {
  92. if (item.MandateType === mandateType) {
  93. return {
  94. value: item.value,
  95. label: item.label,
  96. key: item.value + '任务类别',
  97. };
  98. }
  99. return undefined;
  100. }).filter((item) => item),
  101. });
  102. filters.push({
  103. key: 'status',
  104. placeholder: '任务状态',
  105. options: MandateStatus.map((item) => {
  106. return {
  107. ...item,
  108. };
  109. }),
  110. });
  111. setTopFiltersConfig(filters);
  112. }, [mandateType]);
  113. /**
  114. * 处理选择器变化
  115. * @param value 选择的值,当清空时等于null
  116. */
  117. const onTopFilterChange = (value: any) => {
  118. if (topFiltersConfig.length === 0) {
  119. return;
  120. }
  121. const params: any = {
  122. project_id,
  123. mandate_type: mandateType,
  124. pageSize: 20,
  125. currentPage: 1,
  126. };
  127. for (let i = 0; i < value.length; i++) {
  128. if (value[i] !== null && topFiltersConfig[i] !== undefined) {
  129. params[topFiltersConfig[i].key] = value[i];
  130. }
  131. }
  132. setCurrentParams(params);
  133. getList(params);
  134. };
  135. const goTaskDetail = (mandate: IMandateType) => {
  136. navigate(
  137. `/task-manage/list/detail?project_id=${project_id}&mandate_id=${mandate.Id}`,
  138. );
  139. };
  140. const goTaskOrder = (
  141. orderID: number,
  142. orderType: number | undefined,
  143. mandateClass: number,
  144. ) => {
  145. if (orderType === undefined) {
  146. return;
  147. }
  148. navigate(
  149. `/task-manage/list/order-detail?project_id=${project_id}&order_id=${orderID}&order_type=${orderType}&mandate_class=${mandateClass}`,
  150. );
  151. };
  152. const withdrawOrder = (order: any) => {
  153. setWithdrawOrderOpen(true);
  154. setClickedOrder(order);
  155. };
  156. const withdrawOrderConfirm = async () => {
  157. if (!withdrawReason) {
  158. message.warning('请输入关闭理由');
  159. return;
  160. }
  161. const res = await withdrawOrderRequest({
  162. record_id: clickedOrder.Id,
  163. note: withdrawReason,
  164. type: clickedOrder.RecordType.value,
  165. });
  166. if (res.code === 200) {
  167. message.success('关闭工单成功');
  168. setClickedOrder({});
  169. setWithdrawOrderOpen(false);
  170. getList(currentParams);
  171. }
  172. };
  173. const buildTaskList = (item: IMandateType) => {
  174. const formatItem = {
  175. ...item,
  176. Status: MandateStatus.find((status) => status.value === item.Status),
  177. MandateType: MandateType.find((type) => type.value === item.MandateType),
  178. MandateClass: MandateClass.find(
  179. (itemClass) => itemClass.value === item.MandateClass,
  180. ),
  181. ResponsiblePeople: userList.find(
  182. (user) => user.ID === item.ResponsiblePeople,
  183. ),
  184. CreateTime: dayjs(item.CreateTime).format('YYYY-MM-DD HH:mm'),
  185. };
  186. const workOrder = item.Records.map((record) => {
  187. return {
  188. ...record,
  189. key: record.Id,
  190. Status: OrderStatus.find((status) => status.value === record.Status),
  191. RecordType: OrderType.find((type) => type.value === record.RecordType),
  192. Responsible: userList.find((user) => user.ID === record.Responsible),
  193. CreateTime: dayjs(record.CreateTime).format('YYYY-MM-DD HH:mm'),
  194. };
  195. });
  196. const collapseData: CollapseProps['items'] = [
  197. {
  198. key: '1',
  199. label: (
  200. <span style={{ color: '#5697e4' }}>关联工单({workOrder.length})</span>
  201. ),
  202. children: workOrder.map((order) => {
  203. return (
  204. <div key={order.Id} className={styles.workOrderCard}>
  205. <div className={styles.leftInfo}>
  206. <Row style={{ marginBottom: '15px' }}>
  207. <Col className={styles.fontS24} span={12}>
  208. 工单类型:{order.RecordType?.label || '-'}
  209. </Col>
  210. <Col className={styles.fontS24} span={12}>
  211. 时间:{order.CreateTime}
  212. </Col>
  213. </Row>
  214. <Row>
  215. <Col className={styles.fontS24} span={12}>
  216. 工单状态:
  217. <span style={{ color: '#5697e4' }}>
  218. {order.Status?.label}
  219. </span>
  220. </Col>
  221. <Col className={styles.fontS24} span={12}>
  222. 工单负责人:{order.Responsible?.CName}
  223. </Col>
  224. </Row>
  225. </div>
  226. <Divider type="vertical" style={{ height: '40px' }} />
  227. <div className={styles.rightButtonContainer}>
  228. <div
  229. className={styles.rightButton}
  230. style={{
  231. color: '#5697e4',
  232. marginBottom: `${
  233. order.Status?.value === 0 ? '0.15rem' : '0'
  234. }`,
  235. }}
  236. onClick={() => {
  237. goTaskOrder(
  238. order.Id,
  239. order.RecordType?.value,
  240. item.MandateClass,
  241. );
  242. }}
  243. >
  244. 查看
  245. </div>
  246. {order.Status?.value === 0 && (
  247. <div
  248. className={styles.rightButton}
  249. style={{ color: '#5697e4' }}
  250. onClick={() => {
  251. withdrawOrder(order);
  252. }}
  253. >
  254. 关闭
  255. </div>
  256. )}
  257. </div>
  258. </div>
  259. );
  260. }),
  261. },
  262. ];
  263. return (
  264. <List.Item style={{ borderBottom: '0' }}>
  265. <div className={`${styles.cardContainer} card-box`}>
  266. <Row justify="space-between" style={{ marginBottom: '20px' }}>
  267. <Col className={styles.fontS24}>时间:{formatItem.CreateTime}</Col>
  268. <Col className={styles.fontS24}>
  269. 任务类别:{formatItem.MandateClass?.label}
  270. </Col>
  271. <Col className={styles.fontS24}>
  272. 任务负责人:{formatItem.ResponsiblePeople?.CName || '-'}
  273. </Col>
  274. </Row>
  275. <Row
  276. justify="space-between"
  277. style={{
  278. paddingBottom: '10px',
  279. borderBottom: '1px solid #D5D5D5',
  280. }}
  281. >
  282. <Col className={styles.fontS24}>
  283. 任务状态:{formatItem.Status?.label || '-'}
  284. </Col>
  285. <Col>
  286. <div
  287. className={styles.fontS24}
  288. style={{
  289. backgroundColor: '#f5a623',
  290. color: 'white',
  291. width: '150px',
  292. height: '50px',
  293. display: 'flex',
  294. justifyContent: 'center',
  295. alignItems: 'center',
  296. }}
  297. onClick={() => {
  298. goTaskDetail(item);
  299. }}
  300. >
  301. 任务详情
  302. </div>
  303. </Col>
  304. </Row>
  305. <Row>
  306. <Collapse
  307. className={styles.collapseLabel}
  308. ghost
  309. expandIcon={({ isActive }) => (
  310. <DownOutlined
  311. style={{ color: '#5697e4' }}
  312. rotate={isActive ? 180 : 0}
  313. />
  314. )}
  315. items={collapseData}
  316. />
  317. </Row>
  318. </div>
  319. </List.Item>
  320. );
  321. };
  322. return (
  323. <PageContent closeable={false}>
  324. <PageTitle returnable>
  325. {MandateType.find((item) => item.value === mandateType)?.label}
  326. </PageTitle>
  327. <TopFilter filters={topFiltersConfig} onChange={onTopFilterChange} />
  328. <ScrollLoading
  329. height={180}
  330. loading={loading || loadData}
  331. pagination={pagination}
  332. handleLoadData={(current: number) =>
  333. getList({ ...currentParams, currentPage: current })
  334. }
  335. >
  336. <List
  337. itemLayout="horizontal"
  338. dataSource={mandateList}
  339. renderItem={buildTaskList}
  340. />
  341. </ScrollLoading>
  342. <Modal
  343. className={styles.handleModal}
  344. title="关闭工单"
  345. open={withdrawOrderOpen}
  346. onCancel={() => {
  347. setWithdrawOrderOpen(false);
  348. }}
  349. onOk={withdrawOrderConfirm}
  350. >
  351. <Form>
  352. <Form.Item label="关闭原因">
  353. <Input
  354. onChange={(e) => {
  355. setWithdrawReason(e.target.value);
  356. }}
  357. />
  358. </Form.Item>
  359. </Form>
  360. </Modal>
  361. </PageContent>
  362. );
  363. };
  364. export default connect(
  365. ({
  366. taskUser,
  367. loading,
  368. }: any): {
  369. userList: IUserType[];
  370. loading: boolean;
  371. } => {
  372. return {
  373. userList: taskUser.userList,
  374. loading: loading.models['taskUser'],
  375. };
  376. },
  377. )(TaskList);