index.tsx 8.3 KB

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