index.tsx 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238
  1. import DatePicker from '@/components/DatePicker'
  2. import FileModal from '@/components/FileModal'
  3. import { contractPaidStore } from '@/store/mobx'
  4. import { iReceivableState, iEditableCellProps } from '@/types/contract'
  5. import { iFileModal } from '@/types/file'
  6. import consts from '@/utils/consts'
  7. import { dayjsFormat, formatMoney } from '@/utils/util'
  8. import { DisconnectOutlined } from '@ant-design/icons'
  9. import { Form, Input, Popconfirm, Table } from 'antd'
  10. import locale from 'antd/es/date-picker/locale/zh_CN'
  11. import dayjs from 'dayjs'
  12. import { observer } from 'mobx-react'
  13. import React, { useEffect, useMemo, useState } from 'react'
  14. import { apiDelPaid, apiGetPaids, apiUpdatePaid } from './api'
  15. const EditableCell: React.FC<iEditableCellProps> = ({
  16. editing,
  17. dataIndex,
  18. title,
  19. cellType,
  20. children,
  21. record,
  22. index,
  23. ...restProps
  24. }) => {
  25. // console.log(dataIndex, record)
  26. const cellNode = cellType === 'text' ? <Input size="small" allowClear /> : <DatePicker size="small" allowClear locale={locale} />
  27. const isDate = useMemo(() => {
  28. return dataIndex === 'createTime' || dataIndex === 'time'
  29. }, [ dataIndex ])
  30. return (
  31. <td {...restProps}>
  32. {editing ? (
  33. <Form.Item name={dataIndex} initialValue={isDate ? dayjs(record[dataIndex]) : record[dataIndex]} style={{ margin: 0 }} rules={[ dataIndex === 'remarks' ? {} : { required: true, message: `请输入${title}!` } ]}>
  34. {cellNode}
  35. </Form.Item>
  36. ) : (
  37. children
  38. )}
  39. </td>
  40. )
  41. }
  42. interface ReceivableProps {
  43. updateTreeAndContract: () => Promise<void>
  44. }
  45. const Receivable: React.FC<ReceivableProps> = ({ updateTreeAndContract }) => {
  46. const [ form ] = Form.useForm()
  47. const [ data, setData ] = useState<Array<iReceivableState>>([])
  48. const [ id, setId ] = useState<string>('')
  49. const [ fileModal, setFileModal ] = useState<iFileModal>({
  50. visible: false,
  51. dataType: consts.DATA_TYPE.PAID,
  52. dataId: ''
  53. })
  54. const [ editingKey, setEditingKey ] = useState<string>('')
  55. const delConfirm = async (id: string, contractsId: string, bidsectionId: string) => {
  56. const { code = -1 } = await apiDelPaid(id, contractsId, bidsectionId)
  57. if (code === consts.RET_CODE.SUCCESS) {
  58. const newData = data.filter(item => item.id !== id)
  59. setData(newData)
  60. updateTreeAndContract()
  61. }
  62. }
  63. useEffect(() => {
  64. if (contractPaidStore.contract.id) {
  65. if (contractPaidStore.contract.id !== id) {
  66. setId(contractPaidStore.contract.id)
  67. initData()
  68. } else if (contractPaidStore.shouldUpdate && contractPaidStore.shouldUpdate === '2') {
  69. initData()
  70. }
  71. contractPaidStore.shouldUpdate && (contractPaidStore.changeUpdate(''))
  72. }
  73. }, [ contractPaidStore.contract.id, contractPaidStore.shouldUpdate ])
  74. const initData = async () => {
  75. const { code = -1, data = [] } = await apiGetPaids(contractPaidStore.contract.id, contractPaidStore.contract.bidsectionId)
  76. if (code === consts.RET_CODE.SUCCESS) {
  77. setData(data)
  78. }
  79. }
  80. const save = async (key: React.Key) => {
  81. try {
  82. const row = (await form.validateFields()) as iReceivableState
  83. const newData = [ ...data ]
  84. const index = newData.findIndex(item => key === item.id)
  85. if (index > -1) {
  86. const item = newData[index]
  87. const payload = { ...row, time: dayjsFormat(row.time, 'YYYY-MM-DD'), createTime: dayjsFormat(row.createTime, 'YYYY-MM-DD'), id: item.id, bidsectionId: item.bidsectionId, contractsId: item.contractsId }
  88. const { code = -1 } = await apiUpdatePaid(payload)
  89. if (code === consts.RET_CODE.SUCCESS) {
  90. newData.splice(index, 1, {
  91. ...item,
  92. ...row
  93. })
  94. setData(newData)
  95. updateTreeAndContract()
  96. }
  97. }
  98. setEditingKey('')
  99. } catch (errInfo) {
  100. console.log('Validate Failed:', errInfo)
  101. }
  102. }
  103. const isEditing = (record: iReceivableState) => record.id === editingKey
  104. const columns = [
  105. {
  106. dataIndex: 'sort',
  107. width: '5%',
  108. // eslint-disable-next-line react/display-name
  109. render: (_: any, record: any, index: number) => {
  110. return <span>{index + 1}</span>
  111. }
  112. },
  113. {
  114. title: '支付日期',
  115. dataIndex: 'time',
  116. editable: true,
  117. width: '12%',
  118. // eslint-disable-next-line react/display-name
  119. render: (text: string) => <span>{dayjsFormat(text, 'YYYY-MM-DD')}</span>
  120. },
  121. {
  122. title: '支付金额',
  123. dataIndex: 'price',
  124. editable: true,
  125. width: '12%',
  126. // eslint-disable-next-line react/display-name
  127. render: (text: string) => <span className="pi-text-right pi-width-100P">{formatMoney(parseFloat(text))}</span>
  128. },
  129. {
  130. title: '支付方式',
  131. dataIndex: 'way',
  132. editable: true,
  133. width: '12%'
  134. },
  135. {
  136. title: '创建人',
  137. dataIndex: 'createUser',
  138. editable: false,
  139. width: '12%'
  140. },
  141. {
  142. title: '创建时间',
  143. dataIndex: 'createTime',
  144. editable: true,
  145. width: '12%',
  146. // eslint-disable-next-line react/display-name
  147. render: (text: any) => <span>{dayjsFormat(text, 'YYYY-MM-DD')}</span>
  148. },
  149. {
  150. title: '备注',
  151. dataIndex: 'remarks',
  152. editable: true,
  153. width: '12%'
  154. },
  155. {
  156. title: '附件',
  157. dataIndex: 'fileCounts',
  158. // eslint-disable-next-line react/display-name
  159. render: (text: number, record: iReceivableState) => <span className="pi-pointer" onClick={() => setFileModal({ ...fileModal, dataId: record.id, visible: true })}><DisconnectOutlined /> {text}</span>
  160. },
  161. {
  162. title: '操作',
  163. dataIndex: 'opreate',
  164. width: '10%',
  165. // eslint-disable-next-line react/display-name
  166. render: (text: any, record: iReceivableState) => {
  167. const editable = isEditing(record)
  168. return (
  169. <div>
  170. {
  171. editable ?
  172. (<><span className="pi-link-blue pi-mg-right-5" onClick={() => save(record.id)}>保存</span><span className="pi-link-blue" onClick={() => setEditingKey('')}>取消</span></>)
  173. :
  174. <span className="pi-link-blue" onClick={() => setEditingKey(record.id)}>编辑</span>
  175. }
  176. <Popconfirm title="确认删除?" cancelText="取消" okText="确认" onConfirm={() => delConfirm(record.id, record.contractsId, record.bidsectionId)}>
  177. <span className="pi-link-red pi-mg-left-5">删除</span>
  178. </Popconfirm>
  179. </div>
  180. )
  181. }
  182. }
  183. ]
  184. const cancel = () => {
  185. setEditingKey('')
  186. }
  187. const mergedColumns = columns.map(col => {
  188. if (!col.editable) {
  189. return col
  190. }
  191. return {
  192. ...col,
  193. onCell: (record: iReceivableState) => ({
  194. record,
  195. cellType: col.dataIndex === 'createTime' || col.dataIndex === 'time' ? 'DatePicker' : 'text',
  196. dataIndex: col.dataIndex,
  197. title: col.title,
  198. editing: isEditing(record)
  199. })
  200. }
  201. })
  202. return (
  203. <>
  204. <Form form={form} component={false}>
  205. <Table
  206. components={{ body: { cell: EditableCell } }}
  207. dataSource={data}
  208. columns={mergedColumns}
  209. bordered
  210. rowClassName="editable-row"
  211. pagination={{ onChange: cancel, size: "small", pageSize: 7 }}
  212. rowKey={record => record.id}
  213. />
  214. </Form>
  215. <FileModal
  216. visible={fileModal.visible}
  217. dataType={fileModal.dataType}
  218. dataId={fileModal.dataId}
  219. onCancel={() => setFileModal({ ...fileModal, visible: false })}
  220. showUpload={true}
  221. uploadCallBack={() => initData()}
  222. />
  223. </>
  224. )
  225. }
  226. export default observer(Receivable)