index.vue 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620
  1. <template>
  2. <div>
  3. <el-card class="box-card">
  4. <byTable
  5. :source="sourceList.data"
  6. :pagination="sourceList.pagination"
  7. :config="config"
  8. :loading="loading"
  9. :searchConfig="searchConfig"
  10. highlight-current-row
  11. :action-list="[
  12. {
  13. text: '登记对账',
  14. action: () => clickModal(),
  15. },
  16. ]"
  17. @get-list="getList"
  18. @clickReset="clickReset">
  19. <template #timePeriod="{ item }">
  20. <div>
  21. <el-row>
  22. <el-col :span="11">{{ item.timePeriod }}</el-col>
  23. <el-col :span="2" style="text-align: center">-</el-col>
  24. <el-col :span="11">{{ item.timePeriod }}</el-col>
  25. </el-row>
  26. </div>
  27. </template>
  28. <template #receiptFileList="{ item }">
  29. <div>
  30. <el-button type="primary" @click="clickReceiptFile(item)" v-if="item.receiptFileList && item.receiptFileList.length > 0" text>查看</el-button>
  31. <el-button type="danger" @click="clickReceiptFile(item)" v-else text>上传</el-button>
  32. </div>
  33. </template>
  34. <template #proofFileList="{ item }">
  35. <div>
  36. <el-button type="primary" @click="clickProofFile(item)" v-if="item.proofFileList && item.proofFileList.length > 0" text>查看</el-button>
  37. <el-button type="danger" @click="clickProofFile(item)" v-else text>上传</el-button>
  38. </div>
  39. </template>
  40. </byTable>
  41. </el-card>
  42. <el-dialog :title="modalType == 'add' ? '登记对账' : '编辑对账'" v-if="openDialog" v-model="openDialog" width="80%">
  43. <byForm :formConfig="formConfig" :formOption="formOption" v-model="formData.data">
  44. <template #orderIdList>
  45. <div style="width: 100%">
  46. <el-form-item label="事业部" prop="departmentId" style="margin-bottom: 18px">
  47. <el-select v-model="formData.data.departmentId" placeholder="请选择事业部" clearable @change="changeDepartment" :disabled="formData.data.id">
  48. <el-option v-for="item in departmentList" :key="item.dictKey" :label="item.dictValue" :value="item.dictKey" />
  49. </el-select>
  50. <el-button type="primary" size="small" style="margin-left: 16px" @click="clickAddOrder()">选择合同</el-button>
  51. </el-form-item>
  52. <el-table :data="formData.data.orderList" :row-style="{ height: '35px' }" header-row-class-name="tableHeader">
  53. <el-table-column label="事业部" prop="departmentName" width="140" />
  54. <el-table-column label="订单号" prop="orderCode" min-width="180" />
  55. <el-table-column label="订单总金额 ¥" prop="totalAmount" align="right" width="120" />
  56. <el-table-column label="产品总金额 ¥" prop="productTotalAmount" align="right" width="120" />
  57. <el-table-column label="定制加工费 ¥" prop="customProcessingFee" align="right" width="110" />
  58. <el-table-column label="代发费 ¥" prop="lssueFee" align="right" width="100" />
  59. <el-table-column label="快递包材费 ¥" prop="deliveryMaterialsFee" align="right" width="110" />
  60. <el-table-column label="包装人工费 ¥" prop="packingLabor" align="right" width="110" />
  61. <el-table-column label="包材费 ¥" prop="packagingMaterialCost" align="right" width="100" />
  62. <el-table-column label="管理费 ¥" prop="managementFee" align="right" width="100" />
  63. <el-table-column label="外箱包装费 ¥" prop="outerBoxPackingFee" align="right" width="110" />
  64. <el-table-column label="操作" align="center" fixed="right" width="60">
  65. <template #default="{ $index }">
  66. <el-button type="danger" @click="clickOrderDelete($index)" text>删除</el-button>
  67. </template>
  68. </el-table-column>
  69. </el-table>
  70. </div>
  71. </template>
  72. </byForm>
  73. <template #footer>
  74. <el-button @click="openDialog = false" size="large">取 消</el-button>
  75. <el-button type="primary" @click="submitForm()" size="large" v-preReClick>确 定</el-button>
  76. </template>
  77. </el-dialog>
  78. <el-dialog title="上传对账单" v-if="openReceiptFile" v-model="openReceiptFile" width="600">
  79. <el-upload
  80. v-model:fileList="fileList"
  81. action="https://winfaster.obs.cn-south-1.myhuaweicloud.com"
  82. :data="uploadReceiptData"
  83. multiple
  84. :before-upload="uploadReceiptFile"
  85. :on-success="handleReceiptSuccess"
  86. :on-preview="onPreviewFile">
  87. <el-button type="primary">上传</el-button>
  88. </el-upload>
  89. <template #footer>
  90. <el-button @click="openReceiptFile = false" size="large">取 消</el-button>
  91. <el-button type="primary" @click="submitReceiptFile()" size="large" v-preReClick>确 定</el-button>
  92. </template>
  93. </el-dialog>
  94. <el-dialog title="上传转账凭证" v-if="openProofFile" v-model="openProofFile" width="600">
  95. <el-upload
  96. v-model:fileList="fileList"
  97. action="https://winfaster.obs.cn-south-1.myhuaweicloud.com"
  98. :data="uploadProofData"
  99. multiple
  100. :before-upload="uploadProofFile"
  101. :on-success="handleProofSuccess"
  102. :on-preview="onPreviewFile">
  103. <el-button type="primary">上传</el-button>
  104. </el-upload>
  105. <template #footer>
  106. <el-button @click="openProofFile = false" size="large">取 消</el-button>
  107. <el-button type="primary" @click="submitProofFile()" size="large" v-preReClick>确 定</el-button>
  108. </template>
  109. </el-dialog>
  110. <el-dialog title="选择合同" v-if="openOrder" v-model="openOrder" width="90%">
  111. <SelectOrder :selectStatus="true" :departmentId="formData.data.departmentId" @selectOrder="selectOrder"></SelectOrder>
  112. <template #footer>
  113. <el-button @click="openOrder = false" size="large">关 闭</el-button>
  114. </template>
  115. </el-dialog>
  116. <el-dialog title="打印" v-if="openPrint" v-model="openPrint" width="94%">
  117. <el-tabs v-model="activeName" class="demo-tabs">
  118. <el-tab-pane label="SKU对账单" name="sku">
  119. <PrintSKU :rowData="rowData" @clickCancel="openPrint = false"></PrintSKU>
  120. </el-tab-pane>
  121. <el-tab-pane label="BOM对账单" name="bom">
  122. <PrintBOM :rowData="rowData" @clickCancel="openPrint = false"></PrintBOM>
  123. </el-tab-pane>
  124. <el-tab-pane label="订单对账单" name="order">
  125. <PrintOrder :rowData="rowData" @clickCancel="openPrint = false"></PrintOrder>
  126. </el-tab-pane>
  127. </el-tabs>
  128. </el-dialog>
  129. </div>
  130. </template>
  131. <script setup>
  132. import byTable from "/src/components/byTable/index";
  133. import byForm from "/src/components/byForm/index";
  134. import { ElMessage, ElMessageBox } from "element-plus";
  135. import SelectOrder from "/src/views/group/order/management/index";
  136. import PrintSKU from "/src/views/group/finance/check-bill/printSKU.vue";
  137. import PrintBOM from "/src/views/group/finance/check-bill/printBOM.vue";
  138. import PrintOrder from "/src/views/group/finance/check-bill/printOrder.vue";
  139. import { copyText } from "vue3-clipboard";
  140. const { proxy } = getCurrentInstance();
  141. const departmentList = ref([{ dictKey: "0", dictValue: "胜德体育" }]);
  142. const sourceList = ref({
  143. data: [],
  144. pagination: {
  145. total: 0,
  146. pageNum: 1,
  147. pageSize: 10,
  148. code: "",
  149. departmentId: "",
  150. beginTime: "",
  151. endTime: "",
  152. },
  153. });
  154. const loading = ref(false);
  155. const searchConfig = computed(() => {
  156. return [
  157. {
  158. type: "input",
  159. prop: "code",
  160. label: "对账单号",
  161. },
  162. {
  163. type: "select",
  164. prop: "departmentId",
  165. data: departmentList.value,
  166. label: "事业部",
  167. },
  168. {
  169. type: "date",
  170. propList: ["beginTime", "endTime"],
  171. label: "对账日期",
  172. },
  173. ];
  174. });
  175. const config = computed(() => {
  176. return [
  177. {
  178. attrs: {
  179. label: "对账单号",
  180. prop: "code",
  181. },
  182. },
  183. {
  184. attrs: {
  185. label: "创建时间",
  186. prop: "createTime",
  187. width: 160,
  188. },
  189. },
  190. {
  191. attrs: {
  192. label: "客户",
  193. prop: "departmentName",
  194. },
  195. },
  196. {
  197. attrs: {
  198. label: "对账金额",
  199. prop: "amount",
  200. align: "right",
  201. width: 180,
  202. },
  203. },
  204. {
  205. attrs: {
  206. label: "合同数量",
  207. prop: "orderNum",
  208. width: 100,
  209. },
  210. },
  211. {
  212. attrs: {
  213. label: "对账时间",
  214. prop: "timePeriod",
  215. align: "center",
  216. width: 180,
  217. },
  218. },
  219. {
  220. attrs: {
  221. label: "对账单",
  222. slot: "receiptFileList",
  223. align: "center",
  224. width: 120,
  225. },
  226. },
  227. {
  228. attrs: {
  229. label: "转账凭证",
  230. slot: "proofFileList",
  231. align: "center",
  232. width: 120,
  233. },
  234. },
  235. {
  236. attrs: {
  237. label: "操作",
  238. width: 180,
  239. align: "center",
  240. fixed: "right",
  241. },
  242. renderHTML(row) {
  243. return [
  244. {
  245. attrs: {
  246. label: "复制单号",
  247. type: "primary",
  248. text: true,
  249. },
  250. el: "button",
  251. click() {
  252. clickCopyWLNCode(row);
  253. },
  254. },
  255. {
  256. attrs: {
  257. label: "打印",
  258. type: "primary",
  259. text: true,
  260. },
  261. el: "button",
  262. click() {
  263. clickPrint(row);
  264. },
  265. },
  266. {
  267. attrs: {
  268. label: "编辑",
  269. type: "primary",
  270. text: true,
  271. },
  272. el: "button",
  273. click() {
  274. clickUpdate(row);
  275. },
  276. },
  277. {
  278. attrs: {
  279. label: "删除",
  280. type: "danger",
  281. text: true,
  282. },
  283. el: "button",
  284. click() {
  285. clickDelete(row);
  286. },
  287. },
  288. ];
  289. },
  290. },
  291. ];
  292. });
  293. const getDemandData = () => {
  294. proxy.post("/department/page", { pageNum: 1, pageSize: 999 }).then((res) => {
  295. if (res.rows && res.rows.length > 0) {
  296. departmentList.value = departmentList.value.concat(
  297. res.rows.map((item) => {
  298. return {
  299. dictKey: item.id,
  300. dictValue: item.name,
  301. };
  302. })
  303. );
  304. }
  305. });
  306. };
  307. getDemandData();
  308. const getList = async (req, status) => {
  309. if (status) {
  310. sourceList.value.pagination = {
  311. pageNum: sourceList.value.pagination.pageNum,
  312. pageSize: sourceList.value.pagination.pageSize,
  313. };
  314. } else {
  315. sourceList.value.pagination = { ...sourceList.value.pagination, ...req };
  316. }
  317. loading.value = true;
  318. proxy.post("/statementOfAccount/page", sourceList.value.pagination).then((res) => {
  319. sourceList.value.data = res.rows;
  320. sourceList.value.pagination.total = res.total;
  321. setTimeout(() => {
  322. loading.value = false;
  323. }, 200);
  324. });
  325. };
  326. getList();
  327. const clickReset = () => {
  328. getList("", true);
  329. };
  330. const modalType = ref("add");
  331. const openDialog = ref(false);
  332. const formOption = reactive({
  333. inline: true,
  334. labelWidth: "120px",
  335. itemWidth: 100,
  336. rules: [],
  337. labelPosition: "right",
  338. });
  339. const formData = reactive({
  340. data: {},
  341. });
  342. const formConfig = computed(() => {
  343. return [
  344. {
  345. type: "title",
  346. title: "对账合同",
  347. label: "",
  348. },
  349. {
  350. type: "slot",
  351. slotName: "orderIdList",
  352. label: "",
  353. },
  354. ];
  355. });
  356. const clickModal = () => {
  357. modalType.value = "add";
  358. formData.data = {
  359. departmentId: "",
  360. amount: "",
  361. orderIdList: [],
  362. orderList: [],
  363. };
  364. openDialog.value = true;
  365. };
  366. const changeDepartment = () => {
  367. formData.data.orderList = [];
  368. };
  369. const openOrder = ref(false);
  370. const clickAddOrder = () => {
  371. if (formData.data.departmentId) {
  372. openOrder.value = true;
  373. } else {
  374. return ElMessage("请先选择事业部");
  375. }
  376. };
  377. const selectOrder = (row) => {
  378. if (formData.data.orderList && formData.data.orderList.length > 0) {
  379. let list = formData.data.orderList.filter((item) => item.orderId === row.id);
  380. if (list && list.length > 0) {
  381. return ElMessage("该订单已添加");
  382. }
  383. } else {
  384. formData.data.orderList = [];
  385. }
  386. formData.data.orderList.push({
  387. orderId: row.id,
  388. departmentName: row.departmentName,
  389. orderCode: row.code,
  390. totalAmount: row.totalAmount,
  391. productTotalAmount: row.productTotalAmount,
  392. customProcessingFee: row.customProcessingFee,
  393. lssueFee: row.lssueFee,
  394. deliveryMaterialsFee: row.deliveryMaterialsFee,
  395. packingLabor: row.packingLabor,
  396. managementFee: row.managementFee,
  397. outerBoxPackingFee: row.outerBoxPackingFee,
  398. packagingMaterialCost: row.packagingMaterialCost,
  399. });
  400. ElMessage({ message: "添加成功", type: "success" });
  401. };
  402. const clickOrderDelete = (index) => {
  403. formData.data.orderList.splice(index, 1);
  404. };
  405. const submitForm = () => {
  406. if (formData.data.orderList && formData.data.orderList.length > 0) {
  407. let amount = 0;
  408. for (let i = 0; i < formData.data.orderList.length; i++) {
  409. amount = Number(Math.round((amount + formData.data.orderList[i].totalAmount) * 100) / 100);
  410. }
  411. let orderIdList = formData.data.orderList.map((item) => item.orderId);
  412. proxy
  413. .post("/statementOfAccount/" + modalType.value, {
  414. id: formData.data.id,
  415. departmentId: formData.data.departmentId,
  416. amount: amount,
  417. orderIdList: orderIdList,
  418. })
  419. .then(() => {
  420. ElMessage({
  421. message: modalType.value == "add" ? "添加成功" : "编辑成功",
  422. type: "success",
  423. });
  424. openDialog.value = false;
  425. getList();
  426. });
  427. } else {
  428. return ElMessage("请先添加订单");
  429. }
  430. };
  431. const clickUpdate = (row) => {
  432. formData.data = {
  433. id: row.id,
  434. };
  435. modalType.value = "edit";
  436. proxy.post("/statementOfAccount/detail", { id: row.id }).then((res) => {
  437. if (res && res.length > 0) {
  438. formData.data.departmentId = res[0].departmentId;
  439. formData.data.orderList = res;
  440. }
  441. openDialog.value = true;
  442. });
  443. };
  444. const clickDelete = (row) => {
  445. ElMessageBox.confirm("你是否确认此操作", "提示", {
  446. confirmButtonText: "确定",
  447. cancelButtonText: "取消",
  448. type: "warning",
  449. })
  450. .then(() => {
  451. proxy.post("/statementOfAccount/delete", { id: row.id }).then(() => {
  452. ElMessage({ message: "删除成功", type: "success" });
  453. getList();
  454. });
  455. })
  456. .catch(() => {});
  457. };
  458. const openReceiptFile = ref(false);
  459. const formReceiptData = reactive({
  460. data: {
  461. id: "",
  462. fileList: [],
  463. },
  464. });
  465. const fileList = ref([]);
  466. const clickReceiptFile = (row) => {
  467. formReceiptData.data = {
  468. id: row.id,
  469. fileList: [],
  470. };
  471. if (row.receiptFileList && row.receiptFileList.length > 0) {
  472. fileList.value = proxy.deepClone(
  473. row.receiptFileList.map((item) => {
  474. return {
  475. raw: item,
  476. name: item.fileName,
  477. url: item.fileUrl,
  478. };
  479. })
  480. );
  481. } else {
  482. fileList.value = [];
  483. }
  484. openReceiptFile.value = true;
  485. };
  486. const uploadReceiptData = ref({});
  487. const uploadReceiptFile = async (file) => {
  488. const res = await proxy.post("/fileInfo/getSing", { fileName: file.name });
  489. uploadReceiptData.value = res.uploadBody;
  490. file.id = res.id;
  491. file.fileName = res.fileName;
  492. file.fileUrl = res.fileUrl;
  493. file.uploadState = true;
  494. return true;
  495. };
  496. const handleReceiptSuccess = (any, UploadFile) => {
  497. UploadFile.raw.uploadState = false;
  498. };
  499. const onPreviewFile = (file) => {
  500. window.open(file.raw.fileUrl, "_blank");
  501. };
  502. const submitReceiptFile = () => {
  503. if (fileList.value && fileList.value.length > 0) {
  504. for (let i = 0; i < fileList.value.length; i++) {
  505. if (fileList.value[i].raw.uploadState) {
  506. return ElMessage("文件上传中,请稍后提交");
  507. }
  508. }
  509. formReceiptData.data.fileList = fileList.value.map((item) => {
  510. return {
  511. id: item.raw.id,
  512. fileName: item.raw.fileName,
  513. fileUrl: item.raw.fileUrl,
  514. };
  515. });
  516. } else {
  517. formReceiptData.data.fileList = [];
  518. }
  519. proxy.post("/statementOfAccount/editReceiptFile", formReceiptData.data).then(() => {
  520. openReceiptFile.value = false;
  521. getList();
  522. });
  523. };
  524. const openProofFile = ref(false);
  525. const formProofData = reactive({
  526. data: {
  527. id: "",
  528. fileList: [],
  529. },
  530. });
  531. const clickProofFile = (row) => {
  532. formProofData.data = {
  533. id: row.id,
  534. fileList: [],
  535. };
  536. if (row.proofFileList && row.proofFileList.length > 0) {
  537. fileList.value = proxy.deepClone(
  538. row.proofFileList.map((item) => {
  539. return {
  540. raw: item,
  541. name: item.fileName,
  542. url: item.fileUrl,
  543. };
  544. })
  545. );
  546. } else {
  547. fileList.value = [];
  548. }
  549. openProofFile.value = true;
  550. };
  551. const uploadProofData = ref({});
  552. const uploadProofFile = async (file) => {
  553. const res = await proxy.post("/fileInfo/getSing", { fileName: file.name });
  554. uploadProofData.value = res.uploadBody;
  555. file.id = res.id;
  556. file.fileName = res.fileName;
  557. file.fileUrl = res.fileUrl;
  558. file.uploadState = true;
  559. return true;
  560. };
  561. const handleProofSuccess = (any, UploadFile) => {
  562. UploadFile.raw.uploadState = false;
  563. };
  564. const submitProofFile = () => {
  565. if (fileList.value && fileList.value.length > 0) {
  566. for (let i = 0; i < fileList.value.length; i++) {
  567. if (fileList.value[i].raw.uploadState) {
  568. return ElMessage("文件上传中,请稍后提交");
  569. }
  570. }
  571. formProofData.data.fileList = fileList.value.map((item) => {
  572. return {
  573. id: item.raw.id,
  574. fileName: item.raw.fileName,
  575. fileUrl: item.raw.fileUrl,
  576. };
  577. });
  578. } else {
  579. formProofData.data.fileList = [];
  580. }
  581. proxy.post("/statementOfAccount/editProofFile", formProofData.data).then(() => {
  582. openProofFile.value = false;
  583. getList();
  584. });
  585. };
  586. const openPrint = ref(false);
  587. const rowData = ref({});
  588. const activeName = ref("sku");
  589. const clickPrint = (row) => {
  590. activeName.value = "sku";
  591. rowData.value = row;
  592. openPrint.value = true;
  593. };
  594. const clickCopyWLNCode = (row) => {
  595. ElMessage("复制数据中,请稍后");
  596. proxy.post("/statementOfAccount/getOrderWlnCodeStr", [row.id]).then((res) => {
  597. copyText(res, undefined, (error) => {
  598. if (error) {
  599. ElMessage.error(`复制失败: ${error} !`);
  600. } else {
  601. ElMessage.success(`复制成功!`);
  602. }
  603. });
  604. });
  605. };
  606. </script>
  607. <style lang="scss" scoped>
  608. :deep(.el-dialog) {
  609. margin-top: 10px !important;
  610. margin-bottom: 10px !important;
  611. }
  612. </style>