Modal.jsx 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746
  1. import {
  2. Form,
  3. Modal,
  4. Row,
  5. Col,
  6. Input,
  7. DatePicker,
  8. Icon,
  9. Button,
  10. Divider,
  11. Steps,
  12. Select,
  13. TreeSelect,
  14. InputNumber,
  15. Upload,
  16. Space,
  17. } from 'antd';
  18. import ModuleTitle from '../../../components/ModuleTitle/moduleTitle';
  19. import { useEffect, useMemo, useState } from 'react';
  20. import { queryDepList, querySupplierList } from '@/services/contract';
  21. import { useModel, useRequest } from '@umijs/max';
  22. import { CloudUploadOutlined } from '@ant-design/icons';
  23. import styles from '../index.less';
  24. import dayjs from 'dayjs';
  25. export const Type = {
  26. add: 0, //新增
  27. detail: 1, //详情
  28. cancel: 2, //作废
  29. check: 3, //审核
  30. };
  31. export const StatusText = [
  32. '',
  33. '待审核',
  34. '审核拒绝',
  35. '已存档',
  36. '作废待审核',
  37. '作废拒绝',
  38. '已作废',
  39. ];
  40. export const Status = {
  41. None: 0,
  42. Checking: 1,
  43. CheckReject: 2,
  44. CheckSuccess: 3,
  45. CalChecking: 4,
  46. CalCheckReject: 5,
  47. CalCheckSuccess: 6,
  48. };
  49. const ContractModal = (props) => {
  50. const [form] = Form.useForm();
  51. const { user } = useModel('userInfo');
  52. const { userList, run: userListRun } = useModel('userList');
  53. const { depList, run: depListRun } = useModel('depList');
  54. const FORMAT = 'YYYY-MM-DD';
  55. const {
  56. detail: data,
  57. type,
  58. visible,
  59. projectList = [],
  60. handleOk,
  61. handleCancel,
  62. parent_id,
  63. } = props;
  64. const title =
  65. type == Type.add ? '新增' : type == Type.detail ? '详情' : '作废';
  66. //所属公司为总部时才能选择部门,为子公司时,部门不能操作 所属部门为子公司的需要填经办人
  67. const company = Form.useWatch('company_id', form);
  68. const [depDisable, setDepDisable] = useState(false);
  69. const [dealDisable, setDealDisable] = useState(false);
  70. //项目名称选择后,自动填入对应的项目编号
  71. const project_name = Form.useWatch('project_name', form);
  72. const [isPass, setIsPass] = useState(1);
  73. const [fileList, setFileList] = useState([]);
  74. useEffect(() => {
  75. userListRun();
  76. depListRun();
  77. }, []);
  78. useEffect(() => {
  79. form.resetFields();
  80. }, [data]);
  81. //供应商列表
  82. const { data: supplierList = [], loading } = useRequest(querySupplierList, {
  83. defaultParams: [
  84. {
  85. project_id: 1,
  86. is_super: false,
  87. },
  88. ],
  89. formatResult: (data) => {
  90. return data?.list
  91. ? data.list.map((item) => {
  92. return { ...item, Name: item.name };
  93. })
  94. : [];
  95. },
  96. });
  97. console.log('======================', data);
  98. useEffect(() => {
  99. if (company == 135) {
  100. setDepDisable(false);
  101. setDealDisable(false);
  102. } else {
  103. setDepDisable(true);
  104. setDealDisable(true);
  105. form.setFieldsValue({ deal_by: user?.CName });
  106. }
  107. }, [company]);
  108. useEffect(() => {
  109. const project_code = projectList?.find(
  110. (item) => item.project_name == project_name,
  111. )?.project_full_code;
  112. if (project_code) form.setFieldsValue({ project_code });
  113. }, [project_name]);
  114. const supplyList = useMemo(() => {
  115. return depList ? [...depList, ...supplierList] : supplierList;
  116. }, [depList, supplierList]);
  117. const disableds = useMemo(() => {
  118. if (!visible) {
  119. setFileList([]);
  120. setIsPass(1);
  121. setDepDisable(false);
  122. setDealDisable(false);
  123. }
  124. if (type == Type.add) {
  125. return { contract: false, check: true };
  126. } else if (type == Type.detail) {
  127. return { contract: true, check: true };
  128. } else if (type == Type.cancel) {
  129. return { contract: true, check: true };
  130. }
  131. return { contract: true, check: false };
  132. }, [type, visible]);
  133. const UploadProps = {
  134. action: `/api/contract/v1/attach`,
  135. headers: {
  136. 'JWT-TOKEN': localStorage.getItem('JWT-TOKEN'),
  137. },
  138. onChange({ file, fileList }) {
  139. if (file.status !== 'uploading') {
  140. console.log(file, fileList);
  141. setFileList(fileList.map((item) => item.response?.data?.attach));
  142. }
  143. },
  144. };
  145. const handleSubmit = () => {
  146. form.validateFields().then((values) => {
  147. if (type == Type.add) {
  148. values.effect_on = dayjs(values.effect_on).format(FORMAT);
  149. values.created_on = values.created_on || dayjs().format(FORMAT);
  150. if (parent_id) values.parent_id = parent_id;
  151. if (values.amount || values.amount == 0)
  152. values.amount = values.amount + '';
  153. if (fileList.length > 0) values.attach = JSON.stringify(fileList);
  154. if (values.party_c && values.party_c.length > 0)
  155. values.party_c = values.party_c.join(',');
  156. if (values.company_id == 135 && values.dep_id) {
  157. const item = getDepItemById(values.dep_id);
  158. if (item) {
  159. values.dep_name = item.Name;
  160. values.dep_code = item.Code;
  161. }
  162. const companyItem = getDepItemById(values.company_id);
  163. if (companyItem) values.company_name = companyItem.Name;
  164. } else if (values.company_id) {
  165. const item = getDepItemById(values.company_id);
  166. if (item) {
  167. values.company_name = item.Name;
  168. values.company_code = item.Code;
  169. }
  170. }
  171. values.created_by = user?.ID;
  172. handleOk(values);
  173. } else if (type == Type.cancel) {
  174. let result = {
  175. id: data?.id,
  176. cancel_desc: values.cancel_desc,
  177. };
  178. handleOk(result);
  179. } else if (type == Type.check && data?.status == Status.Checking) {
  180. let result = {
  181. id: data?.id,
  182. check_by: user.CName,
  183. check_result: values.check_result,
  184. is_pass: values.is_pass,
  185. check_desc: values?.check_desc,
  186. };
  187. handleOk(result);
  188. }
  189. });
  190. };
  191. const getDepItemById = (id) => {
  192. const fun = (list) => {
  193. for (let i = 0; i < list.length; i++) {
  194. let item = list[i];
  195. if (item.ID == id) {
  196. return item;
  197. } else if (item.children?.length > 0) {
  198. let res = fun(item.children);
  199. if (res) return res;
  200. }
  201. }
  202. };
  203. return fun(depList);
  204. };
  205. return (
  206. <Modal
  207. width={'85%'}
  208. title={title}
  209. open={visible}
  210. okText="提交"
  211. cancelText="返回"
  212. onOk={handleSubmit}
  213. onCancel={handleCancel}
  214. okButtonProps={type == Type.detail ? { disabled: true } : null}
  215. destroyOnClose
  216. >
  217. <Divider />
  218. <ModuleTitle title="合同信息" />
  219. <Form
  220. form={form}
  221. // initialValues={data}
  222. labelCol={{ span: 7 }}
  223. wrapperCol={{ span: 17 }}
  224. >
  225. <Row>
  226. <Col span={10} offset={1}>
  227. <Form.Item
  228. name="company_id"
  229. label="所属公司:"
  230. tooltip="所属公司"
  231. initialValue={data?.company_id}
  232. rules={[
  233. {
  234. required: true,
  235. message: '请填写所属公司',
  236. },
  237. ]}
  238. >
  239. <TreeSelect
  240. style={{ width: '100%' }}
  241. placeholder="请选择"
  242. showSearch
  243. allowClear
  244. fieldNames={{
  245. label: 'Name',
  246. value: 'ID',
  247. children: 'children',
  248. }}
  249. dropdownStyle={{ maxHeight: 400, overflow: 'auto' }}
  250. treeData={depList}
  251. disabled={disableds.contract}
  252. />
  253. </Form.Item>
  254. <Form.Item
  255. name="name"
  256. label="合同名称:"
  257. tooltip="合同名称"
  258. initialValue={data?.name}
  259. rules={[
  260. {
  261. required: true,
  262. message: '请填写合同名称',
  263. },
  264. ]}
  265. >
  266. <Input disabled={disableds.contract} />
  267. </Form.Item>
  268. <Form.Item
  269. name="effect_on"
  270. label="合同生效日期:"
  271. initialValue={data?.effect_on}
  272. rules={[
  273. {
  274. required: true,
  275. message: '请填写合同名称',
  276. },
  277. ]}
  278. >
  279. {type == Type.add ? (
  280. <DatePicker
  281. style={{ width: '100%' }}
  282. disabled={disableds.contract}
  283. />
  284. ) : (
  285. <Input disabled={disableds.contract} />
  286. )}
  287. </Form.Item>
  288. <Form.Item
  289. name="project_name"
  290. label="项目名称:"
  291. initialValue={data?.project_name}
  292. rules={[
  293. {
  294. required: true,
  295. message: '请填写项目名称',
  296. },
  297. ]}
  298. >
  299. <Select
  300. style={{ width: '100%' }}
  301. placeholder="请选择"
  302. options={projectList?.map((item) => {
  303. return {
  304. value: item.project_name,
  305. label: item.project_name,
  306. };
  307. })}
  308. disabled={disableds.contract}
  309. />
  310. </Form.Item>
  311. <Form.Item
  312. name="party_a"
  313. label="甲方:"
  314. initialValue={data?.party_a}
  315. rules={[
  316. {
  317. required: true,
  318. message: '请选择甲方',
  319. },
  320. ]}
  321. >
  322. <TreeSelect
  323. style={{ width: '100%' }}
  324. placeholder="请选择"
  325. showSearch
  326. allowClear
  327. fieldNames={{
  328. label: 'Name',
  329. value: 'Name',
  330. children: 'children',
  331. }}
  332. dropdownStyle={{ maxHeight: 400, overflow: 'auto' }}
  333. treeData={supplyList}
  334. disabled={disableds.contract}
  335. />
  336. </Form.Item>
  337. <Form.Item
  338. name="party_c"
  339. label="丙方(及其他):"
  340. initialValue={data?.party_c ? JSON.parse(data?.party_c) : []}
  341. rules={[
  342. {
  343. required: true,
  344. message: '请选择(支持多选)',
  345. },
  346. ]}
  347. >
  348. <TreeSelect
  349. style={{ width: '100%' }}
  350. placeholder="请选择"
  351. showSearch
  352. multiple
  353. allowClear
  354. fieldNames={{
  355. label: 'Name',
  356. value: 'Name',
  357. children: 'children',
  358. }}
  359. dropdownStyle={{ maxHeight: 400, overflow: 'auto' }}
  360. treeData={supplyList}
  361. disabled={disableds.contract}
  362. />
  363. </Form.Item>
  364. <Form.Item
  365. name="created_dep"
  366. label="签约承办部门:"
  367. initialValue={data?.created_dep}
  368. rules={[
  369. {
  370. required: true,
  371. message: '请选择签约承办部门',
  372. },
  373. ]}
  374. >
  375. <TreeSelect
  376. style={{ width: '100%' }}
  377. placeholder="请选择"
  378. showSearch
  379. allowClear
  380. disabled={disableds.contract}
  381. fieldNames={{
  382. label: 'Name',
  383. value: 'Name',
  384. children: 'children',
  385. }}
  386. dropdownStyle={{ maxHeight: 400, overflow: 'auto' }}
  387. treeData={depList?.find((item) => item.Code == 'GT')?.children}
  388. />
  389. </Form.Item>
  390. </Col>
  391. <Col span={10}>
  392. <Form.Item
  393. name="dep_id"
  394. label="所属部门:"
  395. initialValue={data?.dep_id}
  396. >
  397. <TreeSelect
  398. style={{ width: '100%' }}
  399. placeholder="请选择"
  400. showSearch
  401. allowClear
  402. fieldNames={{
  403. label: 'Name',
  404. value: 'ID',
  405. children: 'children',
  406. }}
  407. disabled={disableds.contract || depDisable}
  408. dropdownStyle={{ maxHeight: 400, overflow: 'auto' }}
  409. treeData={depList?.find((item) => item.Code == 'GT')?.children}
  410. />
  411. </Form.Item>
  412. {/* {type != Type.add && ( */}
  413. <Form.Item name="code" initialValue={data?.code} label="合同编号:">
  414. <Input placeholder="提交后自动生成" disabled />
  415. </Form.Item>
  416. {/* )} */}
  417. <Form.Item
  418. label="合同总价款:"
  419. name="amount"
  420. initialValue={data?.amount}
  421. rules={[
  422. {
  423. required: true,
  424. message: '请输入合同总价款',
  425. },
  426. ]}
  427. >
  428. <InputNumber
  429. style={{ width: '100%' }}
  430. precision={2}
  431. addonAfter="万元"
  432. disabled={disableds.contract}
  433. />
  434. </Form.Item>
  435. <Form.Item
  436. name="project_code"
  437. initialValue={data?.project_code}
  438. label="项目编号:"
  439. >
  440. <Input disabled />
  441. </Form.Item>
  442. <Form.Item
  443. name="party_b"
  444. label="乙方:"
  445. initialValue={data?.party_b}
  446. rules={[
  447. {
  448. required: true,
  449. message: '请选择乙方',
  450. },
  451. ]}
  452. >
  453. <TreeSelect
  454. style={{ width: '100%' }}
  455. placeholder="请选择"
  456. showSearch
  457. allowClear
  458. disabled={disableds.contract}
  459. fieldNames={{
  460. label: 'Name',
  461. value: 'Name',
  462. children: 'children',
  463. }}
  464. dropdownStyle={{ maxHeight: 400, overflow: 'auto' }}
  465. treeData={supplyList}
  466. />
  467. </Form.Item>
  468. <Form.Item
  469. name="deal_by"
  470. label="经办人:"
  471. initialValue={data?.deal_by}
  472. rules={[
  473. {
  474. required: true,
  475. message: '请选择经办人',
  476. },
  477. ]}
  478. >
  479. <Select
  480. style={{ width: '100%' }}
  481. placeholder="请选择"
  482. disabled={dealDisable || disableds.contract}
  483. options={userList?.map((item) => {
  484. return {
  485. value: item.CName,
  486. label: item.CName,
  487. };
  488. })}
  489. />
  490. </Form.Item>
  491. </Col>
  492. </Row>
  493. <Form.Item
  494. name="perform"
  495. initialValue={data?.perform}
  496. label="合同履行情况:"
  497. labelCol={{ span: 4 }}
  498. >
  499. <Input.TextArea disabled={disableds.contract} />
  500. </Form.Item>
  501. <Form.Item label="合同上传:" labelCol={{ span: 4 }}>
  502. {type == Type.add ? (
  503. <Upload {...UploadProps}>
  504. <Button icon={<CloudUploadOutlined />}>Upload</Button>
  505. </Upload>
  506. ) : (
  507. <ul>
  508. {data?.attach &&
  509. JSON.parse(data?.attach)?.map((item, idx) => (
  510. <li key={`${idx}_${item.name}`}>
  511. <Space>
  512. {item.name} <span>预览</span> <a href={item.url}>下载</a>
  513. </Space>
  514. </li>
  515. ))}
  516. </ul>
  517. )}
  518. </Form.Item>
  519. <Row>
  520. <Col span={10} offset={1}>
  521. <Form.Item
  522. name="created_name"
  523. initialValue={data?.created_name || user.CName}
  524. label="创建人:"
  525. >
  526. <Input disabled />
  527. </Form.Item>
  528. </Col>
  529. <Col span={10}>
  530. <Form.Item
  531. name="created_on"
  532. initialValue={data?.created_on || dayjs().format(FORMAT)}
  533. label="创建时间:"
  534. >
  535. <Input disabled />
  536. </Form.Item>
  537. </Col>
  538. </Row>
  539. {type == Type.check &&
  540. (data.status == Status.Checking ||
  541. data.status == Status.CalChecking) && (
  542. <>
  543. <ModuleTitle title="审核情况" />
  544. <Row>
  545. <Col span={10} offset={1}>
  546. <Form.Item
  547. name="check_by"
  548. initialValue={user.CName}
  549. label="审核人:"
  550. >
  551. <Input disabled />
  552. </Form.Item>
  553. <Form.Item name="is_pass" initialValue={1} label="审核意见:">
  554. <Select
  555. onChange={(e) => {
  556. setIsPass(e);
  557. }}
  558. style={{ width: '100%' }}
  559. options={[
  560. {
  561. value: 1,
  562. label: '同意',
  563. },
  564. {
  565. value: 0,
  566. label: '拒绝',
  567. },
  568. ]}
  569. />
  570. </Form.Item>
  571. </Col>
  572. <Col span={10}>
  573. <Form.Item
  574. name="check_date"
  575. initialValue={dayjs().format(FORMAT)}
  576. label="审核时间:"
  577. >
  578. <Input disabled />
  579. </Form.Item>
  580. </Col>
  581. </Row>
  582. {!isPass && (
  583. <Form.Item
  584. name="check_desc"
  585. label="拒绝原因:"
  586. labelCol={{ span: 4 }}
  587. >
  588. <Input.TextArea />
  589. </Form.Item>
  590. )}
  591. </>
  592. )}
  593. {type != Type.add && (
  594. <>
  595. <ModuleTitle title="归档流程" />
  596. <div className={styles.modelItem}>
  597. <Steps
  598. current={data?.status == Status.Checking ? 1 : 2}
  599. status={
  600. data?.status == Status.CheckReject ? 'error' : 'process'
  601. }
  602. items={[
  603. {
  604. title: '发起',
  605. description: (
  606. <>
  607. <div className={styles.textNowarp}>
  608. 发起人:{data?.created_name}
  609. </div>
  610. <div className={styles.textNowarp}>
  611. 发起时间:{data?.created_on}
  612. </div>
  613. </>
  614. ),
  615. },
  616. {
  617. title: '审核',
  618. description: (
  619. <>
  620. <div className={styles.textNowarp}>
  621. 审核人:{data?.check_by}
  622. </div>
  623. <div className={styles.textNowarp}>
  624. 审核时间:{data?.check_on}
  625. </div>
  626. {data?.check_desc && (
  627. <div
  628. className={styles.textNowarp}
  629. style={{ color: 'red' }}
  630. >
  631. 拒绝原因:{data?.check_desc}
  632. </div>
  633. )}
  634. </>
  635. ),
  636. },
  637. {
  638. title:
  639. data?.status >= Status.CheckSuccess
  640. ? StatusText[Status.CheckSuccess]
  641. : StatusText[data?.status],
  642. },
  643. ]}
  644. />
  645. </div>
  646. </>
  647. )}
  648. {(type == Type.cancel || data?.status >= Status.CalChecking) && (
  649. <>
  650. <ModuleTitle title="作废信息" />
  651. <Form.Item
  652. name="cancel_desc"
  653. label="作废原因:"
  654. initialValue={data?.cancel_desc}
  655. labelCol={{ span: 4 }}
  656. >
  657. <Input />
  658. </Form.Item>
  659. </>
  660. )}
  661. {type == Type.cancel && (
  662. <Form.Item
  663. name="cancel_on"
  664. label="创建时间:"
  665. initialValue={dayjs().format(FORMAT)}
  666. labelCol={{ span: 4 }}
  667. >
  668. <Input
  669. style={{ width: '460px' }}
  670. defaultValue={dayjs().format('YYYY-MM-DD')}
  671. disabled
  672. />
  673. <span
  674. style={{ color: 'red', fontSize: '24px', marginLeft: '40px' }}
  675. >
  676. 确认作废该合同,作废提交后无法撤回
  677. </span>
  678. </Form.Item>
  679. )}
  680. {type == Type.detail && data?.status >= Status.CalChecking && (
  681. <>
  682. <ModuleTitle title="作废流程" />
  683. <div className={styles.modelItem}>
  684. <Steps
  685. current={data?.status == Status.CalChecking ? 1 : 2}
  686. status={
  687. data?.status == Status.CalCheckReject ? 'error' : 'process'
  688. }
  689. items={[
  690. {
  691. title: '发起',
  692. description: (
  693. <>
  694. <div className={styles.textNowarp}>
  695. 发起人:{data?.created_name}
  696. </div>
  697. <div className={styles.textNowarp}>
  698. 发起时间:{data?.created_on}
  699. </div>
  700. </>
  701. ),
  702. },
  703. {
  704. title: '审核',
  705. description: (
  706. <>
  707. <div className={styles.textNowarp}>
  708. 审核人:{data?.cancel_check_by}
  709. </div>
  710. <div className={styles.textNowarp}>
  711. 审核时间:{data?.cancel_check_on}
  712. </div>
  713. </>
  714. ),
  715. },
  716. {
  717. title: StatusText[data?.status],
  718. },
  719. ]}
  720. />
  721. </div>
  722. </>
  723. )}
  724. </Form>
  725. <Divider />
  726. </Modal>
  727. );
  728. };
  729. export default ContractModal;