index.tsx 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341
  1. import Authorization from '@/components/Authorization'
  2. import DatePicker from '@/components/DatePicker'
  3. import MoneyInput from '@/components/MoneyInput'
  4. import { commonStore, contractStore, tenderStore } from '@/store/mobx'
  5. import { ContractType } from '@/store/mobx/contract'
  6. import { iModalCommonProps } from '@/types/contract'
  7. import { apiContractSection } from '@/utils/common/api'
  8. import { contractTreeBaseId } from '@/utils/common/constStatus'
  9. import consts from '@/utils/consts'
  10. import { dayjsFormat, handleAutoCode } from '@/utils/util'
  11. import { Button, Form, Input, Modal, Select, TreeSelect } from 'antd'
  12. import locale from 'antd/es/date-picker/locale/zh_CN'
  13. import dayjs from 'dayjs'
  14. import React, { useEffect, useState } from 'react'
  15. import styles from './index.module.scss'
  16. import { BigNumber } from "bignumber.js"
  17. import { observer } from 'mobx-react'
  18. interface ContractSection {
  19. id: string;
  20. treeType: number;
  21. parentId: string;
  22. name: string;
  23. depth: number;
  24. serial: number;
  25. attribution: string;
  26. code: string;
  27. projectId: string;
  28. bidsectionId: string;
  29. contractId: string;
  30. contractName: string;
  31. contractCode: string;
  32. contractPrice: string;
  33. contractReturned: string;
  34. contractsPaid: string;
  35. contractStatus: number;
  36. contractLocking: number;
  37. createTime: string;
  38. children?: any;
  39. templateNumber: number;
  40. operation: string;
  41. elderBrother: boolean;
  42. isEnd: boolean;
  43. key: string;
  44. title: string;
  45. }
  46. const ContractModal: React.FC<iModalCommonProps> = ({ modalObj: { type, visible, confirmLoading, contractType }, onConfirm, onCancel, reload, row }) => {
  47. const { Option } = Select
  48. const [ contractSection, setContractSection ] = useState<ContractSection[]>([])
  49. const [ form ] = Form.useForm()
  50. const modalObj = {
  51. create: {
  52. title: '新建合同',
  53. cancelText: '取消',
  54. okText: '确认添加'
  55. },
  56. update: {
  57. title: '编辑合同',
  58. cancelText: '取消',
  59. okText: '确认'
  60. },
  61. close: {
  62. title: '关闭合同',
  63. cancelText: '取消',
  64. okText: '确认关闭'
  65. },
  66. del: {
  67. title: '删除合同',
  68. cancelText: '取消',
  69. okText: '确认删除'
  70. },
  71. unlock: {
  72. title: '解锁合同',
  73. cancelText: '取消',
  74. okText: '确认解锁'
  75. },
  76. add: {
  77. title: contractType === ContractType.INCOME ? '添加回款' : '添加支付',
  78. cancelText: '关闭',
  79. okText: '确认'
  80. }
  81. }
  82. const initTreeSection = async () => {
  83. const { code = -1, sectionTree: data = {} } = await apiContractSection(tenderStore.bid, contractType === ContractType.INCOME ? consts.CONTRACT_TREE.RETURN : consts.CONTRACT_TREE.PAID)
  84. if (code === consts.RET_CODE.SUCCESS) {
  85. setContractSection(data.children)
  86. }
  87. }
  88. useEffect(() => {
  89. if (visible) {
  90. form.setFieldsValue({ treeId: row.id, bidsectionId: tenderStore.bid })
  91. if (type === 'create') {
  92. initTreeSection()
  93. form.setFieldsValue({ treeId: row.contractId === contractTreeBaseId ? row.id : row.parentId })
  94. }
  95. if (type === 'update') {
  96. const { content = '', name = '', price = '', partyA = '', partyB = '', partyASigner = '', partyBSigner = '', signerTime = '', remarks = '' } = contractStore.contract
  97. form.setFieldsValue({ content, name, price, partyA, partyB, partyASigner, partyBSigner, signerTime: signerTime ? dayjs(signerTime) : '', remarks })
  98. } else if (type === 'add') {
  99. commonStore.returnWayOptions && commonStore.queryWayOptions()
  100. form.setFieldsValue({ contractsId: contractStore.contract.id })
  101. } else {
  102. form.setFieldsValue({ id: contractStore.contract.id })
  103. }
  104. }
  105. }, [ visible ])
  106. const autoCode = async () => {
  107. const ruleArr = await handleAutoCode(tenderStore.tender.bidsectionId, contractType === ContractType.INCOME ? 'contractReturnRule' : 'contractPaidRule')
  108. form.setFieldsValue({ code: ruleArr.join('-') })
  109. }
  110. // 处理添加回款的金额不应该超出最大值
  111. const maxPrice = new BigNumber(contractStore.contract.price).minus(contractType === ContractType.INCOME ? contractStore.contract.returned : contractStore.contract.paid)
  112. const minPrice = new BigNumber(contractType === ContractType.INCOME ? row.contractReturned : row.contractsPaid)
  113. return (
  114. <Modal
  115. getContainer={false}
  116. visible={visible}
  117. title={modalObj[type]?.title}
  118. onCancel={() => {
  119. form.resetFields()
  120. onCancel()
  121. }}
  122. footer={
  123. <div className="pi-justify-end">
  124. {type === 'update' ? (
  125. <Authorization type="contract" auth="delete">
  126. <Button type="primary" key="delete" size="small" danger onClick={() => reload('del')}>
  127. 删除合同
  128. </Button>
  129. </Authorization>
  130. ) : null}
  131. <Button type="default" size="small" key="cancel" className="pi-btn-secondary" onClick={() => {
  132. form.resetFields()
  133. onCancel()
  134. }}>
  135. {modalObj[type]?.cancelText}
  136. </Button>
  137. <Button
  138. type="primary"
  139. size="small"
  140. key="ok"
  141. loading={confirmLoading}
  142. danger={type === 'del'}
  143. onClick={() => {
  144. form
  145. .validateFields()
  146. .then(values => {
  147. form.resetFields()
  148. if (type === 'update') {
  149. values.signerTime = dayjsFormat(values.signerTime, 'YYYY-MM-DD HH:mm:ss')
  150. }
  151. if (type === 'del') {
  152. delete values.warningText
  153. }
  154. if (type === 'add') {
  155. values.time = dayjsFormat(values.time, 'YYYY-MM-DD HH:mm:ss')
  156. }
  157. onConfirm(values, type)
  158. })
  159. .catch(info => {
  160. console.error('Validate Failed:', info)
  161. })
  162. }}>
  163. {modalObj[type]?.okText}
  164. </Button>
  165. </div>
  166. }>
  167. <Form form={form} layout="vertical" style={type === 'update' ? { maxHeight: '482px', overflowY: 'scroll', paddingRight: 5 } : { overflow: 'hidden' }}>
  168. <Form.Item name="bidsectionId" hidden>
  169. <Input />
  170. </Form.Item>
  171. <Form.Item name="treeId" label="合同劳务" hidden={type === 'create' ? false : true} rules={[ { required: true, message: '请选择项目节' } ]}>
  172. <TreeSelect treeData={contractSection} />
  173. </Form.Item>
  174. {/* <Form.Item name="treeId" label="合同劳务" hidden>
  175. <Input />
  176. </Form.Item> */}
  177. {type === 'create' ? (
  178. <>
  179. <Form.Item name="code" label="合同编号" rules={[ { required: true, message: '请输入合同编号' } ]} className={styles.contractFormItem}>
  180. <Input
  181. autoComplete='off'
  182. addonAfter={
  183. <span className="pi-pd-lr-11" onClick={() => autoCode()}>
  184. 自动编号
  185. </span>
  186. } />
  187. </Form.Item>
  188. <Form.Item name="name" label="合同名称" rules={[ { required: true, message: '请输入合同名称' } ]}>
  189. <Input placeholder="输入合同名称" autoComplete='off'/>
  190. </Form.Item>
  191. {/* <Form.Item name="contractsType" label="合同类型" rules={[ { required: true, message: '请选择合同类型' } ]}>
  192. <Select showSearch>
  193. <Option value={1}>支出合同</Option>
  194. <Option value={2}>收入合同</Option>
  195. </Select>
  196. </Form.Item> */}
  197. <Form.Item label="合同金额" name="price" rules={[ { required: true, message: '请输入合同金额' } ]} className={styles.MoneyFormItem}>
  198. {/* <Input type="number" placeholder="输入合同金额" addonAfter={<span>元</span>} /> */}
  199. <MoneyInput />
  200. </Form.Item>
  201. </>
  202. ) : (
  203. ''
  204. )}
  205. {type === 'update' ? (
  206. <>
  207. <Form.Item name="content" label="项目内容" rules={[ { required: true, message: '请输入项目内容' } ]}>
  208. <Input />
  209. </Form.Item>
  210. <Form.Item name="name" label="合同名称" rules={[ { required: true, message: '请输入项目内容' } ]}>
  211. <Input />
  212. </Form.Item>
  213. <Form.Item label="合同金额" name="price" rules={[ { required: true, message: '请输入合同金额' }, () => ({
  214. validator(_, value) {
  215. if (value && (new BigNumber(value).lt(minPrice))) {
  216. return Promise.reject(`当前金额不能低于${minPrice}`)
  217. }
  218. return Promise.resolve()
  219. }
  220. }) ]}>
  221. <MoneyInput />
  222. </Form.Item>
  223. <Form.Item name="partyA" label="甲方" rules={[ { required: true, message: '请输入甲方' } ]}>
  224. <Input />
  225. </Form.Item>
  226. <Form.Item name="partyASigner" label="甲方签约人" rules={[ { required: true, message: '请输入甲方签约人' } ]}>
  227. <Input />
  228. </Form.Item>
  229. <Form.Item name="partyB" label="乙方" rules={[ { required: true, message: '请输入乙方' } ]}>
  230. <Input />
  231. </Form.Item>
  232. <Form.Item name="partyBSigner" label="乙方签约人" rules={[ { required: true, message: '请输入乙方签约人' } ]}>
  233. <Input />
  234. </Form.Item>
  235. <Form.Item name="signerTime" label="合同签约日期" rules={[ { required: true, message: '请选择签约日期' } ]}>
  236. <DatePicker allowClear locale={locale} className="pi-width-100P" />
  237. </Form.Item>
  238. <Form.Item name="remarks" label="备注">
  239. <Input.TextArea maxLength={100} />
  240. </Form.Item>
  241. </>
  242. ) : null
  243. }
  244. {type === 'close' ? (
  245. <>
  246. <Form.Item name="id" hidden>
  247. <Input />
  248. </Form.Item>
  249. <span>关闭后,合同将锁定,无法进行编辑、上传文件等操作。</span>
  250. </>
  251. ) : (
  252. ''
  253. )}
  254. {type === 'unlock' ? (
  255. <>
  256. <Form.Item name="id" hidden>
  257. <Input />
  258. </Form.Item>
  259. <span>解锁后,合同将锁定,无法进行编辑、上传文件等操作。</span>
  260. </>
  261. ) : (
  262. ''
  263. )}
  264. {type === 'del' ? (
  265. <>
  266. <Form.Item name="id" hidden>
  267. <Input />
  268. </Form.Item>
  269. <p className="mb-2">删除后,数据无法恢复,请谨慎操作。</p>
  270. <p className="mb-2">
  271. 请在下方文本框输入文本「<span className="pi-red">确认删除本合同</span>」,以此确认删除操作。
  272. </p>
  273. <Form.Item
  274. name="warningText"
  275. rules={[
  276. () => ({
  277. validator(rule, value) {
  278. if (!value || value !== '确认删除本合同') {
  279. return Promise.reject('请按照提示信息进行删除操作!')
  280. }
  281. return Promise.resolve()
  282. }
  283. })
  284. ]}>
  285. <Input placeholder="输入文本, 确认删除" />
  286. </Form.Item>
  287. </>
  288. ) : (
  289. ''
  290. )}
  291. {type === 'add' ? (
  292. <>
  293. <Form.Item name="contractsId" hidden>
  294. <Input />
  295. </Form.Item>
  296. <Form.Item name="time" label={`${contractType === ContractType.INCOME ? '回款' : '支付'}日期`} rules={[ { required: true, message: `请选择${contractType === ContractType.INCOME ? '回款' : '支付'}日期` } ]}>
  297. <DatePicker allowClear locale={locale} className="pi-width-100P" />
  298. </Form.Item>
  299. <Form.Item name="price" label={`${contractType === ContractType.INCOME ? '回款' : '支付'}金额`} rules={[ { required: true, message: `请输入${contractType === ContractType.INCOME ? '回款' : '支付'}金额` }, () => ({
  300. validator(_, value) {
  301. if (value && (new BigNumber(value).gt(maxPrice))) {
  302. return Promise.reject(`当前金额不能大于${maxPrice}`)
  303. }
  304. return Promise.resolve()
  305. }
  306. }) ]}>
  307. {/* <Input onChange={handleReturnPrice}/> */}
  308. <MoneyInput />
  309. </Form.Item>
  310. <Form.Item name="way" label={`${contractType === ContractType.INCOME ? '回款' : '支付'}方式`} rules={[ { required: true, message: `请选择${contractType === ContractType.INCOME ? '回款' : '支付'}方式` } ]}>
  311. <Select>
  312. {
  313. commonStore.returnWayOptions.map(item => (
  314. <Option key={item} value={item}>
  315. {item}
  316. </Option>))
  317. }
  318. </Select>
  319. </Form.Item>
  320. <Form.Item name="remarks" label="备注">
  321. <Input.TextArea maxLength={100} />
  322. </Form.Item>
  323. </>
  324. ) : (
  325. ''
  326. )}
  327. </Form>
  328. </Modal>
  329. )
  330. }
  331. export default observer(ContractModal)