index.vue 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544
  1. <script setup lang="ts">
  2. import AForm from '@/components/AForm/index.vue'
  3. import {FormConfigType} from '@/components/AForm/type'
  4. import {ToolbarConfigType} from '@/components/AToolbar/type'
  5. import {ColumnConfigType} from '@/components/ATable/type'
  6. import {StrAnyObj, StrAnyObjArr} from '@/typings'
  7. import {useHandleData} from '@/utils/useHandleData'
  8. import {addApi, deleteApi, editApi, getDetailApi, getPageApi} from '@/api/business/payment/requests'
  9. import {getPageApi as getCorporationPageApi} from '@/api/business/corporation/corporation'
  10. import DeptTreeSelect from '@/views/components/DeptTreeSelect/index.vue'
  11. import {getPageApi as getCapitalAccountPageApi} from '@/api/business/capital/account'
  12. import FileUpload from '@/components/FlieUpload/index.vue'
  13. const queryRef = ref<InstanceType<typeof AForm>>()
  14. const formRef = ref<InstanceType<typeof AForm>>()
  15. const showQuery = ref<boolean>(true)
  16. const selectKeys = ref<string[]>([])
  17. const pageTotal = ref<number>(0)
  18. const queryData = ref<StrAnyObj>({pageNum: 1, pageSize: 10})
  19. const tableData = ref<StrAnyObjArr>([])
  20. const formData = ref<StrAnyObj>({paymentRequestsDetailList: [{}]})
  21. const dialogTitle = ref<string>('')
  22. const dialogVisible = ref<boolean>(false)
  23. const deptIdRef = ref<InstanceType<typeof DeptTreeSelect>>()
  24. const selectDeptIdRef = ref<InstanceType<typeof DeptTreeSelect>>()
  25. const disabled = ref(false)
  26. const queryConfig: FormConfigType[] = [
  27. {
  28. type: 'select',
  29. prop: 'corporationId',
  30. label: '归属公司',
  31. keyName: 'id',
  32. labelName: 'name',
  33. async option() {
  34. const data = await getCorporationPageApi({ searchAll: true })
  35. return data.records
  36. },
  37. },
  38. {
  39. type: 'slot',
  40. prop: 'deptId',
  41. label: '归属部门',
  42. },
  43. {
  44. type: 'select',
  45. prop: 'type',
  46. label: '请款类型',
  47. dict: 'payment_requests_type'
  48. },
  49. {
  50. type: 'select',
  51. prop: 'payType',
  52. label: '付款方式',
  53. dict: 'pay_type'
  54. },
  55. {
  56. type: 'select',
  57. prop: 'approvalStatus',
  58. label: '审批状态',
  59. option: [
  60. {
  61. key: 0,
  62. label: '待发起'
  63. },
  64. {
  65. key: 1,
  66. label: '进行中'
  67. },
  68. {
  69. key: 2,
  70. label: '已通过'
  71. },
  72. {
  73. key: 3,
  74. label: '已驳回'
  75. },
  76. ]
  77. },
  78. {
  79. type: 'select',
  80. prop: 'paymentStatus',
  81. label: '放款状态',
  82. option: [
  83. {
  84. key: 0,
  85. label: '未放款'
  86. },
  87. {
  88. key: 1,
  89. label: '已放款'
  90. }
  91. ]
  92. }
  93. ]
  94. const toolbarConfig: ToolbarConfigType[] = [
  95. {
  96. common: 'search',
  97. click() {
  98. queryData.value.pageNum = 1
  99. getPage()
  100. }
  101. },
  102. {
  103. common: 'reset',
  104. click() {
  105. queryRef.value?.resetFields()
  106. getPage()
  107. }
  108. },
  109. {
  110. common: 'add',
  111. click() {
  112. dialogVisible.value = true
  113. dialogTitle.value = '新增'
  114. nextTick(() => deptIdRef.value?.load())
  115. }
  116. }
  117. ]
  118. const columnConfig: ColumnConfigType[] = [
  119. {
  120. prop: 'corporationName',
  121. label: '归属公司'
  122. },
  123. {
  124. prop: 'deptName',
  125. label: '归属部门'
  126. },
  127. {
  128. prop: 'type',
  129. label: '请款类型',
  130. dict: 'payment_requests_type'
  131. },
  132. {
  133. prop: 'userName',
  134. label: '请款人'
  135. },
  136. {
  137. prop: 'createTime',
  138. label: '请款时间'
  139. },
  140. {
  141. prop: 'useTime',
  142. label: '用款时间'
  143. },
  144. {
  145. prop: 'useRemark',
  146. label: '用款说明',
  147. showOverflowTooltip: true
  148. },
  149. {
  150. prop: 'totalAmount',
  151. label: '请款金额'
  152. },
  153. {
  154. prop: 'payType',
  155. label: '付款方式',
  156. dict: 'pay_type'
  157. },
  158. {
  159. prop: 'capitalAccountName',
  160. label: '付款账户'
  161. },
  162. {
  163. prop: 'approvalStatus',
  164. label: '审批状态',
  165. formatter(row) {
  166. switch (row.approvalStatus) {
  167. case 0:
  168. return '待发起'
  169. case 1:
  170. return '进行中'
  171. case 2:
  172. return '已通过'
  173. case 3:
  174. return '已驳回'
  175. }
  176. }
  177. },
  178. {
  179. prop: 'paymentStatus',
  180. label: '放款状态',
  181. formatter(row) {
  182. switch (row.paymentStatus) {
  183. case 0:
  184. return '未放款'
  185. case 1:
  186. return '已放款'
  187. }
  188. }
  189. },
  190. {
  191. width: 250,
  192. handleConfig: [
  193. {
  194. text: '重新发起',
  195. if(row) {
  196. return row.approvalStatus == 0
  197. },
  198. click(row) {
  199. dialogVisible.value = true
  200. dialogTitle.value = '重新发起'
  201. disabled.value = false
  202. getDetailApi({id: row.id}).then((resp: StrAnyObj) => {
  203. formData.value = resp
  204. })
  205. nextTick(() => deptIdRef.value?.load())
  206. }
  207. },
  208. {
  209. common: 'detail',
  210. click(row) {
  211. dialogVisible.value = true
  212. dialogTitle.value = '详情'
  213. disabled.value = true
  214. nextTick(() => deptIdRef.value?.load())
  215. getDetailApi({ id: row.id }).then((resp: StrAnyObj) => {
  216. formData.value = resp
  217. updateAmount()
  218. updateDetailRemark()
  219. })
  220. }
  221. }
  222. ]
  223. }
  224. ]
  225. const formConfig: FormConfigType[] = [
  226. {
  227. type: 'select',
  228. prop: 'corporationId',
  229. label: '归属公司',
  230. keyName: 'id',
  231. labelName: 'name',
  232. async option() {
  233. const data = await getCorporationPageApi({searchAll: true})
  234. return data.records
  235. },
  236. rule: [{required: true, message: '归属公司id不能为空', trigger: 'blur'}]
  237. },
  238. {
  239. type: 'slot',
  240. prop: 'deptId',
  241. label: '归属部门',
  242. rule: [{required: true, message: '部门id不能为空', trigger: 'blur'}]
  243. },
  244. {
  245. type: 'select',
  246. prop: 'type',
  247. label: '请款类型',
  248. dict: 'payment_requests_type',
  249. rule: [{required: true, message: '请款类型不能为空', trigger: 'blur'}]
  250. },
  251. {
  252. type: 'datePicker',
  253. prop: 'useTime',
  254. label: '用款时间',
  255. datePickerType: 'datetime',
  256. format: 'YYYY-MM-DD 00:00:00',
  257. valueFormat: 'YYYY-MM-DD 00:00:00'
  258. },
  259. {
  260. type: 'input',
  261. itemType: 'textarea',
  262. prop: 'useRemark',
  263. label: '用款说明',
  264. rows: 3,
  265. span: 24,
  266. placeholder: '自动拼接请款明细中的款项说明',
  267. disabled: true
  268. },
  269. {
  270. type: 'slot',
  271. prop: 'atts',
  272. label: '上传附件',
  273. span: 24
  274. },
  275. {
  276. type: 'slot',
  277. prop: 'detailTable',
  278. label: '请款明细',
  279. span: 24
  280. },
  281. {
  282. type: 'input',
  283. prop: 'totalAmount',
  284. label: '付款总金额',
  285. disabled: true,
  286. placeholder: '自动统计请款明细中的请款金额'
  287. },
  288. {
  289. type: 'inputNumber',
  290. prop: 'documentQuantity',
  291. label: '单据数量',
  292. min: 0,
  293. precision: 0,
  294. rule: [{required: true, message: '单据数量不能为空', trigger: 'blur'}]
  295. },
  296. {
  297. type: 'select',
  298. prop: 'payType',
  299. label: '付款方式',
  300. dict: 'pay_type',
  301. rule: [{required: true, message: '付款方式不能为空', trigger: 'blur'}]
  302. },
  303. {
  304. type: 'select',
  305. prop: 'capitalAccountId',
  306. label: '付款账户',
  307. keyName: 'id',
  308. labelName: 'accountAlias',
  309. async option() {
  310. const data = await getCapitalAccountPageApi({searchAll: true})
  311. return data.records
  312. }
  313. },
  314. {
  315. type: 'input',
  316. prop: 'accountName',
  317. label: '户名'
  318. },
  319. {
  320. type: 'input',
  321. prop: 'account',
  322. label: '银行账号'
  323. },
  324. {
  325. type: 'input',
  326. prop: 'depositBank',
  327. label: '开户银行'
  328. },
  329. {
  330. type: 'input',
  331. prop: 'correspondentNumber',
  332. label: '联行号/SWIFT Code'
  333. }
  334. ]
  335. const detailTableColumnConfig: ColumnConfigType[] = [
  336. {
  337. slot: 'expenseType',
  338. label: '费用类型',
  339. width: 250
  340. },
  341. {
  342. slot: 'remark',
  343. label: '款项说明'
  344. },
  345. {
  346. slot: 'amount',
  347. label: '请款金额',
  348. width: 180
  349. },
  350. {
  351. width: 100,
  352. if: () => !disabled.value,
  353. handleConfig: [
  354. {
  355. common: 'delete',
  356. click(row, index) {
  357. formData.value.paymentRequestsDetailList.splice(index, 1)
  358. updateDetailRemark()
  359. updateAmount()
  360. }
  361. }
  362. ]
  363. }
  364. ]
  365. onMounted(() => {
  366. getPage()
  367. selectDeptIdRef.value?.load()
  368. })
  369. function getPage() {
  370. getPageApi(queryData.value).then((resp) => {
  371. tableData.value = resp.records
  372. pageTotal.value = resp.total
  373. })
  374. }
  375. function tableSelectionChange(item: StrAnyObjArr) {
  376. selectKeys.value = item.map((item) => item.id)
  377. }
  378. function formSubmit() {
  379. formRef.value?.validate(() => {
  380. if (formData.value.paymentRequestsDetailList.length === 0) {
  381. ElMessage.error('请款明细为空,无法提交')
  382. return
  383. }
  384. for (const item of formData.value.paymentRequestsDetailList) {
  385. if (!item.expenseType) {
  386. ElMessage.error('请款明细存在费用类型为空,无法提交')
  387. return
  388. }
  389. if (!item.remark) {
  390. ElMessage.error('请款明细存在款项说明为空,无法提交')
  391. return
  392. }
  393. if (!item.amount) {
  394. ElMessage.error('请款明细存在请款金额为空,无法提交')
  395. return
  396. }
  397. }
  398. if (formData.value.id) {
  399. editApi(formData.value).then(() => {
  400. dialogVisible.value = false
  401. ElMessage.success('修改成功')
  402. getPage()
  403. })
  404. } else {
  405. addApi(formData.value).then(() => {
  406. dialogVisible.value = false
  407. ElMessage.success('新增成功')
  408. getPage()
  409. })
  410. }
  411. })
  412. }
  413. function formClosed() {
  414. formRef.value?.resetFields()
  415. }
  416. function handleRemove(idList: string[]) {
  417. useHandleData('是否确认删除?', () => {
  418. deleteApi({idList}).then(() => {
  419. ElMessage.success('删除成功')
  420. getPage()
  421. })
  422. })
  423. }
  424. function addPaymentRequestsDetailList() {
  425. formData.value.paymentRequestsDetailList.push({})
  426. }
  427. function updateDetailRemark() {
  428. formData.value.useRemark = formData.value.paymentRequestsDetailList
  429. .map((item) => item.remark)
  430. .filter((item) => item !== '' && item !== null && item !== undefined)
  431. .join(' - \n')
  432. }
  433. function updateAmount() {
  434. formData.value.totalAmount = formData.value.paymentRequestsDetailList
  435. .map((item) => item.amount)
  436. .filter((item) => item !== '' && item !== null && item !== undefined)
  437. .reduce((pre, next) => pre + next, 0)
  438. }
  439. </script>
  440. <template>
  441. <div>
  442. <el-card v-if="showQuery">
  443. <a-form ref="queryRef" v-model="queryData" :config="queryConfig" :span="6">
  444. <template #deptId>
  445. <dept-tree-select ref="selectDeptIdRef" v-model="formData.deptId" />
  446. </template>
  447. </a-form>
  448. </el-card>
  449. <a-table
  450. selection
  451. :data="tableData"
  452. :page-total="pageTotal"
  453. :toolbar-config="toolbarConfig"
  454. :column-config="columnConfig"
  455. v-model:showQuery="showQuery"
  456. v-model:page-num="queryData.pageNum"
  457. v-model:page-size="queryData.pageSize"
  458. @page-num-change="getPage"
  459. @page-size-change="getPage"
  460. @selection-change="tableSelectionChange"
  461. >
  462. </a-table>
  463. <a-dialog
  464. v-model="dialogVisible"
  465. :title="dialogTitle"
  466. @submit="formSubmit"
  467. @closed="formClosed"
  468. width="1400px"
  469. height="200px"
  470. >
  471. <a-form ref="formRef" v-model="formData" :config="formConfig" :span="12" :disabled="disabled">
  472. <template #deptId>
  473. <dept-tree-select ref="deptIdRef" v-model="formData.deptId" :disabled="disabled"/>
  474. </template>
  475. <template #atts>
  476. <file-upload v-model="formData.atts" :disabled="disabled"/>
  477. </template>
  478. <template #detailTable>
  479. <a-table
  480. :data="formData.paymentRequestsDetailList"
  481. :columnConfig="detailTableColumnConfig"
  482. style="width: 100%"
  483. :card="false"
  484. >
  485. <template #expenseType="scope">
  486. <a-select v-model="scope.row.expenseType" dict="expense_type" :disabled="disabled"/>
  487. </template>
  488. <template #remark="scope">
  489. <a-input
  490. v-model="scope.row.remark"
  491. type="textarea"
  492. :rows="2"
  493. @change="updateDetailRemark"
  494. :disabled="disabled"
  495. />
  496. </template>
  497. <template #amount="scope">
  498. <a-inputNumber
  499. v-model="scope.row.amount"
  500. :min="0.01"
  501. :precision="2"
  502. @change="updateAmount"
  503. :disabled="disabled"
  504. />
  505. </template>
  506. </a-table>
  507. <el-button
  508. v-if="!disabled"
  509. style="width: 100%; margin-bottom: 20px"
  510. type="primary"
  511. @click="addPaymentRequestsDetailList"
  512. >
  513. 添加行
  514. </el-button>
  515. </template>
  516. </a-form>
  517. </a-dialog>
  518. </div>
  519. </template>