From ed94f308b3fb345eb7238b8836a61d0b8bbaa98c Mon Sep 17 00:00:00 2001
From: chenhj <1263187585@qq.com>
Date: 星期五, 27 三月 2026 17:52:23 +0800
Subject: [PATCH] feat(salesLedger): 添加批号字段和销售出库单打印功能
---
src/views/salesManagement/salesLedger/index.vue | 210 ++++++++++++++++++++++++++++++++++++++++++++-------
1 files changed, 179 insertions(+), 31 deletions(-)
diff --git a/src/views/salesManagement/salesLedger/index.vue b/src/views/salesManagement/salesLedger/index.vue
index 457f0f1..884582d 100644
--- a/src/views/salesManagement/salesLedger/index.vue
+++ b/src/views/salesManagement/salesLedger/index.vue
@@ -46,6 +46,7 @@
<el-table-column align="center" label="搴忓彿" type="index"/>
<el-table-column label="浜у搧澶х被" prop="productCategory" />
<el-table-column label="瑙勬牸鍨嬪彿" prop="specificationModel" />
+ <el-table-column label="鎵瑰彿" prop="batchNo" />
<el-table-column label="鍗曚綅" prop="unit" />
<el-table-column label="浜у搧鐘舵��"
width="100px"
@@ -118,11 +119,13 @@
<el-table-column label="褰曞叆鏃ユ湡" prop="entryDate" width="120" show-overflow-tooltip />
<el-table-column label="绛捐鏃ユ湡" prop="executionDate" width="120" show-overflow-tooltip />
<el-table-column label="浜や粯鏃ユ湡" prop="deliveryDate" width="120" show-overflow-tooltip />
+ <el-table-column label="澶囨敞" prop="remarks" width="200" show-overflow-tooltip />
<el-table-column fixed="right" label="鎿嶄綔" min-width="100" align="center">
<template #default="scope">
- <el-button link type="primary" size="small" @click="openForm('edit', scope.row)">缂栬緫</el-button>
+ <el-button link type="primary" size="small" @click="openForm('edit', scope.row)" :disabled="!scope.row.isEdit">缂栬緫</el-button>
<!-- <el-button link type="primary" size="small" @click="openForm('view', scope.row)">璇︽儏</el-button>-->
<el-button link type="primary" size="small" @click="downLoadFile(scope.row)">闄勪欢</el-button>
+ <el-button link type="primary" size="small" @click="exportSaleOutbound(scope.row)">鎵撳嵃閿�鍞嚭搴撳崟</el-button>
<!-- <el-button link type="primary" size="small" @click="openDeliveryForm(scope.row)">鍙戣揣</el-button>-->
</template>
</el-table-column>
@@ -221,10 +224,13 @@
</el-row>
<el-table :data="productData" border @selection-change="productSelected" show-summary
:summary-method="summarizeMainTable">
- <el-table-column align="center" type="selection" width="55" v-if="operationType !== 'view'" />
+ <el-table-column align="center" type="selection" width="55" v-if="operationType !== 'view'"
+ :selectable="(row) => !isProductShipped(row)" />
<el-table-column align="center" label="搴忓彿" type="index" width="60" />
<el-table-column label="浜у搧澶х被" prop="productCategory" />
<el-table-column label="瑙勬牸鍨嬪彿" prop="specificationModel" />
+ <el-table-column label="UID鐮�" prop="uidNo" />
+ <el-table-column label="鎵瑰彿" prop="batchNo" />
<el-table-column label="鍗曚綅" prop="unit" />
<el-table-column label="鏁伴噺" prop="quantity" />
<el-table-column label="绋庣巼(%)" prop="taxRate" />
@@ -233,20 +239,22 @@
<el-table-column label="涓嶅惈绋庢�讳环(鍏�)" prop="taxExclusiveTotalPrice" :formatter="formattedNumber" />
<el-table-column fixed="right" label="鎿嶄綔" min-width="60" align="center" v-if="operationType !== 'view'">
<template #default="scope">
- <el-button link type="primary" size="small" @click="openProductForm('edit', scope.row,scope.$index)">缂栬緫</el-button>
+ <el-button link type="primary" size="small"
+ :disabled="isProductShipped(scope.row)"
+ @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="remark">
- <el-input v-model="form.remark" placeholder="璇疯緭鍏�" clearable type="textarea" :rows="2" :disabled="operationType === 'view'" />
+ <el-form-item label="澶囨敞锛�" prop="remarks">
+ <el-input v-model="form.remarks" placeholder="璇疯緭鍏�" clearable type="textarea" :rows="2" :disabled="operationType === 'view'" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="30">
<el-col :span="24">
- <el-form-item label="闄勪欢鏉愭枡锛�" prop="remark">
+ <el-form-item label="闄勪欢鏉愭枡锛�" prop="salesLedgerFiles">
<el-upload v-model:file-list="fileList" :action="upload.url" multiple ref="fileUpload" auto-upload
:headers="upload.headers" :before-upload="handleBeforeUpload" :on-error="handleUploadError"
:on-success="handleUploadSuccess" :on-remove="handleRemove">
@@ -315,6 +323,15 @@
</el-table-column>
</el-table>
+ <pagination
+ v-show="quotationPage.total > 0"
+ :total="quotationPage.total"
+ layout="total, sizes, prev, pager, next, jumper"
+ :page="quotationPage.current"
+ :limit="quotationPage.size"
+ @pagination="quotationPaginationChange"
+ />
+
<template #footer>
<el-button @click="quotationDialogVisible = false">鍏抽棴</el-button>
</template>
@@ -348,6 +365,15 @@
</el-form-item>
</el-col>
</el-row>
+ <el-row :gutter="30">
+ <el-col :span="24">
+ <el-form-item label="鎵瑰彿锛�" prop="batchNo">
+ <el-select v-model="productForm.batchNo" placeholder="璇烽�夋嫨" clearable filterable>
+ <el-option v-for="item in batchNoOptions" :key="item.value" :label="item.label" :value="item.value" />
+ </el-select>
+ </el-form-item>
+ </el-col>
+ </el-row>
<el-row :gutter="30">
<el-col :span="12">
<el-form-item label="鍗曚綅锛�" prop="unit">
@@ -653,20 +679,22 @@
import FormDialog from '@/components/Dialog/FormDialog.vue';
import { getQuotationList } from "@/api/salesManagement/salesQuotation.js";
import {
- ledgerListPage,
- productList,
- customerList,
- addOrUpdateSalesLedger,
- getSalesLedgerWithProducts,
- delLedger,
- addOrUpdateSalesLedgerProduct,
- delProduct,
- delLedgerFile, getProductInventory,
+ ledgerListPage,
+ productList,
+ customerList,
+ addOrUpdateSalesLedger,
+ getSalesLedgerWithProducts,
+ delLedger,
+ addOrUpdateSalesLedgerProduct,
+ delProduct,
+ delLedgerFile, getProductInventory, saleOutboundExport,
} from "@/api/salesManagement/salesLedger.js";
import { modelList, productTreeList } from "@/api/basicData/product.js";
import useFormData from "@/hooks/useFormData.js";
import dayjs from "dayjs";
import { getCurrentDate } from "@/utils/index.js";
+import {getProductOrderBatchNoOptions} from "@/api/productionManagement/productionOrder.js";
+import {safeTrainingExport} from "@/api/safeProduction/safetyTrainingAssessment.js";
const userStore = useUserStore();
const { proxy } = getCurrentInstance();
@@ -734,10 +762,12 @@
taxInclusiveTotalPrice: "",
taxExclusiveTotalPrice: "",
invoiceType: "",
+ batchNo: "",
},
productRules: {
productCategory: [{ required: true, message: "璇烽�夋嫨", trigger: "change" }],
productModelId: [{ required: true, message: "璇烽�夋嫨", trigger: "change" }],
+ batchNo: [{ required: true, message: "璇烽�夋嫨", trigger: "change" }],
specificationModel: [
{ required: true, message: "璇烽�夋嫨", trigger: "change" },
],
@@ -776,6 +806,12 @@
const quotationSearchForm = reactive({
quotationNo: "",
customer: "",
+});
+// 鎶ヤ环鍗曞脊妗嗗垎椤�
+const quotationPage = reactive({
+ current: 1,
+ size: 10,
+ total: 0,
});
const selectedQuotation = ref(null);
@@ -1001,13 +1037,18 @@
// 娣诲姞琛ㄨ绫诲悕鏂规硶
const tableRowClassName = ({ row }) => {
- switch (row.deliveryDaysDiff) {
- case 15:
- return 'yellow'
- case 10:
- return 'red'
- case 2:
- return 'purple'
+ 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';
}
};
// 涓昏〃鍚堣鏂规硶
@@ -1066,6 +1107,8 @@
const openQuotationDialog = async () => {
if (operationType.value === "view") return;
quotationDialogVisible.value = true;
+ // 鎵撳紑寮圭獥鏃堕噸缃垎椤靛埌绗竴椤�
+ quotationPage.current = 1;
// 鍏堢‘淇濆鎴峰垪琛ㄥ凡鍔犺浇锛屼究浜庡悗缁洖濉� customerId
if (!customerOption.value || customerOption.value.length === 0) {
try {
@@ -1082,14 +1125,15 @@
quotationLoading.value = true;
try {
const params = {
- // 鍏煎鍚庣鍒嗛〉瀛楁锛氳繖閲屾部鐢ㄦ姤浠烽〉闈㈠凡鏈夊彲鐢ㄧ殑瀛楁鍛藉悕
- currentPage: 1,
- pageSize: 100,
+ // 鍚庣鍒嗛〉瀛楁锛歝urrent / size
+ current: quotationPage.current,
+ size: quotationPage.size,
...quotationSearchForm,
status: "閫氳繃",
};
const res = await getQuotationList(params);
quotationList.value = res?.data?.records || [];
+ quotationPage.total = res?.data?.total || 0;
} finally {
quotationLoading.value = false;
}
@@ -1098,7 +1142,15 @@
const resetQuotationSearch = async () => {
quotationSearchForm.quotationNo = "";
quotationSearchForm.customer = "";
+ quotationPage.current = 1;
await fetchQuotationList();
+};
+
+// 鎶ヤ环鍗曞脊妗嗗垎椤靛垏鎹�
+const quotationPaginationChange = (obj) => {
+ quotationPage.current = obj.page;
+ quotationPage.size = obj.limit;
+ fetchQuotationList();
};
// 閫変腑鎶ヤ环鍗曞悗鍥炲~鍒板彴璐﹁〃鍗�
@@ -1210,6 +1262,12 @@
}
});
};
+
+const batchNoOptions = ref([]);
+const fetchBatchNoOptions = async () => {
+ const res = await getProductOrderBatchNoOptions();
+ batchNoOptions.value = res.data;
+};
// 鍏抽棴寮规
const closeDia = () => {
proxy.resetForm("formRef");
@@ -1219,6 +1277,12 @@
const productIndex = ref(0);
// 鎵撳紑浜у搧寮规
const openProductForm = async (type, row, index) => {
+ // 缂栬緫鏃舵鏌ヤ骇鍝佹槸鍚﹀凡鍙戣揣鎴栧鏍搁�氳繃
+ if (type === "edit" && isProductShipped(row)) {
+ proxy.$modal.msgWarning("宸插彂璐ф垨瀹℃牳閫氳繃鐨勪骇鍝佷笉鑳界紪杈�");
+ return;
+ }
+
productOperationType.value = type;
productForm.value = {};
proxy.resetForm("productFormRef");
@@ -1285,6 +1349,14 @@
proxy.$modal.msgWarning("璇烽�夋嫨鏁版嵁");
return;
}
+
+ // 妫�鏌ユ槸鍚︽湁宸插彂璐ф垨瀹℃牳閫氳繃鐨勪骇鍝�
+ const shippedProducts = productSelectedRows.value.filter(row => isProductShipped(row));
+ if (shippedProducts.length > 0) {
+ proxy.$modal.msgWarning("宸插彂璐ф垨瀹℃牳閫氳繃鐨勪骇鍝佷笉鑳藉垹闄�");
+ return;
+ }
+
if (operationType.value === "add") {
productSelectedRows.value.forEach((selectedRow) => {
const index = productData.value.findIndex(
@@ -1359,15 +1431,55 @@
proxy.$modal.msg("宸插彇娑�");
});
};
+/** 鍒ゆ柇鍗曚釜浜у搧鏄惁宸插彂璐э紙鏍规嵁shippingStatus鍒ゆ柇锛屽凡鍙戣揣鎴栧鏍搁�氳繃涓嶅彲缂栬緫鍜屽垹闄わ級 */
+const isProductShipped = (product) => {
+ if (!product) return false;
+ const status = String(product.shippingStatus || "").trim();
+ // 濡傛灉鍙戣揣鐘舵�佹槸"宸插彂璐�"鎴�"瀹℃牳閫氳繃"锛屽垯涓嶅彲缂栬緫鍜屽垹闄�
+ return status === "宸插彂璐�" || status === "瀹℃牳閫氳繃";
+};
+
+/** 鍒ゆ柇閿�鍞鍗曚笅鏄惁瀛樺湪宸插彂璐�/鍙戣揣瀹屾垚鐨勪骇鍝侊紙涓嶅彲鍒犻櫎锛� */
+const hasShippedProducts = (products) => {
+ if (!products || !products.length) return false;
+ return products.some((p) => {
+ const status = String(p.shippingStatus || "").trim();
+ // 鏈夊彂璐ф棩鏈熸垨杞︾墝鍙疯涓哄凡鍙戣揣
+ if (p.shippingDate || p.shippingCarNumber) return true;
+ // 宸茶繘琛屽彂璐с�佸彂璐у畬鎴愩�佸凡鍙戣揣 鍧囦笉鍙垹闄�
+ return status === "宸茶繘琛屽彂璐�" || status === "鍙戣揣瀹屾垚" || status === "宸插彂璐�";
+ });
+};
+
// 鍒犻櫎
-const handleDelete = () => {
- let ids = [];
- if (selectedRows.value.length > 0) {
- ids = selectedRows.value.map((item) => item.id);
- } else {
+const handleDelete = async () => {
+ if (selectedRows.value.length === 0) {
proxy.$modal.msgWarning("璇烽�夋嫨鏁版嵁");
return;
}
+ const ids = selectedRows.value.map((item) => item.id);
+
+ // 妫�鏌ユ槸鍚︽湁宸茶繘琛屽彂璐ф垨鍙戣揣瀹屾垚鐨勯攢鍞鍗曪紝鑻ユ湁鍒欎笉鍏佽鍒犻櫎
+ const cannotDeleteNames = [];
+ for (const row of selectedRows.value) {
+ let products = row.children && row.children.length > 0 ? row.children : null;
+ if (!products) {
+ try {
+ const res = await productList({ salesLedgerId: row.id, type: 1 });
+ products = res.data || [];
+ } catch {
+ products = [];
+ }
+ }
+ if (hasShippedProducts(products)) {
+ cannotDeleteNames.push(row.salesContractNo || `ID:${row.id}`);
+ }
+ }
+ if (cannotDeleteNames.length > 0) {
+ proxy.$modal.msgWarning("宸茶繘琛屽彂璐ф垨鍙戣揣瀹屾垚鐨勯攢鍞鍗曚笉鑳藉垹闄わ細" + cannotDeleteNames.join("銆�"));
+ return;
+ }
+
ElMessageBox.confirm("閫変腑鐨勫唴瀹瑰皢琚垹闄わ紝鏄惁纭鍒犻櫎锛�", "瀵煎嚭", {
confirmButtonText: "纭",
cancelButtonText: "鍙栨秷",
@@ -2093,8 +2205,41 @@
let res = await userStore.getInfo();
currentFactoryName.value = res.user.currentFactoryName;
};
+
+const exportSaleOutbound = row => {
+ saleOutboundExport({id: row.id})
+ .then(res => {
+ // 鍒涘缓Blob瀵硅薄
+ const blob = new Blob([res], {
+ type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
+ });
+ // 鍒涘缓涓嬭浇閾炬帴
+ const url = window.URL.createObjectURL(blob);
+ const link = document.createElement("a");
+ link.href = url;
+ link.download = `閿�鍞嚭搴撳崟.docx`;
+
+ // 妯℃嫙鐐瑰嚮涓嬭浇
+ document.body.appendChild(link);
+ link.click();
+
+ // 娓呯悊涓存椂瀵硅薄
+ setTimeout(() => {
+ document.body.removeChild(link);
+ window.URL.revokeObjectURL(url);
+ }, 100);
+
+ ElMessage.success("瀵煎嚭鎴愬姛");
+ })
+ .catch(err => {
+ console.error("瀵煎嚭澶辫触:", err);
+ ElMessage.error("瀵煎嚭澶辫触锛岃閲嶈瘯");
+ });
+};
+
onMounted(() => {
getList();
+ fetchBatchNoOptions();
userListNoPage().then(res => {
userList.value = res.data;
})
@@ -2111,6 +2256,10 @@
background-color: #FAF0DE;
}
+::v-deep .pink {
+ background-color: #FAE1DE;
+}
+
::v-deep .red {
background-color: #FAE1DE;
}
@@ -2118,7 +2267,6 @@
::v-deep .purple{
background-color: #F4DEFA;
}
-
.table_list {
margin-top: unset;
--
Gitblit v1.9.3