index.vue 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688
  1. <template>
  2. <div class="pageIndexClass">
  3. <div class="content">
  4. <byTable :source="sourceList.data" :pagination="sourceList.pagination" :config="config" :loading="loading" highlight-current-row
  5. :selectConfig="selectConfig" :action-list="[
  6. {
  7. text: '发起请款',
  8. action: () => openModal(),
  9. },
  10. ]" @moreSearch="moreSearch" @get-list="getList">
  11. <template #total="{ item }">
  12. <div style="width: 100%">
  13. <a style="color: #409eff; cursor: pointer" @click="pushProcessApproval(item)">{{ item.currency }}{{ item.total }}</a>
  14. </div>
  15. </template>
  16. <template #type="{ item }">
  17. <div style="width: 100%">
  18. <span>
  19. {{ dictValueLabel(item.type, fundsType) }}
  20. </span>
  21. <span v-if="item.type == 1">(<span v-if="item.writeOffStatus == 0">未核销</span><span v-if="item.writeOffStatus == 1">已核销</span>)</span>
  22. </div>
  23. </template>
  24. <template #file="{ item }">
  25. <div style="width: 100%">
  26. <el-button type="primary" text @click="handleOpenFile(item)">查看</el-button>
  27. </div>
  28. </template>
  29. </byTable>
  30. </div>
  31. <el-dialog title="打印" v-if="openPrint" v-model="openPrint" width="840px">
  32. <FundsPDF :rowData="rowData"></FundsPDF>
  33. <template #footer>
  34. <el-button @click="openPrint = false" size="default">取消</el-button>
  35. <el-button type="primary" v-print="printObj" size="default">打印</el-button>
  36. <el-button type="primary" @click="clickDownload()" size="default">下载PDF</el-button>
  37. </template>
  38. </el-dialog>
  39. <el-dialog title="高级检索" v-if="openSearch" v-model="openSearch" width="600" :before-close="cancelSearch">
  40. <byForm :formConfig="formSearchConfig" :formOption="formOption" v-model="sourceList.pagination">
  41. <template #departmentId>
  42. <div>
  43. <el-tree-select v-model="sourceList.pagination.departmentId" :data="deptTreeData" check-strictly :render-after-expand="false"
  44. node-key="deptId" style="width: 100%" :props="defaultProps" clearable />
  45. </div>
  46. </template>
  47. <template #time>
  48. <div style="width: 100%">
  49. <el-row :gutter="10">
  50. <el-col :span="11">
  51. <el-date-picker v-model="sourceList.pagination.beginTime" type="datetime" placeholder="请选择" style="width: 100%"
  52. value-format="YYYY-MM-DD HH:mm:ss" />
  53. </el-col>
  54. <el-col :span="2" style="text-align: center">到</el-col>
  55. <el-col :span="11">
  56. <el-date-picker v-model="sourceList.pagination.endTime" type="datetime" placeholder="请选择" style="width: 100%"
  57. value-format="YYYY-MM-DD HH:mm:ss" />
  58. </el-col>
  59. </el-row>
  60. </div>
  61. </template>
  62. <template #money>
  63. <div style="width: 100%">
  64. <el-row :gutter="10">
  65. <el-col :span="11">
  66. <el-input-number onmousewheel="return false;" v-model="sourceList.pagination.minAdvanceAmounts" placeholder="请输入" style="width: 100%"
  67. :precision="2" :controls="false" :min="0" />
  68. </el-col>
  69. <el-col :span="2" style="text-align: center">到</el-col>
  70. <el-col :span="11">
  71. <el-input-number onmousewheel="return false;" v-model="sourceList.pagination.maxAdvanceAmounts" placeholder="请输入" style="width: 100%"
  72. :precision="2" :controls="false" :min="0" />
  73. </el-col>
  74. </el-row>
  75. </div>
  76. </template>
  77. </byForm>
  78. <template #footer>
  79. <el-button @click="cancelSearch()" size="default">取 消</el-button>
  80. <el-button type="primary" @click="submitSearch()" size="default">确 定</el-button>
  81. </template>
  82. </el-dialog>
  83. <el-dialog title="关联附件" v-if="openFileDialog" v-model="openFileDialog" width="500">
  84. <byForm :formConfig="fileConfig" :formOption="formOption" v-model="fileData.data">
  85. <template #businessFile>
  86. <div style="width: 100%" v-if="fileData.data && fileData.data.fileList.length > 0">
  87. <div v-for="(item, index) in fileData.data.fileList" :key="index">
  88. <div style="cursor: pointer; color: #409eff" @click="openFile(item)">
  89. {{ item.fileName }}
  90. </div>
  91. </div>
  92. </div>
  93. <div v-else>暂无关联</div>
  94. </template>
  95. <template #fundsFile>
  96. <div style="width: 100%" v-if="fileData.data && fileData.data.fileListOne.length > 0">
  97. <div v-for="(item, index) in fileData.data.fileListOne" :key="index">
  98. <div style="cursor: pointer; color: #409eff" @click="openFile(item)">
  99. {{ item.fileName }}
  100. </div>
  101. </div>
  102. </div>
  103. <div v-else>暂无关联</div>
  104. </template>
  105. </byForm>
  106. <template #footer>
  107. <el-button @click="openFileDialog = false" size="default">取 消</el-button>
  108. </template>
  109. </el-dialog>
  110. </div>
  111. </template>
  112. <script setup>
  113. import byTable from "@/components/byTable/index";
  114. import useUserStore from "@/store/modules/user";
  115. import { ref } from "vue";
  116. import byForm from "@/components/byForm/index";
  117. import FundsPDF from "@/components/PDF/fundsPDF.vue";
  118. const loading = ref(false);
  119. const sourceList = ref({
  120. data: [],
  121. pagination: {
  122. total: 0,
  123. pageNum: 1,
  124. pageSize: 10,
  125. type: "",
  126. keyword: "",
  127. writeOffStatus: "",
  128. corporationId: "",
  129. departmentId: "",
  130. createUser: "",
  131. currency: "",
  132. beginTime: "",
  133. endTime: "",
  134. minAdvanceAmounts: undefined,
  135. maxAdvanceAmounts: undefined,
  136. paymentRemarks: "",
  137. paymentMethod: "",
  138. status: "",
  139. paymentStatus: "",
  140. },
  141. });
  142. const { proxy } = getCurrentInstance();
  143. const fundsType = ref([]);
  144. const userList = ref([]);
  145. const companyData = ref([]);
  146. const accountCurrency = ref([]);
  147. const fundsPaymentMethod = ref([]);
  148. const deptTreeData = ref([]);
  149. const status = ref([
  150. {
  151. label: "审批中",
  152. value: "10",
  153. },
  154. {
  155. label: "驳回",
  156. value: "20",
  157. },
  158. {
  159. label: "审批通过",
  160. value: "30",
  161. },
  162. ]);
  163. const paymentStatus = ref([
  164. {
  165. label: "已打款",
  166. value: "10",
  167. },
  168. {
  169. label: "部分打款",
  170. value: "15",
  171. },
  172. {
  173. label: "未打款",
  174. value: "20",
  175. },
  176. ]);
  177. const selectConfig = computed(() => {
  178. return [
  179. {
  180. label: "类型",
  181. prop: "type",
  182. data: fundsType.value,
  183. },
  184. {
  185. label: "审批状态",
  186. prop: "status",
  187. data: status.value,
  188. },
  189. {
  190. label: "放款状态",
  191. prop: "paymentStatus",
  192. data: paymentStatus.value,
  193. },
  194. ];
  195. });
  196. const config = computed(() => {
  197. return [
  198. {
  199. attrs: {
  200. label: "归属公司",
  201. prop: "corporationName",
  202. width: 180,
  203. },
  204. },
  205. {
  206. attrs: {
  207. label: "归属部门",
  208. prop: "deptName",
  209. width: 140,
  210. },
  211. },
  212. {
  213. attrs: {
  214. label: "请款类型",
  215. prop: "type",
  216. width: 130,
  217. slot: "type",
  218. },
  219. },
  220. {
  221. attrs: {
  222. label: "请款人",
  223. prop: "userName",
  224. width: 120,
  225. },
  226. },
  227. {
  228. attrs: {
  229. label: "请款时间",
  230. prop: "createTime",
  231. width: 160,
  232. },
  233. },
  234. {
  235. attrs: {
  236. label: "请款金额",
  237. slot: "total",
  238. width: 120,
  239. },
  240. },
  241. {
  242. attrs: {
  243. label: "用款说明",
  244. prop: "paymentRemarks",
  245. "min-width": 180,
  246. },
  247. },
  248. {
  249. attrs: {
  250. label: "关联附件",
  251. slot: "file",
  252. width: 80,
  253. align: "left",
  254. },
  255. },
  256. {
  257. attrs: {
  258. label: "付款方式",
  259. prop: "paymentMethod",
  260. width: 120,
  261. },
  262. render(paymentMethod) {
  263. return proxy.dictValueLabel(paymentMethod, fundsPaymentMethod.value);
  264. },
  265. },
  266. {
  267. attrs: {
  268. label: "放款状态",
  269. prop: "accountPaymentStatus",
  270. width: 120,
  271. },
  272. render(status) {
  273. return proxy.dictValueLabel(status, paymentStatus.value);
  274. },
  275. },
  276. {
  277. attrs: {
  278. label: "付款账户",
  279. prop: "accountManagementName",
  280. width: 160,
  281. },
  282. },
  283. {
  284. attrs: {
  285. label: "审批状态",
  286. prop: "status",
  287. width: 120,
  288. },
  289. render(status) {
  290. return status == 10
  291. ? "审批中"
  292. : status == 20
  293. ? "驳回"
  294. : status == 30
  295. ? "审批通过"
  296. : "";
  297. },
  298. },
  299. {
  300. attrs: {
  301. label: "操作",
  302. width: "120",
  303. align: "center",
  304. fixed: "right",
  305. },
  306. renderHTML(row) {
  307. return [
  308. row.type == 1 && row.writeOffStatus == 0 && row.status == "30"
  309. ? {
  310. attrs: {
  311. label: "核销",
  312. type: "primary",
  313. text: true,
  314. },
  315. el: "button",
  316. click() {
  317. openModal(row);
  318. },
  319. }
  320. : {},
  321. {
  322. attrs: {
  323. label: "打印",
  324. type: "primary",
  325. text: true,
  326. },
  327. el: "button",
  328. click() {
  329. clickPrint(row);
  330. },
  331. },
  332. ];
  333. },
  334. },
  335. ];
  336. });
  337. const getList = async (req) => {
  338. sourceList.value.pagination = { ...sourceList.value.pagination, ...req };
  339. loading.value = true;
  340. proxy
  341. .post("/accountRequestFunds/page", sourceList.value.pagination)
  342. .then((message) => {
  343. sourceList.value.data = message.rows;
  344. sourceList.value.pagination.total = message.total;
  345. setTimeout(() => {
  346. loading.value = false;
  347. }, 200);
  348. });
  349. };
  350. const openModal = (row = {}) => {
  351. const flowName = `${userInfo.nickName}的请款申请(发起)`;
  352. let query = {
  353. flowKey: "account_request_funds_flow",
  354. random: proxy.random(),
  355. flowName,
  356. };
  357. if (row.id) {
  358. query.advanceId = row.id;
  359. query.corporationId = row.corporationId;
  360. }
  361. proxy.$router.replace({
  362. path: "/platform_manage/process/processApproval",
  363. query,
  364. });
  365. };
  366. const userInfo = useUserStore().user;
  367. const recursive = (data) => {
  368. data.map((item) => {
  369. item.label = item.deptName;
  370. item.id = item.corporationId;
  371. if (item.children) {
  372. recursive(item.children);
  373. } else {
  374. item.children = [];
  375. }
  376. });
  377. };
  378. const getDictData = () => {
  379. proxy
  380. .getDictOne(["founds_type", "funds_payment_method", "account_currency"])
  381. .then((res) => {
  382. fundsType.value = res["founds_type"].map((x) => ({
  383. label: x.dictValue,
  384. value: x.dictKey,
  385. }));
  386. fundsPaymentMethod.value = res["funds_payment_method"].map((x) => ({
  387. label: x.dictValue,
  388. value: x.dictKey,
  389. }));
  390. accountCurrency.value = res["account_currency"].map((x) => ({
  391. label: x.dictValue,
  392. value: x.dictKey,
  393. }));
  394. });
  395. proxy
  396. .get("/tenantUser/list", {
  397. pageNum: 1,
  398. pageSize: 10000,
  399. tenantId: useUserStore().user.tenantId,
  400. })
  401. .then((res) => {
  402. if (res.rows && res.rows.length > 0) {
  403. userList.value = res.rows.map((item) => {
  404. return {
  405. deptId: item.deptId,
  406. label: item.nickName,
  407. value: item.userId,
  408. };
  409. });
  410. }
  411. });
  412. proxy
  413. .post("/corporation/page", { pageNum: 1, pageSize: 9999 })
  414. .then((res) => {
  415. if (res.rows && res.rows.length > 0) {
  416. companyData.value = res.rows.map((item) => {
  417. return {
  418. label: item.name,
  419. value: item.id,
  420. };
  421. });
  422. }
  423. });
  424. proxy
  425. .get("/tenantDept/list", {
  426. pageNum: 1,
  427. pageSize: 9999,
  428. tenantId: userInfo.tenantId,
  429. })
  430. .then((message) => {
  431. recursive(message.data);
  432. deptTreeData.value = [];
  433. let data = proxy.handleTree(message.data, "deptId");
  434. if (data && data.length > 0) {
  435. for (let i = 0; i < data.length; i++) {
  436. if (data[i].children && data[i].children.length > 0) {
  437. deptTreeData.value = deptTreeData.value.concat(data[i].children);
  438. }
  439. }
  440. }
  441. });
  442. };
  443. getDictData();
  444. getList();
  445. const openPrint = ref(false);
  446. const rowData = ref({});
  447. const clickPrint = (row) => {
  448. rowData.value = row;
  449. openPrint.value = true;
  450. };
  451. const clickDownload = () => {
  452. proxy.getPdf("请款PDF文件");
  453. };
  454. const formOption = reactive({
  455. inline: true,
  456. labelWidth: 100,
  457. itemWidth: 100,
  458. rules: [],
  459. });
  460. const defaultProps = {
  461. children: "children",
  462. label: "deptName",
  463. };
  464. const openSearch = ref(false);
  465. const formSearchConfig = computed(() => {
  466. return [
  467. {
  468. type: "select",
  469. prop: "corporationId",
  470. label: "归属公司",
  471. data: companyData.value,
  472. clearable: true,
  473. filterable: true,
  474. },
  475. {
  476. type: "slot",
  477. slotName: "departmentId",
  478. label: "归属部门",
  479. itemWidth: 50,
  480. },
  481. {
  482. type: "select",
  483. prop: "createUser",
  484. label: "请款人",
  485. data: userList.value,
  486. itemWidth: 50,
  487. clearable: true,
  488. filterable: true,
  489. },
  490. {
  491. type: "select",
  492. prop: "type",
  493. label: "请款类型",
  494. data: fundsType.value,
  495. clearable: true,
  496. filterable: true,
  497. },
  498. {
  499. type: "select",
  500. prop: "currency",
  501. label: "币种",
  502. data: accountCurrency.value,
  503. clearable: true,
  504. filterable: true,
  505. },
  506. {
  507. type: "slot",
  508. slotName: "time",
  509. label: "请款时间",
  510. },
  511. {
  512. type: "slot",
  513. slotName: "money",
  514. label: "请款金额",
  515. },
  516. {
  517. type: "input",
  518. itemType: "textarea",
  519. prop: "paymentRemarks",
  520. label: "用款说明",
  521. },
  522. {
  523. type: "select",
  524. prop: "paymentMethod",
  525. label: "付款方式",
  526. data: fundsPaymentMethod.value,
  527. clearable: true,
  528. itemWidth: 50,
  529. filterable: true,
  530. },
  531. {
  532. type: "input",
  533. prop: "oppositeAccountName",
  534. label: "对方账户",
  535. itemWidth: 50,
  536. },
  537. {
  538. type: "select",
  539. prop: "status",
  540. label: "流程状态",
  541. data: status.value,
  542. itemWidth: 50,
  543. clearable: true,
  544. filterable: true,
  545. },
  546. {
  547. type: "select",
  548. prop: "paymentStatus",
  549. label: "打款状态",
  550. data: paymentStatus.value,
  551. itemWidth: 50,
  552. clearable: true,
  553. filterable: true,
  554. },
  555. ];
  556. });
  557. let copySearch = ref({});
  558. const moreSearch = () => {
  559. copySearch.value = proxy.deepClone(sourceList.value.pagination);
  560. openSearch.value = true;
  561. };
  562. const cancelSearch = () => {
  563. sourceList.value.pagination = copySearch.value;
  564. openSearch.value = false;
  565. };
  566. const submitSearch = () => {
  567. if (
  568. sourceList.value.pagination.minAdvanceAmounts &&
  569. sourceList.value.pagination.maxAdvanceAmounts &&
  570. Number(sourceList.value.pagination.minAdvanceAmounts) >
  571. Number(sourceList.value.pagination.maxAdvanceAmounts)
  572. ) {
  573. return ElMessage("交易金额输入错误");
  574. }
  575. if (
  576. sourceList.value.pagination.beginTime &&
  577. sourceList.value.pagination.endTime &&
  578. sourceList.value.pagination.beginTime > sourceList.value.pagination.endTime
  579. ) {
  580. return ElMessage("开始时间不能大于结束时间");
  581. }
  582. openSearch.value = false;
  583. sourceList.value.pagination.keyword = "";
  584. sourceList.value.pagination.pageNum = 1;
  585. getList();
  586. };
  587. const pushProcessApproval = (row) => {
  588. proxy.$router.push({
  589. path: "/platform_manage/process/processApproval",
  590. query: {
  591. flowKey: "account_request_funds_flow",
  592. id: row.flowInfoId,
  593. processType: 20,
  594. random: proxy.random(),
  595. flowName: "请款详情",
  596. businessId: row.id,
  597. },
  598. });
  599. return;
  600. };
  601. const printObj = ref({
  602. id: "pdfDom",
  603. popTitle: "",
  604. extraCss:
  605. "https://cdn.bootcdn.net/ajax/libs/animate.css/4.1.1/animate.compat.css, https://cdn.bootcdn.net/ajax/libs/hover.css/2.3.1/css/hover-min.css",
  606. extraHead: '<meta http-equiv="Content-Language"content="zh-cn"/>',
  607. });
  608. const fileData = reactive({
  609. data: {},
  610. });
  611. const fileConfig = computed(() => [
  612. {
  613. type: "title",
  614. title: "业务附件",
  615. },
  616. {
  617. type: "slot",
  618. slotName: "businessFile",
  619. },
  620. {
  621. type: "title",
  622. title: "打款附件",
  623. },
  624. {
  625. type: "slot",
  626. slotName: "fundsFile",
  627. },
  628. ]);
  629. const openFileDialog = ref(false);
  630. const handleOpenFile = (row) => {
  631. fileData.data = {
  632. fileList: [],
  633. fileListOne: [],
  634. };
  635. proxy
  636. .post("/fileInfo/getList", { businessIdList: [row.id] })
  637. .then((fileObj) => {
  638. if (fileObj[row.id] && fileObj[row.id].length > 0) {
  639. fileData.data.fileList = fileObj[row.id];
  640. }
  641. });
  642. proxy
  643. .post("/accountPayment/detail", { id: row.accountPaymentId })
  644. .then((res) => {
  645. let ids = [];
  646. if (
  647. res.accountPaymentRecordsList &&
  648. res.accountPaymentRecordsList.length > 0
  649. ) {
  650. ids = res.accountPaymentRecordsList.map((x) => x.id);
  651. }
  652. proxy
  653. .post("/fileInfo/getList", {
  654. businessIdList: [row.accountPaymentId, ...ids],
  655. })
  656. .then((fileObj) => {
  657. if (fileObj) {
  658. let arr = [];
  659. for (const key in fileObj) {
  660. if (fileObj[key]) {
  661. arr = [...arr, ...fileObj[key]];
  662. }
  663. }
  664. fileData.data.fileListOne = arr;
  665. }
  666. });
  667. });
  668. openFileDialog.value = true;
  669. };
  670. const openFile = (item) => {
  671. window.open(item.fileUrl, "_blank");
  672. };
  673. </script>
  674. <style lang="scss" scoped>
  675. .tenant {
  676. padding: 20px;
  677. }
  678. ::v-deep(.el-input-number .el-input__inner) {
  679. text-align: left;
  680. }
  681. </style>