From 11d7990309ddace2fa600cbeae58e3ab5ac368f9 Mon Sep 17 00:00:00 2001
From: gaoluyang <2820782392@qq.com>
Date: 星期日, 20 九月 2026 19:25:45 +0800
Subject: [PATCH] 新疆-融邦 1. 委外管理:新增合作商档案、委外订单、往来台账三个页面及接口;合作商附件弹框支持直接上传/删除并即时保存。 2. 信托基金与代储台账:客户档案加「信托基金 / 代储金额」并对超限标红提示;销售台账拆「卖油 / 代储」页签,代储走油库+储罐+合同金额,卖油保留产品明细;台账列表加审批状态与超限红字;审批中心显示销售合同号并能下钻台账明细。 3. 储罐号带入库存:储罐信息表单加「是否启用 / 用途 / 租客信息」联动与校验;库存管理、批号明细、入库/出库表单与列表补齐储罐号、油品、物流、过磅等字段展示。 4. 出库即发货与往来备件:出库台账区分「发货单 / 油品出库」两类来源并分别处理详情;新增备件出入库流水页面。 5. 车辆管理与残油联动:新增车辆管理页面;油品出入库表单物流信息顺序调整,车牌/车挂牌号联动只读。 6. 油品出库联动发货台账:发货台账加「发货单号 / 销售合同号」筛选,修复油品出库行批号列取错字段。 7. 采购申请模块:新增采购申请页面及接口。 8. 付款流水组件:抽出公共「付款流水」弹框组件,采购付款分栏、收付款台账与委外往来台账复用。 9. 列调整:收付款台账删除「付款金额 / 退货金额」;委外往来台账删除「回款金额」,并去掉金额列右对齐。
---
src/views/procurementManagement/purchaseApplication/index.vue | 1087 +++++++++
src/views/outsourcingManagement/order/index.vue | 952 ++++++++
src/views/inventoryManagement/stockManagement/OilStockInDialog.vue | 56
src/views/salesManagement/receiptPaymentLedger/index.vue | 57
src/api/outsourcing/order.js | 92
src/views/basicData/vehicle/index.vue | 371 +++
src/views/outsourcingManagement/partner/index.vue | 668 ++++++
src/views/salesManagement/salesLedger/index.vue | 321 ++
src/views/officeProcessAutomation/ApproveManage/approve-list/components/ApproveDetailPanel.vue | 51
src/views/basicData/tankInfo/index.vue | 140 +
src/api/inventoryManagement/stockManage.js | 9
src/views/inventoryManagement/dispatchLog/Record.vue | 361 ++
src/views/basicData/customerFile/index.vue | 81
src/api/outsourcing/report.js | 20
src/views/inventoryManagement/stockManagement/index.vue | 2
src/api/equipmentManagement/sparePartsRecord.js | 27
src/views/inventoryManagement/stockManagement/Record.vue | 201 +
src/views/inventoryManagement/receiptManagement/Record.vue | 273 ++
src/components/PaymentRecordDialog/index.vue | 330 ++
src/views/salesManagement/deliveryLedger/index.vue | 233 -
src/views/equipmentManagement/spareParts/index.vue | 87
src/views/officeProcessAutomation/ApproveManage/approve-list/index.vue | 4
src/views/inventoryManagement/issueManagement/index.vue | 48
src/views/inventoryManagement/stockManagement/OilStockOutDialog.vue | 286 +
src/views/equipmentManagement/sparePartsRecord/index.vue | 215 +
src/views/procurementManagement/paymentLedger/index.vue | 74
src/api/basicData/vehicle.js | 53
src/views/officeProcessAutomation/ApproveManage/approve-list/useApproveList.js | 46
src/views/outsourcingManagement/transactions/index.vue | 255 ++
src/api/inventoryManagement/stockOut.js | 18
src/views/inventoryManagement/stockManagement/BatchNoQtyDetail.vue | 16
src/views/officeProcessAutomation/ApproveManage/approve-list/approveListConstants.js | 1
src/api/procurementManagement/purchaseApplication.js | 56
src/views/procurementManagement/transferManagement/index.vue | 8
src/api/financialManagement/paymentRecord.js | 38
src/api/outsourcing/partner.js | 72
36 files changed, 6,122 insertions(+), 487 deletions(-)
diff --git a/src/api/basicData/vehicle.js b/src/api/basicData/vehicle.js
new file mode 100644
index 0000000..c0851cd
--- /dev/null
+++ b/src/api/basicData/vehicle.js
@@ -0,0 +1,53 @@
+import request from "@/utils/request";
+
+// 鍒嗛〉鏌ヨ杞﹁締淇℃伅鍒楄〃
+export function listVehicle(query) {
+ return request({
+ url: "/basic/vehicle/list",
+ method: "get",
+ params: query,
+ });
+}
+
+// 鏌ヨ鍏ㄩ儴鍚敤杞﹁締锛堜緵涓嬫媺閫夋嫨锛�
+export function allVehicle() {
+ return request({
+ url: "/basic/vehicle/all",
+ method: "get",
+ });
+}
+
+// 鏌ヨ杞﹁締璇︽儏
+export function getVehicle(id) {
+ return request({
+ url: "/basic/vehicle/" + id,
+ method: "get",
+ });
+}
+
+// 鏂板杞﹁締
+export function addVehicle(data) {
+ return request({
+ url: "/basic/vehicle/add",
+ method: "post",
+ data: data,
+ });
+}
+
+// 淇敼杞﹁締
+export function updateVehicle(data) {
+ return request({
+ url: "/basic/vehicle/update",
+ method: "post",
+ data: data,
+ });
+}
+
+// 鍒犻櫎杞﹁締
+export function delVehicle(ids) {
+ return request({
+ url: "/basic/vehicle/del",
+ method: "delete",
+ data: ids,
+ });
+}
diff --git a/src/api/equipmentManagement/sparePartsRecord.js b/src/api/equipmentManagement/sparePartsRecord.js
new file mode 100644
index 0000000..cdb1411
--- /dev/null
+++ b/src/api/equipmentManagement/sparePartsRecord.js
@@ -0,0 +1,27 @@
+import request from "@/utils/request";
+
+/**
+ * 澶囦欢鍑哄叆搴撴祦姘� - 鍒嗛〉鏌ヨ
+ * params: { current, size, sparePartsId?, direction?, sourceType? }
+ * direction: 1=鍏ュ簱 2=鍑哄簱
+ * sourceType: 1=閲囪喘鍏ュ簱 2=缁翠慨棰嗙敤 3=淇濆吇棰嗙敤
+ */
+export const getSparePartsRecordPage = (params) => {
+ return request({
+ url: "/sparePartsRecord/listPage",
+ method: "get",
+ params,
+ });
+};
+
+/**
+ * 澶囦欢鍑哄叆搴撴祦姘� - 鎵归噺鍒犻櫎锛坆ody 涓� id 鏁扮粍锛�
+ */
+export const delSparePartsRecord = (ids) => {
+ const idList = Array.isArray(ids) ? ids : [ids];
+ return request({
+ url: "/sparePartsRecord/delete",
+ method: "delete",
+ data: idList,
+ });
+};
diff --git a/src/api/financialManagement/paymentRecord.js b/src/api/financialManagement/paymentRecord.js
new file mode 100644
index 0000000..7145918
--- /dev/null
+++ b/src/api/financialManagement/paymentRecord.js
@@ -0,0 +1,38 @@
+import request from "@/utils/request";
+
+/** 浠樻鍥炴娴佹按鍒嗛〉鍒楄〃 */
+export function paymentRecordPage(params) {
+ return request({
+ url: "/paymentRecord/listPage",
+ method: "get",
+ params,
+ });
+}
+
+/** 鏂板浠樻鍥炴娴佹按 */
+export function addPaymentRecord(data) {
+ return request({
+ url: "/paymentRecord/add",
+ method: "post",
+ data,
+ });
+}
+
+/** 淇敼浠樻鍥炴娴佹按 */
+export function updatePaymentRecord(data) {
+ return request({
+ url: "/paymentRecord/update",
+ method: "post",
+ data,
+ });
+}
+
+/** 鍒犻櫎浠樻鍥炴娴佹按锛坆ody 涓� id 鏁扮粍锛� */
+export function delPaymentRecord(ids) {
+ const idList = Array.isArray(ids) ? ids : [ids];
+ return request({
+ url: "/paymentRecord/delete",
+ method: "delete",
+ data: idList,
+ });
+}
diff --git a/src/api/inventoryManagement/stockManage.js b/src/api/inventoryManagement/stockManage.js
index e2a4ebf..1140987 100644
--- a/src/api/inventoryManagement/stockManage.js
+++ b/src/api/inventoryManagement/stockManage.js
@@ -80,4 +80,13 @@
})
}
+//鍑哄簱绠$悊-鏌ヨ瑙勬牸鍨嬪彿涓嬬殑鍙敤鎵规锛堝彲鐢ㄩ噺 = 搴撳瓨閲� - 閿佸畾鏁伴噺锛�
+export function getBatchNoList(productModelId) {
+ return request({
+ url: '/stockmanagement/batchNoList',
+ method: 'get',
+ params: { productModelId }
+ })
+}
+
//
\ No newline at end of file
diff --git a/src/api/inventoryManagement/stockOut.js b/src/api/inventoryManagement/stockOut.js
index b54e004..b70b7bc 100644
--- a/src/api/inventoryManagement/stockOut.js
+++ b/src/api/inventoryManagement/stockOut.js
@@ -44,3 +44,21 @@
data,
});
}
+
+// 娌瑰搧鍑哄簱鍙敤閲忔煡璇紙涓婃鍓╀綑娌归噺 + 褰撳墠搴撳瓨锛�
+export const getOilOutAvailableQty = (params) => {
+ return request({
+ url: "/stockOutRecord/oilOutAvailableQty",
+ method: "get",
+ params,
+ });
+}
+
+// 褰曞叆閫佸畬璐у悗鐨勫墿浣欐补閲忥紙鍙洿鏂� residualOil 涓�鍒楋級
+export const updateResidualOil = (id, data) => {
+ return request({
+ url: `/stockOutRecord/${id}/residualOil`,
+ method: "put",
+ data,
+ });
+}
diff --git a/src/api/outsourcing/order.js b/src/api/outsourcing/order.js
new file mode 100644
index 0000000..93e3b20
--- /dev/null
+++ b/src/api/outsourcing/order.js
@@ -0,0 +1,92 @@
+import request from "@/utils/request";
+
+// 濮斿璁㈠崟鍒嗛〉鏌ヨ锛圧uoYi 缁撴瀯锛氶《灞� rows锛�
+export function listOutsourcingOrder(query) {
+ return request({
+ url: "/outsourcing/order/list",
+ method: "get",
+ params: query,
+ });
+}
+
+// 濮斿璁㈠崟璇︽儏锛堝惈 productData 鏄庣粏锛�
+export function getOutsourcingOrder(id) {
+ return request({
+ url: "/outsourcing/order/getById",
+ method: "get",
+ params: { id },
+ });
+}
+
+// 鏂板濮斿璁㈠崟锛堝己鍒惰崏绋匡級
+export function addOutsourcingOrder(data) {
+ return request({
+ url: "/outsourcing/order/add",
+ method: "post",
+ data: data,
+ });
+}
+
+// 淇敼濮斿璁㈠崟锛堜粎鑽夌锛屾槑缁嗗叏閲忔彁浜わ級
+export function editOutsourcingOrder(data) {
+ return request({
+ url: "/outsourcing/order/edit",
+ method: "post",
+ data: data,
+ });
+}
+
+// 鍒犻櫎濮斿璁㈠崟锛堜粎鑽夌锛宐ody 涓� id 鏁扮粍锛�
+export function delOutsourcingOrder(ids) {
+ return request({
+ url: "/outsourcing/order/delete",
+ method: "delete",
+ data: ids,
+ });
+}
+
+// 鎻愪氦锛堣崏绋� -> 宸茬‘璁わ級锛宨d 涓� query 鍙傛暟
+export function submitOutsourcingOrder(id) {
+ return request({
+ url: "/outsourcing/order/submit",
+ method: "post",
+ params: { id },
+ });
+}
+
+// 瀹屾垚锛堝凡纭 -> 宸插畬鎴愶級
+export function completeOutsourcingOrder(id) {
+ return request({
+ url: "/outsourcing/order/complete",
+ method: "post",
+ params: { id },
+ });
+}
+
+// 浣滃簾锛堣崏绋�/宸茬‘璁� -> 宸蹭綔搴燂級
+export function cancelOutsourcingOrder(id, cancelReason) {
+ return request({
+ url: "/outsourcing/order/cancel",
+ method: "post",
+ params: { id, cancelReason },
+ });
+}
+
+// 瀵煎嚭濮斿璁㈠崟锛堣繑鍥� Excel 鏂囦欢娴侊級
+export function exportOutsourcingOrder(query) {
+ return request({
+ url: "/outsourcing/order/export",
+ method: "post",
+ data: query,
+ responseType: "blob",
+ });
+}
+
+// 鎶ュ簾鍝侀�夋枡鍒嗛〉锛堝彧璇伙紝杩斿洖 IPage锛歞ata.records锛�
+export function scrapStockPage(query) {
+ return request({
+ url: "/outsourcing/order/scrapStockPage",
+ method: "get",
+ params: query,
+ });
+}
diff --git a/src/api/outsourcing/partner.js b/src/api/outsourcing/partner.js
new file mode 100644
index 0000000..07feabd
--- /dev/null
+++ b/src/api/outsourcing/partner.js
@@ -0,0 +1,72 @@
+import request from "@/utils/request";
+
+// 鍚堜綔鍟嗘。妗堝垎椤垫煡璇紙杩斿洖 IPage锛歞ata.records锛�
+export function listPartner(query) {
+ return request({
+ url: "/outsourcing/partner/listPage",
+ method: "get",
+ params: query,
+ });
+}
+
+// 鍚堜綔鍟嗘。妗堣鎯咃紙鍚� storageBlobVOS 闄勪欢鍥炴樉锛�
+export function getPartner(id) {
+ return request({
+ url: "/outsourcing/partner/" + id,
+ method: "get",
+ });
+}
+
+// 鏂板鍚堜綔鍟�
+export function addPartner(data) {
+ return request({
+ url: "/outsourcing/partner/add",
+ method: "post",
+ data: data,
+ });
+}
+
+// 淇敼鍚堜綔鍟嗭紙storageBlobDTOS 涓哄叏閲忚鐩栵級
+export function updatePartner(data) {
+ return request({
+ url: "/outsourcing/partner/update",
+ method: "post",
+ data: data,
+ });
+}
+
+// 鍒犻櫎鍚堜綔鍟嗭紙body 涓� id 鏁扮粍锛�
+export function delPartner(ids) {
+ return request({
+ url: "/outsourcing/partner/del",
+ method: "delete",
+ data: ids,
+ });
+}
+
+// 瀵煎嚭鍚堜綔鍟嗭紙杩斿洖 Excel 鏂囦欢娴侊級
+export function exportPartner(query) {
+ return request({
+ url: "/outsourcing/partner/export",
+ method: "post",
+ data: query,
+ responseType: "blob",
+ });
+}
+
+// 涓嬭浇瀵煎叆妯℃澘锛堣繑鍥� Excel 鏂囦欢娴侊級
+export function downloadPartnerTemplate() {
+ return request({
+ url: "/outsourcing/partner/downloadTemplate",
+ method: "post",
+ responseType: "blob",
+ });
+}
+
+// 鍚堜綔鍟嗕笅鎷夐�夐」锛堜粎 id + partnerName锛�
+export function getPartnerOptions() {
+ return request({
+ url: "/outsourcing/partner/getOptions",
+ method: "get",
+ });
+}
diff --git a/src/api/outsourcing/report.js b/src/api/outsourcing/report.js
new file mode 100644
index 0000000..70aa71b
--- /dev/null
+++ b/src/api/outsourcing/report.js
@@ -0,0 +1,20 @@
+import request from "@/utils/request";
+
+// 鍚堜綔鍟嗗線鏉ユ眹鎬伙紙杩斿洖 IPage锛歞ata.records锛�
+export function partnerTransactions(query) {
+ return request({
+ url: "/outsourcing/report/partnerTransactions",
+ method: "get",
+ params: query,
+ });
+}
+
+// 鍚堜綔鍟嗗線鏉ユ槑缁嗭紙閫愯鍗曪紝杩斿洖 IPage锛歞ata.records锛�
+// 娉ㄦ剰锛氭瘡琛� paidAmount 鏄悎浣滃晢绾у悎璁★紝涓嶆槸璁㈠崟绾�
+export function partnerTransactionsDetails(partnerId) {
+ return request({
+ url: "/outsourcing/report/partnerTransactionsDetails",
+ method: "get",
+ params: { partnerId },
+ });
+}
diff --git a/src/api/procurementManagement/purchaseApplication.js b/src/api/procurementManagement/purchaseApplication.js
new file mode 100644
index 0000000..b43df27
--- /dev/null
+++ b/src/api/procurementManagement/purchaseApplication.js
@@ -0,0 +1,56 @@
+// 閲囪喘鐢宠椤甸潰鎺ュ彛
+import request from "@/utils/request";
+
+// 鍒嗛〉鏌ヨ閲囪喘鐢宠鍒楄〃
+export function applicationList(query) {
+ return request({
+ url: "/purchase/application/list",
+ method: "get",
+ params: query,
+ });
+}
+
+// 鏌ヨ閲囪喘鐢宠璇︽儏锛堝惈浜у搧鏄庣粏锛�
+export function getApplicationById(query) {
+ return request({
+ url: "/purchase/application/getById",
+ method: "get",
+ params: query,
+ });
+}
+
+// 鏂板閲囪喘鐢宠锛堣崏绋匡級
+export function addApplication(data) {
+ return request({
+ url: "/purchase/application/add",
+ method: "post",
+ data: data,
+ });
+}
+
+// 淇敼閲囪喘鐢宠
+export function editApplication(data) {
+ return request({
+ url: "/purchase/application/edit",
+ method: "post",
+ data: data,
+ });
+}
+
+// 鍒犻櫎閲囪喘鐢宠锛堟敮鎸佹壒閲忥級
+export function delApplication(ids) {
+ return request({
+ url: "/purchase/application/delete",
+ method: "delete",
+ data: ids,
+ });
+}
+
+// 閲囪喘鐢宠杞鍗曪紝杩斿洖鐢熸垚鐨勯噰璐彴璐d
+export function convertToOrder(id) {
+ return request({
+ url: "/purchase/application/convertToOrder",
+ method: "post",
+ params: { id },
+ });
+}
diff --git a/src/components/PaymentRecordDialog/index.vue b/src/components/PaymentRecordDialog/index.vue
new file mode 100644
index 0000000..3804394
--- /dev/null
+++ b/src/components/PaymentRecordDialog/index.vue
@@ -0,0 +1,330 @@
+<template>
+ <el-dialog :model-value="visible"
+ :title="title"
+ width="60%"
+ :close-on-click-modal="false"
+ @update:model-value="handleVisibleChange"
+ @close="closeDialog">
+ <div class="payment-record-dialog">
+ <div class="dialog-header">
+ <span class="party-name">{{ partyName }}</span>
+ <el-button type="primary"
+ @click="openAddForm">鏂板</el-button>
+ </div>
+ <el-table :data="tableData"
+ border
+ v-loading="tableLoading"
+ height="380px">
+ <el-table-column align="center"
+ label="搴忓彿"
+ type="index"
+ width="60" />
+ <el-table-column label="鍙戠敓鏃ユ湡"
+ prop="recordDate"
+ width="130"
+ show-overflow-tooltip />
+ <el-table-column label="閲戦(鍏�)"
+ prop="amount"
+ width="140"
+ show-overflow-tooltip>
+ <template #default="{ row }">
+ {{ formatAmount(row.amount) }}
+ </template>
+ </el-table-column>
+ <el-table-column label="鏂瑰紡"
+ prop="paymentMethod"
+ width="140"
+ show-overflow-tooltip />
+ <el-table-column label="澶囨敞"
+ prop="remark"
+ show-overflow-tooltip />
+ <el-table-column label="鎿嶄綔"
+ align="center"
+ width="140"
+ fixed="right">
+ <template #default="{ row }">
+ <el-button link
+ type="primary"
+ @click="openEditForm(row)">缂栬緫</el-button>
+ <el-button link
+ type="danger"
+ @click="handleDelete(row)">鍒犻櫎</el-button>
+ </template>
+ </el-table-column>
+ </el-table>
+ <pagination v-show="total > 0"
+ :total="total"
+ layout="total, sizes, prev, pager, next, jumper"
+ :page="page.current"
+ :limit="page.size"
+ @pagination="paginationChange" />
+ </div>
+
+ <el-dialog v-model="formVisible"
+ :title="form.id ? '缂栬緫娴佹按' : '鏂板娴佹按'"
+ width="500px"
+ append-to-body
+ :close-on-click-modal="false"
+ @close="resetForm">
+ <el-form :model="form"
+ label-width="100px"
+ :rules="rules"
+ ref="formRef">
+ <el-form-item label="鍙戠敓鏃ユ湡"
+ prop="recordDate">
+ <el-date-picker v-model="form.recordDate"
+ type="date"
+ value-format="YYYY-MM-DD"
+ format="YYYY-MM-DD"
+ placeholder="璇烽�夋嫨"
+ style="width: 100%" />
+ </el-form-item>
+ <el-form-item label="閲戦(鍏�)"
+ prop="amount">
+ <el-input-number v-model="form.amount"
+ :min="0"
+ :precision="2"
+ :step="0.01"
+ placeholder="璇疯緭鍏�"
+ style="width: 100%" />
+ </el-form-item>
+ <el-form-item label="鏂瑰紡"
+ prop="paymentMethod">
+ <el-input v-model="form.paymentMethod"
+ placeholder="璇疯緭鍏�"
+ clearable />
+ </el-form-item>
+ <el-form-item label="澶囨敞"
+ prop="remark">
+ <el-input v-model="form.remark"
+ type="textarea"
+ :rows="3"
+ placeholder="璇疯緭鍏�"
+ clearable />
+ </el-form-item>
+ </el-form>
+ <template #footer>
+ <div class="dialog-footer">
+ <el-button type="primary"
+ @click="submitForm">纭</el-button>
+ <el-button @click="formVisible = false">鍙栨秷</el-button>
+ </div>
+ </template>
+ </el-dialog>
+ </el-dialog>
+</template>
+
+<script setup>
+ import { ref, reactive, watch, getCurrentInstance } from "vue";
+ import { ElMessageBox } from "element-plus";
+ import pagination from "@/components/PIMTable/Pagination.vue";
+ import {
+ paymentRecordPage,
+ addPaymentRecord,
+ updatePaymentRecord,
+ delPaymentRecord,
+ } from "@/api/financialManagement/paymentRecord.js";
+
+ const props = defineProps({
+ visible: {
+ type: Boolean,
+ default: false,
+ },
+ title: {
+ type: String,
+ default: "鏀朵粯娆炬祦姘�",
+ },
+ // 1=瀹㈡埛锛�2=渚涘簲鍟�
+ partyType: {
+ type: [String, Number],
+ required: true,
+ },
+ partyId: {
+ type: [String, Number],
+ default: "",
+ },
+ partyName: {
+ type: String,
+ default: "",
+ },
+ // 1=浠樻锛�2=鍥炴
+ direction: {
+ type: [String, Number],
+ required: true,
+ },
+ });
+
+ const emit = defineEmits(["update:visible", "changed"]);
+
+ const { proxy } = getCurrentInstance();
+
+ const tableData = ref([]);
+ const tableLoading = ref(false);
+ const total = ref(0);
+ const page = reactive({
+ current: 1,
+ size: 10,
+ });
+
+ const formVisible = ref(false);
+ const formRef = ref(null);
+ const form = ref(buildEmptyForm());
+ const rules = {
+ recordDate: [{ required: true, message: "璇烽�夋嫨鍙戠敓鏃ユ湡", trigger: "change" }],
+ amount: [{ required: true, message: "璇疯緭鍏ラ噾棰�", trigger: "blur" }],
+ };
+
+ function buildEmptyForm() {
+ return {
+ id: "",
+ recordDate: "",
+ amount: undefined,
+ paymentMethod: "",
+ remark: "",
+ };
+ }
+
+ const formatAmount = amount => {
+ return amount ? parseFloat(amount).toFixed(2) : "0.00";
+ };
+
+ const handleVisibleChange = val => {
+ emit("update:visible", val);
+ };
+
+ const closeDialog = () => {
+ page.current = 1;
+ tableData.value = [];
+ total.value = 0;
+ formVisible.value = false;
+ };
+
+ const getList = () => {
+ if (!props.partyId) {
+ tableData.value = [];
+ total.value = 0;
+ return;
+ }
+ tableLoading.value = true;
+ paymentRecordPage({
+ pageNum: page.current,
+ pageSize: page.size,
+ partyType: props.partyType,
+ partyId: props.partyId,
+ direction: props.direction,
+ })
+ .then(res => {
+ tableLoading.value = false;
+ const result = res.data || {};
+ tableData.value = result.records || [];
+ total.value = result.total || 0;
+ })
+ .catch(() => {
+ tableLoading.value = false;
+ });
+ };
+
+ const paginationChange = obj => {
+ page.current = obj.page;
+ page.size = obj.limit;
+ getList();
+ };
+
+ const openAddForm = () => {
+ form.value = buildEmptyForm();
+ formVisible.value = true;
+ };
+
+ const openEditForm = row => {
+ form.value = {
+ id: row.id,
+ recordDate: row.recordDate || "",
+ amount: row.amount === null || row.amount === undefined ? undefined : Number(row.amount),
+ paymentMethod: row.paymentMethod || "",
+ remark: row.remark || "",
+ };
+ formVisible.value = true;
+ };
+
+ const resetForm = () => {
+ formRef.value?.resetFields();
+ form.value = buildEmptyForm();
+ };
+
+ const submitForm = () => {
+ formRef.value?.validate(valid => {
+ if (!valid) {
+ return;
+ }
+ const payload = {
+ partyType: props.partyType,
+ partyId: props.partyId,
+ direction: props.direction,
+ amount: form.value.amount,
+ recordDate: form.value.recordDate || null,
+ paymentMethod: form.value.paymentMethod,
+ remark: form.value.remark,
+ };
+ const isEdit = !!form.value.id;
+ const submitRequest = isEdit
+ ? updatePaymentRecord({ ...payload, id: form.value.id })
+ : addPaymentRecord(payload);
+ submitRequest.then(() => {
+ proxy.$modal.msgSuccess(isEdit ? "淇敼鎴愬姛" : "鏂板鎴愬姛");
+ formVisible.value = false;
+ getList();
+ emit("changed");
+ });
+ });
+ };
+
+ const handleDelete = row => {
+ // 璇ユ帴鍙e悗绔棤褰掑睘鏍¢獙锛屼换浣曠櫥褰曠敤鎴峰彲鍒犱换鎰忔祦姘达紝鏁呯‘璁ゆ甯︿笂鏃ユ湡閲戦瀹氫綅
+ ElMessageBox.confirm(
+ `纭畾鍒犻櫎銆�${row.recordDate || "鏈~鏃ユ湡"} / ${formatAmount(row.amount)} 鍏冦�嶈繖鏉℃祦姘村悧锛熷垹闄ゅ悗涓嶅彲鎭㈠銆俙,
+ "鍒犻櫎娴佹按",
+ {
+ confirmButtonText: "纭",
+ cancelButtonText: "鍙栨秷",
+ type: "warning",
+ }
+ )
+ .then(() => {
+ delPaymentRecord([row.id]).then(() => {
+ proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛");
+ getList();
+ emit("changed");
+ });
+ })
+ .catch(() => {
+ proxy.$modal.msg("宸插彇娑�");
+ });
+ };
+
+ watch(
+ () => props.visible,
+ val => {
+ if (val) {
+ page.current = 1;
+ getList();
+ }
+ }
+ );
+</script>
+
+<style scoped lang="scss">
+ .payment-record-dialog {
+ .dialog-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 12px;
+
+ .party-name {
+ font-size: 15px;
+ font-weight: 600;
+ color: #303133;
+ }
+ }
+ }
+</style>
diff --git a/src/views/basicData/customerFile/index.vue b/src/views/basicData/customerFile/index.vue
index d300494..562720d 100644
--- a/src/views/basicData/customerFile/index.vue
+++ b/src/views/basicData/customerFile/index.vue
@@ -48,7 +48,16 @@
:isSelection="true"
@selection-change="handleSelectionChange"
:tableLoading="tableLoading"
- @pagination="pagination"></PIMTable>
+ @pagination="pagination">
+ <template #depositAmountSlot="{ row }">
+ <el-tooltip v-if="row.exceedsTrustFund"
+ :content="`浠e偍閲戦鍚堣宸茶秴鍑鸿瀹㈡埛淇℃墭鍩洪噾锛堜俊鎵樺熀閲戯細${row.trustFund}锛塦"
+ placement="top">
+ <el-text type="danger">{{ formatAmount(row.depositTotalAmount) }}</el-text>
+ </el-tooltip>
+ <span v-else>{{ formatAmount(row.depositTotalAmount) }}</span>
+ </template>
+ </PIMTable>
</div>
<el-dialog v-model="dialogFormVisible"
:title="operationType === 'add' ? '鏂板瀹㈡埛淇℃伅' : '缂栬緫瀹㈡埛淇℃伅'"
@@ -133,6 +142,19 @@
:label="dict.label"
:value="dict.value" />
</el-select>
+ </el-form-item>
+ </el-col>
+ </el-row>
+ <el-row :gutter="30">
+ <el-col :span="12">
+ <el-form-item label="淇℃墭鍩洪噾(鍏�)锛�"
+ prop="trustFund">
+ <el-input-number v-model="form.trustFund"
+ :min="0"
+ :precision="2"
+ :controls="false"
+ placeholder="璇疯緭鍏ワ紝鍙负绌�"
+ style="width: 100%" />
</el-form-item>
</el-col>
</el-row>
@@ -461,6 +483,32 @@
</div>
</el-col>
</el-row>
+ <el-row :gutter="20">
+ <el-col :span="12">
+ <div class="info-item">
+ <span class="info-label">淇℃墭鍩洪噾(鍏�)锛�</span>
+ <span class="info-value">{{ formatAmount(detailForm.trustFund) }}</span>
+ </div>
+ </el-col>
+ <el-col :span="12">
+ <div class="info-item">
+ <span class="info-label">浠e偍閲戦(鍏�)锛�</span>
+ <span class="info-value"
+ :class="{ 'text-danger': detailForm.exceedsTrustFund }">
+ {{ formatAmount(detailForm.depositTotalAmount) }}
+ </span>
+ </div>
+ </el-col>
+ </el-row>
+ <el-row v-if="detailForm.exceedsTrustFund"
+ :gutter="20">
+ <el-col :span="24">
+ <el-alert type="warning"
+ show-icon
+ :closable="false"
+ :title="`璇ュ鎴蜂唬鍌ㄩ噾棰濆悎璁� ${formatAmount(detailForm.depositTotalAmount)} 鍏冿紝宸茶秴鍑轰俊鎵樺熀閲� ${formatAmount(detailForm.trustFund)} 鍏僠" />
+ </el-col>
+ </el-row>
</div>
</div>
<!-- 娲借皥杩涘害璁板綍 -->
@@ -675,6 +723,11 @@
content: [{ required: true, message: "璇疯緭鍏ュ唴瀹�", trigger: "blur" }],
};
+ const formatAmount = value => {
+ return value === null || value === undefined || value === ""
+ ? "-"
+ : Number(value).toFixed(2);
+ };
// 璇︽儏鐩稿叧
const detailDialogVisible = ref(false);
const detailForm = reactive({
@@ -690,6 +743,9 @@
contactPhone: "",
maintainer: "",
maintenanceTime: "",
+ trustFund: "",
+ depositTotalAmount: "",
+ exceedsTrustFund: false,
});
const negotiationRecords = ref([]);
@@ -759,6 +815,22 @@
label: "閾惰璐﹀彿",
prop: "bankAccount",
width: 220,
+ },
+ {
+ label: "淇℃墭鍩洪噾(鍏�)",
+ prop: "trustFund",
+ width: 140,
+ formatData: value =>
+ value === null || value === undefined || value === ""
+ ? "-"
+ : Number(value).toFixed(2),
+ },
+ {
+ label: "浠e偍閲戦(鍏�)",
+ prop: "depositTotalAmount",
+ dataType: "slot",
+ slot: "depositAmountSlot",
+ width: 150,
},
{
label: "寮�鎴疯鍙�",
@@ -877,6 +949,8 @@
bankAccount: "",
bankCode: "",
customerType: "",
+ // 閲戦瀛楁缁� null 鑰岄潪 ""锛岄伩鍏� el-input-number 鎶婄┖涓叉姌绠楁垚 0
+ trustFund: null,
type: 0
},
rules: {
@@ -1576,6 +1650,11 @@
word-break: break-word;
}
+ .text-danger {
+ color: var(--el-color-danger);
+ font-weight: 600;
+ }
+
.no-records {
text-align: center;
padding: 30px;
diff --git a/src/views/basicData/tankInfo/index.vue b/src/views/basicData/tankInfo/index.vue
index e748b66..a93d11e 100644
--- a/src/views/basicData/tankInfo/index.vue
+++ b/src/views/basicData/tankInfo/index.vue
@@ -18,6 +18,30 @@
prefix-icon="Search"
@change="handleQuery" />
</el-form-item>
+ <el-form-item label="鏄惁鍚敤锛�">
+ <el-select v-model="searchForm.isEnabled"
+ placeholder="璇烽�夋嫨"
+ clearable
+ style="width: 140px"
+ @change="handleQuery">
+ <el-option label="鍚敤"
+ :value="1" />
+ <el-option label="鏈惎鐢�"
+ :value="0" />
+ </el-select>
+ </el-form-item>
+ <el-form-item label="鐢ㄩ�旓細">
+ <el-select v-model="searchForm.usageType"
+ placeholder="璇烽�夋嫨"
+ clearable
+ style="width: 140px"
+ @change="handleQuery">
+ <el-option label="绉熻祦"
+ :value="1" />
+ <el-option label="鑷敤"
+ :value="2" />
+ </el-select>
+ </el-form-item>
<el-form-item>
<el-button type="primary"
@click="handleQuery"> 鎼滅储
@@ -64,6 +88,25 @@
<el-table-column label="搴撳瓨浣欓噺(m鲁)"
prop="remaining"
min-width="140" />
+ <el-table-column label="鏄惁鍚敤"
+ prop="isEnabled"
+ align="center"
+ width="100">
+ <template #default="scope">
+ <el-tag :type="scope.row.isEnabled === 1 ? 'success' : 'info'"
+ size="small">
+ {{ scope.row.isEnabled === 1 ? "鍚敤" : "鏈惎鐢�" }}
+ </el-tag>
+ </template>
+ </el-table-column>
+ <el-table-column label="鐢ㄩ��"
+ prop="usageType"
+ align="center"
+ width="90">
+ <template #default="scope">
+ {{ getUsageTypeText(scope.row.usageType) }}
+ </template>
+ </el-table-column>
<el-table-column label="绉熷淇℃伅"
prop="tenantInfo"
min-width="160"
@@ -172,8 +215,38 @@
</el-form-item>
</el-col>
<el-col :span="12">
+ <el-form-item label="鏄惁鍚敤锛�"
+ prop="isEnabled">
+ <el-switch v-model="form.isEnabled"
+ :active-value="1"
+ :inactive-value="0"
+ active-text="鍚敤"
+ inactive-text="鏈惎鐢�" />
+ </el-form-item>
+ </el-col>
+ </el-row>
+ <el-row :gutter="30"
+ v-if="form.isEnabled === 1">
+ <el-col :span="12">
+ <el-form-item label="鐢ㄩ�旓細"
+ prop="usageType">
+ <el-radio-group v-model="form.usageType">
+ <el-radio :value="1">绉熻祦</el-radio>
+ <el-radio :value="2">鑷敤</el-radio>
+ </el-radio-group>
+ </el-form-item>
+ </el-col>
+ <el-col :span="12"
+ v-if="form.usageType === 1">
<el-form-item label="绉熷淇℃伅锛�"
- prop="tenantInfo">
+ prop="tenantInfo"
+ :rules="[
+ {
+ required: true,
+ message: '绉熻祦鐨勫偍缃愬繀椤诲~鍐欑瀹俊鎭�',
+ trigger: 'blur',
+ }
+ ]">
<el-input v-model="form.tenantInfo"
placeholder="璇疯緭鍏ョ瀹俊鎭�"
clearable />
@@ -188,7 +261,7 @@
<script setup>
import FormDialog from "@/components/Dialog/FormDialog.vue";
import pagination from "@/components/PIMTable/Pagination.vue";
- import { ref, reactive, toRefs, getCurrentInstance } from "vue";
+ import { ref, reactive, toRefs, watch, getCurrentInstance } from "vue";
import { Search } from "@element-plus/icons-vue";
import { ElMessageBox } from "element-plus";
import {
@@ -214,6 +287,8 @@
searchForm: {
tankNo: "",
category: "",
+ isEnabled: "",
+ usageType: "",
},
form: {
id: null,
@@ -222,6 +297,8 @@
oilProduct: "",
capacity: null,
remaining: null,
+ isEnabled: 1,
+ usageType: null,
tenantInfo: "",
},
rules: {
@@ -230,6 +307,24 @@
},
});
const { searchForm, form, rules } = toRefs(data);
+
+ // 鐢ㄩ�旀枃妗堬細1 绉熻祦 / 2 鑷敤
+ const getUsageTypeText = value => ({ 1: "绉熻祦", 2: "鑷敤" })[value] || "--";
+
+ // 鏈惎鐢ㄦ椂鐢ㄩ�斾笌绉熷淇℃伅涓�寰嬫竻绌猴紱閫夎嚜鐢ㄦ椂绉熷淇℃伅鑷姩娓呯┖锛堢璧佹墠闇�瑕佺瀹級
+ watch(
+ () => [form.value.isEnabled, form.value.usageType],
+ () => {
+ if (form.value.isEnabled !== 1) {
+ form.value.usageType = null;
+ form.value.tenantInfo = "";
+ return;
+ }
+ if (form.value.usageType !== 1) {
+ form.value.tenantInfo = "";
+ }
+ }
+ );
const getList = () => {
tableLoading.value = true;
@@ -259,14 +354,49 @@
selectedRows.value = selection;
};
+ const buildEmptyForm = () => ({
+ id: null,
+ tankNo: "",
+ category: "",
+ oilProduct: "",
+ capacity: null,
+ remaining: null,
+ isEnabled: 1,
+ usageType: null,
+ tenantInfo: "",
+ });
+
const openForm = (type, row) => {
operationType.value = type;
- form.value = {};
+ // 鍏堥噸缃啀璧嬪�硷細resetFields 浼氭妸瀛楁鎵撳洖鎸傝浇鏃剁殑鍒濆�硷紝鏀惧悗闈細鎶婂垰濉ソ鐨勫�兼竻鎺�
proxy.resetForm("formRef");
+ form.value = buildEmptyForm();
dialogFormVisible.value = true;
if (type === "edit" && row) {
- const { id, tankNo, category, oilProduct, capacity, remaining, tenantInfo } = row;
- form.value = { id, tankNo, category, oilProduct, capacity, remaining, tenantInfo };
+ const {
+ id,
+ tankNo,
+ category,
+ oilProduct,
+ capacity,
+ remaining,
+ isEnabled,
+ usageType,
+ tenantInfo,
+ } = row;
+ form.value = {
+ ...buildEmptyForm(),
+ id,
+ tankNo,
+ category,
+ oilProduct,
+ capacity,
+ remaining,
+ // 鍘嗗彶鏁版嵁娌℃湁璇ュ瓧娈碉紝缂哄け鏃舵寜銆屽惎鐢ㄣ�嶅鐞嗭紝閬垮厤淇濆瓨鏃惰璇疆涓烘湭鍚敤
+ isEnabled: isEnabled ?? 1,
+ usageType: usageType ?? null,
+ tenantInfo: tenantInfo ?? "",
+ };
}
};
diff --git a/src/views/basicData/vehicle/index.vue b/src/views/basicData/vehicle/index.vue
new file mode 100644
index 0000000..26e2200
--- /dev/null
+++ b/src/views/basicData/vehicle/index.vue
@@ -0,0 +1,371 @@
+<template>
+ <div class="app-container">
+ <div class="search_form">
+ <div>
+ <el-form :model="searchForm"
+ :inline="true">
+ <el-form-item label="杞︾墝鍙凤細">
+ <el-input v-model="searchForm.plateNumber"
+ placeholder="璇疯緭鍏�"
+ clearable
+ prefix-icon="Search"
+ @change="handleQuery" />
+ </el-form-item>
+ <el-form-item label="鍙告満濮撳悕锛�">
+ <el-input v-model="searchForm.driverName"
+ placeholder="璇疯緭鍏�"
+ clearable
+ prefix-icon="Search"
+ @change="handleQuery" />
+ </el-form-item>
+ <el-form-item label="鐘舵�侊細">
+ <el-select v-model="searchForm.status"
+ placeholder="璇烽�夋嫨"
+ clearable
+ style="width: 160px"
+ @change="handleQuery">
+ <el-option label="鍚敤"
+ value="鍚敤" />
+ <el-option label="鍋滅敤"
+ value="鍋滅敤" />
+ </el-select>
+ </el-form-item>
+ <el-form-item>
+ <el-button type="primary"
+ @click="handleQuery"> 鎼滅储
+ </el-button>
+ </el-form-item>
+ </el-form>
+ </div>
+ </div>
+ <div class="table_list">
+ <div style="display: flex;justify-content: flex-end;margin-bottom: 20px;">
+ <el-button type="primary"
+ @click="openForm('add')">鏂板杞﹁締</el-button>
+ <el-button type="danger"
+ plain
+ @click="handleDelete()">鍒犻櫎
+ </el-button>
+ </div>
+ <el-table :data="tableData"
+ border
+ v-loading="tableLoading"
+ @selection-change="handleSelectionChange">
+ <el-table-column align="center"
+ type="selection"
+ width="55" />
+ <el-table-column align="center"
+ label="搴忓彿"
+ type="index"
+ width="60" />
+ <el-table-column label="杞︾墝鍙�"
+ prop="plateNumber"
+ min-width="140"
+ show-overflow-tooltip />
+ <el-table-column label="榛樿鍙告満"
+ prop="driverName"
+ min-width="120"
+ show-overflow-tooltip />
+ <el-table-column label="鍙告満鐢佃瘽"
+ prop="driverPhone"
+ min-width="140"
+ show-overflow-tooltip />
+ <el-table-column label="鏍稿畾杞介噸(鍚�)"
+ prop="loadCapacity"
+ min-width="120" />
+ <el-table-column label="鐘舵��"
+ prop="status"
+ min-width="90">
+ <template #default="scope">
+ <el-tag :type="scope.row.status === '鍋滅敤' ? 'info' : 'success'"
+ size="small">
+ {{ scope.row.status || '鍚敤' }}
+ </el-tag>
+ </template>
+ </el-table-column>
+ <el-table-column label="澶囨敞"
+ prop="remark"
+ min-width="160"
+ show-overflow-tooltip />
+ <el-table-column label="鍒涘缓鏃堕棿"
+ prop="createTime"
+ min-width="180">
+ <template #default="scope">
+ {{ scope.row.createTime ? scope.row.createTime.replace('T', ' ').slice(0, 19) : '--' }}
+ </template>
+ </el-table-column>
+ <el-table-column fixed="right"
+ label="鎿嶄綔"
+ width="120"
+ align="center">
+ <template #default="scope">
+ <el-button link
+ type="primary"
+ @click="openForm('edit', scope.row)">缂栬緫
+ </el-button>
+ <el-button link
+ type="danger"
+ @click="handleDelete([scope.row])">鍒犻櫎
+ </el-button>
+ </template>
+ </el-table-column>
+ </el-table>
+ <pagination v-show="total > 0"
+ :total="total"
+ layout="total, sizes, prev, pager, next, jumper"
+ :page="page.current"
+ :limit="page.size"
+ @pagination="paginationChange" />
+ </div>
+ <FormDialog v-model="dialogFormVisible"
+ :title="operationType === 'add' ? '鏂板杞﹁締' : '缂栬緫杞﹁締'"
+ :width="'40%'"
+ :operation-type="operationType"
+ @close="closeDia"
+ @confirm="submitForm"
+ @cancel="closeDia">
+ <el-form :model="form"
+ label-width="120px"
+ label-position="top"
+ :rules="rules"
+ ref="formRef">
+ <el-row :gutter="30">
+ <el-col :span="12">
+ <el-form-item label="杞︾墝鍙凤細"
+ prop="plateNumber">
+ <el-input v-model="form.plateNumber"
+ placeholder="璇疯緭鍏ヨ溅鐗屽彿"
+ clearable />
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item label="鐘舵�侊細"
+ prop="status">
+ <el-select v-model="form.status"
+ placeholder="璇烽�夋嫨"
+ style="width: 100%">
+ <el-option label="鍚敤"
+ value="鍚敤" />
+ <el-option label="鍋滅敤"
+ value="鍋滅敤" />
+ </el-select>
+ </el-form-item>
+ </el-col>
+ </el-row>
+ <el-row :gutter="30">
+ <el-col :span="12">
+ <el-form-item label="榛樿鍙告満锛�"
+ prop="driverName">
+ <el-input v-model="form.driverName"
+ placeholder="璇疯緭鍏ュ徃鏈哄鍚�"
+ clearable />
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item label="鍙告満鐢佃瘽锛�"
+ prop="driverPhone">
+ <el-input v-model="form.driverPhone"
+ placeholder="璇疯緭鍏�11浣嶆暟瀛�"
+ maxlength="11"
+ @input="phoneInput"
+ clearable />
+ </el-form-item>
+ </el-col>
+ </el-row>
+ <el-row :gutter="30">
+ <el-col :span="12">
+ <el-form-item label="鏍稿畾杞介噸(鍚�)锛�"
+ prop="loadCapacity">
+ <el-input-number v-model="form.loadCapacity"
+ :precision="2"
+ :step="1"
+ :min="0"
+ style="width: 100%" />
+ </el-form-item>
+ </el-col>
+ </el-row>
+ <el-row :gutter="30">
+ <el-col :span="24">
+ <el-form-item label="澶囨敞锛�"
+ prop="remark">
+ <el-input v-model="form.remark"
+ type="textarea"
+ :rows="2"
+ placeholder="璇疯緭鍏ュ娉�"
+ clearable />
+ </el-form-item>
+ </el-col>
+ </el-row>
+ </el-form>
+ </FormDialog>
+ </div>
+</template>
+
+<script setup>
+ import FormDialog from "@/components/Dialog/FormDialog.vue";
+ import pagination from "@/components/PIMTable/Pagination.vue";
+ import { ref, reactive, toRefs, getCurrentInstance } from "vue";
+ import { ElMessageBox } from "element-plus";
+ import {
+ listVehicle,
+ addVehicle,
+ updateVehicle,
+ delVehicle,
+ } from "@/api/basicData/vehicle.js";
+
+ const { proxy } = getCurrentInstance();
+ const tableData = ref([]);
+ const selectedRows = ref([]);
+ const tableLoading = ref(false);
+ const dialogFormVisible = ref(false);
+ const operationType = ref("");
+ const page = reactive({
+ current: 1,
+ size: 10,
+ });
+ const total = ref(0);
+
+ const data = reactive({
+ searchForm: {
+ plateNumber: "",
+ driverName: "",
+ status: "",
+ },
+ form: {
+ id: null,
+ plateNumber: "",
+ driverName: "",
+ driverPhone: "",
+ loadCapacity: null,
+ status: "鍚敤",
+ remark: "",
+ },
+ rules: {
+ plateNumber: [{ required: true, message: "璇疯緭鍏ヨ溅鐗屽彿", trigger: "blur" }],
+ driverPhone: [
+ { pattern: /^1[3-9]\d{9}$/, message: "璇疯緭鍏ユ湁鏁堢殑11浣嶆墜鏈哄彿鐮�", trigger: "blur" },
+ ],
+ },
+ });
+ const { searchForm, form, rules } = toRefs(data);
+
+ const getList = () => {
+ tableLoading.value = true;
+ listVehicle({ ...searchForm.value, ...page })
+ .then(res => {
+ tableLoading.value = false;
+ tableData.value = res.data.records || [];
+ total.value = res.data.total || 0;
+ })
+ .catch(() => {
+ tableLoading.value = false;
+ });
+ };
+
+ const handleQuery = () => {
+ page.current = 1;
+ getList();
+ };
+
+ const paginationChange = obj => {
+ page.current = obj.page;
+ page.size = obj.limit;
+ getList();
+ };
+
+ const handleSelectionChange = selection => {
+ selectedRows.value = selection;
+ };
+
+ // 鐢佃瘽杈撳叆闄愬埗锛氬彧鍏佽鏁板瓧锛屾渶澶�11浣�
+ const phoneInput = val => {
+ form.value.driverPhone = String(val ?? "")
+ .replace(/\D/g, "")
+ .slice(0, 11);
+ };
+
+ const openForm = (type, row) => {
+ operationType.value = type;
+ form.value = {};
+ proxy.resetForm("formRef");
+ dialogFormVisible.value = true;
+ if (type === "edit" && row) {
+ const { id, plateNumber, driverName, driverPhone, loadCapacity, status, remark } = row;
+ form.value = {
+ id,
+ plateNumber,
+ driverName,
+ driverPhone,
+ loadCapacity,
+ status: status || "鍚敤",
+ remark,
+ };
+ } else {
+ form.value = {
+ id: null,
+ plateNumber: "",
+ driverName: "",
+ driverPhone: "",
+ loadCapacity: null,
+ status: "鍚敤",
+ remark: "",
+ };
+ }
+ };
+
+ const submitForm = () => {
+ proxy.$refs["formRef"].validate(valid => {
+ if (valid) {
+ const api = form.value.id ? updateVehicle : addVehicle;
+ api(form.value).then(() => {
+ proxy.$modal.msgSuccess("鎿嶄綔鎴愬姛");
+ closeDia();
+ getList();
+ });
+ }
+ });
+ };
+
+ const closeDia = () => {
+ proxy.resetForm("formRef");
+ dialogFormVisible.value = false;
+ };
+
+ const handleDelete = rows => {
+ let ids = [];
+ if (rows) {
+ ids = rows.map(item => item.id);
+ } else if (selectedRows.value.length > 0) {
+ ids = selectedRows.value.map(item => item.id);
+ } else {
+ proxy.$modal.msgWarning("璇烽�夋嫨鏁版嵁");
+ return;
+ }
+ ElMessageBox.confirm(
+ "閫変腑鐨勫唴瀹瑰皢琚垹闄わ紝鏄惁纭鍒犻櫎锛熷缓璁敤銆屽仠鐢ㄣ�嶄笅绾胯溅杈嗭紝鍒犻櫎涓嶄細鏍¢獙鏄惁宸茶鍑哄簱璁板綍寮曠敤銆�",
+ "鍒犻櫎",
+ {
+ confirmButtonText: "纭",
+ cancelButtonText: "鍙栨秷",
+ type: "warning",
+ }
+ )
+ .then(() => {
+ delVehicle(ids).then(() => {
+ proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛");
+ getList();
+ });
+ })
+ .catch(() => {
+ proxy.$modal.msg("宸插彇娑�");
+ });
+ };
+
+ getList();
+</script>
+
+<style scoped lang="scss">
+ .search_form {
+ margin-bottom: 16px;
+ }
+</style>
diff --git a/src/views/equipmentManagement/spareParts/index.vue b/src/views/equipmentManagement/spareParts/index.vue
index 9f5d8fe..dccc99b 100644
--- a/src/views/equipmentManagement/spareParts/index.vue
+++ b/src/views/equipmentManagement/spareParts/index.vue
@@ -63,6 +63,35 @@
<el-form-item label="澶囦欢缂栧彿" prop="sparePartsNo">
<el-input v-model="form.sparePartsNo"></el-input>
</el-form-item>
+ <el-form-item label="浜у搧澶х被" prop="productId">
+ <el-tree-select
+ v-model="form.productId"
+ placeholder="璇烽�夋嫨"
+ clearable
+ filterable
+ check-strictly
+ :data="productOptions"
+ :render-after-expand="false"
+ @change="handleProductChange"
+ style="width: 100%"
+ />
+ </el-form-item>
+ <el-form-item label="瑙勬牸鍨嬪彿" prop="productModelId">
+ <el-select
+ v-model="form.productModelId"
+ placeholder="璇烽�夋嫨"
+ clearable
+ filterable
+ style="width: 100%"
+ >
+ <el-option
+ v-for="item in modelOptions"
+ :key="item.id"
+ :label="item.model"
+ :value="item.id"
+ />
+ </el-select>
+ </el-form-item>
<el-form-item label="鏁伴噺" prop="quantity">
<el-input type="number" v-model="form.quantity"></el-input>
</el-form-item>
@@ -134,6 +163,7 @@
import { ElMessage, ElMessageBox } from 'element-plus';
import { getSparePartsList, addSparePart, editSparePart, delSparePart } from "@/api/equipmentManagement/spareParts";
import { getDeviceLedger } from "@/api/equipmentManagement/ledger";
+import { productTreeList, modelList } from "@/api/basicData/product.js";
import PIMTable from "@/components/PIMTable/PIMTable.vue";
import { getSparePartsUsagePage } from "@/api/equipmentManagement/sparePartsUsage";
@@ -153,6 +183,9 @@
const operationType = ref('add')
// 璁惧閫夐」
const deviceOptions = ref([]);
+// 浜у搧澶х被鏍�/瑙勬牸鍨嬪彿閫夐」
+const productOptions = ref([]);
+const modelOptions = ref([]);
// 琛ㄥ崟寮曠敤
const formRef = ref(null);
// 鏌ヨ鍙傛暟
@@ -253,6 +286,8 @@
id:'',
name: '',
sparePartsNo: '',
+ productId: '',
+ productModelId: '',
status: '',
description: '',
deviceLedgerIds: [],
@@ -415,17 +450,55 @@
}
};
+// 鍔犺浇浜у搧澶х被鏍戯紙id -> value锛屼緵 el-tree-select 浣跨敤锛�
+const loadProductOptions = async () => {
+ try {
+ const res = await productTreeList();
+ productOptions.value = convertIdToValue(Array.isArray(res) ? res : res?.data || []);
+ } catch (error) {
+ ElMessage.error('鑾峰彇浜у搧澶х被澶辫触');
+ }
+};
+
+function convertIdToValue(data) {
+ return (data || []).map(item => {
+ const { id, children, ...rest } = item;
+ const newItem = { ...rest, value: id };
+ if (children && children.length > 0) {
+ newItem.children = convertIdToValue(children);
+ }
+ return newItem;
+ });
+}
+
+// 鍒囨崲浜у搧澶х被锛氭竻绌鸿鏍煎瀷鍙峰苟閲嶆柊鎷夊彇瑙勬牸鍨嬪彿閫夐」
+const handleProductChange = async value => {
+ form.productModelId = '';
+ modelOptions.value = [];
+ if (!value) return;
+ try {
+ const res = await modelList({ id: value });
+ modelOptions.value = Array.isArray(res) ? res : res?.data || [];
+ } catch (error) {
+ ElMessage.error('鑾峰彇瑙勬牸鍨嬪彿澶辫触');
+ }
+};
+
// 鏂板鍒嗙被
const addCategory = async () => {
await loadDeviceName();
+ await loadProductOptions();
form.id = '';
form.name = '';
form.sparePartsNo = '';
+ form.productId = '';
+ form.productModelId = '';
form.status = '';
form.description = '';
form.deviceLedgerIds = [];
form.quantity = undefined;
form.price = null;
+ modelOptions.value = [];
operationType.value = 'add'
dialogVisible.value = true;
};
@@ -433,7 +506,21 @@
// 缂栬緫鍒嗙被
const editCategory = async (row) => {
await loadDeviceName();
+ await loadProductOptions();
Object.assign(form, row);
+ form.productId = row.productId ?? '';
+ form.productModelId = row.productModelId ?? '';
+ // 鍙嶆樉瑙勬牸鍨嬪彿閫夐」锛堢紪杈戞椂涓嶄細瑙﹀彂浜у搧澶х被 change锛�
+ if (form.productId) {
+ try {
+ const res = await modelList({ id: form.productId });
+ modelOptions.value = Array.isArray(res) ? res : res?.data || [];
+ } catch (error) {
+ modelOptions.value = [];
+ }
+ } else {
+ modelOptions.value = [];
+ }
// 濡傛灉鍚庣杩斿洖鐨勬槸 deviceIds 瀛楃涓诧紝闇�瑕佽浆鎹负鏁扮粍
if (row.deviceIds && typeof row.deviceIds === 'string') {
// 纭繚ID绫诲瀷涓庤澶囬�夐」涓殑ID绫诲瀷涓�鑷�
diff --git a/src/views/equipmentManagement/sparePartsRecord/index.vue b/src/views/equipmentManagement/sparePartsRecord/index.vue
new file mode 100644
index 0000000..2fb306e
--- /dev/null
+++ b/src/views/equipmentManagement/sparePartsRecord/index.vue
@@ -0,0 +1,215 @@
+<template>
+ <div class="app-container">
+ <div class="search_form">
+ <el-form :inline="true" :model="searchForm">
+ <el-form-item label="澶囦欢">
+ <el-select v-model="searchForm.sparePartsId"
+ placeholder="璇烽�夋嫨"
+ clearable
+ filterable
+ style="width: 240px">
+ <el-option v-for="item in sparePartsOptions"
+ :key="item.id"
+ :label="item.name"
+ :value="item.id" />
+ </el-select>
+ </el-form-item>
+ <el-form-item label="鏂瑰悜">
+ <el-select v-model="searchForm.direction"
+ placeholder="璇烽�夋嫨"
+ clearable
+ style="width: 160px">
+ <el-option label="鍏ュ簱" :value="1" />
+ <el-option label="鍑哄簱" :value="2" />
+ </el-select>
+ </el-form-item>
+ <el-form-item label="鏉ユ簮绫诲瀷">
+ <el-select v-model="searchForm.sourceType"
+ placeholder="璇烽�夋嫨"
+ clearable
+ style="width: 160px">
+ <el-option v-for="item in sourceTypeOptions"
+ :key="item.value"
+ :label="item.label"
+ :value="item.value" />
+ </el-select>
+ </el-form-item>
+ <el-form-item>
+ <el-button type="primary" @click="handleQuery">鏌ヨ</el-button>
+ <el-button @click="resetQuery">閲嶇疆</el-button>
+ </el-form-item>
+ </el-form>
+ <div>
+ <el-button type="danger" plain @click="handleDelete">鍒犻櫎</el-button>
+ </div>
+ </div>
+ <div class="table_list">
+ <el-table :data="tableData"
+ border
+ v-loading="tableLoading"
+ @selection-change="handleSelectionChange"
+ :row-key="row => row.id"
+ height="calc(100vh - 18.5em)">
+ <el-table-column align="center" type="selection" width="55" />
+ <el-table-column align="center" label="搴忓彿" type="index" width="60" />
+ <el-table-column label="澶囦欢鍚嶇О" prop="sparePartsName" show-overflow-tooltip />
+ <el-table-column label="鏂瑰悜" width="100">
+ <template #default="{ row }">
+ <el-tag :type="row.direction === 1 || row.direction === '1' ? 'success' : 'warning'"
+ size="small">
+ {{ getDirectionText(row.direction) }}
+ </el-tag>
+ </template>
+ </el-table-column>
+ <el-table-column label="鏁伴噺" prop="quantity" width="120" show-overflow-tooltip />
+ <el-table-column label="鏉ユ簮绫诲瀷" prop="sourceType" width="120" show-overflow-tooltip>
+ <template #default="{ row }">
+ {{ getSourceTypeText(row.sourceType) }}
+ </template>
+ </el-table-column>
+ <el-table-column label="鏉ユ簮鍗曞彿" prop="sourceNo" show-overflow-tooltip />
+ <el-table-column label="澶囨敞" prop="remark" show-overflow-tooltip />
+ <el-table-column label="鏃堕棿" prop="createTime" width="170" show-overflow-tooltip />
+ </el-table>
+ <pagination v-show="total > 0"
+ :total="total"
+ layout="total, sizes, prev, pager, next, jumper"
+ :page="page.current"
+ :limit="page.size"
+ @pagination="paginationChange" />
+ </div>
+ </div>
+</template>
+
+<script setup>
+ import pagination from "@/components/PIMTable/Pagination.vue";
+ import { ref, reactive, getCurrentInstance } from "vue";
+ import { ElMessageBox } from "element-plus";
+ import {
+ getSparePartsRecordPage,
+ delSparePartsRecord,
+ } from "@/api/equipmentManagement/sparePartsRecord.js";
+ import { getSparePartsList } from "@/api/equipmentManagement/spareParts.js";
+
+ const { proxy } = getCurrentInstance();
+
+ const tableData = ref([]);
+ const selectedRows = ref([]);
+ const tableLoading = ref(false);
+ const total = ref(0);
+ const page = reactive({
+ current: 1,
+ size: 10,
+ });
+ const searchForm = ref({
+ sparePartsId: "",
+ direction: "",
+ sourceType: "",
+ });
+ // 澶囦欢涓嬫媺閫夐」
+ const sparePartsOptions = ref([]);
+ // 鏉ユ簮绫诲瀷鏋氫妇
+ const sourceTypeOptions = [
+ { label: "閲囪喘鍏ュ簱", value: 1 },
+ { label: "缁翠慨棰嗙敤", value: 2 },
+ { label: "淇濆吇棰嗙敤", value: 3 },
+ ];
+
+ const getDirectionText = direction => {
+ const map = { 1: "鍏ュ簱", 2: "鍑哄簱" };
+ return map[direction] || "";
+ };
+
+ const getSourceTypeText = sourceType => {
+ return (
+ sourceTypeOptions.find(item => item.value === sourceType)?.label || ""
+ );
+ };
+
+ const getList = () => {
+ tableLoading.value = true;
+ getSparePartsRecordPage({
+ ...searchForm.value,
+ current: page.current,
+ size: page.size,
+ })
+ .then(res => {
+ tableLoading.value = false;
+ const result = res.data || {};
+ tableData.value = result.records || [];
+ total.value = result.total || 0;
+ })
+ .catch(() => {
+ tableLoading.value = false;
+ });
+ };
+
+ const loadSparePartsOptions = () => {
+ getSparePartsList({ current: 1, size: 1000 })
+ .then(res => {
+ sparePartsOptions.value = res?.data?.records || [];
+ })
+ .catch(() => {
+ sparePartsOptions.value = [];
+ });
+ };
+
+ const handleQuery = () => {
+ page.current = 1;
+ getList();
+ };
+
+ const resetQuery = () => {
+ searchForm.value = {
+ sparePartsId: "",
+ direction: "",
+ sourceType: "",
+ };
+ page.current = 1;
+ getList();
+ };
+
+ const paginationChange = obj => {
+ page.current = obj.page;
+ page.size = obj.limit;
+ getList();
+ };
+
+ const handleSelectionChange = selection => {
+ selectedRows.value = selection.filter(item => item.id);
+ };
+
+ const handleDelete = () => {
+ if (selectedRows.value.length === 0) {
+ proxy.$modal.msgWarning("璇烽�夋嫨鏁版嵁");
+ return;
+ }
+ const ids = selectedRows.value.map(item => item.id);
+ ElMessageBox.confirm("閫変腑鐨勫唴瀹瑰皢琚垹闄わ紝鏄惁纭鍒犻櫎锛�", "鍒犻櫎", {
+ confirmButtonText: "纭",
+ cancelButtonText: "鍙栨秷",
+ type: "warning",
+ })
+ .then(() => {
+ delSparePartsRecord(ids).then(() => {
+ proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛");
+ getList();
+ });
+ })
+ .catch(() => {
+ proxy.$modal.msg("宸插彇娑�");
+ });
+ };
+
+ getList();
+ loadSparePartsOptions();
+</script>
+
+<style scoped lang="scss">
+ .search_form {
+ display: flex;
+ align-items: flex-start;
+ justify-content: space-between;
+ margin-bottom: 10px;
+ }
+</style>
diff --git a/src/views/inventoryManagement/dispatchLog/Record.vue b/src/views/inventoryManagement/dispatchLog/Record.vue
index 5a056d6..5658104 100644
--- a/src/views/inventoryManagement/dispatchLog/Record.vue
+++ b/src/views/inventoryManagement/dispatchLog/Record.vue
@@ -18,7 +18,7 @@
</el-form-item>
</el-col>
<el-col :span="4">
- <el-form-item label="浜у搧澶х被"
+ <el-form-item label="娌瑰搧"
prop="productName">
<el-input v-model="searchForm.productName"
style="width: 240px"
@@ -102,46 +102,211 @@
label="搴忓彿"
type="index"
width="60" />
- <el-table-column label="鍑哄簱鎵规"
- prop="outboundBatches"
- min-width="100"
- show-overflow-tooltip />
- <el-table-column label="鍑哄簱鏃ユ湡"
- prop="createTime"
- show-overflow-tooltip />
- <el-table-column label="浜у搧澶х被"
- prop="productName"
- show-overflow-tooltip />
- <el-table-column label="瑙勬牸鍨嬪彿"
- prop="model"
- show-overflow-tooltip />
- <el-table-column label="鎵瑰彿"
- prop="batchNo"
- show-overflow-tooltip />
- <el-table-column label="鍗曚綅"
- prop="unit"
- show-overflow-tooltip />
- <el-table-column label="鍑哄簱鏁伴噺"
- prop="stockOutNum"
- show-overflow-tooltip />
- <el-table-column label="鍑哄簱浜�"
- prop="createBy"
- show-overflow-tooltip />
- <el-table-column label="鏉ユ簮"
- prop="recordType"
- show-overflow-tooltip>
- <template #default="scope">
- {{ getRecordType(scope.row.recordType) }}
- </template>
+ <el-table-column label="鍑哄簱淇℃伅"
+ align="center"
+ :header-cell-style="groupHeaderStyle">
+ <el-table-column label="鍑哄簱鎵规"
+ prop="outboundBatches"
+ min-width="120"
+ show-overflow-tooltip />
+ <el-table-column label="鍑哄簱鏃ユ湡"
+ prop="outDate"
+ min-width="110"
+ show-overflow-tooltip />
+ <el-table-column label="鍑哄簱绫诲埆"
+ prop="outCategory"
+ min-width="90"
+ show-overflow-tooltip />
+ <el-table-column label="娌瑰簱"
+ prop="oilDepotName"
+ min-width="160"
+ show-overflow-tooltip />
+ <el-table-column label="鍌ㄧ綈鍙�"
+ prop="tankNo"
+ min-width="90"
+ show-overflow-tooltip />
+ <el-table-column label="娌瑰搧"
+ prop="productName"
+ min-width="90"
+ show-overflow-tooltip />
+ <el-table-column label="鍑哄簱鍚ㄦ暟(鍚�)"
+ prop="outTonnage"
+ min-width="110"
+ show-overflow-tooltip />
+ <el-table-column label="瑙勬牸鍨嬪彿"
+ prop="model"
+ min-width="110"
+ show-overflow-tooltip />
+ <el-table-column label="鎵瑰彿"
+ prop="batchNo"
+ min-width="140"
+ show-overflow-tooltip />
+ <el-table-column label="鍗曚綅"
+ prop="unit"
+ min-width="70"
+ show-overflow-tooltip />
+ <el-table-column label="鍑哄簱鏁伴噺"
+ prop="stockOutNum"
+ min-width="100"
+ show-overflow-tooltip />
+ <el-table-column label="瀹為檯鎵e噺鏁伴噺"
+ prop="deductQuantity"
+ min-width="120"
+ show-overflow-tooltip />
+ <el-table-column label="鍓╀綑娌归噺(鍚�)"
+ prop="residualOil"
+ min-width="110"
+ show-overflow-tooltip />
+ <el-table-column label="鍑哄簱浜�"
+ prop="createBy"
+ min-width="90"
+ show-overflow-tooltip />
+ <el-table-column label="鍙戣揣鍗曞彿"
+ prop="shippingNo"
+ min-width="140"
+ show-overflow-tooltip />
+ <el-table-column label="鏉ユ簮"
+ prop="recordType"
+ min-width="90"
+ show-overflow-tooltip>
+ <template #default="scope">
+ {{ getRecordType(scope.row.recordType) }}
+ </template>
+ </el-table-column>
+ <el-table-column label="瀹℃壒鐘舵��"
+ prop="approvalStatus"
+ min-width="100"
+ show-overflow-tooltip>
+ <template #default="scope">
+ <el-tag :type="getApprovalStatusTagType(scope.row.approvalStatus)"
+ size="small">
+ {{ getApprovalStatusLabel(scope.row.approvalStatus) }}
+ </el-tag>
+ </template>
+ </el-table-column>
</el-table-column>
- <el-table-column label="瀹℃壒鐘舵��"
- prop="approvalStatus"
- show-overflow-tooltip>
+ <el-table-column label="瀹㈡埛淇℃伅"
+ align="center"
+ :header-cell-style="groupHeaderStyle">
+ <el-table-column label="瀹㈡埛鍚嶇О"
+ prop="customerName"
+ min-width="160"
+ show-overflow-tooltip />
+ <el-table-column label="瀹㈡埛瀵规帴浜�"
+ prop="contactPerson"
+ min-width="100"
+ show-overflow-tooltip />
+ <el-table-column label="瀵规帴浜虹數璇�"
+ prop="contactPhone"
+ min-width="120"
+ show-overflow-tooltip />
+ </el-table-column>
+ <el-table-column label="鐗╂祦淇℃伅"
+ align="center"
+ :header-cell-style="groupHeaderStyle">
+ <el-table-column label="鍙告満濮撳悕"
+ prop="driverName"
+ min-width="100"
+ show-overflow-tooltip />
+ <el-table-column label="鍙告満鐢佃瘽"
+ prop="driverPhone"
+ min-width="120"
+ show-overflow-tooltip />
+ <el-table-column label="鎶艰繍鍛樺鍚�"
+ prop="escortName"
+ min-width="100"
+ show-overflow-tooltip />
+ <el-table-column label="鎶艰繍鍛樼數璇�"
+ prop="escortPhone"
+ min-width="120"
+ show-overflow-tooltip />
+ <el-table-column label="杞︾墝鍙�"
+ prop="truckPlateNo"
+ min-width="110"
+ show-overflow-tooltip />
+ <el-table-column label="杞︽寕鐗屽彿"
+ prop="trailerPlateNo"
+ min-width="110"
+ show-overflow-tooltip />
+ </el-table-column>
+ <el-table-column label="瑁呮补鎿嶄綔淇℃伅"
+ align="center"
+ :header-cell-style="groupHeaderStyle">
+ <el-table-column label="瑁呮补妫�楠�"
+ prop="loadingInspectionStatus"
+ min-width="90"
+ show-overflow-tooltip />
+ <el-table-column label="瑁呰溅楣や綅"
+ prop="loadingCranePosition"
+ min-width="100"
+ show-overflow-tooltip />
+ <el-table-column label="瀵嗗害"
+ prop="density"
+ min-width="80"
+ show-overflow-tooltip />
+ <el-table-column label="瑁呮补鎷嶇収"
+ prop="loadingPhotos"
+ align="center"
+ min-width="90">
+ <template #default="scope">
+ <el-image v-if="toPhotoArray(scope.row.loadingPhotos).length"
+ :src="getPhotoUrl(toPhotoArray(scope.row.loadingPhotos)[0])"
+ :preview-src-list="getPhotoUrls(scope.row.loadingPhotos)"
+ preview-teleported
+ fit="cover"
+ style="width: 40px; height: 40px; border-radius: 4px" />
+ <span v-else>--</span>
+ </template>
+ </el-table-column>
+ </el-table-column>
+ <el-table-column label="鍑哄簱杩囩鏁版嵁"
+ align="center"
+ :header-cell-style="groupHeaderStyle">
+ <el-table-column label="姣涢噸(鍚�)"
+ prop="grossWeight"
+ min-width="90"
+ show-overflow-tooltip />
+ <el-table-column label="鐨噸(鍚�)"
+ prop="tareWeight"
+ min-width="90"
+ show-overflow-tooltip />
+ <el-table-column label="鍑�閲�(鍚�)"
+ prop="netWeight"
+ min-width="90"
+ show-overflow-tooltip />
+ <el-table-column label="浜忛噺(鍚�)"
+ prop="lossQuantity"
+ min-width="90"
+ show-overflow-tooltip />
+ <el-table-column label="瀹為檯鍑哄簱浣撶Н(鍗�)"
+ prop="actualVolume"
+ min-width="130"
+ show-overflow-tooltip />
+ <el-table-column label="纾呭崟"
+ prop="poundPhotos"
+ align="center"
+ min-width="90">
+ <template #default="scope">
+ <el-image v-if="toPhotoArray(scope.row.poundPhotos).length"
+ :src="getPhotoUrl(toPhotoArray(scope.row.poundPhotos)[0])"
+ :preview-src-list="getPhotoUrls(scope.row.poundPhotos)"
+ preview-teleported
+ fit="cover"
+ style="width: 40px; height: 40px; border-radius: 4px" />
+ <span v-else>--</span>
+ </template>
+ </el-table-column>
+ </el-table-column>
+ <el-table-column fixed="right"
+ label="鎿嶄綔"
+ width="110"
+ align="center">
<template #default="scope">
- <el-tag :type="getApprovalStatusTagType(scope.row.approvalStatus)"
- size="small">
- {{ getApprovalStatusLabel(scope.row.approvalStatus) }}
- </el-tag>
+ <el-button v-if="isSalesOutRecord(scope.row)"
+ link
+ type="primary"
+ @click="openResidualOil(scope.row)">褰曞叆娈嬫补
+ </el-button>
</template>
</el-table-column>
</el-table>
@@ -152,6 +317,41 @@
:limit="page.size"
@pagination="paginationChange" />
</div>
+ <!-- 娈嬫补褰曞叆锛氶�佸畬璐у悗鍥炲~鍓╀綑娌归噺锛屽彧鏇存柊 residualOil 涓�鍒� -->
+ <el-dialog v-model="residualDialogVisible"
+ title="褰曞叆鍓╀綑娌归噺"
+ width="30%"
+ append-to-body>
+ <el-form label-width="130px"
+ label-position="top">
+ <el-form-item label="鍑哄簱鎵规">
+ <el-input :model-value="residualForm.outboundBatches || '--'"
+ readonly />
+ </el-form-item>
+ <el-form-item label="杞︾墝鍙�">
+ <el-input :model-value="residualForm.truckPlateNo || '--'"
+ readonly />
+ </el-form-item>
+ <el-form-item label="鍑哄簱鏁伴噺(鍚�)">
+ <el-input :model-value="residualForm.stockOutNum ?? '--'"
+ readonly />
+ </el-form-item>
+ <el-form-item label="鍓╀綑娌归噺(鍚�)">
+ <el-input-number v-model="residualForm.residualOil"
+ :precision="4"
+ :step="1"
+ :min="0"
+ style="width: 100%" />
+ </el-form-item>
+ </el-form>
+ <template #footer>
+ <div class="dialog-footer">
+ <el-button type="primary"
+ @click="submitResidualOil">纭</el-button>
+ <el-button @click="residualDialogVisible = false">鍙栨秷</el-button>
+ </div>
+ </template>
+ </el-dialog>
</div>
</template>
@@ -166,6 +366,7 @@
delPendingStockOut,
batchApproveStockOutRecords,
batchUnapproveStockOutRecords,
+ updateResidualOil,
} from "@/api/inventoryManagement/stockOut.js";
import {
findAllQualifiedStockOutRecordTypeOptions,
@@ -290,6 +491,40 @@
return "warning";
};
+ // 鍒嗙粍鐖惰〃澶存牱寮忥細鍖哄垎鍚勫垎绫�
+ const groupHeaderStyle = {
+ background: "#e8f3ff",
+ color: "#1d6fd8",
+ fontWeight: "700",
+ fontSize: "13px",
+ };
+
+ // 鐓х墖瀛楁鍚庣瀛樼殑鏄� JSON 瀛楃涓诧紝鍑哄弬闇�鑷瑙f瀽
+ const toPhotoArray = value => {
+ if (!value) {
+ return [];
+ }
+ if (Array.isArray(value)) {
+ return value;
+ }
+ if (typeof value === "string") {
+ try {
+ const parsed = JSON.parse(value);
+ return Array.isArray(parsed) ? parsed : [];
+ } catch (e) {
+ return [];
+ }
+ }
+ return [];
+ };
+
+ const getPhotoUrl = photo =>
+ typeof photo === "string" ? photo : photo?.url || photo?.previewURL || "";
+
+ // 鍥剧墖鍒楋細鍙栧叏閮ㄥ湴鍧�鐢ㄤ簬棰勮
+ const getPhotoUrls = photos =>
+ toPhotoArray(photos).map(getPhotoUrl).filter(Boolean);
+
// 鑾峰彇鏉ユ簮绫诲瀷閫夐」
const fetchStockRecordTypeOptions = () => {
if (props.type === "0") {
@@ -349,6 +584,54 @@
const expandedRowKeys = ref([]);
+ // 娈嬫补褰曞叆锛氬彧瀵归攢鍞被鍑哄簱寮�鏀撅紙娈嬫补閾惧彧瀵� outCategory='閿�鍞�' 鐢熸晥锛�
+ const residualDialogVisible = ref(false);
+ const residualForm = ref({
+ id: null,
+ outboundBatches: "",
+ truckPlateNo: "",
+ stockOutNum: null,
+ residualOil: null,
+ });
+
+ const isSalesOutRecord = row => row?.outCategory === "閿�鍞�";
+
+ const openResidualOil = row => {
+ residualForm.value = {
+ id: row.id,
+ outboundBatches: row.outboundBatches || "",
+ truckPlateNo: row.truckPlateNo || "",
+ stockOutNum: row.stockOutNum ?? null,
+ residualOil: row.residualOil ?? null,
+ };
+ residualDialogVisible.value = true;
+ };
+
+ const submitResidualOil = () => {
+ const { id, residualOil, stockOutNum } = residualForm.value;
+ if (residualOil === null || residualOil === undefined || residualOil === "") {
+ proxy.$modal.msgWarning("璇疯緭鍏ュ墿浣欐补閲�");
+ return;
+ }
+ if (Number(residualOil) < 0) {
+ proxy.$modal.msgWarning("鍓╀綑娌归噺涓嶈兘灏忎簬0");
+ return;
+ }
+ if (
+ stockOutNum !== null &&
+ stockOutNum !== undefined &&
+ Number(residualOil) > Number(stockOutNum)
+ ) {
+ proxy.$modal.msgWarning("鍓╀綑娌归噺涓嶈兘澶т簬鍑哄簱鏁伴噺");
+ return;
+ }
+ updateResidualOil(id, { residualOil }).then(() => {
+ proxy.$modal.msgSuccess("褰曞叆鎴愬姛");
+ residualDialogVisible.value = false;
+ getList();
+ });
+ };
+
const handleBatchApprove = () => {
if (selectedRows.value.length === 0) {
proxy.$modal.msgWarning("璇烽�夋嫨鏁版嵁");
diff --git a/src/views/inventoryManagement/issueManagement/index.vue b/src/views/inventoryManagement/issueManagement/index.vue
index 2bba7d0..f89648f 100644
--- a/src/views/inventoryManagement/issueManagement/index.vue
+++ b/src/views/inventoryManagement/issueManagement/index.vue
@@ -53,8 +53,15 @@
</div>
<el-dialog v-model="dialogFormVisible" :title="'鏂板鍑哄簱'" width="40%" @close="closeDia">
<el-form :model="form" label-width="140px" label-position="top" :rules="rules" ref="formRef">
- <el-form-item label="鍑哄簱鏁伴噺锛�" prop="salesContractNo">
+ <el-form-item label="鍑哄簱鏁伴噺锛�" prop="inboundQuantity">
<el-input-number :step="0.01" :min="0" style="width: 100%" v-model="form.inboundQuantity" placeholder="璇疯緭鍏�" clearable />
+ </el-form-item>
+ <el-form-item label="鍑哄簱鎵规锛�" prop="batchNo">
+ <el-select v-model="form.batchNo" placeholder="璇烽�夋嫨鍑哄簱鎵规" clearable filterable
+ v-loading="batchLoading" style="width: 100%">
+ <el-option v-for="item in batchOptions" :key="item.batchNo"
+ :label="`${item.batchNo}锛堝彲鐢� ${item.availableQuantity}锛塦" :value="item.batchNo" />
+ </el-select>
</el-form-item>
<el-form-item label="鍑哄簱鏃ユ湡锛�" prop="projectName">
<el-date-picker style="width: 100%" v-model="form.inboundTime" value-format="YYYY-MM-DD" format="YYYY-MM-DD"
@@ -89,6 +96,7 @@
getStockManagePage,
delStockManage,
stockOut,
+ getBatchNoList,
} from "@/api/inventoryManagement/stockManage.js";
const userStore = useUserStore()
@@ -121,6 +129,7 @@
rules: {
inboundTime: [{ required: true, message: "璇烽�夋嫨", trigger: "change" }],
inboundQuantity: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
+ batchNo: [{ required: true, message: "璇烽�夋嫨鍑哄簱鎵规", trigger: "change" }],
nickname: [{ required: true, message: "璇烽�夋嫨", trigger: "change" }]
}
})
@@ -179,6 +188,10 @@
const currentRowNum = ref(0)
const salesLedgerProductId = ref(null);
+const currentRowProductModelId = ref(null)
+// 鍑哄簱鎵规鍙�夐」锛堝彲鐢ㄩ噺 = 搴撳瓨閲� - 閿佸畾鏁伴噺锛�
+const batchOptions = ref([])
+const batchLoading = ref(false)
// 鎵撳紑寮规
const openForm = async (row) => {
@@ -186,15 +199,18 @@
currentRowId.value = row.id
currentRowNum.value = row.inboundNum0
salesLedgerProductId.value = row.salesLedgerProductId
+ currentRowProductModelId.value = row.productModelId
form.value = {}
// 鍒濆鍖栬〃鍗曟暟鎹�
form.value = {
productrecordId: '',
inboundQuantity: '', // 鍑哄簱鏁伴噺娓呯┖
+ batchNo: '', // 鍑哄簱鎵规娓呯┖
inboundTime: getCurrentDate(), // 榛樿褰撳墠鏃ユ湡
nickName: '', // 榛樿褰撳墠鐢ㄦ埛
}
console.log('form',form.value)
+ loadBatchOptions()
// 鍔犺浇鐢ㄦ埛鍒楄〃
try {
const userLists = await userListNoPageByTenantId()
@@ -204,17 +220,47 @@
}
}
+// 鍔犺浇瑙勬牸鍨嬪彿涓嬬殑鍙敤鎵规
+const loadBatchOptions = async () => {
+ batchOptions.value = []
+ if (!currentRowProductModelId.value) {
+ proxy.$modal.msgWarning('褰撳墠璁板綍缂哄皯瑙勬牸鍨嬪彿锛屾棤娉曟煡璇㈠嚭搴撴壒娆�')
+ return
+ }
+ batchLoading.value = true
+ try {
+ const res = await getBatchNoList(currentRowProductModelId.value)
+ batchOptions.value = res.data || []
+ } catch (error) {
+ console.error('鏌ヨ鍑哄簱鎵规澶辫触:', error)
+ } finally {
+ batchLoading.value = false
+ }
+}
+
// 鎻愪氦琛ㄥ崟
const submitForm = () => {
let num = Number(form.value.inboundQuantity)
if(num <= 0 || num > currentRowNum.value){
return proxy.$modal.msgWarning("璇峰~鍏ユ湁鏁堟暟瀛�")
}
+ // 搴撳瓨鎸夋壒娆$鐞嗐�佹棤鍏堣繘鍏堝嚭锛屾暟閲忎笉鑳借秴杩囨墍閫夋壒娆$殑鍙敤閲�
+ const selectedBatch = batchOptions.value.find(
+ item => item.batchNo === form.value.batchNo
+ )
+ if (selectedBatch && num > Number(selectedBatch.availableQuantity)) {
+ return proxy.$modal.msgWarning(
+ `鍑哄簱鏁伴噺涓嶈兘瓒呰繃鎵规銆�${selectedBatch.batchNo}銆嶇殑鍙敤閲� ${selectedBatch.availableQuantity}`
+ )
+ }
proxy.$refs["formRef"].validate(valid => {
if (valid && currentRowId.value) {
const outData = {
id: currentRowId.value, // 鍘熷璁板綍ID
+ type: 2, // 2=閿�鍞嚭搴擄紝鍑哄簱鍗宠嚜鍔ㄧ敓鎴愬彂璐у彴璐�
salesLedgerProductId: salesLedgerProductId.value,
+ productModelId: currentRowProductModelId.value,
+ batchNo: form.value.batchNo, // 鍑哄簱鎵规锛堥攢鍞嚭搴撳繀濉級
quantity: form.value.inboundQuantity, // 鍑哄簱鏁伴噺
time: form.value.inboundTime, // 鍑哄簱鏃堕棿
userId: form.value.nickName // 鎿嶄綔浜�
diff --git a/src/views/inventoryManagement/receiptManagement/Record.vue b/src/views/inventoryManagement/receiptManagement/Record.vue
index 0a01def..d52ffaf 100644
--- a/src/views/inventoryManagement/receiptManagement/Record.vue
+++ b/src/views/inventoryManagement/receiptManagement/Record.vue
@@ -18,7 +18,7 @@
</el-form-item>
</el-col>
<el-col :span="4">
- <el-form-item label="浜у搧澶х被"
+ <el-form-item label="娌瑰搧"
prop="productName">
<el-input v-model="searchForm.productName"
style="width: 240px"
@@ -103,56 +103,197 @@
label="搴忓彿"
type="index"
width="60" />
- <el-table-column label="鍏ュ簱鎵规"
- prop="inboundBatches"
- width="200"
- show-overflow-tooltip />
- <el-table-column label="鍏ュ簱鏃堕棿"
- prop="createTime"
- show-overflow-tooltip />
- <el-table-column label="浜у搧澶х被"
- prop="productName"
- show-overflow-tooltip />
- <el-table-column label="瑙勬牸鍨嬪彿"
- prop="model"
- show-overflow-tooltip />
- <el-table-column label="鎵瑰彿"
- prop="batchNo"
- show-overflow-tooltip />
- <el-table-column label="鍗曚綅"
- prop="unit"
- show-overflow-tooltip />
- <el-table-column label="鍏ュ簱鏁伴噺"
- prop="stockInNum"
- show-overflow-tooltip />
- <el-table-column label="鍏ュ簱浜�"
- prop="createBy"
- show-overflow-tooltip />
- <el-table-column label="鏉ユ簮"
- prop="recordType"
- show-overflow-tooltip>
- <template #default="scope">
- {{ getRecordType(scope.row.recordType) }}
- </template>
+ <el-table-column label="鍏ュ簱淇℃伅"
+ align="center"
+ :header-cell-style="groupHeaderStyle">
+ <el-table-column label="鍏ュ簱鎵规"
+ prop="inboundBatches"
+ min-width="150"
+ show-overflow-tooltip />
+ <el-table-column label="鍏ュ簱鏃堕棿"
+ prop="inboundTime"
+ min-width="160"
+ show-overflow-tooltip />
+ <el-table-column label="鍏ュ簱绫诲埆"
+ prop="inboundCategory"
+ min-width="100"
+ show-overflow-tooltip />
+ <el-table-column label="娌瑰簱"
+ prop="oilDepotName"
+ min-width="130"
+ show-overflow-tooltip />
+ <el-table-column label="鍌ㄧ綈鍙�"
+ prop="tankNo"
+ min-width="90"
+ show-overflow-tooltip />
+ <el-table-column label="娌瑰搧"
+ prop="productName"
+ min-width="90"
+ show-overflow-tooltip />
+ <el-table-column label="鍘熷巶鏁伴噺(鍚�)"
+ prop="originalQuantity"
+ min-width="110"
+ show-overflow-tooltip />
+ <el-table-column label="瑙勬牸鍨嬪彿"
+ prop="model"
+ min-width="110"
+ show-overflow-tooltip />
+ <el-table-column label="鎵瑰彿"
+ prop="batchNo"
+ min-width="140"
+ show-overflow-tooltip />
+ <el-table-column label="鍗曚綅"
+ prop="unit"
+ min-width="70"
+ show-overflow-tooltip />
+ <el-table-column label="鍏ュ簱鏁伴噺"
+ prop="stockInNum"
+ min-width="100"
+ show-overflow-tooltip />
+ <el-table-column label="鍏ュ簱浜�"
+ prop="createBy"
+ min-width="90"
+ show-overflow-tooltip />
+ <el-table-column label="鏉ユ簮"
+ prop="recordType"
+ min-width="90"
+ show-overflow-tooltip>
+ <template #default="scope">
+ {{ getRecordType(scope.row.recordType) }}
+ </template>
+ </el-table-column>
+ <el-table-column v-if="showSourceOrderNoColumn"
+ label="婧愬崟鍙�"
+ min-width="150"
+ prop="sourceOrderNo"
+ show-overflow-tooltip>
+ <template #default="scope">
+ {{ formatSourceOrderNo(scope.row?.sourceOrderNo) }}
+ </template>
+ </el-table-column>
+ <el-table-column label="瀹℃壒鐘舵��"
+ prop="approvalStatus"
+ min-width="100"
+ show-overflow-tooltip>
+ <template #default="scope">
+ <el-tag :type="getApprovalStatusTagType(scope.row.approvalStatus)"
+ size="small">
+ {{ getApprovalStatusLabel(scope.row.approvalStatus) }}
+ </el-tag>
+ </template>
+ </el-table-column>
</el-table-column>
- <el-table-column v-if="showSourceOrderNoColumn"
- label="婧愬崟鍙�"
- width="150"
- prop="sourceOrderNo"
- show-overflow-tooltip>
- <template #default="scope">
- {{ formatSourceOrderNo(scope.row?.sourceOrderNo) }}
- </template>
+ <el-table-column label="瀹㈡埛淇℃伅"
+ align="center"
+ :header-cell-style="groupHeaderStyle">
+ <el-table-column label="瀹㈡埛鍚嶇О"
+ prop="customerName"
+ min-width="160"
+ show-overflow-tooltip />
+ <el-table-column label="瀹㈡埛瀵规帴浜�"
+ prop="contactPerson"
+ min-width="100"
+ show-overflow-tooltip />
+ <el-table-column label="瀵规帴浜虹數璇�"
+ prop="contactPhone"
+ min-width="120"
+ show-overflow-tooltip />
</el-table-column>
- <el-table-column label="瀹℃壒鐘舵��"
- prop="approvalStatus"
- show-overflow-tooltip>
- <template #default="scope">
- <el-tag :type="getApprovalStatusTagType(scope.row.approvalStatus)"
- size="small">
- {{ getApprovalStatusLabel(scope.row.approvalStatus) }}
- </el-tag>
- </template>
+ <el-table-column label="鐗╂祦淇℃伅"
+ align="center"
+ :header-cell-style="groupHeaderStyle">
+ <el-table-column label="鍙告満濮撳悕"
+ prop="driverName"
+ min-width="100"
+ show-overflow-tooltip />
+ <el-table-column label="鍙告満鐢佃瘽"
+ prop="driverPhone"
+ min-width="120"
+ show-overflow-tooltip />
+ <el-table-column label="鎶艰繍鍛樺鍚�"
+ prop="escortName"
+ min-width="100"
+ show-overflow-tooltip />
+ <el-table-column label="鎶艰繍鍛樼數璇�"
+ prop="escortPhone"
+ min-width="120"
+ show-overflow-tooltip />
+ <el-table-column label="杞︾墝鍙�"
+ prop="truckPlateNo"
+ min-width="110"
+ show-overflow-tooltip />
+ <el-table-column label="杞︽寕鐗屽彿"
+ prop="trailerPlateNo"
+ min-width="110"
+ show-overflow-tooltip />
+ </el-table-column>
+ <el-table-column label="鍗告补鎿嶄綔淇℃伅"
+ align="center"
+ :header-cell-style="groupHeaderStyle">
+ <el-table-column label="杞︿綋/閾呭皝妫�楠�"
+ prop="tankInspectionStatus"
+ min-width="120"
+ show-overflow-tooltip />
+ <el-table-column label="鍗歌溅楣や綅"
+ prop="unloadingCranePosition"
+ min-width="100"
+ show-overflow-tooltip />
+ <el-table-column label="瀵嗗害"
+ prop="density"
+ min-width="80"
+ show-overflow-tooltip />
+ <el-table-column label="鍗告补鎷嶇収"
+ prop="unloadPhotos"
+ align="center"
+ min-width="90">
+ <template #default="scope">
+ <el-image v-if="toPhotoArray(scope.row.unloadPhotos).length"
+ :src="getPhotoUrl(toPhotoArray(scope.row.unloadPhotos)[0])"
+ :preview-src-list="getPhotoUrls(scope.row.unloadPhotos)"
+ preview-teleported
+ fit="cover"
+ style="width: 40px; height: 40px; border-radius: 4px" />
+ <span v-else>--</span>
+ </template>
+ </el-table-column>
+ </el-table-column>
+ <el-table-column label="杩囩鏁版嵁"
+ align="center"
+ :header-cell-style="groupHeaderStyle">
+ <el-table-column label="姣涢噸(鍚�)"
+ prop="grossWeight"
+ min-width="90"
+ show-overflow-tooltip />
+ <el-table-column label="鐨噸(鍚�)"
+ prop="tareWeight"
+ min-width="90"
+ show-overflow-tooltip />
+ <el-table-column label="鍑�閲�(鍚�)"
+ prop="netWeight"
+ min-width="90"
+ show-overflow-tooltip />
+ <el-table-column label="浜忛噺(鍚�)"
+ prop="lossQuantity"
+ min-width="90"
+ show-overflow-tooltip />
+ <el-table-column label="瀹為檯鍏ュ簱浣撶Н(鍗�)"
+ prop="actualVolume"
+ min-width="130"
+ show-overflow-tooltip />
+ <el-table-column label="纾呭崟"
+ prop="poundPhotos"
+ align="center"
+ min-width="90">
+ <template #default="scope">
+ <el-image v-if="toPhotoArray(scope.row.poundPhotos).length"
+ :src="getPhotoUrl(toPhotoArray(scope.row.poundPhotos)[0])"
+ :preview-src-list="getPhotoUrls(scope.row.poundPhotos)"
+ preview-teleported
+ fit="cover"
+ style="width: 40px; height: 40px; border-radius: 4px" />
+ <span v-else>--</span>
+ </template>
+ </el-table-column>
</el-table-column>
</el-table>
<pagination v-show="total > 0"
@@ -356,6 +497,40 @@
});
};
+ // 鍒嗙粍鐖惰〃澶存牱寮忥細鍖哄垎鍚勫垎绫�
+ const groupHeaderStyle = {
+ background: "#e8f3ff",
+ color: "#1d6fd8",
+ fontWeight: "700",
+ fontSize: "13px",
+ };
+
+ // 鐓х墖瀛楁鍚庣瀛樼殑鏄� JSON 瀛楃涓诧紝鍑哄弬闇�鑷瑙f瀽
+ const toPhotoArray = value => {
+ if (!value) {
+ return [];
+ }
+ if (Array.isArray(value)) {
+ return value;
+ }
+ if (typeof value === "string") {
+ try {
+ const parsed = JSON.parse(value);
+ return Array.isArray(parsed) ? parsed : [];
+ } catch (e) {
+ return [];
+ }
+ }
+ return [];
+ };
+
+ const getPhotoUrl = photo =>
+ typeof photo === "string" ? photo : photo?.url || photo?.previewURL || "";
+
+ // 鍥剧墖鍒楋細鍙栧叏閮ㄥ湴鍧�鐢ㄤ簬棰勮
+ const getPhotoUrls = photos =>
+ toPhotoArray(photos).map(getPhotoUrl).filter(Boolean);
+
// 鑾峰彇鏉ユ簮绫诲瀷閫夐」
const fetchStockRecordTypeOptions = () => {
if (props.type === "0") {
diff --git a/src/views/inventoryManagement/stockManagement/BatchNoQtyDetail.vue b/src/views/inventoryManagement/stockManagement/BatchNoQtyDetail.vue
index c131ec2..8be2811 100644
--- a/src/views/inventoryManagement/stockManagement/BatchNoQtyDetail.vue
+++ b/src/views/inventoryManagement/stockManagement/BatchNoQtyDetail.vue
@@ -21,7 +21,7 @@
height="100%"
>
<el-table-column
- label="浜у搧鍚嶇О"
+ label="娌瑰搧"
prop="productName"
show-overflow-tooltip
/>
@@ -34,18 +34,8 @@
show-overflow-tooltip
/>
<el-table-column
- label="涓嶅悎鏍煎簱瀛樻暟閲�"
- prop="unQualifiedQuantity"
- show-overflow-tooltip
- />
- <el-table-column
label="鍚堟牸鍐荤粨鏁伴噺"
prop="qualifiedLockedQuantity"
- show-overflow-tooltip
- />
- <el-table-column
- label="涓嶅悎鏍煎喕缁撴暟閲�"
- prop="unQualifiedLockedQuantity"
show-overflow-tooltip
/>
<el-table-column
@@ -111,7 +101,7 @@
<el-table-column label="鍏ュ簱淇℃伅"
align="center"
:header-cell-style="groupHeaderStyle">
- <el-table-column label="娌瑰搧" prop="oilProduct" min-width="90" show-overflow-tooltip />
+ <el-table-column label="娌瑰搧" prop="productName" min-width="90" show-overflow-tooltip />
<el-table-column label="鍏ュ簱鍚嶇О" prop="oilDepotName" min-width="130" show-overflow-tooltip />
<el-table-column label="鍏ュ簱绫诲埆" prop="inboundCategory" min-width="100" show-overflow-tooltip />
<el-table-column label="鍌ㄧ綈鍙�" prop="tankNo" min-width="100" show-overflow-tooltip />
@@ -191,7 +181,7 @@
<el-table-column label="鍌ㄥ簱鍚嶇О" prop="outOilDepotName" min-width="130" show-overflow-tooltip />
<el-table-column label="鍌ㄧ綈鍙�" prop="outTankNo" min-width="100" show-overflow-tooltip />
<el-table-column label="鍑哄簱鏃ユ湡" prop="outDate" min-width="110" show-overflow-tooltip />
- <el-table-column label="娌瑰搧" prop="outOilProduct" min-width="90" show-overflow-tooltip />
+ <el-table-column label="娌瑰搧" prop="productName" min-width="90" show-overflow-tooltip />
<el-table-column label="鍑哄簱鍚ㄤ綅(鍚�)" prop="outTonnage" min-width="110" show-overflow-tooltip />
<el-table-column label="瀹㈡埛鍚嶇О" prop="outCustomerName" min-width="120" show-overflow-tooltip />
<el-table-column label="瀹㈡埛瀵规帴浜�" prop="outContactPerson" min-width="100" show-overflow-tooltip />
diff --git a/src/views/inventoryManagement/stockManagement/OilStockInDialog.vue b/src/views/inventoryManagement/stockManagement/OilStockInDialog.vue
index 8433960..722db7e 100644
--- a/src/views/inventoryManagement/stockManagement/OilStockInDialog.vue
+++ b/src/views/inventoryManagement/stockManagement/OilStockInDialog.vue
@@ -151,22 +151,10 @@
<el-col :span="12">
<el-form-item label="娌瑰搧"
prop="oilProduct"
- :rules="isOilProduct ? [
- {
- required: true,
- message: '璇烽�夋嫨娌瑰搧',
- trigger: 'change',
- }
- ] : []">
- <el-input v-if="isOilProduct"
- :model-value="formState.oilProduct"
+ :rules="[{ required: true, message: '璇烽�夋嫨娌瑰搧', trigger: 'change' }]">
+ <el-input :model-value="formState.oilProduct"
readonly
placeholder="--" />
- <el-select v-else
- v-model="formState.oilProduct"
- placeholder="璇烽�夋嫨"
- disabled
- style="width: 100%" />
</el-form-item>
</el-col>
<el-col :span="12">
@@ -461,7 +449,6 @@
import { allOilDepot } from "@/api/basicData/oilDepot.js";
import { allTankInfo } from "@/api/basicData/tankInfo.js";
import { listCustomer } from "@/api/basicData/customer.js";
- import { productTreeList } from "@/api/basicData/product.js";
const props = defineProps({
visible: {
@@ -507,8 +494,6 @@
const oilDepotList = ref([]);
const tankList = ref([]);
const customerList = ref([]);
- const oilProductOptions = ref([]);
-
const isShow = computed({
get() {
return props.visible;
@@ -517,32 +502,6 @@
emit("update:visible", val);
},
});
-
- // 鍦ㄤ骇鍝佹爲涓煡鎵�"娌瑰搧"鍒嗙被鑺傜偣
- const findOilNode = nodes => {
- for (const node of nodes || []) {
- if (node.productName === "娌瑰搧") {
- return node;
- }
- if (node.children && node.children.length) {
- const found = findOilNode(node.children);
- if (found) {
- return found;
- }
- }
- }
- return null;
- };
-
- // 鎵�閫夊晢鍝佸悕绉板懡涓殑娌瑰搧閫夐」锛堟补鍝佽妭鐐瑰瓙椤癸紝鏉ヨ嚜浜у搧缁存姢妯″潡锛�
- const matchedOilOption = computed(() =>
- oilProductOptions.value.find(
- item => item.productName === formState.value.productName
- )
- );
-
- // 鍙湁閫変腑娌瑰搧瀛楁閫夐」閲屾湁鐨勫叿浣撴补鍝佹墠鍚敤娌瑰搧瀛楁锛�"娌瑰搧"澶х被鏈韩涓嶅惎鐢�
- const isOilProduct = computed(() => !!matchedOilOption.value);
// 鍑�閲�(鍚�) = 姣涢噸 - 鐨噸
const netWeight = computed(() => {
@@ -609,10 +568,9 @@
formState.value.productModelName = product.model;
formState.value.productModelId = product.id;
formState.value.unit = product.unit;
- // 娌瑰搧閫夐」涓庡晢鍝佸悕绉板悓姝ワ細鍛戒腑娌瑰搧閫夐」鍒欒嚜鍔ㄥ甫鍑鸿娌瑰搧锛屾湭鍛戒腑鍒欐竻绌哄苟绂佺敤
- formState.value.oilProduct = matchedOilOption.value
- ? product.productName
- : undefined;
+ // 鏈鎴枫�屾补鍝併�嶅氨鏄骇鍝佸悕绉帮紙搴撳瓨鏄庣粏/鍑哄簱鍙拌处鐨勬补鍝佸垪缁戠殑閮芥槸 productName锛夛紝
+ // 鐩存帴鍙栨墍閫変骇鍝佸悕锛屼笉鍐嶅幓浜у搧鏍戦噷姣斿锛岄伩鍏嶆瘮瀵逛笉涓婃椂娌瑰搧鏍忕┖鐧�
+ formState.value.oilProduct = product.productName || undefined;
showProductSelectDialog.value = false;
// 瑙﹀彂琛ㄥ崟楠岃瘉鏇存柊
proxy.$refs["formRef"]?.validateField("productModelId");
@@ -652,10 +610,6 @@
});
listCustomer({ current: 1, size: 1000, type: 0 }).then(res => {
customerList.value = res?.data?.records || [];
- });
- productTreeList().then(res => {
- const oilNode = findOilNode(res);
- oilProductOptions.value = oilNode?.children || [];
});
});
</script>
diff --git a/src/views/inventoryManagement/stockManagement/OilStockOutDialog.vue b/src/views/inventoryManagement/stockManagement/OilStockOutDialog.vue
index c3a7865..0b6244a 100644
--- a/src/views/inventoryManagement/stockManagement/OilStockOutDialog.vue
+++ b/src/views/inventoryManagement/stockManagement/OilStockOutDialog.vue
@@ -44,7 +44,7 @@
</el-row>
<div class="form-section-title">鍑哄簱淇℃伅</div>
<el-row :gutter="30">
- <el-col :span="12">
+ <el-col :span="8">
<el-form-item label="鍑哄簱绫诲埆"
prop="outCategory"
:rules="[{ required: true, message: '璇烽�夋嫨鍑哄簱绫诲埆', trigger: 'change' }]">
@@ -61,7 +61,25 @@
</el-select>
</el-form-item>
</el-col>
- <el-col :span="12">
+ <el-col :span="8">
+ <el-form-item label="鍑哄簱鎵规"
+ prop="batchNo"
+ :rules="isSalesOut
+ ? [{ required: true, message: '閿�鍞被鍑哄簱蹇呴』閫夋嫨鍑哄簱鎵规', trigger: 'change' }]
+ : []">
+ <el-select v-model="formState.batchNo"
+ placeholder="璇烽�夋嫨"
+ clearable
+ filterable
+ style="width: 100%">
+ <el-option v-for="item in batchNoOptions"
+ :key="item"
+ :label="item"
+ :value="item" />
+ </el-select>
+ </el-form-item>
+ </el-col>
+ <el-col :span="8">
<el-form-item label="鍌ㄥ簱鍚嶇О"
prop="oilDepotId"
:rules="[{ required: true, message: '璇烽�夋嫨鍌ㄥ簱鍚嶇О', trigger: 'change' }]">
@@ -130,22 +148,10 @@
<el-col :span="12">
<el-form-item label="娌瑰搧"
prop="oilProduct"
- :rules="isOilProduct ? [
- {
- required: true,
- message: '璇烽�夋嫨娌瑰搧',
- trigger: 'change',
- }
- ] : []">
- <el-input v-if="isOilProduct"
- :model-value="formState.oilProduct"
+ :rules="[{ required: true, message: '璇烽�夋嫨娌瑰搧', trigger: 'change' }]">
+ <el-input :model-value="formState.oilProduct"
readonly
placeholder="--" />
- <el-select v-else
- v-model="formState.oilProduct"
- placeholder="璇烽�夋嫨"
- disabled
- style="width: 100%" />
</el-form-item>
</el-col>
<el-col :span="12">
@@ -317,6 +323,45 @@
<div class="form-section-title">鐗╂祦淇℃伅</div>
<el-row :gutter="30">
<el-col :span="12">
+ <el-form-item label="杞﹁締"
+ prop="vehicleId"
+ :rules="isSalesOut
+ ? [{ required: true, message: '閿�鍞被鍑哄簱蹇呴』閫夋嫨杞﹁締', trigger: 'change' }]
+ : []">
+ <el-select v-model="formState.vehicleId"
+ placeholder="璇烽�夋嫨"
+ clearable
+ filterable
+ popper-class="vehicle-select-popper"
+ style="width: 100%"
+ @change="vehicleChange">
+ <el-option v-for="item in vehicleList"
+ :key="item.id"
+ :label="item.plateNumber"
+ :value="item.id">
+ <div class="vehicle-option">
+ <div class="vehicle-option-title">{{ item.plateNumber }}</div>
+ <div class="vehicle-option-line">榛樿鍙告満锛歿{ item.driverName || '--' }}</div>
+ <div class="vehicle-option-line">鍙告満鐢佃瘽锛歿{ item.driverPhone || '--' }}</div>
+ <div class="vehicle-option-line">鏍稿畾杞介噸(鍚�)锛歿{ item.loadCapacity ?? '--' }}</div>
+ </div>
+ </el-option>
+ </el-select>
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item label="杞︽寕鐗屽彿"
+ prop="trailerPlateNo"
+ :rules="[{ required: true, message: '璇疯緭鍏ヨ溅鎸傜墝鍙�', trigger: 'blur' }]">
+ <el-input v-model="formState.trailerPlateNo"
+ :readonly="!!formState.vehicleId"
+ placeholder="璇疯緭鍏�"
+ clearable />
+ </el-form-item>
+ </el-col>
+ </el-row>
+ <el-row :gutter="30">
+ <el-col :span="12">
<el-form-item label="鍙告満濮撳悕"
prop="driverName"
:rules="[{ required: true, message: '璇疯緭鍏ュ徃鏈哄鍚�', trigger: 'blur' }]">
@@ -365,23 +410,16 @@
</el-form-item>
</el-col>
</el-row>
- <el-row :gutter="30">
- <el-col :span="12">
- <el-form-item label="杞︾墝鍙�"
- prop="truckPlateNo"
- :rules="[{ required: true, message: '璇疯緭鍏ヨ溅鐗屽彿', trigger: 'blur' }]">
- <el-input v-model="formState.truckPlateNo"
- placeholder="璇疯緭鍏�"
- clearable />
- </el-form-item>
- </el-col>
- <el-col :span="12">
- <el-form-item label="杞︽寕鐗屽彿"
- prop="trailerPlateNo"
- :rules="[{ required: true, message: '璇疯緭鍏ヨ溅鎸傜墝鍙�', trigger: 'blur' }]">
- <el-input v-model="formState.trailerPlateNo"
- placeholder="璇疯緭鍏�"
- clearable />
+ <el-row :gutter="30"
+ v-if="isSalesOut">
+ <el-col :span="24">
+ <el-form-item label="鍙敤閲�">
+ <div class="available-qty-tip">
+ <span>涓婃鍓╀綑(鍚�)锛歿{ availableQtyText.lastResidualOil }}</span>
+ <span>褰撳墠搴撳瓨(鍚�)锛歿{ availableQtyText.stockQuantity }}</span>
+ <span class="available-qty-strong">鍙敤閲�(鍚�)锛歿{ availableQtyText.availableQty }}</span>
+ <span class="available-qty-rule">鍙敤閲� = 涓婃鍓╀綑 + 褰撳墠搴撳瓨</span>
+ </div>
</el-form-item>
</el-col>
</el-row>
@@ -402,14 +440,18 @@
</template>
<script setup>
- import { ref, computed, getCurrentInstance, onMounted } from "vue";
+ import { ref, computed, watch, getCurrentInstance, onMounted } from "vue";
import ProductSelectDialog from "@/views/basicData/product/ProductSelectDialog.vue";
import ImageUpload from "@/components/AttachmentUpload/image/index.vue";
- import { addOilOutRecordOnly } from "@/api/inventoryManagement/stockInventory.js";
+ import {
+ addOilOutRecordOnly,
+ getStockInventoryBatchNoQty,
+ } from "@/api/inventoryManagement/stockInventory.js";
+ import { getOilOutAvailableQty } from "@/api/inventoryManagement/stockOut.js";
import { allOilDepot } from "@/api/basicData/oilDepot.js";
import { allTankInfo } from "@/api/basicData/tankInfo.js";
+ import { allVehicle } from "@/api/basicData/vehicle.js";
import { listCustomer } from "@/api/basicData/customer.js";
- import { productTreeList } from "@/api/basicData/product.js";
const props = defineProps({
visible: {
@@ -427,6 +469,7 @@
model: "",
unit: "",
outCategory: undefined,
+ batchNo: undefined,
oilDepotId: undefined,
oilDepotName: "",
tankId: undefined,
@@ -448,6 +491,7 @@
driverPhone: "",
escortName: "",
escortPhone: "",
+ vehicleId: undefined,
truckPlateNo: "",
trailerPlateNo: "",
});
@@ -457,34 +501,67 @@
const oilDepotList = ref([]);
const tankList = ref([]);
const customerList = ref([]);
- const oilProductOptions = ref([]);
+ const vehicleList = ref([]);
const showProductSelectDialog = ref(false);
+ // 鍑哄簱鎵规鍊欓�夊彇璇ヨ鏍肩殑鍏ュ簱鎵规
+ const batchNoOptions = ref([]);
+ // 閿�鍞被鍑哄簱鐨勫彲鐢ㄩ噺鎻愮ず锛堜笂娆″墿浣� + 褰撳墠搴撳瓨锛�
+ const availableQty = ref(null);
- // 鍦ㄤ骇鍝佹爲涓煡鎵�"娌瑰搧"鍒嗙被鑺傜偣
- const findOilNode = nodes => {
- for (const node of nodes || []) {
- if (node.productName === "娌瑰搧") {
- return node;
- }
- if (node.children && node.children.length) {
- const found = findOilNode(node.children);
- if (found) {
- return found;
- }
- }
+ const fetchBatchNoOptions = () => {
+ batchNoOptions.value = [];
+ const productModelId = formState.value.productModelId;
+ if (!productModelId) {
+ return;
}
- return null;
+ getStockInventoryBatchNoQty({
+ current: 1,
+ size: 200,
+ productModelId,
+ }).then(res => {
+ const records = res?.data?.records || [];
+ batchNoOptions.value = [
+ ...new Set(records.map(item => item.batchNo).filter(Boolean)),
+ ];
+ });
};
- // 鎵�閫夊晢鍝佸悕绉板懡涓殑娌瑰搧閫夐」锛堟补鍝佽妭鐐瑰瓙椤癸紝鏉ヨ嚜浜у搧缁存姢妯″潡锛�
- const matchedOilOption = computed(() =>
- oilProductOptions.value.find(
- item => item.productName === formState.value.productName
- )
- );
+ // 閿�鍞被鍑哄簱蹇呴』缁欏嚭搴撴壒娆★紙鍚庣鏂板鏃跺嵆鏍¢獙锛屼笖瀹℃壒閫氳繃浼氭寜鎵规鎵e簱瀛橈級
+ const isSalesOut = computed(() => formState.value.outCategory === "閿�鍞�");
- // 鍙湁閫変腑娌瑰搧瀛楁閫夐」閲屾湁鐨勫叿浣撴补鍝佹墠鍚敤娌瑰搧瀛楁锛�"娌瑰搧"澶х被鏈韩涓嶅惎鐢�
- const isOilProduct = computed(() => !!matchedOilOption.value);
+ // 鍙敤閲忔彁绀猴細杞﹁締 + 瑙勬牸锛�+鎵规锛夌‘瀹氬悗鍚戝悗绔彇銆屼笂娆″墿浣� / 褰撳墠搴撳瓨 / 鍙敤閲忋��
+ const fetchAvailableQty = () => {
+ availableQty.value = null;
+ const { vehicleId, productModelId, batchNo } = formState.value;
+ if (!isSalesOut.value || !vehicleId || !productModelId) {
+ return;
+ }
+ getOilOutAvailableQty({ vehicleId, productModelId, batchNo })
+ .then(res => {
+ availableQty.value = res?.data || null;
+ })
+ .catch(() => {
+ availableQty.value = null;
+ });
+ };
+
+ const availableQtyText = computed(() => ({
+ lastResidualOil: availableQty.value?.lastResidualOil ?? "--",
+ stockQuantity: availableQty.value?.stockQuantity ?? "--",
+ availableQty: availableQty.value?.availableQty ?? "--",
+ }));
+
+ watch(
+ () => [
+ formState.value.outCategory,
+ formState.value.vehicleId,
+ formState.value.productModelId,
+ formState.value.batchNo,
+ ],
+ () => {
+ fetchAvailableQty();
+ }
+ );
const isShow = computed({
get() {
@@ -549,10 +626,11 @@
formState.value.model = product.model;
formState.value.productModelId = product.id;
formState.value.unit = product.unit;
- // 娌瑰搧閫夐」涓庡晢鍝佸悕绉板悓姝ワ細鍛戒腑娌瑰搧閫夐」鍒欒嚜鍔ㄥ甫鍑鸿娌瑰搧锛屾湭鍛戒腑鍒欐竻绌哄苟绂佺敤
- formState.value.oilProduct = matchedOilOption.value
- ? product.productName
- : undefined;
+ // 鏈鎴枫�屾补鍝併�嶅氨鏄骇鍝佸悕绉帮紙搴撳瓨鏄庣粏/鍑哄簱鍙拌处鐨勬补鍝佸垪缁戠殑閮芥槸 productName锛夛紝
+ // 鐩存帴鍙栨墍閫変骇鍝佸悕锛屼笉鍐嶅幓浜у搧鏍戦噷姣斿锛岄伩鍏嶆瘮瀵逛笉涓婃椂娌瑰搧鏍忕┖鐧�
+ formState.value.oilProduct = product.productName || undefined;
+ formState.value.batchNo = undefined;
+ fetchBatchNoOptions();
showProductSelectDialog.value = false;
proxy.$refs["formRef"]?.validateField("productModelId");
}
@@ -586,6 +664,21 @@
}
};
+ // 杞﹁締閫夋嫨鏀瑰彉锛氳溅鐗屽彿銆佽溅鎸傜墝鍙蜂笌鍙告満淇℃伅鐢辫溅杈嗕富鏁版嵁甯﹀嚭锛屽嚭搴撴椂鍙告満鍙墜鍔ㄨ鐩�
+ const vehicleChange = id => {
+ const vehicle = vehicleList.value.find(item => item.id === id);
+ if (vehicle) {
+ formState.value.truckPlateNo = vehicle.plateNumber || "";
+ // 杞︽寕鐗屽彿涓庤溅鐗屽彿鏄悓涓�涓墝锛岄�変簡杞﹁締灏卞甫鍑哄苟缃负鍙锛岄伩鍏嶆墜濉嚭涓や釜涓嶄竴鑷寸殑鍊�
+ formState.value.trailerPlateNo = vehicle.plateNumber || "";
+ formState.value.driverName = vehicle.driverName || "";
+ formState.value.driverPhone = vehicle.driverPhone || "";
+ } else {
+ formState.value.truckPlateNo = "";
+ formState.value.trailerPlateNo = "";
+ }
+ };
+
const handleSubmit = () => {
proxy.$refs["formRef"].validate(valid => {
if (valid) {
@@ -615,15 +708,14 @@
allOilDepot().then(res => {
oilDepotList.value = res?.data || [];
});
- productTreeList().then(res => {
- const oilNode = findOilNode(res);
- oilProductOptions.value = oilNode?.children || [];
- });
allTankInfo().then(res => {
tankList.value = res?.data || [];
});
listCustomer({ current: 1, size: 1000, type: 0 }).then(res => {
customerList.value = res?.data?.records || [];
+ });
+ allVehicle().then(res => {
+ vehicleList.value = res?.data || [];
});
});
</script>
@@ -671,6 +763,48 @@
}
.tank-option-line {
+ font-size: 12px;
+ color: #909399;
+ line-height: 1.8;
+ }
+ }
+
+ // 鍙敤閲忔彁绀猴細涓婃鍓╀綑 + 褰撳墠搴撳瓨 = 鍙敤閲�
+ .available-qty-tip {
+ display: flex;
+ flex-wrap: wrap;
+ align-items: center;
+ gap: 6px 20px;
+ width: 100%;
+ padding: 6px 14px;
+ font-size: 13px;
+ color: #303133;
+ background: rgba(64, 158, 255, 0.08);
+ border-left: 3px solid #409eff;
+ border-radius: 3px;
+
+ .available-qty-strong {
+ font-weight: 600;
+ color: #1d6fd8;
+ }
+
+ .available-qty-rule {
+ color: #909399;
+ }
+ }
+
+ // 杞﹁締涓嬫媺閫夐」锛氬皬鍗$墖鏍峰紡
+ .vehicle-option {
+ padding: 8px 10px;
+
+ .vehicle-option-title {
+ font-size: 14px;
+ font-weight: 600;
+ color: #303133;
+ margin-bottom: 4px;
+ }
+
+ .vehicle-option-line {
font-size: 12px;
color: #909399;
line-height: 1.8;
@@ -737,6 +871,26 @@
}
}
+ // 杞﹁締涓嬫媺闈㈡澘锛坆ody 涓嬶紝闇�鍏ㄥ眬鏍峰紡锛�
+ .vehicle-select-popper {
+ .el-select-dropdown__item {
+ height: auto;
+ line-height: normal;
+ padding: 4px 0;
+ }
+
+ .vehicle-option {
+ border: 1px solid #e4e7ed;
+ border-radius: 6px;
+ background-color: #fafbfc;
+
+ &:hover {
+ border-color: #409eff;
+ background-color: #f0f7ff;
+ }
+ }
+ }
+
// 瀹㈡埛鍚嶇О涓嬫媺闈㈡澘锛坆ody 涓嬶紝闇�鍏ㄥ眬鏍峰紡锛�
.customer-select-popper {
.el-select-dropdown__item {
diff --git a/src/views/inventoryManagement/stockManagement/Record.vue b/src/views/inventoryManagement/stockManagement/Record.vue
index bdd00b4..7799ce1 100644
--- a/src/views/inventoryManagement/stockManagement/Record.vue
+++ b/src/views/inventoryManagement/stockManagement/Record.vue
@@ -3,37 +3,34 @@
<div class="search_form mb10">
<el-form ref="searchFormRef"
:model="searchForm"
- class="demo-form-inline">
+ class="demo-form-inline"
+ style="width: 100%">
<el-row :gutter="20">
- <el-col :span="4">
+ <el-col :span="6">
<el-form-item label="浜у搧澶х被"
prop="productName">
<el-input v-model="searchForm.productName"
- style="width: 240px"
placeholder="璇疯緭鍏�"
clearable />
</el-form-item>
</el-col>
- <el-col :span="4">
+ <el-col :span="6">
<el-form-item label="瑙勬牸鍨嬪彿"
prop="model">
<el-input v-model="searchForm.model"
- style="width: 240px"
placeholder="璇疯緭鍏�"
clearable />
</el-form-item>
</el-col>
- <el-col :span="4">
+ <el-col :span="6">
<el-form-item label="鎵瑰彿"
prop="batchNo">
<el-input v-model="searchForm.batchNo"
- style="width: 240px"
placeholder="璇疯緭鍏�"
clearable />
</el-form-item>
</el-col>
- <!-- 鎸夐挳 -->
- <el-col :span="4">
+ <el-col :span="6">
<el-form-item>
<el-button type="primary"
@click="getList">
@@ -46,23 +43,26 @@
</el-col>
</el-row>
</el-form>
- <div style="display: flex; gap: 10px; flex-wrap: nowrap;">
- <el-button type="primary"
- @click="isShowNewModal = true">鏂板搴撳瓨</el-button>
- <el-button type="primary"
- @click="isShowOilInModal = true">鏂板娌瑰搧鍏ュ簱</el-button>
- <el-button type="primary"
- @click="isShowOilOutModal = true">鏂板娌瑰搧鍑哄簱</el-button>
- <el-button type="info"
- plain
- icon="Upload"
- @click="isShowImportModal = true">
- 瀵煎叆搴撳瓨
- </el-button>
- <el-button @click="handleOut">瀵煎嚭</el-button>
- </div>
</div>
<div class="table_list">
+ <div class="actions">
+ <div></div>
+ <div>
+ <el-button type="primary"
+ @click="isShowNewModal = true">鏂板搴撳瓨</el-button>
+ <el-button type="primary"
+ @click="isShowOilInModal = true">鏂板娌瑰搧鍏ュ簱</el-button>
+ <el-button type="primary"
+ @click="isShowOilOutModal = true">鏂板娌瑰搧鍑哄簱</el-button>
+ <el-button type="info"
+ plain
+ icon="Upload"
+ @click="isShowImportModal = true">
+ 瀵煎叆搴撳瓨
+ </el-button>
+ <el-button @click="handleOut">瀵煎嚭</el-button>
+ </div>
+ </div>
<el-table :data="tableData"
border
v-loading="tableLoading"
@@ -71,18 +71,30 @@
:row-key="(row, index) => index"
style="width: 100%"
:row-class-name="tableRowClassName"
- height="calc(100vh - 18.5em)">
+ :span-method="spanMethod"
+ height="calc(100vh - 21em)">
<el-table-column align="center"
type="selection"
+ :selectable="isRowSelectable"
width="55" />
<el-table-column align="center"
label="搴忓彿"
type="index"
+ :index="indexMethod"
width="60" />
<el-table-column label="浜у搧鍚嶇О"
prop="productName"
- min-width="120"
- show-overflow-tooltip />
+ min-width="120">
+ <template #default="scope">
+ <div v-if="scope.row.__isGroup"
+ class="tank-group-band">
+ <span class="tank-group-band__name">鍌ㄧ綈鍙� {{ scope.row.tankNo || "鏈垎閰�" }}</span>
+ <span class="tank-group-band__stat">鍚堟牸 {{ scope.row.qualifiedQuantity }}</span>
+ <span class="tank-group-band__stat">鍐荤粨 {{ scope.row.qualifiedLockedQuantity }}</span>
+ </div>
+ <span v-else>{{ scope.row.productName }}</span>
+ </template>
+ </el-table-column>
<el-table-column label="瑙勬牸鍨嬪彿"
prop="model"
min-width="120"
@@ -95,30 +107,26 @@
prop="batchNo"
min-width="150"
show-overflow-tooltip />
+ <el-table-column label="鍌ㄧ綈鍙�"
+ prop="tankNo"
+ min-width="100"
+ show-overflow-tooltip />
<el-table-column label="鍚堟牸搴撳瓨鏁伴噺"
prop="qualifiedQuantity"
min-width="110"
- show-overflow-tooltip />
- <el-table-column label="涓嶅悎鏍煎簱瀛樻暟閲�"
- prop="unQualifiedQuantity"
- min-width="120"
show-overflow-tooltip />
<el-table-column label="鍚堟牸鍐荤粨鏁伴噺"
prop="qualifiedLockedQuantity"
min-width="110"
show-overflow-tooltip />
- <el-table-column label="涓嶅悎鏍煎喕缁撴暟閲�"
- prop="unQualifiedLockedQuantity"
- min-width="120"
- show-overflow-tooltip />
<el-table-column label="搴撳瓨棰勮鏁伴噺"
prop="warnNum"
min-width="110"
show-overflow-tooltip />
- <el-table-column label="澶囨敞"
- prop="remark"
- min-width="150"
- show-overflow-tooltip />
+<!-- <el-table-column label="澶囨敞"-->
+<!-- prop="remark"-->
+<!-- min-width="150"-->
+<!-- show-overflow-tooltip />-->
<el-table-column label="鏈�杩戞洿鏂版椂闂�"
prop="updateTime"
min-width="160"
@@ -179,7 +187,7 @@
<script setup>
import pagination from "@/components/PIMTable/Pagination.vue";
- import { ref, reactive, toRefs, onMounted, getCurrentInstance } from "vue";
+ import { ref, reactive, toRefs, computed, onMounted, getCurrentInstance } from "vue";
import { ElMessage, ElMessageBox } from "element-plus";
import { getStockInventoryListPageCombined } from "@/api/inventoryManagement/stockInventory.js";
const props = defineProps({
@@ -187,6 +195,10 @@
type: Number,
required: true,
default: 0,
+ },
+ productName: {
+ type: String,
+ default: "",
},
});
@@ -221,6 +233,72 @@
size: 100,
});
const total = ref(0);
+ // 鍌ㄧ綈鍙峰垎绫昏鍗犳弧鏁磋锛屾槑缁嗚姝e父娓叉煋
+ const TABLE_COLUMN_COUNT = 13;
+ const GROUP_ROW_LEAD_COLUMNS = 2; // 閫夋嫨鍒� + 搴忓彿鍒�
+ const isRowSelectable = row => !row?.__isGroup;
+ // 鍙湁鎴愬搧銆佸師鏂欎袱绫讳細鎸夊偍缃愬垎缃愬瓨鏀撅紝鍏朵綑澶х被涓嶅仛鍌ㄧ綈鍙峰垎缁�
+ const TANK_GROUP_CATEGORIES = ["鎴愬搧", "鍘熸枡"];
+ const shouldGroupByTank = computed(() =>
+ TANK_GROUP_CATEGORIES.includes(props.productName)
+ );
+ const sumBy = (rows, key) =>
+ rows.reduce((acc, row) => acc + Number(row[key] || 0), 0);
+ const buildGroupedRows = rows => {
+ const groups = new Map();
+ rows.forEach(row => {
+ const tankNo = row.tankNo || "";
+ if (!groups.has(tankNo)) {
+ groups.set(tankNo, []);
+ }
+ groups.get(tankNo).push(row);
+ });
+ const sortedTankNos = [...groups.keys()].sort((a, b) => {
+ if (!a) return 1;
+ if (!b) return -1;
+ return a.localeCompare(b, "zh-CN", { numeric: true });
+ });
+ return sortedTankNos.flatMap(tankNo => {
+ const groupRows = groups.get(tankNo);
+ return [
+ {
+ __isGroup: true,
+ tankNo,
+ qualifiedQuantity: sumBy(groupRows, "qualifiedQuantity"),
+ qualifiedLockedQuantity: sumBy(groupRows, "qualifiedLockedQuantity"),
+ },
+ ...groupRows,
+ ];
+ });
+ };
+ const spanMethod = ({ row, columnIndex }) => {
+ if (!row?.__isGroup) {
+ return { rowspan: 1, colspan: 1 };
+ }
+ if (columnIndex === 0) {
+ return { rowspan: 1, colspan: GROUP_ROW_LEAD_COLUMNS };
+ }
+ if (columnIndex === GROUP_ROW_LEAD_COLUMNS) {
+ return {
+ rowspan: 1,
+ colspan: TABLE_COLUMN_COUNT - GROUP_ROW_LEAD_COLUMNS,
+ };
+ }
+ return { rowspan: 0, colspan: 0 };
+ };
+ // 搴忓彿鍙鏄庣粏琛岃繛缁紪鍙凤紝鍒嗙被琛岀暀绌�
+ const indexMethod = index => {
+ if (tableData.value[index]?.__isGroup) {
+ return "";
+ }
+ let count = 0;
+ for (let i = 0; i <= index; i++) {
+ if (!tableData.value[i]?.__isGroup) {
+ count += 1;
+ }
+ }
+ return count;
+ };
// 鏄惁鏄剧ず鏂板寮规
const isShowNewModal = ref(false);
// 鏄惁鏄剧ず娌瑰搧鍏ュ簱寮规
@@ -270,7 +348,9 @@
getStockInventoryListPageCombined({ ...searchForm.value, ...page })
.then(res => {
tableLoading.value = false;
- tableData.value = res.data.records;
+ tableData.value = shouldGroupByTank.value
+ ? buildGroupedRows(res.data.records)
+ : res.data.records;
total.value = res.data.total;
// 鏁版嵁鍔犺浇瀹屾垚鍚庢鏌ュ簱瀛�
// checkStockAndCreatePurchase();
@@ -346,6 +426,9 @@
// 琛ㄦ牸琛岀被鍚�
const tableRowClassName = ({ row }) => {
+ if (row?.__isGroup) {
+ return "row-tank-group";
+ }
const stock = Number(row?.qualifiedUnLockedQuantity ?? 0);
const warn = Number(row?.warnNum ?? 0);
if (!Number.isFinite(stock) || !Number.isFinite(warn)) {
@@ -379,6 +462,12 @@
</script>
<style scoped lang="scss">
+ .actions {
+ display: flex;
+ justify-content: space-between;
+ margin-bottom: 12px;
+ }
+
:deep(.row-low-stock td) {
background-color: #fde2e2;
color: #c45656;
@@ -387,4 +476,34 @@
:deep(.row-low-stock:hover > td) {
background-color: #fcd4d4;
}
+
+ :deep(.row-tank-group > td) {
+ background-color: #eef5ff;
+ }
+
+ :deep(.row-tank-group:hover > td) {
+ background-color: #e4eefc;
+ }
+
+ :deep(.tank-group-band) {
+ display: flex;
+ align-items: center;
+ width: 100%;
+ font-size: 13px;
+ line-height: 20px;
+ }
+
+ :deep(.tank-group-band__name) {
+ margin-right: auto;
+ padding-left: 8px;
+ border-left: 3px solid #1d6fd8;
+ font-weight: 700;
+ color: #1d6fd8;
+ }
+
+ :deep(.tank-group-band__stat) {
+ margin-left: 24px;
+ font-weight: 600;
+ color: #3a7bd5;
+ }
</style>
diff --git a/src/views/inventoryManagement/stockManagement/index.vue b/src/views/inventoryManagement/stockManagement/index.vue
index b3aa7ee..21d9827 100644
--- a/src/views/inventoryManagement/stockManagement/index.vue
+++ b/src/views/inventoryManagement/stockManagement/index.vue
@@ -6,7 +6,7 @@
:label="tab.productName"
:name="tab.id"
:key="tab.id">
- <Record :product-id="tab.id" v-if="tab.id === activeTab" />
+ <Record :product-id="tab.id" :product-name="tab.productName" v-if="tab.id === activeTab" />
</el-tab-pane>
</el-tabs>
</div>
diff --git a/src/views/officeProcessAutomation/ApproveManage/approve-list/approveListConstants.js b/src/views/officeProcessAutomation/ApproveManage/approve-list/approveListConstants.js
index e2f1e45..3419f53 100644
--- a/src/views/officeProcessAutomation/ApproveManage/approve-list/approveListConstants.js
+++ b/src/views/officeProcessAutomation/ApproveManage/approve-list/approveListConstants.js
@@ -460,6 +460,7 @@
purchaseContractNumber: row.purchaseContractNumber || "",
quotationNo: row.quotationNo || "",
shippingNo: row.shippingNo || "",
+ salesContractNo: row.salesContractNo || "",
};
}
diff --git a/src/views/officeProcessAutomation/ApproveManage/approve-list/components/ApproveDetailPanel.vue b/src/views/officeProcessAutomation/ApproveManage/approve-list/components/ApproveDetailPanel.vue
index d057f16..807c458 100644
--- a/src/views/officeProcessAutomation/ApproveManage/approve-list/components/ApproveDetailPanel.vue
+++ b/src/views/officeProcessAutomation/ApproveManage/approve-list/components/ApproveDetailPanel.vue
@@ -145,6 +145,53 @@
</div>
</div>
</template>
+
+ <!-- 閿�鍞彴璐﹀鎵硅鎯咃紙鍗栨补 / 浠e偍锛� -->
+ <template v-else-if="row.businessType === 19">
+ <div v-if="detailData && Object.keys(detailData).length" class="sales-ledger-detail">
+ <el-divider content-position="left">閿�鍞彴璐﹁鎯�</el-divider>
+ <el-descriptions :column="2" border>
+ <el-descriptions-item label="閿�鍞悎鍚屽彿">{{ detailData.salesContractNo || "--" }}</el-descriptions-item>
+ <el-descriptions-item label="閿�鍞被鍨�">{{ detailData.ledgerType || "--" }}</el-descriptions-item>
+ <el-descriptions-item label="瀹㈡埛鍚嶇О">{{ detailData.customerName || "--" }}</el-descriptions-item>
+ <el-descriptions-item label="涓氬姟鍛�">{{ detailData.salesman || "--" }}</el-descriptions-item>
+ <el-descriptions-item label="椤圭洰鍚嶇О">{{ detailData.projectName || "--" }}</el-descriptions-item>
+ <el-descriptions-item label="浠樻鏂瑰紡">{{ detailData.paymentMethod || "--" }}</el-descriptions-item>
+ <el-descriptions-item v-if="detailData.ledgerType === '浠e偍'"
+ label="娌瑰簱">{{ detailData.oilDepotName || "--" }}</el-descriptions-item>
+ <el-descriptions-item v-if="detailData.ledgerType === '浠e偍'"
+ label="鍌ㄧ綈鍙�">{{ detailData.tankNo || "--" }}</el-descriptions-item>
+ <el-descriptions-item label="鍚堝悓閲戦" :span="2">
+ <span style="font-size: 18px; color: #e6a23c; font-weight: bold;">
+ 楼{{ Number(detailData.contractAmount ?? 0).toFixed(2) }}
+ </span>
+ <span v-if="detailData.exceedsTrustFund"
+ class="reject-text"
+ style="margin-left: 12px;">
+ 宸茶秴鍑鸿瀹㈡埛淇℃墭鍩洪噾{{ detailData.trustFund != null ? `锛堜俊鎵樺熀閲戯細${detailData.trustFund}锛塦 : "" }}
+ </span>
+ </el-descriptions-item>
+ </el-descriptions>
+ <div v-if="detailData.productData && detailData.productData.length"
+ style="margin-top: 20px;">
+ <h4>浜у搧鏄庣粏</h4>
+ <el-table :data="detailData.productData"
+ border
+ style="width: 100%">
+ <el-table-column prop="productCategory" label="浜у搧澶х被" />
+ <el-table-column prop="specificationModel" label="瑙勬牸鍨嬪彿" />
+ <el-table-column prop="unit" label="鍗曚綅" />
+ <el-table-column prop="quantity" label="鏁伴噺" />
+ <el-table-column prop="taxInclusiveUnitPrice" label="鍚◣鍗曚环">
+ <template #default="scope">楼{{ Number(scope.row.taxInclusiveUnitPrice ?? 0).toFixed(2) }}</template>
+ </el-table-column>
+ <el-table-column prop="taxInclusiveTotalPrice" label="鍚◣鎬讳环">
+ <template #default="scope">楼{{ Number(scope.row.taxInclusiveTotalPrice ?? 0).toFixed(2) }}</template>
+ </el-table-column>
+ </el-table>
+ </div>
+ </div>
+ </template>
</div>
<div v-if="attachmentList.length"
class="detail-block">
@@ -199,9 +246,9 @@
const formResolved = computed(() => resolveInstanceFormFields(props.row));
- // 鏄惁涓虹壒娈婂鎵圭被鍨嬶紙閲囪喘銆佸彂璐с�佹姤浠凤級
+ // 鏄惁涓虹壒娈婂鎵圭被鍨嬶紙閲囪喘銆佸彂璐с�佹姤浠枫�侀攢鍞彴璐︼級
const isSpecialApprovalType = computed(() => {
- return [5, 7, 6].includes(props.row.businessType);
+ return [5, 7, 6, 19].includes(props.row.businessType);
});
// 璇︽儏鏁版嵁锛堢洿鎺ヤ娇鐢ㄤ紶鍏ョ殑 detail-data 鍙傛暟锛�
diff --git a/src/views/officeProcessAutomation/ApproveManage/approve-list/index.vue b/src/views/officeProcessAutomation/ApproveManage/approve-list/index.vue
index 4a9cf29..853b4f7 100644
--- a/src/views/officeProcessAutomation/ApproveManage/approve-list/index.vue
+++ b/src/views/officeProcessAutomation/ApproveManage/approve-list/index.vue
@@ -63,6 +63,9 @@
:total="page.total"
@pagination="pagination"
>
+ <template #bizNo="{ row }">
+ <span>{{ businessNoOf(row) || "-" }}</span>
+ </template>
<template #approveType="{ row }">
<span class="approve-type-cell" :style="approvalTypeStyle(row.approvalType)">
{{ approvalTypeLabel(row.approvalType) }}
@@ -361,6 +364,7 @@
onBusinessTypePick,
backToBusinessTypePick,
approvalTypeLabel,
+ businessNoOf,
approvalActionLabel,
searchForm,
tableLoading,
diff --git a/src/views/officeProcessAutomation/ApproveManage/approve-list/useApproveList.js b/src/views/officeProcessAutomation/ApproveManage/approve-list/useApproveList.js
index 737e2de..b413fd8 100644
--- a/src/views/officeProcessAutomation/ApproveManage/approve-list/useApproveList.js
+++ b/src/views/officeProcessAutomation/ApproveManage/approve-list/useApproveList.js
@@ -13,6 +13,7 @@
import { getQuotationList } from "@/api/salesManagement/salesQuotation.js";
import { getPurchaseByCode } from "@/api/procurementManagement/procurementLedger.js";
import { getDeliveryDetailByShippingNo } from "@/api/salesManagement/deliveryLedger.js";
+import { ledgerListPage } from "@/api/salesManagement/salesLedger.js";
import useUserStore from "@/store/modules/user";
import { Search } from "@element-plus/icons-vue";
import { ElMessage, ElMessageBox } from "element-plus";
@@ -120,6 +121,18 @@
return hit?.label || "";
}
+ // 鍒楄〃銆屼笟鍔″崟鍙枫�嶏細鎸変笟鍔$被鍨嬪彇瀵瑰簲鐨勫崟鍙凤紙19=閿�鍞彴璐﹀鎵癸級
+ function businessNoOf(row) {
+ return (
+ row?.salesContractNo ||
+ row?.quotationNo ||
+ row?.purchaseContractNumber ||
+ row?.shippingNo ||
+ row?.bizId ||
+ ""
+ );
+ }
+
function countTemplatesByBusinessType(type) {
return allSubmitTemplates.value.filter((card) => matchBusinessTypeValue(card.businessType, type)).length;
}
@@ -142,6 +155,13 @@
// { label: "鐢宠浜虹紪鍙�", prop: "applicantNo", width: 110 },
{ label: "鍙戣捣浜�", prop: "applicantName", minWidth: 100 },
{ label: "妯℃澘绫诲瀷", prop: "businessName", minWidth: 120 },
+ {
+ label: "涓氬姟鍗曞彿",
+ prop: "businessNo",
+ minWidth: 170,
+ dataType: "slot",
+ slot: "bizNo",
+ },
{
label: "瀹℃壒绫诲瀷",
prop: "approvalType",
@@ -326,13 +346,29 @@
detailData.value = res?.data || res || {};
}
}
-
+
+ // 閿�鍞彴璐﹀鎵癸細鐢ㄥ鎵逛簨鐢卞瓧娈垫壙杞界殑"閿�鍞悎鍚屽彿"鍘绘煡鍙拌处鍒楄〃
+ else if (row.businessType === 19) {
+ detailData.value = await fetchSalesLedgerByContractNo(row?.salesContractNo);
+ }
+
// 鍏朵粬瀹℃壒绫诲瀷
else {
detailData.value = {};
}
-
+
detailDialog.visible = true;
+ }
+
+ async function fetchSalesLedgerByContractNo(salesContractNo) {
+ if (!salesContractNo) return {};
+ // /sales/ledger/listPage 鐩存帴杩斿洖 IPage锛堥《灞� records锛夛紝娌℃湁 code/msg 澶栧眰
+ const res = await ledgerListPage({
+ salesContractNo,
+ pageNum: 1,
+ pageSize: 1,
+ });
+ return res?.records?.[0] || {};
}
async function openApprove(row) {
@@ -373,6 +409,11 @@
const res = await getDeliveryDetailByShippingNo({ shippingNo });
detailData.value = res?.data || res || {};
}
+ }
+
+ // 閿�鍞彴璐﹀鎵癸細鐢ㄥ鎵逛簨鐢卞瓧娈垫壙杞界殑"閿�鍞悎鍚屽彿"鍘绘煡鍙拌处鍒楄〃
+ else if (row.businessType === 19) {
+ detailData.value = await fetchSalesLedgerByContractNo(row?.salesContractNo);
}
// 鍏朵粬瀹℃壒绫诲瀷
@@ -686,6 +727,7 @@
selectedBusinessType,
selectedBusinessTypeLabel,
businessTypeLabel,
+ businessNoOf,
countTemplatesByBusinessType,
submitTemplatesLoading,
handleQuery,
diff --git a/src/views/outsourcingManagement/order/index.vue b/src/views/outsourcingManagement/order/index.vue
new file mode 100644
index 0000000..1c68210
--- /dev/null
+++ b/src/views/outsourcingManagement/order/index.vue
@@ -0,0 +1,952 @@
+<template>
+ <div class="app-container">
+ <div class="search_form">
+ <el-form :model="searchForm"
+ :inline="true">
+ <el-form-item label="濮斿璁㈠崟鍙凤細">
+ <el-input v-model="searchForm.orderNo"
+ placeholder="璇疯緭鍏�"
+ clearable
+ prefix-icon="Search"
+ style="width: 180px"
+ @change="handleQuery" />
+ </el-form-item>
+ <el-form-item label="鍚堜綔鍟嗭細">
+ <el-select v-model="searchForm.partnerId"
+ placeholder="璇烽�夋嫨"
+ clearable
+ filterable
+ style="width: 200px"
+ @change="handleQuery">
+ <el-option v-for="item in partnerOptions"
+ :key="item.id"
+ :label="item.partnerName"
+ :value="item.id" />
+ </el-select>
+ </el-form-item>
+ <el-form-item label="椤圭洰鍚嶇О锛�">
+ <el-input v-model="searchForm.projectName"
+ placeholder="璇疯緭鍏�"
+ clearable
+ style="width: 160px"
+ @change="handleQuery" />
+ </el-form-item>
+ <el-form-item label="鐘舵�侊細">
+ <el-select v-model="searchForm.status"
+ placeholder="璇烽�夋嫨"
+ clearable
+ style="width: 140px"
+ @change="handleQuery">
+ <el-option v-for="item in statusOptions"
+ :key="item.value"
+ :label="item.label"
+ :value="item.value" />
+ </el-select>
+ </el-form-item>
+ <el-form-item label="鐢宠鏃ユ湡锛�">
+ <el-date-picker v-model="dateRange"
+ type="daterange"
+ value-format="YYYY-MM-DD"
+ format="YYYY-MM-DD"
+ range-separator="鑷�"
+ start-placeholder="寮�濮嬫棩鏈�"
+ end-placeholder="缁撴潫鏃ユ湡"
+ @change="handleQuery" />
+ </el-form-item>
+ <el-form-item>
+ <el-button type="primary"
+ @click="handleQuery">鎼滅储</el-button>
+ <el-button @click="resetSearch">閲嶇疆</el-button>
+ </el-form-item>
+ </el-form>
+ </div>
+ <div class="table_list">
+ <div class="actions">
+ <div></div>
+ <div>
+ <el-button type="primary"
+ @click="openForm('add')">鏂板濮斿璁㈠崟</el-button>
+ <el-button @click="handleExport">瀵煎嚭</el-button>
+ <el-button type="danger"
+ plain
+ @click="handleBatchDelete">鍒犻櫎</el-button>
+ </div>
+ </div>
+ <el-table :data="tableData"
+ border
+ v-loading="tableLoading"
+ @selection-change="handleSelectionChange"
+ style="width: 100%"
+ height="calc(100vh - 21.5em)">
+ <el-table-column align="center"
+ type="selection"
+ :selectable="isDraft"
+ width="55" />
+ <el-table-column align="center"
+ label="搴忓彿"
+ type="index"
+ width="60" />
+ <el-table-column label="濮斿璁㈠崟鍙�"
+ prop="orderNo"
+ min-width="160"
+ show-overflow-tooltip />
+ <el-table-column label="鍚堜綔鍟�"
+ prop="partnerName"
+ min-width="180"
+ show-overflow-tooltip />
+ <el-table-column label="椤圭洰鍚嶇О"
+ prop="projectName"
+ min-width="140"
+ show-overflow-tooltip />
+ <el-table-column label="鐢宠鏃ユ湡"
+ prop="applyDate"
+ min-width="110"
+ show-overflow-tooltip />
+ <el-table-column label="棰勮浜や粯鏃ユ湡"
+ prop="expectedDeliveryDate"
+ min-width="120"
+ show-overflow-tooltip />
+ <el-table-column label="鍚堝悓閲戦(鍏�)"
+ prop="contractAmount"
+ min-width="120"
+ align="right">
+ <template #default="scope">
+ {{ formatAmount(scope.row.contractAmount) }}
+ </template>
+ </el-table-column>
+ <el-table-column label="浠樻鏂瑰紡"
+ prop="paymentMethod"
+ min-width="100"
+ show-overflow-tooltip />
+ <el-table-column label="鐘舵��"
+ prop="status"
+ align="center"
+ width="100">
+ <template #default="scope">
+ <el-tag :type="statusTagType(scope.row.status)"
+ size="small">
+ {{ statusLabel(scope.row.status) }}
+ </el-tag>
+ </template>
+ </el-table-column>
+ <el-table-column label="纭鏃堕棿"
+ prop="confirmTime"
+ min-width="160"
+ show-overflow-tooltip />
+ <el-table-column label="浣滃簾鍘熷洜"
+ prop="cancelReason"
+ min-width="140"
+ show-overflow-tooltip />
+ <el-table-column label="鍒涘缓鏃堕棿"
+ prop="createTime"
+ min-width="160"
+ show-overflow-tooltip />
+ <el-table-column fixed="right"
+ label="鎿嶄綔"
+ width="220"
+ align="center">
+ <template #default="scope">
+ <el-button link
+ type="primary"
+ @click="openForm('view', scope.row)">璇︽儏</el-button>
+ <el-button v-if="scope.row.status === 0"
+ link
+ type="primary"
+ @click="openForm('edit', scope.row)">缂栬緫</el-button>
+ <el-button v-if="scope.row.status === 0"
+ link
+ type="primary"
+ @click="handleSubmitOrder(scope.row)">鎻愪氦</el-button>
+ <el-button v-if="scope.row.status === 1"
+ link
+ type="primary"
+ @click="handleCompleteOrder(scope.row)">瀹屾垚</el-button>
+ <el-button v-if="scope.row.status === 0 || scope.row.status === 1"
+ link
+ type="danger"
+ @click="handleCancelOrder(scope.row)">浣滃簾</el-button>
+ </template>
+ </el-table-column>
+ </el-table>
+ <pagination v-show="total > 0"
+ :total="total"
+ layout="total, sizes, prev, pager, next, jumper"
+ :page="page.current"
+ :limit="page.size"
+ @pagination="paginationChange" />
+ </div>
+ <el-dialog v-model="dialogFormVisible"
+ :title="dialogTitle"
+ width="90%"
+ top="5vh"
+ :close-on-click-modal="false"
+ @close="closeDia">
+ <el-form :model="form"
+ label-width="130px"
+ label-position="top"
+ :rules="rules"
+ ref="formRef"
+ :disabled="isView">
+ <el-row :gutter="30">
+ <el-col :span="8">
+ <el-form-item label="鍚堜綔鍟嗭細"
+ prop="partnerId">
+ <el-select v-model="form.partnerId"
+ placeholder="璇烽�夋嫨"
+ clearable
+ filterable
+ style="width: 100%">
+ <el-option v-for="item in partnerOptions"
+ :key="item.id"
+ :label="item.partnerName"
+ :value="item.id" />
+ </el-select>
+ </el-form-item>
+ </el-col>
+ <el-col :span="8">
+ <el-form-item label="椤圭洰鍚嶇О锛�"
+ prop="projectName">
+ <el-input v-model="form.projectName"
+ placeholder="璇疯緭鍏�"
+ clearable />
+ </el-form-item>
+ </el-col>
+ <el-col :span="8">
+ <el-form-item label="浠樻鏂瑰紡锛�"
+ prop="paymentMethod">
+ <el-input v-model="form.paymentMethod"
+ placeholder="璇疯緭鍏�"
+ clearable />
+ </el-form-item>
+ </el-col>
+ </el-row>
+ <el-row :gutter="30">
+ <el-col :span="8">
+ <el-form-item label="鐢宠鏃ユ湡锛�"
+ prop="applyDate">
+ <el-date-picker v-model="form.applyDate"
+ type="date"
+ value-format="YYYY-MM-DD"
+ format="YYYY-MM-DD"
+ placeholder="璇烽�夋嫨"
+ style="width: 100%" />
+ </el-form-item>
+ </el-col>
+ <el-col :span="8">
+ <el-form-item label="棰勮浜や粯鏃ユ湡锛�"
+ prop="expectedDeliveryDate">
+ <el-date-picker v-model="form.expectedDeliveryDate"
+ type="date"
+ value-format="YYYY-MM-DD"
+ format="YYYY-MM-DD"
+ placeholder="璇烽�夋嫨"
+ style="width: 100%" />
+ </el-form-item>
+ </el-col>
+ <el-col :span="8">
+ <el-form-item label="鍚堝悓閲戦(鍏�)">
+ <el-input :model-value="formatAmount(totalAmount)"
+ readonly />
+ </el-form-item>
+ </el-col>
+ </el-row>
+ <el-row :gutter="30">
+ <el-col :span="24">
+ <el-form-item label="澶囨敞锛�"
+ prop="remarks">
+ <el-input v-model="form.remarks"
+ type="textarea"
+ :rows="2"
+ placeholder="璇疯緭鍏�"
+ clearable />
+ </el-form-item>
+ </el-col>
+ </el-row>
+ </el-form>
+ <div class="detail-header">
+ <span class="detail-title">浜у搧鏄庣粏</span>
+ <el-button v-if="!isView"
+ type="primary"
+ size="small"
+ @click="openScrapSelect">閫夋姤搴熷搧</el-button>
+ <el-button v-if="!isView"
+ size="small"
+ @click="addEmptyDetail">鏂板琛�</el-button>
+ </div>
+ <el-table :data="form.productData"
+ border
+ size="small"
+ style="width: 100%">
+ <el-table-column align="center"
+ label="搴忓彿"
+ type="index"
+ width="55" />
+ <el-table-column label="浜у搧澶х被"
+ prop="productCategory"
+ min-width="120"
+ show-overflow-tooltip />
+ <el-table-column label="瑙勬牸鍨嬪彿"
+ prop="specificationModel"
+ min-width="110"
+ show-overflow-tooltip />
+ <el-table-column label="鍗曚綅"
+ prop="unit"
+ min-width="70" />
+ <el-table-column label="鏁伴噺"
+ prop="quantity"
+ min-width="110">
+ <template #default="scope">
+ <el-input-number v-if="!isView"
+ v-model="scope.row.quantity"
+ :min="0"
+ :precision="4"
+ :step="1"
+ style="width: 100%" />
+ <span v-else>{{ scope.row.quantity }}</span>
+ </template>
+ </el-table-column>
+ <el-table-column label="绋庣巼(%)"
+ prop="taxRate"
+ min-width="110">
+ <template #default="scope">
+ <el-input-number v-if="!isView"
+ v-model="scope.row.taxRate"
+ :min="0"
+ :max="100"
+ :precision="2"
+ :step="1"
+ style="width: 100%" />
+ <span v-else>{{ scope.row.taxRate }}</span>
+ </template>
+ </el-table-column>
+ <el-table-column label="鍚◣鍗曚环(鍏�)"
+ prop="taxInclusiveUnitPrice"
+ min-width="130">
+ <template #default="scope">
+ <el-input-number v-if="!isView"
+ v-model="scope.row.taxInclusiveUnitPrice"
+ :min="0"
+ :precision="2"
+ :step="0.01"
+ style="width: 100%" />
+ <span v-else>{{ scope.row.taxInclusiveUnitPrice }}</span>
+ </template>
+ </el-table-column>
+ <el-table-column label="鍚◣鎬讳环(鍏�)"
+ prop="taxInclusiveTotalPrice"
+ min-width="120"
+ align="right">
+ <template #default="scope">
+ {{ formatAmount(lineTotal(scope.row)) }}
+ </template>
+ </el-table-column>
+ <el-table-column label="鎶ュ簾鍝�"
+ prop="isScrap"
+ align="center"
+ width="80">
+ <template #default="scope">
+ <el-tag :type="scope.row.isScrap ? 'warning' : 'info'"
+ size="small">
+ {{ scope.row.isScrap ? "鏄�" : "鍚�" }}
+ </el-tag>
+ </template>
+ </el-table-column>
+ <el-table-column label="鎵瑰彿"
+ prop="batchNo"
+ min-width="130"
+ show-overflow-tooltip />
+ <el-table-column label="鍙敤閲�"
+ prop="availableQuantity"
+ min-width="100"
+ align="right" />
+ <el-table-column label="澶囨敞"
+ prop="remark"
+ min-width="130">
+ <template #default="scope">
+ <el-input v-if="!isView"
+ v-model="scope.row.remark"
+ placeholder="璇疯緭鍏�"
+ clearable />
+ <span v-else>{{ scope.row.remark }}</span>
+ </template>
+ </el-table-column>
+ <el-table-column v-if="!isView"
+ fixed="right"
+ label="鎿嶄綔"
+ width="70"
+ align="center">
+ <template #default="scope">
+ <el-button link
+ type="danger"
+ @click="removeDetail(scope.$index)">鍒犻櫎</el-button>
+ </template>
+ </el-table-column>
+ </el-table>
+ <template #footer>
+ <div class="dialog-footer">
+ <el-button v-if="!isView"
+ type="primary"
+ @click="submitForm">纭</el-button>
+ <el-button @click="closeDia">{{ isView ? "鍏抽棴" : "鍙栨秷" }}</el-button>
+ </div>
+ </template>
+ </el-dialog>
+ <!-- 鎶ュ簾鍝侀�夋枡锛堝彧璇伙紝涓嶆墸搴撳瓨銆佷笉鍗犵敤锛� -->
+ <el-dialog v-model="scrapDialogVisible"
+ title="閫夋嫨鎶ュ簾鍝�"
+ width="70%"
+ append-to-body>
+ <el-form :model="scrapSearch"
+ :inline="true">
+ <el-form-item label="浜у搧鍚嶇О锛�">
+ <el-input v-model="scrapSearch.productName"
+ placeholder="璇疯緭鍏�"
+ clearable
+ style="width: 160px" />
+ </el-form-item>
+ <el-form-item label="鎵瑰彿锛�">
+ <el-input v-model="scrapSearch.batchNo"
+ placeholder="璇疯緭鍏�"
+ clearable
+ style="width: 160px" />
+ </el-form-item>
+ <el-form-item>
+ <el-button type="primary"
+ @click="handleScrapQuery">鎼滅储</el-button>
+ </el-form-item>
+ </el-form>
+ <el-table :data="scrapTableData"
+ border
+ v-loading="scrapLoading"
+ highlight-current-row
+ style="width: 100%"
+ height="360px"
+ @current-change="handleScrapCurrentChange">
+ <el-table-column align="center"
+ label="搴忓彿"
+ type="index"
+ width="60" />
+ <el-table-column label="浜у搧鍚嶇О"
+ prop="productName"
+ min-width="140"
+ show-overflow-tooltip />
+ <el-table-column label="瑙勬牸鍨嬪彿"
+ prop="model"
+ min-width="120"
+ show-overflow-tooltip />
+ <el-table-column label="鎵瑰彿"
+ prop="batchNo"
+ min-width="140"
+ show-overflow-tooltip />
+ <el-table-column label="鍗曚綅"
+ prop="unit"
+ min-width="70" />
+ <el-table-column label="搴撳瓨鏁伴噺"
+ prop="qualitity"
+ min-width="100"
+ align="right" />
+ <el-table-column label="閿佸畾鏁伴噺"
+ prop="lockedQuantity"
+ min-width="100"
+ align="right" />
+ <el-table-column label="鍙敤鏁伴噺"
+ prop="unLockedQuantity"
+ min-width="100"
+ align="right" />
+ <el-table-column label="澶囨敞"
+ prop="remark"
+ min-width="120"
+ show-overflow-tooltip />
+ </el-table>
+ <pagination v-show="scrapTotal > 0"
+ :total="scrapTotal"
+ layout="total, sizes, prev, pager, next, jumper"
+ :page="scrapPage.current"
+ :limit="scrapPage.size"
+ @pagination="scrapPaginationChange" />
+ <template #footer>
+ <div class="dialog-footer">
+ <el-button type="primary"
+ @click="confirmScrapSelect">纭</el-button>
+ <el-button @click="scrapDialogVisible = false">鍙栨秷</el-button>
+ </div>
+ </template>
+ </el-dialog>
+ </div>
+</template>
+
+<script setup>
+ import { ref, reactive, computed, toRefs, getCurrentInstance, onMounted } from "vue";
+ import pagination from "@/components/PIMTable/Pagination.vue";
+ import { ElMessageBox } from "element-plus";
+ import {
+ listOutsourcingOrder,
+ getOutsourcingOrder,
+ addOutsourcingOrder,
+ editOutsourcingOrder,
+ delOutsourcingOrder,
+ submitOutsourcingOrder,
+ completeOutsourcingOrder,
+ cancelOutsourcingOrder,
+ exportOutsourcingOrder,
+ scrapStockPage,
+ } from "@/api/outsourcing/order.js";
+ import { getPartnerOptions } from "@/api/outsourcing/partner.js";
+
+ const { proxy } = getCurrentInstance();
+ const tableData = ref([]);
+ const selectedRows = ref([]);
+ const tableLoading = ref(false);
+ const dialogFormVisible = ref(false);
+ const operationType = ref("");
+ const partnerOptions = ref([]);
+ const dateRange = ref([]);
+ const page = reactive({
+ current: 1,
+ size: 10,
+ });
+ const total = ref(0);
+
+ const statusOptions = [
+ { label: "鑽夌", value: 0 },
+ { label: "宸茬‘璁�", value: 1 },
+ { label: "宸插畬鎴�", value: 2 },
+ { label: "宸蹭綔搴�", value: 3 },
+ ];
+
+ const statusLabelMap = {
+ 0: "鑽夌",
+ 1: "宸茬‘璁�",
+ 2: "宸插畬鎴�",
+ 3: "宸蹭綔搴�",
+ };
+
+ const statusTagMap = {
+ 0: "info",
+ 1: "primary",
+ 2: "success",
+ 3: "danger",
+ };
+
+ const statusLabel = status => statusLabelMap[status] ?? status;
+ const statusTagType = status => statusTagMap[status] || "info";
+
+ const data = reactive({
+ searchForm: {
+ orderNo: "",
+ partnerId: "",
+ projectName: "",
+ status: "",
+ },
+ form: {},
+ rules: {
+ partnerId: [{ required: true, message: "璇烽�夋嫨鍚堜綔鍟�", trigger: "change" }],
+ projectName: [{ required: true, message: "璇疯緭鍏ラ」鐩悕绉�", trigger: "blur" }],
+ applyDate: [{ required: true, message: "璇烽�夋嫨鐢宠鏃ユ湡", trigger: "change" }],
+ },
+ });
+ const { searchForm, form, rules } = toRefs(data);
+
+ const isView = computed(() => operationType.value === "view");
+ const isDraft = row => row.status === 0;
+ const dialogTitle = computed(() => {
+ if (operationType.value === "add") {
+ return "鏂板濮斿璁㈠崟";
+ }
+ return operationType.value === "edit" ? "缂栬緫濮斿璁㈠崟" : "濮斿璁㈠崟璇︽儏";
+ });
+
+ const buildEmptyForm = () => ({
+ id: null,
+ partnerId: undefined,
+ projectName: "",
+ applyDate: today(),
+ expectedDeliveryDate: "",
+ paymentMethod: "",
+ remarks: "",
+ productData: [],
+ });
+
+ function today() {
+ const d = new Date();
+ const month = String(d.getMonth() + 1).padStart(2, "0");
+ const day = String(d.getDate()).padStart(2, "0");
+ return `${d.getFullYear()}-${month}-${day}`;
+ }
+
+ const formatAmount = value => {
+ const num = Number(value);
+ return Number.isFinite(num) ? num.toFixed(2) : "0.00";
+ };
+
+ // 鍚◣鎬讳环鐢辨暟閲� 脳 鍚◣鍗曚环寰楀嚭锛屽悗绔細鎸夋槑缁嗗悎璁¤鐩� contractAmount
+ const lineTotal = row => {
+ const quantity = Number(row?.quantity) || 0;
+ const price = Number(row?.taxInclusiveUnitPrice) || 0;
+ return Math.round(quantity * price * 100) / 100;
+ };
+
+ const totalAmount = computed(() =>
+ (form.value.productData || []).reduce((sum, row) => sum + lineTotal(row), 0)
+ );
+
+ // 鍘绘帀绌哄�硷紝閬垮厤 status='' 杩欑被绌轰覆琚悗绔綋鎴� 0锛堣崏绋匡級瑙f瀽
+ const compact = obj =>
+ Object.fromEntries(
+ Object.entries(obj).filter(([, v]) => v !== "" && v !== null && v !== undefined)
+ );
+
+ const getList = () => {
+ tableLoading.value = true;
+ const [startDate, endDate] = dateRange.value || [];
+ listOutsourcingOrder({
+ pageNum: page.current,
+ pageSize: page.size,
+ ...compact(searchForm.value),
+ startDate,
+ endDate,
+ })
+ .then(res => {
+ tableLoading.value = false;
+ // 濮斿璁㈠崟鍒楄〃杩斿洖鐨勬槸 RuoYi 缁撴瀯锛岃鍦ㄩ《灞� rows
+ tableData.value = res.rows || [];
+ total.value = res.total || 0;
+ })
+ .catch(() => {
+ tableLoading.value = false;
+ });
+ };
+
+ const handleQuery = () => {
+ page.current = 1;
+ getList();
+ };
+
+ const resetSearch = () => {
+ searchForm.value = { orderNo: "", partnerId: "", projectName: "", status: "" };
+ dateRange.value = [];
+ handleQuery();
+ };
+
+ const paginationChange = obj => {
+ page.current = obj.page;
+ page.size = obj.limit;
+ getList();
+ };
+
+ const handleSelectionChange = selection => {
+ selectedRows.value = selection;
+ };
+
+ const openForm = (type, row) => {
+ operationType.value = type;
+ form.value = buildEmptyForm();
+ proxy.resetForm("formRef");
+ dialogFormVisible.value = true;
+ if (type !== "add" && row) {
+ getOutsourcingOrder(row.id).then(res => {
+ const detail = res.data || {};
+ form.value = {
+ ...buildEmptyForm(),
+ ...detail,
+ productData: (detail.productData || []).map(item => ({ ...item })),
+ };
+ });
+ }
+ };
+
+ const addEmptyDetail = () => {
+ form.value.productData.push({
+ productId: undefined,
+ productModelId: undefined,
+ productCategory: "",
+ specificationModel: "",
+ unit: "",
+ quantity: undefined,
+ taxRate: undefined,
+ taxInclusiveUnitPrice: undefined,
+ taxInclusiveTotalPrice: undefined,
+ isScrap: false,
+ stockInventoryId: undefined,
+ batchNo: "",
+ availableQuantity: undefined,
+ remark: "",
+ });
+ };
+
+ const removeDetail = index => {
+ form.value.productData.splice(index, 1);
+ };
+
+ const submitForm = () => {
+ proxy.$refs["formRef"].validate(valid => {
+ if (!valid) {
+ return;
+ }
+ if (!form.value.productData.length) {
+ proxy.$modal.msgWarning("璇疯嚦灏戞坊鍔犱竴鏉′骇鍝佹槑缁�");
+ return;
+ }
+ // 鍙彁浜ゆ枃妗g害瀹氱殑瀛楁锛氬崟鍙�/鐘舵��/纭鏃堕棿/鍚堝悓閲戦/鍚堜綔鍟嗗悕绉扮瓑鐢卞悗绔淮鎶�
+ const payload = {
+ partnerId: form.value.partnerId,
+ projectName: form.value.projectName,
+ applyDate: form.value.applyDate,
+ expectedDeliveryDate: form.value.expectedDeliveryDate,
+ paymentMethod: form.value.paymentMethod,
+ remarks: form.value.remarks,
+ productData: form.value.productData.map(row => ({
+ productId: row.productId,
+ productModelId: row.productModelId,
+ productCategory: row.productCategory,
+ specificationModel: row.specificationModel,
+ unit: row.unit,
+ quantity: row.quantity,
+ taxRate: row.taxRate,
+ taxInclusiveUnitPrice: row.taxInclusiveUnitPrice,
+ taxInclusiveTotalPrice: lineTotal(row),
+ isScrap: !!row.isScrap,
+ stockInventoryId: row.stockInventoryId,
+ batchNo: row.batchNo,
+ availableQuantity: row.availableQuantity,
+ remark: row.remark,
+ })),
+ ...(form.value.id ? { id: form.value.id } : {}),
+ };
+ const api = form.value.id ? editOutsourcingOrder : addOutsourcingOrder;
+ api(payload).then(() => {
+ proxy.$modal.msgSuccess("鎿嶄綔鎴愬姛");
+ closeDia();
+ getList();
+ });
+ });
+ };
+
+ const closeDia = () => {
+ proxy.resetForm("formRef");
+ dialogFormVisible.value = false;
+ };
+
+ const handleSubmitOrder = row => {
+ ElMessageBox.confirm(
+ `鏄惁纭鎻愪氦璁㈠崟 ${row.orderNo}锛熸彁浜ゅ悗灏嗗崰鐢ㄦ墍閫夋姤搴熷搧鎵规鐨勫彲鐢ㄩ噺锛岄渶鍒板嚭搴撶鐞嗗鎵归�氳繃鍚庢墠鑳藉畬鎴愯鍗曘�俙,
+ "鎻愪氦",
+ {
+ confirmButtonText: "纭",
+ cancelButtonText: "鍙栨秷",
+ type: "warning",
+ }
+ )
+ .then(() => {
+ submitOutsourcingOrder(row.id).then(() => {
+ proxy.$modal.msgSuccess("鎻愪氦鎴愬姛");
+ getList();
+ });
+ })
+ .catch(() => {
+ proxy.$modal.msg("宸插彇娑�");
+ });
+ };
+
+ const handleCompleteOrder = row => {
+ ElMessageBox.confirm(`鏄惁纭瀹屾垚璁㈠崟 ${row.orderNo}锛焋, "瀹屾垚", {
+ confirmButtonText: "纭",
+ cancelButtonText: "鍙栨秷",
+ type: "warning",
+ })
+ .then(() => {
+ completeOutsourcingOrder(row.id).then(() => {
+ proxy.$modal.msgSuccess("鎿嶄綔鎴愬姛");
+ getList();
+ });
+ })
+ .catch(() => {
+ proxy.$modal.msg("宸插彇娑�");
+ });
+ };
+
+ const handleCancelOrder = row => {
+ ElMessageBox.prompt("璇疯緭鍏ヤ綔搴熷師鍥狅紙鍙笉濉級", `浣滃簾璁㈠崟 ${row.orderNo}`, {
+ confirmButtonText: "纭浣滃簾",
+ cancelButtonText: "鍙栨秷",
+ inputPlaceholder: "浣滃簾鍘熷洜",
+ })
+ .then(({ value }) => {
+ cancelOutsourcingOrder(row.id, value || "").then(() => {
+ proxy.$modal.msgSuccess("浣滃簾鎴愬姛");
+ getList();
+ });
+ })
+ .catch(() => {
+ proxy.$modal.msg("宸插彇娑�");
+ });
+ };
+
+ const handleBatchDelete = () => {
+ if (selectedRows.value.length === 0) {
+ proxy.$modal.msgWarning("璇烽�夋嫨鏁版嵁");
+ return;
+ }
+ const ids = selectedRows.value.map(item => item.id);
+ ElMessageBox.confirm("閫変腑鐨勫唴瀹瑰皢琚垹闄わ紝鏄惁纭鍒犻櫎锛熶粎鑽夌鐘舵�佸厑璁稿垹闄ゃ��", "鍒犻櫎", {
+ confirmButtonText: "纭",
+ cancelButtonText: "鍙栨秷",
+ type: "warning",
+ })
+ .then(() => {
+ delOutsourcingOrder(ids).then(() => {
+ proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛");
+ getList();
+ });
+ })
+ .catch(() => {
+ proxy.$modal.msg("宸插彇娑�");
+ });
+ };
+
+ const saveExcelBlob = (blobData, filename) => {
+ const blob = new Blob([blobData], {
+ type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
+ });
+ const url = window.URL.createObjectURL(blob);
+ const link = document.createElement("a");
+ link.href = url;
+ link.download = filename;
+ document.body.appendChild(link);
+ link.click();
+ setTimeout(() => {
+ document.body.removeChild(link);
+ window.URL.revokeObjectURL(url);
+ }, 100);
+ };
+
+ const handleExport = () => {
+ ElMessageBox.confirm("鏄惁纭瀵煎嚭锛�", "瀵煎嚭", {
+ confirmButtonText: "纭",
+ cancelButtonText: "鍙栨秷",
+ type: "warning",
+ })
+ .then(() => {
+ const [startDate, endDate] = dateRange.value || [];
+ exportOutsourcingOrder({
+ ...compact(searchForm.value),
+ startDate,
+ endDate,
+ }).then(res => {
+ saveExcelBlob(res, "濮斿璁㈠崟.xlsx");
+ });
+ })
+ .catch(() => {
+ proxy.$modal.msg("宸插彇娑�");
+ });
+ };
+
+ // 鎶ュ簾鍝侀�夋枡锛氬彧璇诲甫鍑猴紝涓嶆墸搴撳瓨銆佷笉鍗犵敤
+ const scrapDialogVisible = ref(false);
+ const scrapLoading = ref(false);
+ const scrapTableData = ref([]);
+ const scrapSelectedRow = ref(null);
+ const scrapTotal = ref(0);
+ const scrapPage = reactive({ current: 1, size: 10 });
+ const scrapSearch = reactive({ productName: "", batchNo: "" });
+
+ const getScrapList = () => {
+ scrapLoading.value = true;
+ scrapStockPage({
+ pageNum: scrapPage.current,
+ pageSize: scrapPage.size,
+ productName: scrapSearch.productName || undefined,
+ batchNo: scrapSearch.batchNo || undefined,
+ })
+ .then(res => {
+ scrapLoading.value = false;
+ scrapTableData.value = res.data?.records || [];
+ scrapTotal.value = res.data?.total || 0;
+ })
+ .catch(() => {
+ scrapLoading.value = false;
+ });
+ };
+
+ const openScrapSelect = () => {
+ scrapSelectedRow.value = null;
+ scrapSearch.productName = "";
+ scrapSearch.batchNo = "";
+ scrapPage.current = 1;
+ scrapDialogVisible.value = true;
+ getScrapList();
+ };
+
+ const handleScrapQuery = () => {
+ scrapPage.current = 1;
+ getScrapList();
+ };
+
+ const scrapPaginationChange = obj => {
+ scrapPage.current = obj.page;
+ scrapPage.size = obj.limit;
+ getScrapList();
+ };
+
+ const handleScrapCurrentChange = row => {
+ scrapSelectedRow.value = row;
+ };
+
+ const confirmScrapSelect = () => {
+ const row = scrapSelectedRow.value;
+ if (!row) {
+ proxy.$modal.msgWarning("璇烽�夋嫨涓�鏉℃姤搴熷搧鎵规");
+ return;
+ }
+ form.value.productData.push({
+ productId: row.productId,
+ productModelId: row.productModelId,
+ productCategory: row.productName || "",
+ specificationModel: row.model || "",
+ unit: row.unit || "",
+ quantity: undefined,
+ taxRate: undefined,
+ taxInclusiveUnitPrice: undefined,
+ taxInclusiveTotalPrice: undefined,
+ isScrap: true,
+ stockInventoryId: row.id,
+ batchNo: row.batchNo || "",
+ availableQuantity: row.unLockedQuantity,
+ remark: "",
+ });
+ scrapDialogVisible.value = false;
+ };
+
+ onMounted(() => {
+ getList();
+ getPartnerOptions().then(res => {
+ partnerOptions.value = res.data || [];
+ });
+ });
+</script>
+
+<style scoped lang="scss">
+ .search_form {
+ margin-bottom: 16px;
+ }
+
+ .actions {
+ display: flex;
+ justify-content: flex-end;
+ margin-bottom: 12px;
+ }
+
+ .detail-header {
+ display: flex;
+ align-items: center;
+ gap: 12px;
+ margin: 8px 0 12px;
+
+ .detail-title {
+ flex: 1;
+ font-size: 15px;
+ font-weight: 600;
+ color: #1d6fd8;
+ }
+ }
+</style>
diff --git a/src/views/outsourcingManagement/partner/index.vue b/src/views/outsourcingManagement/partner/index.vue
new file mode 100644
index 0000000..5ba20b0
--- /dev/null
+++ b/src/views/outsourcingManagement/partner/index.vue
@@ -0,0 +1,668 @@
+<template>
+ <div class="app-container">
+ <div class="search_form">
+ <el-form :model="searchForm"
+ :inline="true">
+ <el-form-item label="鍚堜綔鍟嗗悕绉帮細">
+ <el-input v-model="searchForm.partnerName"
+ placeholder="璇疯緭鍏�"
+ clearable
+ prefix-icon="Search"
+ @change="handleQuery" />
+ </el-form-item>
+ <el-form-item label="鍚堜綔鍟嗙被鍨嬶細">
+ <el-input v-model="searchForm.partnerType"
+ placeholder="璇疯緭鍏�"
+ clearable
+ @change="handleQuery" />
+ </el-form-item>
+ <el-form-item label="鏄惁鐧藉悕鍗曪細">
+ <el-select v-model="searchForm.isWhite"
+ placeholder="璇烽�夋嫨"
+ clearable
+ style="width: 140px"
+ @change="handleQuery">
+ <el-option label="鏄�"
+ :value="1" />
+ <el-option label="鍚�"
+ :value="0" />
+ </el-select>
+ </el-form-item>
+ <el-form-item>
+ <el-button type="primary"
+ @click="handleQuery">鎼滅储</el-button>
+ </el-form-item>
+ </el-form>
+ </div>
+ <div class="table_list">
+ <div class="actions">
+ <div></div>
+ <div>
+ <el-button type="primary"
+ @click="openForm('add')">鏂板鍚堜綔鍟�</el-button>
+ <el-button @click="handleExport">瀵煎嚭</el-button>
+ <el-button type="info"
+ plain
+ icon="Upload"
+ @click="handleImport">瀵煎叆</el-button>
+ <el-button type="danger"
+ plain
+ @click="handleDelete">鍒犻櫎</el-button>
+ </div>
+ </div>
+ <el-table :data="tableData"
+ border
+ v-loading="tableLoading"
+ @selection-change="handleSelectionChange"
+ style="width: 100%"
+ height="calc(100vh - 19em)">
+ <el-table-column align="center"
+ type="selection"
+ width="55" />
+ <el-table-column align="center"
+ label="搴忓彿"
+ type="index"
+ width="60" />
+ <el-table-column label="鍚堜綔鍟嗗悕绉�"
+ prop="partnerName"
+ min-width="200"
+ show-overflow-tooltip />
+ <el-table-column label="鍚堜綔鍟嗙被鍨�"
+ prop="partnerType"
+ min-width="110"
+ show-overflow-tooltip />
+ <el-table-column label="绾崇◣浜鸿瘑鍒彿"
+ prop="taxpayerIdentificationNum"
+ min-width="190"
+ show-overflow-tooltip />
+ <el-table-column label="鍏徃鍦板潃"
+ prop="companyAddress"
+ min-width="180"
+ show-overflow-tooltip />
+ <el-table-column label="鍏徃鐢佃瘽"
+ prop="companyPhone"
+ min-width="130"
+ show-overflow-tooltip />
+ <el-table-column label="寮�鎴疯"
+ prop="bankAccountName"
+ min-width="170"
+ show-overflow-tooltip />
+ <el-table-column label="璐﹀彿"
+ prop="bankAccountNum"
+ min-width="170"
+ show-overflow-tooltip />
+ <el-table-column label="鑱旂郴浜�"
+ prop="contactUserName"
+ min-width="90"
+ show-overflow-tooltip />
+ <el-table-column label="鑱旂郴鐢佃瘽"
+ prop="contactUserPhone"
+ min-width="130"
+ show-overflow-tooltip />
+ <el-table-column label="鏄惁鐧藉悕鍗�"
+ prop="isWhite"
+ align="center"
+ width="100">
+ <template #default="scope">
+ <el-tag :type="scope.row.isWhite === 1 ? 'success' : 'info'"
+ size="small">
+ {{ scope.row.isWhite === 1 ? "鏄�" : "鍚�" }}
+ </el-tag>
+ </template>
+ </el-table-column>
+ <el-table-column label="缁存姢浜�"
+ prop="maintainUserName"
+ min-width="90"
+ show-overflow-tooltip />
+ <el-table-column label="缁存姢鏃堕棿"
+ prop="maintainTime"
+ min-width="110"
+ show-overflow-tooltip />
+ <el-table-column label="澶囨敞"
+ prop="remark"
+ min-width="140"
+ show-overflow-tooltip />
+ <el-table-column fixed="right"
+ label="鎿嶄綔"
+ width="160"
+ align="center">
+ <template #default="scope">
+ <el-button link
+ type="primary"
+ @click="openForm('edit', scope.row)">缂栬緫</el-button>
+ <el-button link
+ type="primary"
+ @click="openAttachment(scope.row)">闄勪欢</el-button>
+ </template>
+ </el-table-column>
+ </el-table>
+ <pagination v-show="total > 0"
+ :total="total"
+ layout="total, sizes, prev, pager, next, jumper"
+ :page="page.current"
+ :limit="page.size"
+ @pagination="paginationChange" />
+ </div>
+ <el-dialog v-model="dialogFormVisible"
+ :title="operationType === 'add' ? '鏂板鍚堜綔鍟�' : '缂栬緫鍚堜綔鍟�'"
+ width="70%"
+ :close-on-click-modal="false"
+ @close="closeDia">
+ <el-form :model="form"
+ label-width="140px"
+ label-position="top"
+ :rules="rules"
+ ref="formRef">
+ <el-row :gutter="30">
+ <el-col :span="12">
+ <el-form-item label="鍚堜綔鍟嗗悕绉帮細"
+ prop="partnerName">
+ <el-input v-model="form.partnerName"
+ placeholder="璇疯緭鍏�"
+ clearable />
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item label="绾崇◣浜鸿瘑鍒彿锛�"
+ prop="taxpayerIdentificationNum">
+ <el-input v-model="form.taxpayerIdentificationNum"
+ placeholder="璇疯緭鍏�"
+ clearable />
+ </el-form-item>
+ </el-col>
+ </el-row>
+ <el-row :gutter="30">
+ <el-col :span="12">
+ <el-form-item label="鍏徃鍦板潃锛�"
+ prop="companyAddress">
+ <el-input v-model="form.companyAddress"
+ placeholder="璇疯緭鍏�"
+ clearable />
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item label="鍏徃鐢佃瘽锛�"
+ prop="companyPhone">
+ <el-input v-model="form.companyPhone"
+ placeholder="璇疯緭鍏�"
+ clearable />
+ </el-form-item>
+ </el-col>
+ </el-row>
+ <el-row :gutter="30">
+ <el-col :span="12">
+ <el-form-item label="寮�鎴疯锛�"
+ prop="bankAccountName">
+ <el-input v-model="form.bankAccountName"
+ placeholder="璇疯緭鍏�"
+ clearable />
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item label="璐﹀彿锛�"
+ prop="bankAccountNum">
+ <el-input v-model="form.bankAccountNum"
+ placeholder="璇疯緭鍏�"
+ clearable />
+ </el-form-item>
+ </el-col>
+ </el-row>
+ <el-row :gutter="30">
+ <el-col :span="12">
+ <el-form-item label="鑱旂郴浜猴細"
+ prop="contactUserName">
+ <el-input v-model="form.contactUserName"
+ placeholder="璇疯緭鍏�"
+ clearable />
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item label="鑱旂郴鐢佃瘽锛�"
+ prop="contactUserPhone">
+ <el-input v-model="form.contactUserPhone"
+ placeholder="璇疯緭鍏�"
+ clearable />
+ </el-form-item>
+ </el-col>
+ </el-row>
+ <el-row :gutter="30">
+ <el-col :span="12">
+ <el-form-item label="鍚堜綔鍟嗙被鍨嬶細"
+ prop="partnerType">
+ <el-input v-model="form.partnerType"
+ placeholder="璇疯緭鍏ワ紝濡傦細鍗卞簾澶勭疆"
+ clearable />
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item label="鏄惁鐧藉悕鍗曪細"
+ prop="isWhite">
+ <el-select v-model="form.isWhite"
+ placeholder="璇烽�夋嫨"
+ clearable
+ style="width: 100%">
+ <el-option label="鏄�"
+ :value="1" />
+ <el-option label="鍚�"
+ :value="0" />
+ </el-select>
+ </el-form-item>
+ </el-col>
+ </el-row>
+ <el-row :gutter="30">
+ <el-col :span="24">
+ <el-form-item label="澶囨敞锛�"
+ prop="remark">
+ <el-input v-model="form.remark"
+ type="textarea"
+ :rows="2"
+ placeholder="璇疯緭鍏�"
+ clearable />
+ </el-form-item>
+ </el-col>
+ </el-row>
+ <el-row :gutter="30">
+ <el-col :span="24">
+ <el-form-item label="闄勪欢锛�">
+ <AttachmentUploadFile v-model:file-list="partnerFileList"
+ :limit="10" />
+ </el-form-item>
+ </el-col>
+ </el-row>
+ </el-form>
+ <template #footer>
+ <div class="dialog-footer">
+ <el-button type="primary"
+ @click="submitForm">纭</el-button>
+ <el-button @click="closeDia">鍙栨秷</el-button>
+ </div>
+ </template>
+ </el-dialog>
+ <!-- 瀵煎叆瀵硅瘽妗� -->
+ <el-dialog :title="upload.title"
+ v-model="upload.open"
+ width="400px"
+ append-to-body>
+ <el-upload ref="uploadRef"
+ :limit="1"
+ accept=".xlsx, .xls"
+ :headers="upload.headers"
+ :action="upload.url"
+ :disabled="upload.isUploading"
+ :on-progress="handleFileUploadProgress"
+ :on-success="handleFileSuccess"
+ :on-error="handleFileError"
+ :auto-upload="false"
+ drag>
+ <el-icon class="el-icon--upload">
+ <upload-filled />
+ </el-icon>
+ <div class="el-upload__text">灏嗘枃浠舵嫋鍒版澶勶紝鎴�<em>鐐瑰嚮涓婁紶</em></div>
+ <template #tip>
+ <div class="el-upload__tip text-center">
+ <span>浠呭厑璁稿鍏ls銆亁lsx鏍煎紡鏂囦欢銆�</span>
+ <el-link type="primary"
+ :underline="false"
+ style="font-size: 12px; vertical-align: baseline"
+ @click="importTemplate">涓嬭浇妯℃澘</el-link>
+ </div>
+ </template>
+ </el-upload>
+ <template #footer>
+ <div class="dialog-footer">
+ <el-button type="primary"
+ @click="submitFileForm">纭� 瀹�</el-button>
+ <el-button @click="upload.open = false">鍙� 娑�</el-button>
+ </div>
+ </template>
+ </el-dialog>
+ <!-- 闄勪欢锛氬彲鐩存帴涓婁紶/鍒犻櫎锛屾敼鍔ㄥ嵆鏃朵繚瀛樺埌璇ュ悎浣滃晢 -->
+ <el-dialog v-model="attachmentVisible"
+ title="闄勪欢"
+ width="600px"
+ append-to-body>
+ <div v-loading="attachmentLoading"
+ style="min-height: 140px">
+ <AttachmentUploadFile v-model:file-list="attachmentList"
+ :limit="10"
+ @change="saveAttachments" />
+ </div>
+ </el-dialog>
+ </div>
+</template>
+
+<script setup>
+ import { ref, reactive, toRefs, getCurrentInstance, onMounted } from "vue";
+ import pagination from "@/components/PIMTable/Pagination.vue";
+ import AttachmentUploadFile from "@/components/AttachmentUpload/file/index.vue";
+ import { ElMessageBox } from "element-plus";
+ import { getToken } from "@/utils/auth.js";
+ import {
+ listPartner,
+ getPartner,
+ addPartner,
+ updatePartner,
+ delPartner,
+ exportPartner,
+ downloadPartnerTemplate,
+ } from "@/api/outsourcing/partner.js";
+
+ const { proxy } = getCurrentInstance();
+ const tableData = ref([]);
+ const selectedRows = ref([]);
+ const tableLoading = ref(false);
+ const dialogFormVisible = ref(false);
+ const attachmentVisible = ref(false);
+ const attachmentList = ref([]);
+ const attachmentLoading = ref(false);
+ // 闄勪欢寮规瀵瑰簲鐨勫悎浣滃晢璇︽儏锛氬垪琛ㄨ瀛楁涓嶅叏锛屼繚瀛樻椂鐢ㄨ鎯呴噷鐨勫瓧娈垫暣浣撳洖浼�
+ const attachmentDetail = ref({});
+ const operationType = ref("");
+ const page = reactive({
+ current: 1,
+ size: 10,
+ });
+ const total = ref(0);
+
+ const data = reactive({
+ searchForm: {
+ partnerName: "",
+ partnerType: "",
+ isWhite: "",
+ },
+ form: {},
+ rules: {
+ partnerName: [{ required: true, message: "璇疯緭鍏ュ悎浣滃晢鍚嶇О", trigger: "blur" }],
+ },
+ });
+ const { searchForm, form, rules } = toRefs(data);
+ // 闄勪欢闅忚〃鍗曚竴骞舵彁浜わ紝鍚庣鎸� storageBlobDTOS 鍏ㄩ噺瑕嗙洊锛屾竻绌哄嵆瑙g粦
+ const partnerFileList = ref([]);
+
+ // 鍚堜綔鍟嗙被鍨嬬瓑瀛楁鍚庣鏈粰瀛楀吀锛岀被鍨嬫寜鑷敱鏂囨湰缁存姢
+ const buildEmptyForm = () => ({
+ id: null,
+ partnerName: "",
+ taxpayerIdentificationNum: "",
+ companyAddress: "",
+ companyPhone: "",
+ bankAccountName: "",
+ bankAccountNum: "",
+ contactUserName: "",
+ contactUserPhone: "",
+ partnerType: "",
+ isWhite: 0,
+ remark: "",
+ });
+
+ // 鍘绘帀绌哄�硷紝閬垮厤 isWhite='' 杩欑被绌轰覆琚悗绔綋鎴� 0 瑙f瀽
+ const compact = obj =>
+ Object.fromEntries(
+ Object.entries(obj).filter(([, v]) => v !== "" && v !== null && v !== undefined)
+ );
+
+ const getList = () => {
+ tableLoading.value = true;
+ listPartner({
+ pageNum: page.current,
+ pageSize: page.size,
+ ...compact(searchForm.value),
+ })
+ .then(res => {
+ tableLoading.value = false;
+ tableData.value = res.data?.records || [];
+ total.value = res.data?.total || 0;
+ })
+ .catch(() => {
+ tableLoading.value = false;
+ });
+ };
+
+ const handleQuery = () => {
+ page.current = 1;
+ getList();
+ };
+
+ const paginationChange = obj => {
+ page.current = obj.page;
+ page.size = obj.limit;
+ getList();
+ };
+
+ const handleSelectionChange = selection => {
+ selectedRows.value = selection;
+ };
+
+ const openForm = (type, row) => {
+ operationType.value = type;
+ form.value = buildEmptyForm();
+ partnerFileList.value = [];
+ proxy.resetForm("formRef");
+ dialogFormVisible.value = true;
+ if (type === "edit" && row) {
+ // 璇︽儏杩斿洖鐨勯檮浠跺彲鑳芥槸 storageBlobVOS 鎴� storageBlobDTOS锛屽洖鏄惧悗缁熶竴杞垚 storageBlobDTOS 鍏ㄩ噺鎻愪氦
+ getPartner(row.id).then(res => {
+ const { storageBlobVOS, storageBlobDTOS, ...detail } = res.data || {};
+ form.value = { ...buildEmptyForm(), ...detail };
+ partnerFileList.value = storageBlobVOS || storageBlobDTOS || [];
+ });
+ }
+ };
+
+ // 鍙彁浜ゆ枃妗g害瀹氱殑瀛楁锛岀淮鎶や汉/缁存姢鏃堕棿/鍒涘缓鏃堕棿绛夌敱鍚庣缁存姢
+ // source/files 鍙紶鍙傦細闄勪欢寮规淇濆瓨鏃剁敤鐨勬槸璇︽儏鏁版嵁 + 寮规鍐呯殑闄勪欢鍒楄〃
+ const buildPayload = (source = form.value, files = partnerFileList.value) => {
+ const {
+ id,
+ partnerName,
+ taxpayerIdentificationNum,
+ companyAddress,
+ companyPhone,
+ bankAccountName,
+ bankAccountNum,
+ contactUserName,
+ contactUserPhone,
+ partnerType,
+ isWhite,
+ remark,
+ } = source || {};
+ return {
+ partnerName,
+ taxpayerIdentificationNum,
+ companyAddress,
+ companyPhone,
+ bankAccountName,
+ bankAccountNum,
+ contactUserName,
+ contactUserPhone,
+ partnerType,
+ isWhite,
+ remark,
+ storageBlobDTOS: (files || [])
+ .filter(item => item && item.id != null)
+ .map(item => ({ id: item.id })),
+ ...(id ? { id } : {}),
+ };
+ };
+
+ const submitForm = () => {
+ proxy.$refs["formRef"].validate(valid => {
+ if (valid) {
+ const api = form.value.id ? updatePartner : addPartner;
+ api(buildPayload()).then(() => {
+ proxy.$modal.msgSuccess("鎿嶄綔鎴愬姛");
+ closeDia();
+ getList();
+ });
+ }
+ });
+ };
+
+ const closeDia = () => {
+ proxy.resetForm("formRef");
+ partnerFileList.value = [];
+ dialogFormVisible.value = false;
+ };
+
+ // 鍏堝脊妗嗗啀鎷夐檮浠讹細璇︽儏鎺ュ彛鎱㈡垨澶辫触鏃讹紝鎸夐挳涔熶笉浼氱湅璧锋潵鍍忔病鍙嶅簲
+ const openAttachment = row => {
+ attachmentList.value = [];
+ attachmentDetail.value = {};
+ attachmentVisible.value = true;
+ attachmentLoading.value = true;
+ getPartner(row.id)
+ .then(res => {
+ attachmentDetail.value = res.data || {};
+ // 鍚堜綔鍟嗚鎯呭悗绔妸闄勪欢鏀惧湪 storageBlobDTOS锛堝叾瀹冩ā鍧楃殑 VO 鎵嶅彨 storageBlobVOS锛夛紝涓や釜閮藉厹浣�
+ attachmentList.value = res.data?.storageBlobVOS || res.data?.storageBlobDTOS || [];
+ })
+ .catch(() => {
+ attachmentList.value = [];
+ })
+ .finally(() => {
+ attachmentLoading.value = false;
+ });
+ };
+
+ // 寮规鍐呬笂浼�/鍒犻櫎鍚庣珛鍗充繚瀛橈細鍚庣鎸� storageBlobDTOS 鍏ㄩ噺瑕嗙洊锛屾墍浠ユ暣浠藉垪琛ㄥ洖浼�
+ const saveAttachments = files => {
+ const list = Array.isArray(files) ? files : attachmentList.value;
+ attachmentList.value = list;
+ if (!attachmentDetail.value?.id) {
+ proxy.$modal.msgError("鏈幏鍙栧埌鍚堜綔鍟嗕俊鎭紝闄勪欢淇濆瓨澶辫触");
+ return;
+ }
+ attachmentLoading.value = true;
+ updatePartner(buildPayload(attachmentDetail.value, list))
+ .then(() => {
+ attachmentDetail.value = {
+ ...attachmentDetail.value,
+ storageBlobDTOS: list,
+ };
+ proxy.$modal.msgSuccess("闄勪欢宸蹭繚瀛�");
+ })
+ .finally(() => {
+ attachmentLoading.value = false;
+ });
+ };
+
+ const handleDelete = () => {
+ if (selectedRows.value.length === 0) {
+ proxy.$modal.msgWarning("璇烽�夋嫨鏁版嵁");
+ return;
+ }
+ const ids = selectedRows.value.map(item => item.id);
+ ElMessageBox.confirm(
+ "閫変腑鐨勫唴瀹瑰皢琚垹闄わ紝鏄惁纭鍒犻櫎锛熷凡瀛樺湪濮斿璁㈠崟鎴栧線鏉ユ敹浠樻璁板綍鐨勫悎浣滃晢涓嶅厑璁稿垹闄ゃ��",
+ "鍒犻櫎",
+ {
+ confirmButtonText: "纭",
+ cancelButtonText: "鍙栨秷",
+ type: "warning",
+ }
+ )
+ .then(() => {
+ delPartner(ids).then(() => {
+ proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛");
+ getList();
+ });
+ })
+ .catch(() => {
+ proxy.$modal.msg("宸插彇娑�");
+ });
+ };
+
+ // 淇濆瓨 Excel 鏂囦欢娴�
+ const saveExcelBlob = (data, filename) => {
+ const blob = new Blob([data], {
+ type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
+ });
+ const url = window.URL.createObjectURL(blob);
+ const link = document.createElement("a");
+ link.href = url;
+ link.download = filename;
+ document.body.appendChild(link);
+ link.click();
+ setTimeout(() => {
+ document.body.removeChild(link);
+ window.URL.revokeObjectURL(url);
+ }, 100);
+ };
+
+ const handleExport = () => {
+ ElMessageBox.confirm("鏄惁纭瀵煎嚭锛�", "瀵煎嚭", {
+ confirmButtonText: "纭",
+ cancelButtonText: "鍙栨秷",
+ type: "warning",
+ })
+ .then(() => {
+ exportPartner(compact(searchForm.value)).then(res => {
+ saveExcelBlob(res, "鍚堜綔鍟嗘。妗�.xlsx");
+ });
+ })
+ .catch(() => {
+ proxy.$modal.msg("宸插彇娑�");
+ });
+ };
+
+ const upload = reactive({
+ open: false,
+ title: "",
+ isUploading: false,
+ headers: { Authorization: "Bearer " + getToken() },
+ url: import.meta.env.VITE_APP_BASE_API + "/outsourcing/partner/import",
+ });
+
+ const handleImport = () => {
+ upload.title = "鍚堜綔鍟嗗鍏�";
+ upload.open = true;
+ };
+
+ const importTemplate = () => {
+ downloadPartnerTemplate().then(res => {
+ saveExcelBlob(res, "鍚堜綔鍟嗗鍏ユā鏉�.xlsx");
+ });
+ };
+
+ const handleFileUploadProgress = () => {
+ upload.isUploading = true;
+ };
+
+ const handleFileSuccess = response => {
+ upload.isUploading = false;
+ if (response.code === 200) {
+ proxy.$modal.msgSuccess("鏂囦欢涓婁紶鎴愬姛");
+ upload.open = false;
+ proxy.$refs["uploadRef"].clearFiles();
+ getList();
+ } else {
+ proxy.$modal.msgError(response.msg);
+ }
+ };
+
+ const handleFileError = () => {
+ upload.isUploading = false;
+ proxy.$modal.msgError("鏂囦欢涓婁紶澶辫触");
+ };
+
+ const submitFileForm = () => {
+ upload.isUploading = true;
+ proxy.$refs["uploadRef"].submit();
+ };
+
+ onMounted(() => {
+ getList();
+ });
+</script>
+
+<style scoped lang="scss">
+ .search_form {
+ margin-bottom: 16px;
+ }
+
+ .actions {
+ display: flex;
+ justify-content: flex-end;
+ margin-bottom: 12px;
+ }
+</style>
diff --git a/src/views/outsourcingManagement/transactions/index.vue b/src/views/outsourcingManagement/transactions/index.vue
new file mode 100644
index 0000000..ed08afc
--- /dev/null
+++ b/src/views/outsourcingManagement/transactions/index.vue
@@ -0,0 +1,255 @@
+<template>
+ <div class="app-container">
+ <div class="search_form"
+ style="margin-bottom: 20px;">
+ <span class="search_title">鍚堜綔鍟嗗悕绉帮細</span>
+ <el-input v-model="searchForm.partnerName"
+ style="width: 240px"
+ placeholder="杈撳叆鍚堜綔鍟嗗悕绉�"
+ @change="handleQuery"
+ clearable
+ :prefix-icon="Search" />
+ <el-button type="primary"
+ @click="handleQuery"
+ style="margin-left: 10px">鎼滅储</el-button>
+ </div>
+ <el-row :gutter="20">
+ <el-col :span="14">
+ <div class="table_list">
+ <el-table border
+ v-loading="tableLoading"
+ :data="tableData"
+ highlight-current-row
+ style="width: 100%"
+ height="calc(100vh - 18.5em)"
+ @row-click="rowClick">
+ <el-table-column align="center"
+ label="搴忓彿"
+ type="index"
+ width="60" />
+ <el-table-column label="鍚堜綔鍟嗗悕绉�"
+ prop="partnerName"
+ min-width="180"
+ show-overflow-tooltip />
+ <el-table-column label="鍚堝悓閲戦(鍏�)"
+ prop="contractAmounts"
+ min-width="130">
+ <template #default="scope">
+ {{ formatAmount(scope.row.contractAmounts) }}
+ </template>
+ </el-table-column>
+ <el-table-column label="浠樻閲戦(鍏�)"
+ prop="paidAmount"
+ min-width="130">
+ <template #default="scope">
+ {{ formatAmount(scope.row.paidAmount) }}
+ </template>
+ </el-table-column>
+ <el-table-column label="搴斾粯閲戦(鍏�)"
+ prop="payableAmount"
+ min-width="130">
+ <template #default="scope">
+ <el-text type="danger">{{ formatAmount(scope.row.payableAmount) }}</el-text>
+ </template>
+ </el-table-column>
+ </el-table>
+ <pagination v-show="total > 0"
+ :total="total"
+ layout="total, sizes, prev, pager, next, jumper"
+ :page="page.current"
+ :limit="page.size"
+ @pagination="paginationChange" />
+ </div>
+ </el-col>
+ <el-col :span="10">
+ <div class="table_list">
+ <div class="detail-tip">
+ 搴斾粯浣欓 = 宸茬‘璁�/宸插畬鎴愮殑濮斿璁㈠崟閲戦 鈭� 宸蹭粯娆撅紱鏄庣粏姣忚鐨勩�屽凡浠樻銆嶄负鍚堜綔鍟嗙骇鍚堣锛岄潪璁㈠崟绾с��
+ </div>
+ <el-table border
+ v-loading="detailLoading"
+ :data="detailTableData"
+ style="width: 100%"
+ height="calc(100vh - 21em)">
+ <el-table-column align="center"
+ label="搴忓彿"
+ type="index"
+ width="60" />
+ <el-table-column label="濮斿璁㈠崟鍙�"
+ prop="orderNo"
+ min-width="150"
+ show-overflow-tooltip />
+ <el-table-column label="鐢宠鏃ユ湡"
+ prop="applyDate"
+ min-width="110"
+ show-overflow-tooltip />
+ <el-table-column label="璁㈠崟閲戦(鍏�)"
+ prop="contractAmount"
+ min-width="120">
+ <template #default="scope">
+ {{ formatAmount(scope.row.contractAmount) }}
+ </template>
+ </el-table-column>
+ <el-table-column label="鐘舵��"
+ prop="status"
+ align="center"
+ width="90">
+ <template #default="scope">
+ <el-tag :type="statusTagType(scope.row.status)"
+ size="small">
+ {{ statusLabel(scope.row.status) }}
+ </el-tag>
+ </template>
+ </el-table-column>
+ <el-table-column label="鍚堜綔鍟嗗凡浠樻(鍏�)"
+ prop="paidAmount"
+ min-width="140">
+ <template #default="scope">
+ {{ formatAmount(scope.row.paidAmount) }}
+ </template>
+ </el-table-column>
+ <el-table-column label="搴斾粯閲戦(鍏�)"
+ prop="payableAmount"
+ min-width="120">
+ <template #default="scope">
+ <el-text type="danger">{{ formatAmount(scope.row.payableAmount) }}</el-text>
+ </template>
+ </el-table-column>
+ <el-table-column fixed="right"
+ label="鎿嶄綔"
+ width="110"
+ align="center">
+ <template #default="scope">
+ <el-button link
+ type="primary"
+ @click.stop="openPaymentDialog(scope.row)">鏂板浠樻</el-button>
+ </template>
+ </el-table-column>
+ </el-table>
+ </div>
+ </el-col>
+ </el-row>
+ <PaymentRecordDialog v-model:visible="paymentDialogVisible"
+ title="鍚堜綔鍟嗕粯娆炬祦姘�"
+ :party-type="3"
+ :party-id="paymentPartyId"
+ :party-name="paymentPartyName"
+ :direction="1"
+ @changed="refreshAll" />
+ </div>
+</template>
+
+<script setup>
+ import { ref, reactive, toRefs } from "vue";
+ import { Search } from "@element-plus/icons-vue";
+ import pagination from "@/components/PIMTable/Pagination.vue";
+ import PaymentRecordDialog from "@/components/PaymentRecordDialog/index.vue";
+ import {
+ partnerTransactions,
+ partnerTransactionsDetails,
+ } from "@/api/outsourcing/report.js";
+
+ const tableData = ref([]);
+ const detailTableData = ref([]);
+ const tableLoading = ref(false);
+ const detailLoading = ref(false);
+ const total = ref(0);
+ const page = reactive({ current: 1, size: 10 });
+ const currentPartnerId = ref("");
+
+ const paymentDialogVisible = ref(false);
+ const paymentPartyId = ref("");
+ const paymentPartyName = ref("");
+
+ const statusLabelMap = { 0: "鑽夌", 1: "宸茬‘璁�", 2: "宸插畬鎴�", 3: "宸蹭綔搴�" };
+ const statusTagMap = { 0: "info", 1: "primary", 2: "success", 3: "danger" };
+ const statusLabel = status => statusLabelMap[status] ?? status;
+ const statusTagType = status => statusTagMap[status] || "info";
+
+ const data = reactive({
+ searchForm: {
+ partnerName: "",
+ },
+ });
+ const { searchForm } = toRefs(data);
+
+ const formatAmount = value => {
+ const num = Number(value);
+ return Number.isFinite(num) ? num.toFixed(2) : "0.00";
+ };
+
+ const getList = () => {
+ tableLoading.value = true;
+ partnerTransactions({
+ pageNum: page.current,
+ pageSize: page.size,
+ ...searchForm.value,
+ })
+ .then(res => {
+ tableLoading.value = false;
+ tableData.value = res.data?.records || [];
+ total.value = res.data?.total || 0;
+ })
+ .catch(() => {
+ tableLoading.value = false;
+ });
+ };
+
+ const getDetailList = partnerId => {
+ if (!partnerId) {
+ detailTableData.value = [];
+ return;
+ }
+ detailLoading.value = true;
+ partnerTransactionsDetails(partnerId)
+ .then(res => {
+ detailLoading.value = false;
+ detailTableData.value = res.data?.records || [];
+ })
+ .catch(() => {
+ detailLoading.value = false;
+ });
+ };
+
+ const handleQuery = () => {
+ page.current = 1;
+ getList();
+ };
+
+ const paginationChange = obj => {
+ page.current = obj.page;
+ page.size = obj.limit;
+ getList();
+ };
+
+ const rowClick = row => {
+ currentPartnerId.value = row.partnerId;
+ getDetailList(row.partnerId);
+ };
+
+ const openPaymentDialog = row => {
+ paymentPartyId.value = row.partnerId;
+ paymentPartyName.value = row.partnerName || "";
+ paymentDialogVisible.value = true;
+ };
+
+ const refreshAll = () => {
+ getList();
+ getDetailList(currentPartnerId.value);
+ };
+
+ getList();
+</script>
+
+<style scoped lang="scss">
+ .detail-tip {
+ margin-bottom: 10px;
+ padding: 8px 12px;
+ font-size: 12px;
+ line-height: 1.6;
+ color: #909399;
+ background: rgba(64, 158, 255, 0.08);
+ border-left: 3px solid #409eff;
+ border-radius: 3px;
+ }
+</style>
diff --git a/src/views/procurementManagement/paymentLedger/index.vue b/src/views/procurementManagement/paymentLedger/index.vue
index 7fba9cb..1478399 100644
--- a/src/views/procurementManagement/paymentLedger/index.vue
+++ b/src/views/procurementManagement/paymentLedger/index.vue
@@ -46,17 +46,31 @@
prop="paymentAmount"
show-overflow-tooltip
:formatter="formattedNumber" />
- <el-table-column label="閫�璐ч噾棰�(鍏�)"
- prop="returnAmount"
- show-overflow-tooltip
- :formatter="formattedNumber" />
+<!-- <el-table-column label="鍥炴閲戦(鍏�)"-->
+<!-- prop="receiptAmount"-->
+<!-- show-overflow-tooltip-->
+<!-- :formatter="formattedNumber" />-->
+<!-- <el-table-column label="閫�璐ч噾棰�(鍏�)"-->
+<!-- prop="returnAmount"-->
+<!-- show-overflow-tooltip-->
+<!-- :formatter="formattedNumber" />-->
<el-table-column label="搴斾粯閲戦(鍏�)"
- prop="payableAmount"
+ prop="paymentRecordAmount"
show-overflow-tooltip>
<template #default="{ row, column }">
<el-text type="danger">
- {{ formattedNumber(row, column, row.payableAmount) }}
+ {{ formattedNumber(row, column, row.paymentRecordAmount) }}
</el-text>
+ </template>
+ </el-table-column>
+ <el-table-column label="鎿嶄綔"
+ align="center"
+ width="90"
+ fixed="right">
+ <template #default="{ row }">
+ <el-button link
+ type="primary"
+ @click.stop="openRecordDialog(row)">娴佹按</el-button>
</template>
</el-table-column>
</el-table>
@@ -90,6 +104,14 @@
</div>
</el-col>
</el-row>
+ <!-- 渚涘簲鍟嗗洖娆炬祦姘寸淮鎶� -->
+ <PaymentRecordDialog v-model:visible="recordDialogVisible"
+ title="渚涘簲鍟嗗洖娆炬祦姘�"
+ :party-type="2"
+ :party-id="recordPartyId"
+ :party-name="recordPartyName"
+ :direction="2"
+ @changed="getList" />
</div>
</template>
@@ -101,6 +123,7 @@
paymentRecordList,
} from "@/api/procurementManagement/paymentLedger.js";
import Pagination from "../../../components/PIMTable/Pagination.vue";
+ import PaymentRecordDialog from "@/components/PaymentRecordDialog/index.vue";
const tableData = ref([]);
const tableLoading = ref(false);
@@ -123,6 +146,16 @@
const isShowSummary = ref(true);
const { searchForm } = toRefs(data);
const currentSupplierId = ref("");
+ // 鍥炴娴佹按寮圭獥
+ const recordDialogVisible = ref(false);
+ const recordPartyId = ref("");
+ const recordPartyName = ref("");
+
+ const openRecordDialog = row => {
+ recordPartyId.value = row.supplierId;
+ recordPartyName.value = row.supplierName || "";
+ recordDialogVisible.value = true;
+ };
const rowClick = row => {
currentSupplierId.value = row.supplierId;
sonPage.current = 1;
@@ -156,19 +189,28 @@
return params ? parseFloat(params).toFixed(2) : 0;
},
},
- {
- label: "閫�璐ч噾棰�(鍏�)",
- prop: "returnAmount",
- width: 200,
- formatData: params => {
- return params ? parseFloat(params).toFixed(2) : 0;
- },
- },
+ // 寰�鏉ユ柟缁村害鍚堣锛屾槑缁嗗悇琛屽悓鍊硷紝鏁呬笉鍙備笌鍚堣琛�
+ // {
+ // label: "鍥炴閲戦(鍏�)",
+ // prop: "receiptAmount",
+ // width: 200,
+ // formatData: params => {
+ // return params ? parseFloat(params).toFixed(2) : 0;
+ // },
+ // },
+ // {
+ // label: "閫�璐ч噾棰�(鍏�)",
+ // prop: "returnAmount",
+ // width: 200,
+ // formatData: params => {
+ // return params ? parseFloat(params).toFixed(2) : 0;
+ // },
+ // },
{
label: "搴斾粯閲戦(鍏�)",
dataType: "slot",
width: 200,
- prop: "payableAmount",
+ prop: "paymentRecordAmount",
slot: "payableAmountSlot",
},
]);
@@ -182,7 +224,7 @@
const summarizeMainTable = param => {
return proxy.summarizeTable(
param,
- ["contractAmounts", "paymentAmount", "returnAmount", "payableAmount"],
+ ["contractAmounts", "paymentAmount", "receiptAmount", "returnAmount", "payableAmount"],
{
ticketsNum: { noDecimal: true }, // 涓嶄繚鐣欏皬鏁�
futureTickets: { noDecimal: true }, // 涓嶄繚鐣欏皬鏁�
diff --git a/src/views/procurementManagement/purchaseApplication/index.vue b/src/views/procurementManagement/purchaseApplication/index.vue
new file mode 100644
index 0000000..a9cf71f
--- /dev/null
+++ b/src/views/procurementManagement/purchaseApplication/index.vue
@@ -0,0 +1,1087 @@
+<template>
+ <div class="app-container">
+ <div class="search_form">
+ <div>
+ <el-form :model="searchForm"
+ :inline="true">
+ <el-form-item label="鐢宠鍗曞彿锛�">
+ <el-input v-model="searchForm.applicationNo"
+ placeholder="璇疯緭鍏�"
+ clearable
+ :prefix-icon="Search"
+ @change="handleQuery" />
+ </el-form-item>
+ <el-form-item label="椤圭洰鍚嶇О锛�">
+ <el-input v-model="searchForm.projectName"
+ placeholder="璇疯緭鍏�"
+ clearable
+ :prefix-icon="Search"
+ @change="handleQuery" />
+ </el-form-item>
+ <el-form-item label="渚涘簲鍟嗭細">
+ <el-select v-model="searchForm.supplierId"
+ placeholder="璇烽�夋嫨"
+ filterable
+ clearable
+ style="width: 240px"
+ @change="handleQuery">
+ <el-option v-for="item in supplierOptions"
+ :key="item.id"
+ :label="item.supplierName"
+ :value="item.id" />
+ </el-select>
+ </el-form-item>
+ <el-form-item label="鐘舵�侊細">
+ <el-select v-model="searchForm.status"
+ placeholder="璇烽�夋嫨"
+ clearable
+ style="width: 160px"
+ @change="handleQuery">
+ <el-option label="鑽夌"
+ :value="0" />
+ <el-option label="宸茬敓鎴愯鍗�"
+ :value="1" />
+ </el-select>
+ </el-form-item>
+ <el-form-item label="鐢宠鏃ユ湡锛�">
+ <el-date-picker v-model="searchForm.applyDate"
+ value-format="YYYY-MM-DD"
+ format="YYYY-MM-DD"
+ type="daterange"
+ placeholder="璇烽�夋嫨"
+ clearable
+ @change="changeDaterange" />
+ </el-form-item>
+ <el-form-item>
+ <el-button type="primary"
+ @click="handleQuery"> 鎼滅储 </el-button>
+ </el-form-item>
+ </el-form>
+ </div>
+ </div>
+ <div class="table_list">
+ <div style="display: flex;justify-content: flex-end;margin-bottom: 20px;">
+ <el-button type="primary"
+ @click="openForm('add')">鏂板鐢宠鍗�</el-button>
+ <el-button type="danger"
+ plain
+ @click="handleDelete()">鍒犻櫎</el-button>
+ </div>
+ <el-table :data="tableData"
+ border
+ v-loading="tableLoading"
+ @selection-change="handleSelectionChange"
+ :row-key="(row) => row.id"
+ show-summary
+ :summary-method="summarizeMainTable"
+ height="calc(100vh - 21.5em)">
+ <el-table-column align="center"
+ type="selection"
+ width="55"
+ :selectable="row => row.status === 0" />
+ <el-table-column align="center"
+ label="搴忓彿"
+ type="index"
+ width="60" />
+ <el-table-column label="鐢宠鍗曞彿"
+ prop="applicationNo"
+ width="170"
+ show-overflow-tooltip />
+ <el-table-column label="渚涘簲鍟嗗悕绉�"
+ prop="supplierName"
+ width="200"
+ show-overflow-tooltip />
+ <el-table-column label="椤圭洰鍚嶇О"
+ prop="projectName"
+ width="280"
+ show-overflow-tooltip />
+ <el-table-column label="閿�鍞悎鍚屽彿"
+ prop="salesContractNo"
+ width="170"
+ show-overflow-tooltip />
+ <el-table-column label="鐢宠浜�"
+ prop="applicantName"
+ width="100"
+ show-overflow-tooltip />
+ <el-table-column label="鐢宠鏃ユ湡"
+ prop="applyDate"
+ width="110"
+ show-overflow-tooltip />
+ <el-table-column label="鏈熸湜鍒拌揣鏃ユ湡"
+ prop="expectedArrivalDate"
+ width="130"
+ show-overflow-tooltip />
+ <el-table-column label="鍚堝悓閲戦(鍏�)"
+ prop="contractAmount"
+ width="140"
+ show-overflow-tooltip
+ :formatter="formattedNumber" />
+ <el-table-column label="浠樻鏂瑰紡"
+ prop="paymentMethod"
+ width="100"
+ show-overflow-tooltip />
+ <el-table-column label="鐘舵��"
+ prop="status"
+ width="110"
+ align="center">
+ <template #default="scope">
+ <el-tag :type="scope.row.status === 1 ? 'success' : 'info'"
+ size="small">
+ {{ statusText[scope.row.status] || '--' }}
+ </el-tag>
+ </template>
+ </el-table-column>
+ <el-table-column label="澶囨敞"
+ prop="remarks"
+ width="200"
+ show-overflow-tooltip />
+ <el-table-column fixed="right"
+ label="鎿嶄綔"
+ width="200"
+ align="center">
+ <template #default="scope">
+ <template v-if="scope.row.status === 0">
+ <el-button link
+ type="primary"
+ @click="openForm('edit', scope.row)">缂栬緫</el-button>
+ <el-button link
+ type="primary"
+ @click="handleConvert(scope.row)">杞鍗�</el-button>
+ <el-button link
+ type="danger"
+ @click="handleDelete(scope.row)">鍒犻櫎</el-button>
+ </template>
+ <el-button v-else
+ link
+ type="primary"
+ @click="openForm('detail', scope.row)">鏌ョ湅</el-button>
+ </template>
+ </el-table-column>
+ </el-table>
+ <pagination v-show="total > 0"
+ :total="total"
+ layout="total, sizes, prev, pager, next, jumper"
+ :page="page.current"
+ :limit="page.size"
+ @pagination="paginationChange" />
+ </div>
+ <FormDialog v-model="dialogFormVisible"
+ :title="dialogTitle"
+ :width="'70%'"
+ :operation-type="operationType"
+ @close="closeDia"
+ @confirm="submitForm"
+ @cancel="closeDia">
+ <el-form :model="form"
+ :disabled="isDetail"
+ label-width="140px"
+ label-position="top"
+ :rules="rules"
+ ref="formRef">
+ <el-row :gutter="30">
+ <el-col :span="12">
+ <el-form-item label="鐢宠鍗曞彿锛�"
+ prop="applicationNo">
+ <el-input v-model="form.applicationNo"
+ placeholder="淇濆瓨鍚庤嚜鍔ㄧ敓鎴�"
+ disabled />
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item label="鐢宠鏃ユ湡锛�"
+ prop="applyDate">
+ <el-date-picker style="width: 100%"
+ v-model="form.applyDate"
+ value-format="YYYY-MM-DD"
+ format="YYYY-MM-DD"
+ type="date"
+ placeholder="璇烽�夋嫨"
+ clearable />
+ </el-form-item>
+ </el-col>
+ </el-row>
+ <el-row :gutter="30">
+ <el-col :span="12">
+ <el-form-item label="渚涘簲鍟嗗悕绉帮細"
+ prop="supplierId">
+ <el-select v-model="form.supplierId"
+ placeholder="璇烽�夋嫨"
+ filterable
+ clearable
+ style="width: 100%">
+ <el-option v-for="item in supplierList"
+ :key="item.id"
+ :label="item.supplierName"
+ :value="item.id" />
+ </el-select>
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item label="椤圭洰鍚嶇О"
+ prop="projectName">
+ <el-input v-model="form.projectName"
+ placeholder="璇疯緭鍏�"
+ clearable />
+ </el-form-item>
+ </el-col>
+ </el-row>
+ <el-row :gutter="30">
+ <el-col :span="12">
+ <el-form-item label="閿�鍞悎鍚屽彿锛�"
+ prop="salesLedgerId">
+ <el-select v-model="form.salesLedgerId"
+ placeholder="璇烽�夋嫨"
+ filterable
+ clearable
+ style="width: 100%"
+ @change="salesLedgerChange">
+ <el-option v-for="item in salesContractList"
+ :key="item.id"
+ :label="item.salesContractNo"
+ :value="item.id" />
+ </el-select>
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item label="鐢宠浜猴細"
+ prop="applicantId">
+ <el-select v-model="form.applicantId"
+ placeholder="涓嶉�夊垯榛樿涓哄綋鍓嶇櫥褰曠敤鎴�"
+ filterable
+ clearable
+ style="width: 100%">
+ <el-option v-for="item in userList"
+ :key="item.userId"
+ :label="item.nickName"
+ :value="item.userId" />
+ </el-select>
+ </el-form-item>
+ </el-col>
+ </el-row>
+ <el-row :gutter="30">
+ <el-col :span="8">
+ <el-form-item label="鏈熸湜鍒拌揣鏃ユ湡锛�"
+ prop="expectedArrivalDate">
+ <el-date-picker style="width: 100%"
+ v-model="form.expectedArrivalDate"
+ value-format="YYYY-MM-DD"
+ format="YYYY-MM-DD"
+ type="date"
+ placeholder="璇烽�夋嫨"
+ clearable />
+ </el-form-item>
+ </el-col>
+ <el-col :span="8">
+ <el-form-item label="鍚堝悓閲戦(鍏�)锛�"
+ prop="contractAmount">
+ <el-input-number v-model="form.contractAmount"
+ :precision="2"
+ :step="0.1"
+ :min="0"
+ placeholder="璇疯緭鍏�"
+ style="width: 100%" />
+ </el-form-item>
+ </el-col>
+ <el-col :span="8">
+ <el-form-item label="浠樻鏂瑰紡锛�"
+ prop="paymentMethod">
+ <el-input v-model="form.paymentMethod"
+ placeholder="璇疯緭鍏�"
+ clearable />
+ </el-form-item>
+ </el-col>
+ </el-row>
+ <el-row>
+ <el-form-item label="浜у搧淇℃伅锛�">
+ <el-button v-if="!isDetail"
+ type="primary"
+ @click="openProductForm('add')">娣诲姞</el-button>
+ <el-button v-if="!isDetail"
+ plain
+ type="danger"
+ @click="deleteProduct">鍒犻櫎</el-button>
+ </el-form-item>
+ </el-row>
+ <el-table :data="productData"
+ border
+ @selection-change="productSelected"
+ show-summary
+ :summary-method="summarizeProTable">
+ <el-table-column align="center"
+ type="selection"
+ width="55"
+ :selectable="() => !isDetail" />
+ <el-table-column align="center"
+ label="搴忓彿"
+ type="index"
+ width="60" />
+ <el-table-column label="浜у搧澶х被"
+ prop="productCategory"
+ show-overflow-tooltip />
+ <el-table-column label="瑙勬牸鍨嬪彿"
+ prop="specificationModel"
+ show-overflow-tooltip />
+ <el-table-column label="鍗曚綅"
+ prop="unit"
+ width="70" />
+ <el-table-column label="鏁伴噺"
+ prop="quantity"
+ width="80" />
+ <el-table-column label="绋庣巼(%)"
+ prop="taxRate"
+ width="80" />
+ <el-table-column label="鍚◣鍗曚环(鍏�)"
+ prop="taxInclusiveUnitPrice"
+ width="120"
+ :formatter="formattedNumber" />
+ <el-table-column label="鍚◣鎬讳环(鍏�)"
+ prop="taxInclusiveTotalPrice"
+ width="120"
+ :formatter="formattedNumber" />
+ <el-table-column label="涓嶅惈绋庢�讳环(鍏�)"
+ prop="taxExclusiveTotalPrice"
+ width="130"
+ :formatter="formattedNumber" />
+ <el-table-column label="鍙戠エ绫诲瀷"
+ prop="invoiceType"
+ width="110"
+ show-overflow-tooltip />
+ <el-table-column label="鏄惁璐ㄦ"
+ prop="isChecked"
+ width="90">
+ <template #default="scope">
+ <el-tag :type="scope.row.isChecked ? 'success' : 'info'"
+ size="small">
+ {{ scope.row.isChecked ? '鏄�' : '鍚�' }}
+ </el-tag>
+ </template>
+ </el-table-column>
+ <el-table-column label="澶囨敞"
+ prop="remark"
+ show-overflow-tooltip />
+ <el-table-column v-if="!isDetail"
+ fixed="right"
+ label="鎿嶄綔"
+ width="80"
+ align="center">
+ <template #default="scope">
+ <el-button link
+ type="primary"
+ @click="openProductForm('edit', scope.row, scope.$index)">缂栬緫
+ </el-button>
+ </template>
+ </el-table-column>
+ </el-table>
+ <el-row :gutter="30">
+ <el-col :span="24">
+ <el-form-item label="澶囨敞锛�"
+ prop="remarks">
+ <el-input v-model="form.remarks"
+ placeholder="璇疯緭鍏�"
+ clearable
+ type="textarea"
+ :rows="2" />
+ </el-form-item>
+ </el-col>
+ </el-row>
+ </el-form>
+ </FormDialog>
+ <FormDialog v-model="productFormVisible"
+ :title="productOperationType === 'add' ? '鏂板浜у搧' : '缂栬緫浜у搧'"
+ :width="'40%'"
+ :operation-type="productOperationType"
+ @close="closeProductDia"
+ @confirm="submitProduct"
+ @cancel="closeProductDia">
+ <el-form :model="productForm"
+ label-width="140px"
+ label-position="top"
+ :rules="productRules"
+ ref="productFormRef">
+ <el-row :gutter="30">
+ <el-col :span="24">
+ <el-form-item label="浜у搧澶х被锛�"
+ prop="productId">
+ <el-tree-select v-model="productForm.productId"
+ placeholder="璇烽�夋嫨"
+ clearable
+ filterable
+ check-strictly
+ @change="getModels"
+ :data="productOptions"
+ :render-after-expand="false"
+ style="width: 100%" />
+ </el-form-item>
+ </el-col>
+ </el-row>
+ <el-row :gutter="30">
+ <el-col :span="24">
+ <el-form-item label="瑙勬牸鍨嬪彿锛�"
+ prop="productModelId">
+ <el-select v-model="productForm.productModelId"
+ placeholder="璇烽�夋嫨"
+ filterable
+ clearable
+ style="width: 100%"
+ @change="getProductModel">
+ <el-option v-for="item in modelOptions"
+ :key="item.id"
+ :label="item.model"
+ :value="item.id" />
+ </el-select>
+ </el-form-item>
+ </el-col>
+ </el-row>
+ <el-row :gutter="30">
+ <el-col :span="12">
+ <el-form-item label="鍗曚綅锛�"
+ prop="unit">
+ <el-input v-model="productForm.unit"
+ placeholder="璇疯緭鍏�"
+ clearable />
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item label="绋庣巼(%)锛�"
+ prop="taxRate">
+ <el-select v-model="productForm.taxRate"
+ placeholder="璇烽�夋嫨"
+ clearable
+ style="width: 100%"
+ @change="calcTotals">
+ <el-option v-for="dict in tax_rate"
+ :key="dict.value"
+ :label="dict.label"
+ :value="dict.value" />
+ </el-select>
+ </el-form-item>
+ </el-col>
+ </el-row>
+ <el-row :gutter="30">
+ <el-col :span="12">
+ <el-form-item label="鍚◣鍗曚环(鍏�)锛�"
+ prop="taxInclusiveUnitPrice">
+ <el-input-number v-model="productForm.taxInclusiveUnitPrice"
+ :precision="2"
+ :step="0.1"
+ :min="0"
+ style="width: 100%"
+ @change="calcTotals" />
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item label="鏁伴噺锛�"
+ prop="quantity">
+ <el-input-number v-model="productForm.quantity"
+ :precision="2"
+ :step="0.1"
+ :min="0"
+ placeholder="璇疯緭鍏�"
+ style="width: 100%"
+ @change="calcTotals" />
+ </el-form-item>
+ </el-col>
+ </el-row>
+ <el-row :gutter="30">
+ <el-col :span="12">
+ <el-form-item label="鍚◣鎬讳环(鍏�)锛�"
+ prop="taxInclusiveTotalPrice">
+ <el-input-number v-model="productForm.taxInclusiveTotalPrice"
+ :precision="2"
+ disabled
+ style="width: 100%" />
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item label="涓嶅惈绋庢�讳环(鍏�)锛�"
+ prop="taxExclusiveTotalPrice">
+ <el-input-number v-model="productForm.taxExclusiveTotalPrice"
+ :precision="2"
+ disabled
+ style="width: 100%" />
+ </el-form-item>
+ </el-col>
+ </el-row>
+ <el-row :gutter="30">
+ <el-col :span="12">
+ <el-form-item label="鍙戠エ绫诲瀷锛�"
+ prop="invoiceType">
+ <el-select v-model="productForm.invoiceType"
+ placeholder="璇烽�夋嫨"
+ clearable
+ style="width: 100%">
+ <el-option label="澧炴櫘绁�"
+ value="澧炴櫘绁�" />
+ <el-option label="澧炰笓绁�"
+ value="澧炰笓绁�" />
+ </el-select>
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item label="鏄惁璐ㄦ锛�"
+ prop="isChecked">
+ <el-radio-group v-model="productForm.isChecked">
+ <el-radio label="鏄�"
+ :value="true" />
+ <el-radio label="鍚�"
+ :value="false" />
+ </el-radio-group>
+ </el-form-item>
+ </el-col>
+ </el-row>
+ <el-row :gutter="30">
+ <el-col :span="24">
+ <el-form-item label="澶囨敞锛�"
+ prop="remark">
+ <el-input v-model="productForm.remark"
+ placeholder="璇疯緭鍏�"
+ clearable />
+ </el-form-item>
+ </el-col>
+ </el-row>
+ </el-form>
+ </FormDialog>
+ </div>
+</template>
+
+<script setup>
+ import FormDialog from "@/components/Dialog/FormDialog.vue";
+ import pagination from "@/components/PIMTable/Pagination.vue";
+ import {
+ ref,
+ reactive,
+ toRefs,
+ computed,
+ getCurrentInstance,
+ nextTick,
+ onMounted,
+ } from "vue";
+ import { ElMessage, ElMessageBox } from "element-plus";
+ import { Search } from "@element-plus/icons-vue";
+ import dayjs from "dayjs";
+ import router from "@/router";
+ import usePermissionStore from "@/store/modules/permission";
+ import {
+ applicationList,
+ getApplicationById,
+ addApplication,
+ editApplication,
+ delApplication,
+ convertToOrder,
+ } from "@/api/procurementManagement/purchaseApplication.js";
+ import {
+ getSalesNo,
+ getOptions,
+ } from "@/api/procurementManagement/procurementLedger.js";
+ import { modelList, productTreeList } from "@/api/basicData/product.js";
+ import { userListNoPage } from "@/api/system/user.js";
+
+ const { proxy } = getCurrentInstance();
+ const { tax_rate } = proxy.useDict("tax_rate");
+ const permissionStore = usePermissionStore();
+
+ const statusText = {
+ 0: "鑽夌",
+ 1: "宸茬敓鎴愯鍗�",
+ };
+
+ const tableData = ref([]);
+ const productData = ref([]);
+ const selectedRows = ref([]);
+ const productSelectedRows = ref([]);
+ const supplierOptions = ref([]);
+ const supplierList = ref([]);
+ const salesContractList = ref([]);
+ const userList = ref([]);
+ const productOptions = ref([]);
+ const modelOptions = ref([]);
+ const tableLoading = ref(false);
+ const dialogFormVisible = ref(false);
+ const productFormVisible = ref(false);
+ const operationType = ref("");
+ const productOperationType = ref("");
+ const productOperationIndex = ref("");
+ const currentId = ref("");
+ const total = ref(0);
+ const page = reactive({ current: 1, size: 10 });
+
+ const isDetail = computed(() => operationType.value === "detail");
+ const dialogTitle = computed(() => {
+ if (operationType.value === "add") return "鏂板閲囪喘鐢宠";
+ if (operationType.value === "edit") return "缂栬緫閲囪喘鐢宠";
+ return "閲囪喘鐢宠璇︽儏";
+ });
+
+ const searchForm = reactive({
+ applicationNo: "",
+ projectName: "",
+ supplierId: "",
+ status: "",
+ applyDate: null,
+ startDate: undefined,
+ endDate: undefined,
+ });
+
+ const data = reactive({
+ form: {
+ id: "",
+ applicationNo: "",
+ supplierId: "",
+ projectName: "",
+ salesContractNo: "",
+ salesLedgerId: "",
+ applicantId: "",
+ applyDate: "",
+ expectedArrivalDate: "",
+ contractAmount: "",
+ paymentMethod: "",
+ remarks: "",
+ },
+ rules: {
+ applyDate: [{ required: true, message: "璇烽�夋嫨", trigger: "change" }],
+ },
+ });
+ const { form, rules } = toRefs(data);
+
+ const productFormData = reactive({
+ productForm: {
+ productId: "",
+ productCategory: "",
+ productModelId: "",
+ specificationModel: "",
+ unit: "",
+ quantity: "",
+ taxRate: "",
+ taxInclusiveUnitPrice: "",
+ taxInclusiveTotalPrice: "",
+ taxExclusiveTotalPrice: "",
+ invoiceType: "",
+ isChecked: false,
+ remark: "",
+ },
+ productRules: {
+ productId: [{ required: true, message: "璇烽�夋嫨", trigger: "change" }],
+ productModelId: [{ required: true, message: "璇烽�夋嫨", trigger: "change" }],
+ unit: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
+ quantity: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
+ taxRate: [{ required: true, message: "璇烽�夋嫨", trigger: "change" }],
+ taxInclusiveUnitPrice: [
+ { required: true, message: "璇疯緭鍏�", trigger: "blur" },
+ ],
+ },
+ });
+ const { productForm, productRules } = toRefs(productFormData);
+
+ const formattedNumber = (row, column, cellValue) => {
+ if (cellValue === null || cellValue === undefined || cellValue === "") {
+ return "--";
+ }
+ return Number(cellValue).toFixed(2);
+ };
+
+ const changeDaterange = value => {
+ if (value) {
+ searchForm.startDate = dayjs(value[0]).format("YYYY-MM-DD");
+ searchForm.endDate = dayjs(value[1]).format("YYYY-MM-DD");
+ } else {
+ searchForm.startDate = undefined;
+ searchForm.endDate = undefined;
+ }
+ handleQuery();
+ };
+
+ const handleQuery = () => {
+ page.current = 1;
+ getList();
+ };
+
+ const paginationChange = obj => {
+ page.current = obj.page;
+ page.size = obj.limit;
+ getList();
+ };
+
+ const getList = () => {
+ tableLoading.value = true;
+ const { applyDate, ...rest } = searchForm;
+ applicationList({ ...rest, pageNum: page.current, pageSize: page.size })
+ .then(res => {
+ const rows = res.rows || res.data?.rows || res.data?.records || [];
+ tableData.value = rows;
+ total.value = res.total ?? res.data?.total ?? 0;
+ })
+ .catch(() => {
+ tableData.value = [];
+ total.value = 0;
+ })
+ .finally(() => {
+ tableLoading.value = false;
+ });
+ };
+
+ const handleSelectionChange = selection => {
+ selectedRows.value = selection;
+ };
+
+ const productSelected = selection => {
+ productSelectedRows.value = selection;
+ };
+
+ const summarizeMainTable = param => {
+ return proxy.summarizeTable(param, ["contractAmount"]);
+ };
+
+ const summarizeProTable = param => {
+ return proxy.summarizeTable(param, [
+ "taxInclusiveUnitPrice",
+ "taxInclusiveTotalPrice",
+ "taxExclusiveTotalPrice",
+ ]);
+ };
+
+ // 渚涘簲鍟嗕笅鎷夛細绛涢�夌敤鍏ㄩ噺锛岃〃鍗曚粎鍙�夌櫧鍚嶅崟渚涘簲鍟嗭紙涓庨噰璐彴璐︿竴鑷达級
+ const getSupplierOptions = async () => {
+ const res = await getOptions();
+ const list = res.data || [];
+ supplierOptions.value = list;
+ supplierList.value = list.filter(item => item.isWhite === 0);
+ };
+
+ const getSalesContractList = async () => {
+ const res = await getSalesNo();
+ salesContractList.value = Array.isArray(res) ? res : res?.data || [];
+ };
+
+ const getUserList = async () => {
+ const res = await userListNoPage();
+ userList.value = res.data || [];
+ };
+
+ const getProductOptions = async () => {
+ const res = await productTreeList();
+ productOptions.value = convertIdToValue(res || []);
+ };
+
+ function convertIdToValue(list) {
+ return list.map(item => {
+ const { id, children, ...rest } = item;
+ const newItem = { ...rest, value: id };
+ if (children && children.length > 0) {
+ newItem.children = convertIdToValue(children);
+ }
+ return newItem;
+ });
+ }
+
+ const findNodeLabelById = (nodes, value) => {
+ for (const node of nodes) {
+ if (node.value === value) {
+ return node.label;
+ }
+ if (node.children && node.children.length > 0) {
+ const found = findNodeLabelById(node.children, value);
+ if (found) return found;
+ }
+ }
+ return "";
+ };
+
+ const findNodeValueByLabel = (nodes, label) => {
+ for (const node of nodes) {
+ if (node.label === label) {
+ return node.value;
+ }
+ if (node.children && node.children.length > 0) {
+ const found = findNodeValueByLabel(node.children, label);
+ if (found) return found;
+ }
+ }
+ return "";
+ };
+
+ const getModels = value => {
+ if (!value) {
+ productForm.value.productCategory = "";
+ modelOptions.value = [];
+ return Promise.resolve([]);
+ }
+ productForm.value.productCategory = findNodeLabelById(
+ productOptions.value,
+ value
+ );
+ return modelList({ id: value }).then(res => {
+ modelOptions.value = res || [];
+ return res;
+ });
+ };
+
+ const getProductModel = value => {
+ const matched = modelOptions.value.find(item => item.id === value);
+ if (matched) {
+ productForm.value.specificationModel = matched.model;
+ productForm.value.unit = matched.unit;
+ } else {
+ productForm.value.specificationModel = "";
+ productForm.value.unit = "";
+ }
+ };
+
+ // 鍚◣鎬讳环 = 鍚◣鍗曚环 脳 鏁伴噺锛涗笉鍚◣鎬讳环鐢卞惈绋庢�讳环涓庣◣鐜囧弽绠�
+ const calcTotals = () => {
+ const { taxInclusiveUnitPrice, quantity, taxRate } = productForm.value;
+ if (!taxInclusiveUnitPrice && taxInclusiveUnitPrice !== 0) return;
+ if (!quantity && quantity !== 0) return;
+ productForm.value.taxInclusiveTotalPrice =
+ proxy.calculateTaxIncludeTotalPrice(taxInclusiveUnitPrice, quantity);
+ if (taxRate) {
+ productForm.value.taxExclusiveTotalPrice =
+ proxy.calculateTaxExclusiveTotalPrice(
+ productForm.value.taxInclusiveTotalPrice,
+ taxRate
+ );
+ }
+ };
+
+ const openProductForm = async (type, row, index) => {
+ productOperationType.value = type;
+ productOperationIndex.value = index;
+ productForm.value = {};
+ proxy.resetForm("productFormRef");
+ productFormVisible.value = true;
+
+ if (productOptions.value.length === 0) {
+ await getProductOptions();
+ }
+ await nextTick();
+
+ if (type === "add") {
+ productForm.value.isChecked = false;
+ return;
+ }
+
+ productForm.value = { ...row };
+ if (!productForm.value.productId && productForm.value.productCategory) {
+ productForm.value.productId = findNodeValueByLabel(
+ productOptions.value,
+ productForm.value.productCategory
+ );
+ }
+ if (productForm.value.productId) {
+ await getModels(productForm.value.productId);
+ await nextTick();
+ if (productForm.value.productModelId) {
+ getProductModel(productForm.value.productModelId);
+ }
+ }
+ };
+
+ const submitProduct = () => {
+ proxy.$refs["productFormRef"].validate(valid => {
+ if (!valid) return;
+ if (productOperationType.value === "add") {
+ productData.value.push({ ...productForm.value });
+ } else {
+ productData.value[productOperationIndex.value] = {
+ ...productForm.value,
+ };
+ }
+ closeProductDia();
+ });
+ };
+
+ const deleteProduct = () => {
+ if (productSelectedRows.value.length === 0) {
+ proxy.$modal.msgWarning("璇烽�夋嫨鏁版嵁");
+ return;
+ }
+ productData.value = productData.value.filter(
+ item => !productSelectedRows.value.includes(item)
+ );
+ productSelectedRows.value = [];
+ };
+
+ const closeProductDia = () => {
+ proxy.resetForm("productFormRef");
+ productFormVisible.value = false;
+ };
+
+ const salesLedgerChange = value => {
+ const matched = salesContractList.value.find(item => item.id === value);
+ form.value.salesContractNo = matched ? matched.salesContractNo : "";
+ };
+
+ const openForm = async (type, row) => {
+ operationType.value = type;
+ currentId.value = row?.id || "";
+ proxy.resetForm("formRef");
+ form.value = {
+ id: "",
+ applicationNo: "",
+ supplierId: "",
+ projectName: "",
+ salesContractNo: "",
+ salesLedgerId: "",
+ applicantId: "",
+ applyDate: dayjs().format("YYYY-MM-DD"),
+ expectedArrivalDate: "",
+ contractAmount: "",
+ paymentMethod: "",
+ remarks: "",
+ };
+ productData.value = [];
+ productSelectedRows.value = [];
+
+ try {
+ await Promise.all([
+ getSupplierOptions(),
+ getSalesContractList(),
+ getUserList(),
+ getProductOptions(),
+ ]);
+ } catch (error) {
+ proxy.$modal.msgError("鍔犺浇鍩虹鏁版嵁澶辫触");
+ return;
+ }
+
+ if (type === "add") {
+ dialogFormVisible.value = true;
+ return;
+ }
+
+ try {
+ const res = await getApplicationById({ id: row.id });
+ form.value = { ...(res.data || {}), id: row.id };
+ productData.value = res.data?.productData || [];
+ } catch (error) {
+ proxy.$modal.msgError("鍔犺浇閲囪喘鐢宠璇︽儏澶辫触");
+ return;
+ }
+ dialogFormVisible.value = true;
+ };
+
+ const closeDia = () => {
+ proxy.resetForm("formRef");
+ dialogFormVisible.value = false;
+ };
+
+ // 鍙彁浜ゅ悗绔厑璁哥殑瀛楁锛岃嚜鍔ㄥ瓧娈碉紙鍗曞彿銆佺姸鎬併�佺鎴风瓑锛変竴寰嬩笉浼�
+ const buildPayload = () => {
+ const payload = {
+ supplierId: form.value.supplierId || null,
+ projectName: form.value.projectName,
+ salesContractNo: form.value.salesContractNo,
+ salesLedgerId: form.value.salesLedgerId || null,
+ applyDate: form.value.applyDate,
+ expectedArrivalDate: form.value.expectedArrivalDate || null,
+ contractAmount:
+ form.value.contractAmount === "" ? null : form.value.contractAmount,
+ paymentMethod: form.value.paymentMethod,
+ remarks: form.value.remarks,
+ productData: productData.value.map(
+ ({ id, purchaseApplicationId, ...rest }) => rest
+ ),
+ };
+ // 鐢宠浜虹暀绌鸿〃绀虹敱鍚庣鍙栧綋鍓嶇櫥褰曠敤鎴�
+ if (form.value.applicantId) {
+ payload.applicantId = form.value.applicantId;
+ }
+ return payload;
+ };
+
+ const submitForm = () => {
+ proxy.$refs["formRef"].validate(async valid => {
+ if (!valid) return;
+ const payload = buildPayload();
+ try {
+ if (operationType.value === "add") {
+ await addApplication(payload);
+ } else {
+ await editApplication({ id: currentId.value, ...payload });
+ }
+ proxy.$modal.msgSuccess("淇濆瓨鎴愬姛");
+ closeDia();
+ getList();
+ } catch (error) {
+ // 閿欒淇℃伅鐢� request 鎷︽埅鍣ㄧ粺涓�鎻愮ず
+ }
+ });
+ };
+
+ const handleDelete = row => {
+ let ids = [];
+ if (row) {
+ ids = [row.id];
+ } else if (selectedRows.value.length > 0) {
+ ids = selectedRows.value.map(item => item.id);
+ } else {
+ proxy.$modal.msgWarning("璇烽�夋嫨鏁版嵁");
+ return;
+ }
+ ElMessageBox.confirm("閫変腑鐨勫唴瀹瑰皢琚垹闄わ紝鏄惁纭鍒犻櫎锛�", "鍒犻櫎", {
+ confirmButtonText: "纭",
+ cancelButtonText: "鍙栨秷",
+ type: "warning",
+ })
+ .then(async () => {
+ await delApplication(ids);
+ proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛");
+ getList();
+ })
+ .catch(() => {});
+ };
+
+ const handleConvert = row => {
+ ElMessageBox.confirm(
+ `纭灏嗙敵璇峰崟銆�${row.applicationNo}銆嶈浆涓洪噰璐鍗曞悧锛熻浆璁㈠崟鍚庡皢鑷姩鎻愪氦閲囪喘瀹℃壒銆俙,
+ "杞鍗�",
+ {
+ confirmButtonText: "纭",
+ cancelButtonText: "鍙栨秷",
+ type: "warning",
+ }
+ )
+ .then(async () => {
+ const res = await convertToOrder(row.id);
+ getList();
+ ElMessageBox.confirm(
+ `杞鍗曟垚鍔燂紝宸茬敓鎴愰噰璐彴璐︼紙id锛�${res.data}锛夈�傛槸鍚﹀墠寰�閲囪喘鍙拌处鏌ョ湅锛焋,
+ "鎻愮ず",
+ {
+ confirmButtonText: "鍓嶅線鏌ョ湅",
+ cancelButtonText: "鐣欏湪鏈〉",
+ type: "success",
+ }
+ )
+ .then(() => {
+ const path = findLedgerPath(permissionStore.sidebarRouters);
+ if (path) {
+ router.push(path);
+ } else {
+ ElMessage.info("璇峰墠寰�銆岄噰璐彴璐︺�嶆煡鐪嬬敓鎴愮殑璁㈠崟");
+ }
+ })
+ .catch(() => {});
+ })
+ .catch(() => {});
+ };
+
+ // 浠庝晶杈规爮鑿滃崟涓畾浣嶉噰璐彴璐﹁矾鐢憋紝鎵句笉鍒版椂涓嶈烦杞粎鎻愮ず
+ const findLedgerPath = (routes, prefix = "") => {
+ for (const route of routes || []) {
+ const fullPath = String(route.path || "").startsWith("/")
+ ? route.path
+ : `${prefix}/${route.path || ""}`;
+ if (route.meta?.title === "閲囪喘鍙拌处") {
+ return fullPath;
+ }
+ const found = findLedgerPath(route.children, fullPath);
+ if (found) return found;
+ }
+ return "";
+ };
+
+ onMounted(() => {
+ getList();
+ getSupplierOptions();
+ });
+</script>
diff --git a/src/views/procurementManagement/transferManagement/index.vue b/src/views/procurementManagement/transferManagement/index.vue
index 3646449..afa1c12 100644
--- a/src/views/procurementManagement/transferManagement/index.vue
+++ b/src/views/procurementManagement/transferManagement/index.vue
@@ -191,9 +191,9 @@
});
// 浣跨敤 Promise.all 澶勭悊鎵�鏈夊紓姝ヨ姹�
const processedOrders = await Promise.all(response.data.records.map(async (order) => {
- // 绛夊緟寮傛鑾峰彇浜у搧璁板綍
+ // 绛夊緟寮傛鑾峰彇浜у搧璁板綍锛堟寜閲囪喘鍙拌处id绮剧‘鍙嶆樉锛屽悎鍚屽彿鍙兘閲嶅/涓虹┖锛�
const productRes = await selectProductRecordListByPuechaserId({
- purchaseContractNumber: order.purchaseContractNumber
+ purchaseLedgerId: order.id
});
// 纭繚 productRes.data 瀛樺湪
@@ -317,9 +317,9 @@
{
try {
loadingProducts.value = true
- // 鏍规嵁鍚堝悓鏌ヨ浜у搧璁板綍
+ // 鏍规嵁閲囪喘鍙拌处id鏌ヨ浜у搧璁板綍
const productRes = await selectProductRecordListByPuechaserId({
- purchaseContractNumber: receiptForm.value.purchaseContractNumber
+ purchaseLedgerId: receiptForm.value.purchaseLedgerId
});
console.log('productRes:', productRes)
operationType.value = 'add'
diff --git a/src/views/salesManagement/deliveryLedger/index.vue b/src/views/salesManagement/deliveryLedger/index.vue
index 33fa45f..e8eaeae 100644
--- a/src/views/salesManagement/deliveryLedger/index.vue
+++ b/src/views/salesManagement/deliveryLedger/index.vue
@@ -2,7 +2,17 @@
<div class="app-container">
<div class="search_form">
<el-form :model="searchForm" :inline="true">
- <el-form-item label="閿�鍞鍗曞彿锛�">
+ <el-form-item label="鍙戣揣鍗曞彿锛�">
+ <el-input
+ v-model="searchForm.shippingNo"
+ placeholder="璇疯緭鍏�"
+ clearable
+ prefix-icon="Search"
+ style="width: 200px"
+ @change="handleQuery"
+ />
+ </el-form-item>
+ <el-form-item label="閿�鍞悎鍚屽彿锛�">
<el-input
v-model="searchForm.salesContractNo"
placeholder="璇疯緭鍏�"
@@ -15,16 +25,6 @@
<el-form-item label="杞︾墝鍙凤細">
<el-input
v-model="searchForm.shippingCarNumber"
- placeholder="璇疯緭鍏�"
- clearable
- prefix-icon="Search"
- style="width: 200px"
- @change="handleQuery"
- />
- </el-form-item>
- <el-form-item label="蹇�掑崟鍙凤細">
- <el-input
- v-model="searchForm.expressNumber"
placeholder="璇疯緭鍏�"
clearable
prefix-icon="Search"
@@ -50,17 +50,27 @@
border
v-loading="tableLoading"
@selection-change="handleSelectionChange"
- :row-key="(row) => row.id"
+ :row-key="(row) => `${row.source || 'shipping'}-${row.stockOutRecordId ?? row.id}`"
style="width: 100%"
height="calc(100vh - 21.5em)"
>
- <el-table-column align="center" type="selection" width="55" />
- <el-table-column align="center" label="搴忓彿" type="index" width="60" />
<el-table-column
- label="閿�鍞鍗�"
- prop="salesContractNo"
- show-overflow-tooltip
+ align="center"
+ type="selection"
+ :selectable="isRowSelectable"
+ width="55"
/>
+ <el-table-column align="center" label="搴忓彿" type="index" width="60" />
+ <el-table-column label="鏉ユ簮" prop="source" align="center" width="100">
+ <template #default="scope">
+ <el-tag
+ :type="isOilOutRow(scope.row) ? 'success' : 'info'"
+ size="small"
+ >
+ {{ isOilOutRow(scope.row) ? "娌瑰搧鍑哄簱" : "鍙戣揣鍗�" }}
+ </el-tag>
+ </template>
+ </el-table-column>
<el-table-column
label="鍙戣揣璁㈠崟鍙�"
prop="shippingNo"
@@ -72,6 +82,16 @@
show-overflow-tooltip
/>
<el-table-column
+ label="瀹㈡埛瀵规帴浜�"
+ prop="contactPerson"
+ show-overflow-tooltip
+ />
+ <el-table-column
+ label="瀵规帴浜虹數璇�"
+ prop="contactPhone"
+ show-overflow-tooltip
+ />
+ <el-table-column
label="浜у搧鍚嶇О"
prop="productName"
show-overflow-tooltip
@@ -79,6 +99,27 @@
<el-table-column
label="瑙勬牸鍨嬪彿"
prop="specificationModel"
+ show-overflow-tooltip
+ />
+ <el-table-column label="鎵瑰彿" show-overflow-tooltip>
+ <!-- 娌瑰搧鍑哄簱琛岀殑鎵瑰彿鏄� outBatchNo锛坆atch_no锛夛紝鍙戣揣鍗曡娌℃湁璇ュ瓧娈� -->
+ <template #default="scope">
+ {{
+ isOilOutRow(scope.row)
+ ? scope.row.outBatchNo || "--"
+ : scope.row.batchNo || "--"
+ }}
+ </template>
+ </el-table-column>
+ <el-table-column
+ label="鍑哄簱绫诲埆"
+ prop="outCategory"
+ show-overflow-tooltip
+ />
+ <el-table-column label="鍌ㄧ綈鍙�" prop="tankNo" show-overflow-tooltip />
+ <el-table-column
+ label="娌瑰簱"
+ prop="oilDepotName"
show-overflow-tooltip
/>
<el-table-column
@@ -97,13 +138,8 @@
show-overflow-tooltip
/>
<el-table-column
- label="蹇�掑叕鍙�"
- prop="expressCompany"
- show-overflow-tooltip
- />
- <el-table-column
- label="蹇�掑崟鍙�"
- prop="expressNumber"
+ label="鍙告満濮撳悕"
+ prop="driverName"
show-overflow-tooltip
/>
<el-table-column
@@ -173,21 +209,6 @@
>
<el-row :gutter="30">
<el-col :span="24">
- <el-form-item label="鍙戣揣绫诲瀷锛�" prop="type">
- <el-select
- v-model="form.type"
- placeholder="璇烽�夋嫨鍙戣揣绫诲瀷"
- style="width: 100%"
- @change="handleShippingTypeChange"
- >
- <el-option label="璐ц溅" value="璐ц溅" />
- <el-option label="蹇��" value="蹇��" />
- </el-select>
- </el-form-item>
- </el-col>
- </el-row>
- <el-row :gutter="30">
- <el-col :span="24">
<el-form-item label="鍙戣揣鏃ユ湡锛�" prop="shippingDate">
<el-date-picker
style="width: 100%"
@@ -202,31 +223,11 @@
</el-col>
</el-row>
<el-row :gutter="30">
- <el-col :span="24" v-if="form.type === '璐ц溅'">
+ <el-col :span="24">
<el-form-item label="鍙戣揣杞︾墝鍙凤細" prop="shippingCarNumber">
<el-input
v-model="form.shippingCarNumber"
placeholder="璇疯緭鍏ュ彂璐ц溅鐗屽彿"
- clearable
- />
- </el-form-item>
- </el-col>
- <el-col :span="24" v-else>
- <el-form-item label="蹇�掑叕鍙革細" prop="expressCompany">
- <el-input
- v-model="form.expressCompany"
- placeholder="璇疯緭鍏ュ揩閫掑叕鍙�"
- clearable
- />
- </el-form-item>
- </el-col>
- </el-row>
- <el-row :gutter="30" v-if="form.type === '蹇��'">
- <el-col :span="24">
- <el-form-item label="蹇�掑崟鍙凤細" prop="expressNumber">
- <el-input
- v-model="form.expressNumber"
- placeholder="璇疯緭鍏ュ揩閫掑崟鍙�"
clearable
/>
</el-form-item>
@@ -257,14 +258,17 @@
>
<div v-if="detailRow" class="detail-wrapper">
<el-descriptions :column="2" border>
- <el-descriptions-item label="閿�鍞鍗�">{{
- detailRow.salesContractNo || "--"
- }}</el-descriptions-item>
<el-descriptions-item label="鍙戣揣璁㈠崟鍙�">{{
detailRow.shippingNo || "--"
}}</el-descriptions-item>
<el-descriptions-item label="瀹㈡埛鍚嶇О">{{
detailRow.customerName || "--"
+ }}</el-descriptions-item>
+ <el-descriptions-item label="瀹㈡埛瀵规帴浜�">{{
+ detailRow.contactPerson || "--"
+ }}</el-descriptions-item>
+ <el-descriptions-item label="瀵规帴浜虹數璇�">{{
+ detailRow.contactPhone || "--"
}}</el-descriptions-item>
<el-descriptions-item label="浜у搧鍚嶇О">{{
detailRow.productName || "--"
@@ -272,8 +276,14 @@
<el-descriptions-item label="瑙勬牸鍨嬪彿">{{
detailRow.specificationModel || "--"
}}</el-descriptions-item>
- <el-descriptions-item label="鍙戣揣绫诲瀷">{{
- detailRow.type || "--"
+ <el-descriptions-item label="鍑哄簱绫诲埆">{{
+ detailRow.outCategory || "--"
+ }}</el-descriptions-item>
+ <el-descriptions-item label="鍌ㄧ綈鍙�">{{
+ detailRow.tankNo || "--"
+ }}</el-descriptions-item>
+ <el-descriptions-item label="娌瑰簱">{{
+ detailRow.oilDepotName || "--"
}}</el-descriptions-item>
<el-descriptions-item label="鍙戣揣鏃ユ湡">{{
detailRow.shippingDate || "--"
@@ -284,11 +294,8 @@
<el-descriptions-item label="鍙戣揣杞︾墝鍙�">{{
detailRow.shippingCarNumber || "--"
}}</el-descriptions-item>
- <el-descriptions-item label="蹇�掑叕鍙�">{{
- detailRow.expressCompany || "--"
- }}</el-descriptions-item>
- <el-descriptions-item label="蹇�掑崟鍙�" :span="2">{{
- detailRow.expressNumber || "--"
+ <el-descriptions-item label="鍙告満濮撳悕">{{
+ detailRow.driverName || "--"
}}</el-descriptions-item>
<el-descriptions-item label="鍑哄簱鍗曞彿" :span="2">{{
detailRow.outboundBatches || "--"
@@ -321,7 +328,7 @@
/>
<el-table-column
label="鍙戣揣鏁伴噺"
- prop="deliveryQuantity"
+ prop="totalQuantity"
min-width="120"
align="center"
/>
@@ -356,7 +363,6 @@
const tableData = ref([]);
const selectedRows = ref([]);
const tableLoading = ref(false);
-const salesOrderOptions = ref([]);
const page = reactive({
current: 1,
size: 100,
@@ -373,46 +379,27 @@
const dialogFormVisible = ref(false);
const data = reactive({
searchForm: {
- salesContractNo: "", // 閿�鍞鍗曞彿
+ shippingNo: "", // 鍙戣揣鍗曞彿锛堜袱绫绘潵婧愰兘鍖归厤锛�
+ salesContractNo: "", // 閿�鍞悎鍚屽彿锛堝彧鍖归厤鍙戣揣鍗曡锛�
shippingCarNumber: "", // 杞︾墝鍙�
- expressNumber: "", // 蹇�掑崟鍙�
},
form: {
id: null,
- salesContractNo: "",
customerName: "",
specificationModel: "",
productName: "",
- type: "璐ц溅", // 璐ц溅, 蹇��
shippingDate: "",
shippingCarNumber: "",
- expressCompany: "",
- expressNumber: "", // 蹇�掑崟鍙�
},
rules: {
- salesContractNo: [
- { required: true, message: "璇烽�夋嫨閿�鍞鍗�", trigger: "change" },
- ],
customerName: [
{ required: true, message: "璇疯緭鍏ュ鎴峰悕绉�", trigger: "blur" },
],
- type: [{ required: true, message: "璇烽�夋嫨鍙戣揣绫诲瀷", trigger: "change" }],
shippingDate: [
{ required: true, message: "璇烽�夋嫨鍙戣揣鏃堕棿", trigger: "change" },
],
shippingCarNumber: [
- {
- validator: (_, value, callback) =>
- validateShippingCarNumber(value, callback),
- trigger: "blur",
- },
- ],
- expressCompany: [
- {
- validator: (_, value, callback) =>
- validateExpressCompany(value, callback),
- trigger: "blur",
- },
+ { required: true, message: "璇疯緭鍏ュ彂璐ц溅鐗屽彿", trigger: "blur" },
],
},
});
@@ -444,19 +431,14 @@
});
};
-// 閿�鍞鍗曞彉鍖栨椂鑷姩濉厖瀹㈡埛鍚嶇О
-const handleSalesOrderChange = (value) => {
- const selectedOrder = salesOrderOptions.value.find(
- (item) => item.salesContractNo === value
- );
- if (selectedOrder) {
- form.value.customerName = selectedOrder.customerName;
- }
-};
+// 娌瑰搧鍑哄簱琛屾潵鑷� stock_out_record锛宨d 涓� shipping_info 浼氭挒鍙凤紝
+// 鍙戣揣/鍒犻櫎/璇︽儏绛夋帴鍙h儗鍚庨兘鏄� shipping_info锛屼笉鑳界敤浜庤繖绫昏
+const isOilOutRow = (row) => row?.source === "oilOut";
+const isRowSelectable = (row) => !isOilOutRow(row);
// 琛ㄦ牸閫夋嫨鏁版嵁
const handleSelectionChange = (selection) => {
- selectedRows.value = selection;
+ selectedRows.value = selection.filter((item) => !isOilOutRow(item));
};
// 鎵撳紑寮规
@@ -472,13 +454,9 @@
if (type === "edit" && row) {
form.value = {
id: row.id ?? null,
- salesContractNo: row.salesContractNo ?? "",
customerName: row.customerName ?? "",
- type: row.type || "璐ц溅",
shippingDate: row.shippingDate || getCurrentDate(),
shippingCarNumber: row.shippingCarNumber ?? "",
- expressCompany: row.expressCompany ?? "",
- expressNumber: row.expressNumber ?? "",
};
deliveryFileList.value = row.storageBlobVOs || [];
}
@@ -491,7 +469,8 @@
detailRow.value = row || null;
detailProductList.value = [];
detailDialogVisible.value = true;
- if (!row?.id) return;
+ // 娌瑰搧鍑哄簱琛屼笉璧板彂璐у崟璇︽儏鎺ュ彛锛坕d 浼氭挒鍙凤級锛岀洿鎺ョ敤鍒楄〃琛屾暟鎹睍绀�
+ if (!row?.id || isOilOutRow(row)) return;
try {
const res = await getDeliveryDetail(row.id);
const detailData = res?.data;
@@ -554,7 +533,9 @@
productName: item.productName ?? row.productName ?? "--",
specificationModel:
item.specificationModel ?? item.model ?? row.specificationModel ?? "--",
- deliveryQuantity:
+ totalQuantity:
+ item.totalQuantity ??
+ row.totalQuantity ??
item.deliveryQuantity ??
item.quantity ??
item.shippingQuantity ??
@@ -582,14 +563,8 @@
if (valid) {
const payload = {
id: form.value.id,
- type: form.value.type,
shippingDate: form.value.shippingDate,
- shippingCarNumber:
- form.value.type === "璐ц溅" ? form.value.shippingCarNumber : "",
- expressCompany:
- form.value.type === "蹇��" ? form.value.expressCompany : "",
- expressNumber:
- form.value.type === "蹇��" ? form.value.expressNumber : "",
+ shippingCarNumber: form.value.shippingCarNumber,
storageBlobDTOs: deliveryFileList.value || [],
};
deductStock(payload).then((res) => {
@@ -680,20 +655,6 @@
});
};
-// 鍙戣揣绫诲瀷鏍¢獙锛氳揣杞︽椂瑕佹眰杞︾墝锛屽揩閫掓椂瑕佹眰蹇�掑叕鍙�
-const validateShippingCarNumber = (value, callback) => {
- if (form.value.type === "璐ц溅") {
- if (!value) return callback(new Error("璇疯緭鍏ュ彂璐ц溅鐗屽彿"));
- }
- callback();
-};
-const validateExpressCompany = (value, callback) => {
- if (form.value.type === "蹇��") {
- if (!value) return callback(new Error("璇疯緭鍏ュ揩閫掑叕鍙�"));
- }
- callback();
-};
-
// 鍙戣揣鍥剧墖涓婁紶鍓嶆牎妫�
function handleDeliveryBeforeUpload(file) {
// 鏍℃鏂囦欢绫诲瀷
@@ -764,16 +725,6 @@
}
}
}
-
-// 鍙戣揣绫诲瀷鍒囨崲鏃舵竻绌哄搴斿瓧娈�
-const handleShippingTypeChange = (val) => {
- if (val === "璐ц溅") {
- form.value.expressCompany = "";
- form.value.expressNumber = "";
- } else {
- form.value.shippingCarNumber = "";
- }
-};
// 鑾峰彇瀹℃牳鐘舵�佹枃鏈�
const getApprovalStatusText = (status) => {
diff --git a/src/views/salesManagement/receiptPaymentLedger/index.vue b/src/views/salesManagement/receiptPaymentLedger/index.vue
index c5e3415..e8aecbc 100644
--- a/src/views/salesManagement/receiptPaymentLedger/index.vue
+++ b/src/views/salesManagement/receiptPaymentLedger/index.vue
@@ -42,12 +42,7 @@
:formatter="formattedNumber"
width="200" />
<el-table-column label="鍥炴閲戦(鍏�)"
- prop="receiptPaymentAmount"
- show-overflow-tooltip
- :formatter="formattedNumber"
- width="200" />
- <el-table-column label="閫�璐ч噾棰�(鍏�)"
- prop="returnAmount"
+ prop="receiptRecordAmount"
show-overflow-tooltip
:formatter="formattedNumber"
width="200" />
@@ -59,6 +54,16 @@
<el-text type="danger">
{{ formattedNumber(row, column, row.receiptableAmount) }}
</el-text>
+ </template>
+ </el-table-column>
+ <el-table-column label="鎿嶄綔"
+ align="center"
+ width="90"
+ fixed="right">
+ <template #default="{ row }">
+ <el-button link
+ type="primary"
+ @click.stop="openRecordDialog(row)">娴佹按</el-button>
</template>
</el-table-column>
</el-table>
@@ -97,12 +102,7 @@
:formatter="formattedNumber"
width="200" />
<el-table-column label="鍥炴閲戦(鍏�)"
- prop="receiptPaymentAmount"
- show-overflow-tooltip
- :formatter="formattedNumber"
- width="200" />
- <el-table-column label="閫�璐ч噾棰�(鍏�)"
- prop="returnAmount"
+ prop="receiptRecordAmount"
show-overflow-tooltip
:formatter="formattedNumber"
width="200" />
@@ -126,6 +126,14 @@
</div>
</el-col>
</el-row>
+ <!-- 瀹㈡埛浠樻娴佹按缁存姢 -->
+ <PaymentRecordDialog v-model:visible="recordDialogVisible"
+ title="瀹㈡埛浠樻娴佹按"
+ :party-type="1"
+ :party-id="recordPartyId"
+ :party-name="recordPartyName"
+ :direction="1"
+ @changed="getList" />
</div>
</template>
@@ -136,6 +144,7 @@
customewTransactionsDetails,
} from "@/api/salesManagement/indicatorStats.js";
import Pagination from "../../../components/PIMTable/Pagination.vue";
+ import PaymentRecordDialog from "@/components/PaymentRecordDialog/index.vue";
const { proxy } = getCurrentInstance();
const tableData = ref([]);
const receiptRecord = ref([]);
@@ -159,6 +168,16 @@
const customerId = ref("");
const { searchForm } = toRefs(data);
const originReceiptRecord = ref([]);
+ // 浠樻娴佹按寮圭獥
+ const recordDialogVisible = ref(false);
+ const recordPartyId = ref("");
+ const recordPartyName = ref("");
+
+ const openRecordDialog = row => {
+ recordPartyId.value = row.customerId;
+ recordPartyName.value = row.customerName || "";
+ recordDialogVisible.value = true;
+ };
// 鏌ヨ鍒楄〃
/** 鎼滅储鎸夐挳鎿嶄綔 */
const handleQuery = () => {
@@ -190,12 +209,7 @@
const summarizeMainTable = param => {
return proxy.summarizeTable(
param,
- [
- "contractAmounts",
- "receiptPaymentAmount",
- "returnAmount",
- "receiptableAmount",
- ],
+ ["contractAmounts", "receiptPaymentAmount", "receiptableAmount"],
{
ticketsNum: { noDecimal: true }, // 涓嶄繚鐣欏皬鏁�
futureTickets: { noDecimal: true }, // 涓嶄繚鐣欏皬鏁�
@@ -206,12 +220,7 @@
const summarizeMainTable1 = param => {
var summarizeTable = proxy.summarizeTable(
param,
- [
- "contractAmount",
- "receiptPaymentAmount",
- "returnAmount",
- "receiptableAmount",
- ],
+ ["contractAmount", "receiptPaymentAmount", "receiptableAmount"],
{
ticketsNum: { noDecimal: true }, // 涓嶄繚鐣欏皬鏁�
futureTickets: { noDecimal: true }, // 涓嶄繚鐣欏皬鏁�
diff --git a/src/views/salesManagement/salesLedger/index.vue b/src/views/salesManagement/salesLedger/index.vue
index d2e82b5..c7d4c54 100644
--- a/src/views/salesManagement/salesLedger/index.vue
+++ b/src/views/salesManagement/salesLedger/index.vue
@@ -40,11 +40,32 @@
@change="changeDaterange"
/>
</el-form-item>
+ <el-form-item label="瀹℃壒鐘舵�侊細">
+ <el-select
+ v-model="searchForm.approvalStatus"
+ placeholder="璇烽�夋嫨"
+ clearable
+ style="width: 160px"
+ @change="handleQuery"
+ >
+ <el-option
+ v-for="item in approvalStatusOptions"
+ :key="item"
+ :label="item"
+ :value="item"
+ />
+ </el-select>
+ </el-form-item>
<el-form-item>
<el-button type="primary" @click="handleQuery"> 鎼滅储 </el-button>
</el-form-item>
</el-form>
</div>
+ <!-- 鍗栨补 / 浠e偍鎷嗘垚涓や釜椤电锛屽悇鑷浐瀹氬甫 ledgerType 璋冨悓涓�涓垪琛ㄦ帴鍙� -->
+ <el-tabs v-model="activeTab" @tab-change="handleTabChange" class="ledger-tabs">
+ <el-tab-pane label="閿�鍞彴璐︼紙鍗栨补锛�" name="鍗栨补" />
+ <el-tab-pane label="浠e偍鍙拌处" name="浠e偍" />
+ </el-tabs>
<div class="table_list">
<div class="actions">
<div></div>
@@ -72,7 +93,7 @@
style="width: 100%"
:summary-method="summarizeMainTable"
@expand-change="expandChange"
- height="calc(100vh - 18.5em)"
+ height="calc(100vh - 21.5em)"
>
<el-table-column
align="center"
@@ -221,9 +242,22 @@
label="鍚堝悓閲戦(鍏�)"
prop="contractAmount"
width="220"
- show-overflow-tooltip
- :formatter="formattedNumber"
- />
+ >
+ <template #default="{ row, column }">
+ <el-tooltip
+ v-if="row.exceedsTrustFund"
+ :content="`鍚堝悓閲戦宸茶秴鍑鸿瀹㈡埛淇℃墭鍩洪噾锛堜俊鎵樺熀閲戯細${row.trustFund}锛塦"
+ placement="top"
+ >
+ <el-text type="danger">
+ {{ formattedNumber(row, column, row.contractAmount) }}
+ </el-text>
+ </el-tooltip>
+ <span v-else>
+ {{ formattedNumber(row, column, row.contractAmount) }}
+ </span>
+ </template>
+ </el-table-column>
<el-table-column
label="瀹為檯鍚堝悓閲戦(鍏�)"
prop="netContractAmount"
@@ -255,6 +289,33 @@
width="120"
show-overflow-tooltip
/>
+ <el-table-column
+ label="閿�鍞被鍨�"
+ prop="ledgerType"
+ width="100"
+ show-overflow-tooltip
+ />
+ <el-table-column
+ v-if="activeTab === '浠e偍'"
+ label="娌瑰簱"
+ prop="oilDepotName"
+ width="180"
+ show-overflow-tooltip
+ />
+ <el-table-column
+ v-if="activeTab === '浠e偍'"
+ label="鍌ㄧ綈鍙�"
+ prop="tankNo"
+ width="140"
+ show-overflow-tooltip
+ />
+ <el-table-column label="瀹℃壒鐘舵��" width="110" align="center">
+ <template #default="{ row }">
+ <el-tag :type="getApprovalStatusType(row.approvalStatus)" size="small">
+ {{ row.approvalStatus || "-" }}
+ </el-tag>
+ </template>
+ </el-table-column>
<el-table-column
label="澶囨敞"
prop="remarks"
@@ -314,8 +375,24 @@
:rules="rules"
ref="formRef"
>
+ <el-row :gutter="30">
+ <el-col :span="12">
+ <el-form-item label="閿�鍞被鍨嬶細" prop="ledgerType">
+ <el-radio-group
+ v-model="form.ledgerType"
+ :disabled="operationType === 'view'"
+ >
+ <el-radio value="鍗栨补">鍗栨补</el-radio>
+ <el-radio value="浠e偍">鍗栧瓨鍌ㄤ綅缃紙浠e偍锛�</el-radio>
+ </el-radio-group>
+ </el-form-item>
+ </el-col>
+ </el-row>
<!-- 鎶ヤ环鍗曞鍏ュ叆鍙o細鏀惧湪琛ㄥ崟椤堕儴锛岄�夋嫨鍚庡弽鏄惧鎴�/涓氬姟鍛樼瓑 -->
- <el-row v-if="operationType === 'add'" style="margin-bottom: 10px">
+ <el-row
+ v-if="operationType === 'add' && form.ledgerType !== '浠e偍'"
+ style="margin-bottom: 10px"
+ >
<el-col :span="24" style="text-align: right">
<el-button type="primary" plain @click="openQuotationDialog">
浠庨攢鍞姤浠峰鍏�
@@ -379,6 +456,7 @@
clearable
:disabled="operationType === 'view'"
filterable
+ @change="loadCustomerTrustFund"
>
<el-option
v-for="item in customerOption"
@@ -483,7 +561,72 @@
</el-form-item>
</el-col>
</el-row>
- <el-row>
+ <!-- 浠e偍锛氬崠瀛樺偍浣嶇疆锛屽彧閫夋补搴�+鍌ㄧ綈骞舵墜宸ュ~鍚堝悓閲戦 -->
+ <el-row v-if="form.ledgerType === '浠e偍'" :gutter="30">
+ <el-col :span="12">
+ <el-form-item label="娌瑰簱锛�" prop="oilDepotId">
+ <el-select
+ v-model="form.oilDepotId"
+ placeholder="璇烽�夋嫨"
+ clearable
+ filterable
+ :disabled="operationType === 'view'"
+ style="width: 100%"
+ >
+ <el-option
+ v-for="item in oilDepotOptions"
+ :key="item.id"
+ :label="item.depotName"
+ :value="item.id"
+ />
+ </el-select>
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item label="鍌ㄧ綈鍙凤細" prop="tankId">
+ <el-select
+ v-model="form.tankId"
+ placeholder="璇烽�夋嫨"
+ clearable
+ filterable
+ :disabled="operationType === 'view'"
+ style="width: 100%"
+ >
+ <el-option
+ v-for="item in tankOptions"
+ :key="item.id"
+ :label="item.tankNo"
+ :value="item.id"
+ />
+ </el-select>
+ </el-form-item>
+ </el-col>
+ </el-row>
+ <el-row v-if="form.ledgerType === '浠e偍'" :gutter="30">
+ <el-col :span="12">
+ <el-form-item label="鍚堝悓閲戦(鍏�)锛�" prop="contractAmount">
+ <el-input-number
+ v-model="form.contractAmount"
+ :min="0"
+ :precision="2"
+ :controls="false"
+ placeholder="璇疯緭鍏�"
+ :disabled="operationType === 'view'"
+ style="width: 100%"
+ />
+ <!-- 閫変腑瀹㈡埛鍚庢湰鍦版瘮瀵逛俊鎵樺熀閲戯紝瀹炴椂棰勮鏄惁瓒呴檺 -->
+ <el-alert
+ v-if="isContractOverTrustFund"
+ type="warning"
+ show-icon
+ :closable="false"
+ class="trust-fund-alert"
+ :title="`鍚堝悓閲戦宸茶秴鍑鸿瀹㈡埛淇℃墭鍩洪噾锛堜俊鎵樺熀閲戯細${selectedCustomerTrustFund}锛塦"
+ />
+ </el-form-item>
+ </el-col>
+ </el-row>
+ <el-row v-if="form.ledgerType !== '浠e偍'">
<el-form-item label="浜у搧淇℃伅锛�" prop="entryDate">
<el-button
v-if="operationType !== 'view'"
@@ -501,6 +644,7 @@
</el-form-item>
</el-row>
<el-table
+ v-if="form.ledgerType !== '浠e偍'"
:data="productData"
border
@selection-change="productSelected"
@@ -1172,7 +1316,7 @@
<script setup>
import { getToken } from "@/utils/auth";
import pagination from "@/components/PIMTable/Pagination.vue";
-import { onMounted, ref, getCurrentInstance } from "vue";
+import { computed, onMounted, ref, getCurrentInstance } from "vue";
import { addShippingInfo } from "@/api/salesManagement/deliveryLedger.js";
import { ElMessageBox, ElMessage } from "element-plus";
import useUserStore from "@/store/modules/user";
@@ -1198,7 +1342,9 @@
import FileUpload from "@/components/AttachmentUpload/file/index.vue";
import ImageUpload from "@/components/AttachmentUpload/image/index.vue";
import { getCurrentDate } from "@/utils/index.js";
-import { listCustomer } from "@/api/basicData/customer.js";
+import { getCustomer, listCustomer } from "@/api/basicData/customer.js";
+import { allOilDepot } from "@/api/basicData/oilDepot.js";
+import { allTankInfo } from "@/api/basicData/tankInfo.js";
const FileList = defineAsyncComponent(() =>
import("@/components/Dialog/FileList.vue")
@@ -1209,6 +1355,14 @@
const userStore = useUserStore();
const { proxy } = getCurrentInstance();
const { tax_rate } = proxy.useDict("tax_rate");
+// 鍗栨补 / 浠e偍涓や釜椤电锛屽浐瀹氬甫 ledgerType 璋冨悓涓�涓垪琛ㄦ帴鍙�
+const activeTab = ref("鍗栨补");
+const approvalStatusOptions = ["寰呭鎵�", "瀹℃牳涓�", "閫氳繃", "鎷掔粷"];
+// 浠e偍琛ㄥ崟锛氭补搴撱�佸偍缃愪笅鎷夛紙涓よ�呬簰鐩哥嫭绔嬶紝鍌ㄧ綈涓嶆寕鍦ㄦ补搴撲笅锛�
+const oilDepotOptions = ref([]);
+const tankOptions = ref([]);
+// 浠e偍琛ㄥ崟锛氶�変腑瀹㈡埛鐨勪俊鎵樺熀閲戯紝鐢ㄤ簬鍚堝悓閲戦瓒呴檺瀹炴椂棰勮
+const selectedCustomerTrustFund = ref(null);
const tableData = ref([]);
const productData = ref([]);
const selectedRows = ref([]);
@@ -1236,6 +1390,7 @@
entryDate: null, // 褰曞叆鏃ユ湡
entryDateStart: undefined,
entryDateEnd: undefined,
+ approvalStatus: "", // 瀹℃壒鐘舵��
},
form: {
salesContractNo: "",
@@ -1250,8 +1405,13 @@
executionDate: "",
hasProductionRecord: false,
createTime: "",
+ ledgerType: "鍗栨补", // 閿�鍞被鍨嬶細鍗栨补 / 浠e偍
+ oilDepotId: "", // 浠e偍锛氭补搴�
+ tankId: "", // 浠e偍锛氬偍缃�
+ contractAmount: null, // 浠e偍锛氬悎鍚岄噾棰濈敱鍓嶇鎵嬪伐濉�
},
rules: {
+ ledgerType: [{ required: true, message: "璇烽�夋嫨", trigger: "change" }],
salesman: [{ required: true, message: "璇烽�夋嫨", trigger: "change" }],
customerId: [{ required: true, message: "璇烽�夋嫨", trigger: "change" }],
entryPerson: [{ required: true, message: "璇烽�夋嫨", trigger: "change" }],
@@ -1262,6 +1422,14 @@
});
const { form, rules } = toRefs(data);
const { form: searchForm } = useFormData(data.searchForm);
+// 浠e偍鍚堝悓閲戦瓒呬俊鎵樺熀閲戯紙瀹㈡埛鏈淮鎶や俊鎵樺熀閲戞椂涓嶆彁绀猴級
+const isContractOverTrustFund = computed(() => {
+ const fund = selectedCustomerTrustFund.value;
+ if (fund === null || fund === undefined || fund === "") return false;
+ const amount = Number(form.value.contractAmount);
+ if (!(amount > 0)) return false;
+ return amount > Number(fund);
+});
// 浜у搧琛ㄥ崟寮规鏁版嵁
const productFormVisible = ref(false);
const productOperationType = ref("");
@@ -1514,14 +1682,14 @@
const getList = () => {
tableLoading.value = true;
const { entryDate, ...rest } = searchForm;
- // 灏嗚寖鍥存棩鏈熷瓧娈典紶閫掔粰鍚庣
- const params = { ...rest, ...page };
+ // 灏嗚寖鍥存棩鏈熷瓧娈典紶閫掔粰鍚庣锛沴edgerType 鐢卞綋鍓嶉〉绛惧喅瀹�
+ const params = { ...rest, ...page, ledgerType: activeTab.value };
// 绉婚櫎褰曞叆鏃ユ湡鐨勯粯璁ゅ�艰缃紝鍙繚鐣欒寖鍥存棩鏈熷瓧娈�
delete params.entryDate;
return ledgerListPage(params)
.then((res) => {
tableLoading.value = false;
- tableData.value = res.records;
+ tableData.value = res.records || [];
tableData.value.map((item) => {
item.children = [];
});
@@ -1692,19 +1860,40 @@
// 娣诲姞琛ㄨ绫诲悕鏂规硶
const tableRowClassName = ({ row }) => {
- if (!row.deliveryDate) return "";
- if (row.isFh) return "";
-
- const diff = row.deliveryDaysDiff;
- if (diff === 15) {
- return "yellow";
- } else if (diff === 10) {
- return "pink";
- } else if (diff === 2) {
- return "purple";
- } else if (diff < 2) {
- return "red";
+ let className = "";
+ if (row.deliveryDate && !row.isFh) {
+ const diff = row.deliveryDaysDiff;
+ if (diff === 15) {
+ className = "yellow";
+ } else if (diff === 10) {
+ className = "pink";
+ } else if (diff === 2) {
+ className = "purple";
+ } else if (diff < 2) {
+ className = "red";
+ }
}
+ // 鏈瑪鍚堝悓閲戦瓒呭鎴蜂俊鎵樺熀閲戯細宸︿晶绾㈣竟锛屼笉瑕嗙洊涓婇潰鐨勪氦鏈熷簳鑹�
+ if (row.exceedsTrustFund) {
+ className = className ? `${className} trust-fund-exceed` : "trust-fund-exceed";
+ }
+ return className;
+};
+// 鍙拌处瀹℃壒鐘舵�侊紙鍚庣缁存姢锛屼笌鍙戣揣鐘舵�� statusTextMap 鏃犲叧锛�
+const getApprovalStatusType = status => {
+ const map = {
+ 寰呭鎵�: "warning",
+ 瀹℃牳涓�: "primary",
+ 閫氳繃: "success",
+ 鎷掔粷: "danger",
+ };
+ return map[status] || "info";
+};
+// 椤电鍒囨崲锛氭崲 ledgerType 閲嶆柊鏌ュ垪琛�
+const handleTabChange = () => {
+ page.current = 1;
+ expandedRowKeys.value = [];
+ getList();
};
// 涓昏〃鍚堣鏂规硶
const summarizeMainTable = (param) => {
@@ -1750,14 +1939,24 @@
form.value = {};
productData.value = [];
selectedQuotation.value = null;
+ selectedCustomerTrustFund.value = null;
let userLists = await userListNoPage();
userList.value = userLists.data;
listCustomer({ current: -1, size: -1, type: 0 }).then((res) => {
customerOption.value = res.data.records;
});
+ // 娌瑰簱/鍌ㄧ綈涓嬫媺锛堜唬鍌ㄧ敤锛夛紝涓よ�呬簰鐩哥嫭绔�
+ allOilDepot().then((res) => {
+ oilDepotOptions.value = res.data || [];
+ });
+ allTankInfo().then((res) => {
+ tankOptions.value = res.data || [];
+ });
form.value.entryPerson = userStore.id;
if (type === "add") {
- // 鏂板鏃惰缃綍鍏ユ棩鏈熶负褰撳ぉ
+ // 鏂板鏃堕粯璁よ窡闅忓綋鍓嶉〉绛剧殑閿�鍞被鍨�
+ form.value.ledgerType = activeTab.value;
+ // 褰曞叆鏃ユ湡榛樿涓哄綋澶�
form.value.entryDate = getCurrentDate();
// 绛捐鏃ユ湡榛樿涓哄綋澶�
form.value.executionDate = getCurrentDate();
@@ -1770,10 +1969,15 @@
getSalesLedgerWithProducts({ id: row.id, type: 1 }).then((res) => {
form.value = { ...res };
form.value.entryPerson = Number(res.entryPerson);
- productData.value = form.value.productData;
+ // 鍘嗗彶鍗曟嵁/鍚庣鏈繑鍥炴椂鍏滃簳涓哄崠娌癸紝鍚﹀垯蹇呭~鏍¢獙浼氬崱浣忕紪杈�
+ form.value.ledgerType = form.value.ledgerType || "鍗栨补";
+ // 浠e偍娌℃湁浜у搧鏄庣粏
+ productData.value = form.value.productData || [];
fileList.value = form.value.storageBlobVOs;
// 缂栬緫鏃惰缃嚜鍔ㄧ敓鎴愪负false锛屽厑璁告墜鍔ㄤ慨鏀�
form.value.autoGenerateContractNo = false;
+ // 鍥炴樉淇℃墭鍩洪噾锛屼緵浠e偍鍚堝悓閲戦瀹炴椂姣斿
+ loadCustomerTrustFund();
});
}
// let userAll = await userStore.getInfo()
@@ -1784,6 +1988,18 @@
// });
form.value.entryDate = getCurrentDate(); // 璁剧疆榛樿褰曞叆鏃ユ湡涓哄綋鍓嶆棩鏈�
dialogFormVisible.value = true;
+};
+
+// 鍙栬瀹㈡埛鐨勪俊鎵樺熀閲戯紝渚涗唬鍌ㄥ悎鍚岄噾棰濆疄鏃舵瘮瀵癸紙瀹㈡埛鏈淮鎶ゅ嵆涓虹┖锛屼笉鎻愮ず锛�
+const loadCustomerTrustFund = () => {
+ const id = form.value.customerId;
+ if (!id) {
+ selectedCustomerTrustFund.value = null;
+ return;
+ }
+ getCustomer(id).then((res) => {
+ selectedCustomerTrustFund.value = res?.data?.trustFund ?? null;
+ });
};
// 鎵撳紑鎶ヤ环鍗曢�夋嫨寮圭獥锛堜粎瀹℃壒閫氳繃锛�
@@ -1901,15 +2117,36 @@
const submitForm = () => {
proxy.$refs["formRef"].validate((valid) => {
if (valid) {
- console.log("productData.value--", productData.value);
- if (productData.value !== null && productData.value.length > 0) {
- form.value.productData = proxy.HaveJson(productData.value);
+ form.value.type = 1;
+ form.value.ledgerType = form.value.ledgerType || "鍗栨补";
+ if (form.value.ledgerType === "浠e偍") {
+ // 浠e偍锛氬崠瀛樺偍浣嶇疆锛屾病鏈変骇鍝佹槑缁嗭紝鍚堝悓閲戦鎵嬪伐濉�
+ if (!form.value.oilDepotId) {
+ proxy.$modal.msgWarning("浠e偍蹇呴』閫夋嫨娌瑰簱");
+ return;
+ }
+ if (!form.value.tankId) {
+ proxy.$modal.msgWarning("浠e偍蹇呴』閫夋嫨鍌ㄧ綈");
+ return;
+ }
+ if (!(Number(form.value.contractAmount) > 0)) {
+ proxy.$modal.msgWarning("浠e偍蹇呴』濉啓鍚堝悓閲戦");
+ return;
+ }
+ delete form.value.productData;
} else {
- proxy.$modal.msgWarning("璇锋坊鍔犱骇鍝佷俊鎭�");
- return;
+ // 鍗栨补锛氬悎鍚岄噾棰濈敱浜у搧鏄庣粏姹囨�伙紝浠e偍瀛楁娓呯┖
+ if (productData.value !== null && productData.value.length > 0) {
+ form.value.productData = proxy.HaveJson(productData.value);
+ } else {
+ proxy.$modal.msgWarning("璇锋坊鍔犱骇鍝佷俊鎭�");
+ return;
+ }
+ form.value.oilDepotId = "";
+ form.value.tankId = "";
+ form.value.contractAmount = null;
}
form.value.storageBlobDTOs = fileList;
- form.value.type = 1;
if (form.value.autoGenerateContractNo) {
form.value.salesContractNo = "";
}
@@ -3024,6 +3261,28 @@
background-color: #f4defa;
}
+/* 鏈瑪鍚堝悓閲戦瓒呭鎴蜂俊鎵樺熀閲戯細鍙姞宸︿晶绾㈣竟锛岄伩鍏嶈鐩栦笂闈㈢殑浜ゆ湡搴曡壊 */
+:deep(.trust-fund-exceed) td:first-child {
+ box-shadow: inset 3px 0 0 var(--el-color-danger);
+}
+
+.trust-fund-alert {
+ margin-top: 6px;
+ width: 100%;
+}
+
+.ledger-tabs {
+ margin-bottom: 8px;
+}
+
+.ledger-tabs :deep(.el-tabs__header) {
+ margin-bottom: 0;
+}
+
+.ledger-tabs :deep(.el-tabs__content) {
+ display: none;
+}
+
.table_list {
margin-top: unset;
}
--
Gitblit v1.9.3