index.tsx 13 KB

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