From e57932740c39954cf0baa5d7eaa6c3eaeb313fb4 Mon Sep 17 00:00:00 2001
From: gongchunyi <deslre0381@gmail.com>
Date: 星期五, 04 九月 2026 11:46:04 +0800
Subject: [PATCH] Merge branch 'dev_pro_河南鹤壁' of http://114.132.189.42:9002/r/product-inventory-management into dev_pro_河南鹤壁
---
src/views/productionManagement/productionOrder/components/MaterialLedgerDialog.vue | 40 ++
src/views/productionManagement/productionOrder/components/PurchaseLedgerAddDialog.vue | 856 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 896 insertions(+), 0 deletions(-)
diff --git a/src/views/productionManagement/productionOrder/components/MaterialLedgerDialog.vue b/src/views/productionManagement/productionOrder/components/MaterialLedgerDialog.vue
index ca25853..9067dde 100644
--- a/src/views/productionManagement/productionOrder/components/MaterialLedgerDialog.vue
+++ b/src/views/productionManagement/productionOrder/components/MaterialLedgerDialog.vue
@@ -6,12 +6,19 @@
@close="handleClose">
<div class="material-toolbar">
<el-button type="primary"
+ plain
+ @click="handleAddPurchase">鏂板閲囪喘</el-button>
+ <el-button type="primary"
@click="handleAddMaterialRow">鏂板</el-button>
</div>
<el-table v-loading="materialTableLoading"
:data="materialTableData"
border
+ @selection-change="handleMaterialSelectionChange"
row-key="tempId">
+ <el-table-column align="center"
+ type="selection"
+ width="55" />
<el-table-column label="宸ュ簭鍚嶇О"
min-width="100">
<template #default="{ row }">
@@ -143,6 +150,8 @@
<ProductSelectDialog v-model="materialProductDialogVisible"
@confirm="handleMaterialProductConfirm"
single />
+ <PurchaseLedgerAddDialog ref="purchaseLedgerAddDialogRef"
+ v-model="purchaseLedgerDialogVisible" />
<!-- request-url="/stockInventory/rawMaterials" -->
</div>
</template>
@@ -151,6 +160,7 @@
import { computed, ref, watch } from "vue";
import { ElMessage } from "element-plus";
import ProductSelectDialog from "@/views/basicData/product/ProductSelectDialog.vue";
+ import PurchaseLedgerAddDialog from "./PurchaseLedgerAddDialog.vue";
import { findProductProcessRouteItemList } from "@/api/productionManagement/productProcessRoute.js";
import { getStockInventoryByModelId } from "@/api/inventoryManagement/stockInventory.js";
import {
@@ -174,6 +184,9 @@
const materialTableLoading = ref(false);
const materialSaving = ref(false);
const materialTableData = ref([]);
+ const selectedMaterialRows = ref([]);
+ const purchaseLedgerDialogVisible = ref(false);
+ const purchaseLedgerAddDialogRef = ref(null);
const isSaveDisabled = computed(() => {
if (materialTableLoading.value) return true;
@@ -378,6 +391,7 @@
const handleClose = () => {
materialTableData.value = [];
+ selectedMaterialRows.value = [];
currentMaterialSelectRowIndex.value = -1;
};
@@ -419,6 +433,32 @@
materialTableData.value.push(createMaterialRow());
};
+ const handleMaterialSelectionChange = selection => {
+ selectedMaterialRows.value = selection;
+ };
+
+ const handleAddPurchase = () => {
+ if (selectedMaterialRows.value.length === 0) {
+ ElMessage.warning("璇烽�夋嫨闇�瑕佹柊澧為噰璐殑鍘熸枡琛�");
+ return;
+ }
+ const rowsWithModel = selectedMaterialRows.value.filter(
+ row => row.materialModelId
+ );
+ if (rowsWithModel.length === 0) {
+ ElMessage.warning("鎵�閫夎缂哄皯鍘熸枡鍨嬪彿锛岃鍏堥�夋嫨鍘熸枡鍚庡啀鏂板閲囪喘");
+ return;
+ }
+ const productRows = rowsWithModel.map(row => ({
+ productModelId: row.materialModelId,
+ productCategory: row.materialName,
+ specificationModel: row.materialModel,
+ unit: row.unit,
+ isChecked: false,
+ }));
+ purchaseLedgerAddDialogRef.value?.open(productRows);
+ };
+
const handleDeleteMaterialRow = index => {
materialTableData.value.splice(index, 1);
};
diff --git a/src/views/productionManagement/productionOrder/components/PurchaseLedgerAddDialog.vue b/src/views/productionManagement/productionOrder/components/PurchaseLedgerAddDialog.vue
new file mode 100644
index 0000000..a0da9d4
--- /dev/null
+++ b/src/views/productionManagement/productionOrder/components/PurchaseLedgerAddDialog.vue
@@ -0,0 +1,856 @@
+<template>
+ <div>
+ <FormDialog v-model="dialogVisible"
+ title="鏂板閲囪喘鍙拌处椤甸潰"
+ :width="'70%'"
+ operation-type="add"
+ @close="closeDia"
+ @confirm="submitForm"
+ @cancel="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="purchaseContractNumber">
+ <el-input v-model="form.purchaseContractNumber" placeholder="璇疯緭鍏�" clearable />
+ </el-form-item>
+ </el-col>
+ <el-col v-if="showSalesContractBinding" :span="12">
+ <el-form-item label="閿�鍞悎鍚屽彿锛�" prop="salesLedgerId">
+ <el-select v-model="form.salesLedgerId"
+ placeholder="璇烽�夋嫨"
+ filterable
+ clearable
+ @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-row>
+ <el-row :gutter="30">
+ <el-col :span="12">
+ <el-form-item label="渚涘簲鍟嗗悕绉帮細" prop="supplierId">
+ <el-select v-model="form.supplierId" placeholder="璇烽�夋嫨" filterable clearable>
+ <el-option v-for="item in supplierList"
+ :key="item.id"
+ :label="item.supplierName"
+ :value="item.id">
+ {{ item.supplierName }}
+ </el-option>
+ </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="浠樻鏂瑰紡">
+ <el-input v-model="form.paymentMethod" placeholder="璇疯緭鍏�" clearable />
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item label="绛捐鏃ユ湡锛�" prop="executionDate">
+ <el-date-picker v-model="form.executionDate"
+ style="width: 100%"
+ 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="recorderId">
+ <el-select v-model="form.recorderId"
+ placeholder="璇烽�夋嫨"
+ clearable
+ filterable
+ disabled>
+ <el-option v-for="item in userList"
+ :key="item.userId"
+ :label="item.nickName"
+ :value="item.userId" />
+ </el-select>
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item label="褰曞叆鏃ユ湡锛�" prop="entryDate">
+ <el-date-picker v-model="form.entryDate"
+ style="width: 100%"
+ value-format="YYYY-MM-DD"
+ format="YYYY-MM-DD"
+ type="date"
+ placeholder="璇烽�夋嫨"
+ clearable />
+ </el-form-item>
+ </el-col>
+ </el-row>
+ <el-row>
+ <el-form-item label="浜у搧淇℃伅锛�">
+ <el-button type="primary" @click="openProductForm('add')">娣诲姞</el-button>
+ <el-button plain type="danger" @click="deleteProduct">鍒犻櫎</el-button>
+ </el-form-item>
+ <div class="select-button-group">
+ <el-select v-model="templateName"
+ filterable
+ allow-create
+ :reserve-keyword="true"
+ :default-first-option="false"
+ clearable
+ :input-value="filterInputValue"
+ @filter-change="onTemplateFilterChange"
+ @change="onTemplateChange"
+ @focus="getTemplateList"
+ placeholder="璇烽�夋嫨妯$増鎴栬�呰緭鍏ユ柊鐨勬ā鐗堝悕绉板悗閫夋嫨"
+ class="no-arrow-select template-select">
+ <el-option v-for="item in templateList"
+ :key="item.id || item.value"
+ :label="item.templateName"
+ :value="item.templateName">
+ <div class="template-option">
+ <span>{{ item.templateName }}</span>
+ <el-icon v-if="item.id"
+ class="delete-icon"
+ @click.stop="handleDeleteTemplate(item)">
+ <Delete />
+ </el-icon>
+ </div>
+ </el-option>
+ </el-select>
+ <el-button size="small"
+ class="template-save-button"
+ :disabled="!templateName || templateName.trim() === '' || (!currentTemplateId && isTemplateNameDuplicate)"
+ @click="handleButtonClick">
+ 淇濆瓨
+ </el-button>
+ </div>
+ </el-row>
+ <el-table :data="productData"
+ border
+ show-summary
+ :summary-method="summarizeProTable"
+ @selection-change="productSelected">
+ <el-table-column align="center" type="selection" width="55" />
+ <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="鍗曚綅" prop="unit" width="70" />
+ <el-table-column label="鏁伴噺" prop="quantity" width="70" />
+ <el-table-column label="绋庣巼(%)" prop="taxRate" width="80" />
+ <el-table-column label="鍚◣鍗曚环(鍏�)" prop="taxInclusiveUnitPrice" :formatter="formattedNumber" width="150" />
+ <el-table-column label="鍚◣鎬讳环(鍏�)" prop="taxInclusiveTotalPrice" :formatter="formattedNumber" width="150" />
+ <el-table-column label="涓嶅惈绋庢�讳环(鍏�)" prop="taxExclusiveTotalPrice" :formatter="formattedNumber" width="150" />
+ <el-table-column label="鏄惁璐ㄦ" prop="isChecked" width="150">
+ <template #default="{ row }">
+ <el-tag :type="row.isChecked ? 'success' : 'info'">
+ {{ row.isChecked ? '鏄�' : '鍚�' }}
+ </el-tag>
+ </template>
+ </el-table-column>
+ <el-table-column fixed="right" label="鎿嶄綔" min-width="60" align="center">
+ <template #default="{ row, $index }">
+ <el-button link type="primary" @click="openProductForm('edit', row, $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-row :gutter="30">
+ <el-col :span="24">
+ <el-form-item label="闄勪欢鏉愭枡锛�" prop="purchaseLedgerFiles">
+ <FileUpload v-model:file-list="fileList" />
+ </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="12">
+ <el-form-item label="浜у搧澶х被锛�" prop="productCategory">
+ <el-input v-model="productForm.productCategory"
+ placeholder="鐐瑰嚮閫夋嫨"
+ readonly
+ style="cursor:pointer"
+ @click="showProductSelectDialog = true" />
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item label="瑙勬牸鍨嬪彿锛�" prop="productModelId">
+ <el-input v-model="productForm.specificationModel"
+ placeholder="鐐瑰嚮閫夋嫨"
+ readonly
+ style="cursor:pointer"
+ @click="showProductSelectDialog = true" />
+ </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 @change="mathNum">
+ <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="6"
+ :step="0.000001"
+ :min="0"
+ clearable
+ style="width: 100%"
+ @change="mathNum" />
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item label="鏁伴噺锛�" prop="quantity">
+ <el-input-number v-model="productForm.quantity"
+ :step="0.1"
+ clearable
+ :precision="2"
+ :min="0"
+ style="width: 100%"
+ placeholder="璇疯緭鍏�"
+ @change="mathNum" />
+ </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="6"
+ :step="0.000001"
+ :min="0"
+ clearable
+ style="width: 100%"
+ @change="reverseMathNum('taxInclusiveTotalPrice')" />
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item label="涓嶅惈绋庢�讳环(鍏�)锛�" prop="taxExclusiveTotalPrice">
+ <el-input-number v-model="productForm.taxExclusiveTotalPrice"
+ :precision="6"
+ :step="0.000001"
+ :min="0"
+ clearable
+ style="width: 100%"
+ @change="reverseMathNum('taxExclusiveTotalPrice')" />
+ </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>
+ <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-form>
+ </FormDialog>
+
+ <ProductSelectDialog v-model="showProductSelectDialog"
+ @confirm="handleProductSelect"
+ single />
+ </div>
+</template>
+
+<script setup>
+ import { computed, getCurrentInstance, nextTick, reactive, ref, toRefs } from "vue";
+ import { Delete } from "@element-plus/icons-vue";
+ import { ElMessage, ElMessageBox } from "element-plus";
+ import FormDialog from "@/components/Dialog/FormDialog.vue";
+ import FileUpload from "@/components/AttachmentUpload/file/index.vue";
+ import ProductSelectDialog from "@/views/basicData/product/ProductSelectDialog.vue";
+ import useUserStore from "@/store/modules/user";
+ import { userListNoPage } from "@/api/system/user.js";
+ import { getProductInfoByContractNo } from "@/api/salesManagement/salesLedger.js";
+ import {
+ addOrEditPurchase,
+ addPurchaseTemplate,
+ createPurchaseNo,
+ delPurchaseTemplate,
+ getOptions,
+ getPurchaseTemplateList,
+ getSalesNo,
+ updatePurchaseTemplate,
+ } from "@/api/procurementManagement/procurementLedger.js";
+ import { tableAmountFormatter, formatDecimal, buildAmountSummaryFormat } from "@/utils/numberFormat";
+
+ const props = defineProps({
+ modelValue: { type: Boolean, default: false },
+ initialProducts: { type: Array, default: () => [] },
+ });
+ const emit = defineEmits(["update:modelValue", "saved"]);
+
+ const dialogVisible = computed({
+ get: () => props.modelValue,
+ set: val => emit("update:modelValue", val),
+ });
+
+ const { proxy } = getCurrentInstance();
+ const { tax_rate } = proxy.useDict("tax_rate");
+ const userStore = useUserStore();
+ const showSalesContractBinding = false;
+
+ const productData = ref([]);
+ const productSelectedRows = ref([]);
+ const userList = ref([]);
+ const salesContractList = ref([]);
+ const supplierList = ref([]);
+ const fileList = ref([]);
+ const productFormVisible = ref(false);
+ const showProductSelectDialog = ref(false);
+ const productOperationType = ref("");
+ const productOperationIndex = ref(-1);
+ const templateName = ref("");
+ const filterInputValue = ref("");
+ const templateList = ref([]);
+ const isTemplateNameDuplicate = ref(false);
+ const currentTemplateId = ref(null);
+ let duplicateCheckTimer = null;
+
+ const data = reactive({
+ form: {
+ purchaseContractNumber: "",
+ salesLedgerId: "",
+ projectName: "",
+ recorderId: "",
+ entryDate: "",
+ productData: [],
+ supplierName: "",
+ supplierId: "",
+ paymentMethod: "",
+ executionDate: "",
+ isChecked: false,
+ },
+ rules: {
+ purchaseContractNumber: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
+ projectName: [{ required: true, message: "璇疯緭鍏ラ」鐩悕绉�", trigger: "blur" }],
+ supplierId: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
+ entryDate: [{ required: true, message: "璇烽�夋嫨", trigger: "change" }],
+ executionDate: [{ required: true, message: "璇烽�夋嫨", trigger: "change" }],
+ },
+ });
+ const { form, rules } = toRefs(data);
+
+ const productFormData = reactive({
+ productForm: {
+ productId: "",
+ productCategory: "",
+ productModelId: "",
+ specificationModel: "",
+ unit: "",
+ quantity: "",
+ taxInclusiveUnitPrice: "",
+ taxRate: "",
+ taxInclusiveTotalPrice: "",
+ taxExclusiveTotalPrice: "",
+ invoiceType: "",
+ warnNum: "",
+ isChecked: false,
+ },
+ productRules: {
+ productCategory: [{ required: true, message: "璇烽�夋嫨浜у搧澶х被", trigger: "change" }],
+ productModelId: [{ required: true, message: "璇烽�夋嫨瑙勬牸鍨嬪彿", trigger: "change" }],
+ unit: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
+ quantity: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
+ taxInclusiveUnitPrice: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
+ taxRate: [{ required: true, message: "璇烽�夋嫨", trigger: "change" }],
+ taxInclusiveTotalPrice: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
+ taxExclusiveTotalPrice: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
+ invoiceType: [{ required: true, message: "璇烽�夋嫨", trigger: "change" }],
+ isChecked: [{ required: true, message: "璇烽�夋嫨", trigger: "change" }],
+ },
+ });
+ const { productForm, productRules } = toRefs(productFormData);
+
+ const formattedNumber = tableAmountFormatter;
+
+ const getCurrentDate = () => {
+ const today = new Date();
+ const year = today.getFullYear();
+ const month = String(today.getMonth() + 1).padStart(2, "0");
+ const day = String(today.getDate()).padStart(2, "0");
+ return `${year}-${month}-${day}`;
+ };
+
+ const normalizeProductRow = row => ({
+ productId: row.productId || "",
+ productCategory: row.productCategory || row.materialName || row.productName || "",
+ productModelId: row.productModelId || row.materialModelId || row.modelId || "",
+ specificationModel: row.specificationModel || row.materialModel || row.model || "",
+ unit: row.unit || "",
+ quantity: row.quantity || "",
+ taxInclusiveUnitPrice: row.taxInclusiveUnitPrice || "",
+ taxRate: row.taxRate || "",
+ taxInclusiveTotalPrice: row.taxInclusiveTotalPrice || "",
+ taxExclusiveTotalPrice: row.taxExclusiveTotalPrice || "",
+ invoiceType: row.invoiceType || "",
+ warnNum: row.warnNum || "",
+ isChecked: row.isChecked ?? false,
+ });
+
+ const resetDialogState = () => {
+ form.value = {
+ purchaseContractNumber: "",
+ salesLedgerId: "",
+ projectName: "",
+ recorderId: "",
+ entryDate: "",
+ productData: [],
+ supplierName: "",
+ supplierId: "",
+ paymentMethod: "",
+ executionDate: "",
+ isChecked: false,
+ };
+ productData.value = [];
+ productSelectedRows.value = [];
+ fileList.value = [];
+ templateName.value = "";
+ filterInputValue.value = "";
+ isTemplateNameDuplicate.value = false;
+ currentTemplateId.value = null;
+ };
+
+ const open = async products => {
+ resetDialogState();
+ await Promise.all([
+ getTemplateList(),
+ userListNoPage().then(res => {
+ userList.value = res.data || [];
+ }),
+ getSalesNo().then(res => {
+ salesContractList.value = res || [];
+ }),
+ getOptions().then(res => {
+ supplierList.value = (res.data || []).filter(item => item.isWhite === 0);
+ }),
+ ]);
+
+ form.value.entryDate = getCurrentDate();
+ form.value.recorderId = userStore.id;
+ try {
+ const purchaseNoRes = await createPurchaseNo();
+ if (purchaseNoRes?.data) {
+ form.value.purchaseContractNumber = purchaseNoRes.data;
+ }
+ } catch (error) {
+ console.error("鐢熸垚閲囪喘鍚堝悓鍙峰け璐�:", error);
+ proxy.$modal.msgWarning("鐢熸垚閲囪喘鍚堝悓鍙峰け璐�");
+ }
+
+ const sourceProducts = Array.isArray(products) ? products : props.initialProducts;
+ productData.value = sourceProducts.map(normalizeProductRow);
+ dialogVisible.value = true;
+ await nextTick();
+ if (productData.value.length > 0) {
+ ElMessage.warning("宸叉寜鎵�閫夐鏂欒鐢熸垚浜у搧淇℃伅锛岃缂栬緫琛ュ叏鏁伴噺銆佺◣鐜囥�佸崟浠枫�佸彂绁ㄧ被鍨嬬瓑蹇呭~椤瑰悗鍐嶆彁浜ら噰璐彴璐�");
+ }
+ };
+
+ defineExpose({ open });
+
+ const checkTemplateNameDuplicate = name => {
+ if (!name || name.trim() === "") {
+ isTemplateNameDuplicate.value = false;
+ return false;
+ }
+ const isDuplicate = templateList.value.some(item => item.templateName === name.trim());
+ isTemplateNameDuplicate.value = isDuplicate;
+ return isDuplicate;
+ };
+
+ const onTemplateFilterChange = val => {
+ filterInputValue.value = val ?? "";
+ if (duplicateCheckTimer) {
+ clearTimeout(duplicateCheckTimer);
+ }
+ if (val && val.trim()) {
+ duplicateCheckTimer = setTimeout(() => {
+ if (checkTemplateNameDuplicate(val)) {
+ ElMessage.warning("妯℃澘鍚嶇О宸插瓨鍦紝璇锋洿鎹㈡ā鏉垮悕绉�");
+ }
+ }, 300);
+ } else {
+ isTemplateNameDuplicate.value = false;
+ }
+ };
+
+ const onTemplateChange = async val => {
+ if (typeof val === "string") {
+ filterInputValue.value = val;
+ checkTemplateNameDuplicate(val);
+ }
+ const matchedTemplate = templateList.value.find(item => item.templateName === val);
+ if (matchedTemplate?.id) {
+ currentTemplateId.value = matchedTemplate.id;
+ isTemplateNameDuplicate.value = false;
+ form.value.supplierId = matchedTemplate.supplierId || form.value.supplierId;
+ form.value.supplierName = matchedTemplate.supplierName || form.value.supplierName;
+ form.value.projectName = matchedTemplate.projectName || form.value.projectName;
+ form.value.paymentMethod = matchedTemplate.paymentMethod || form.value.paymentMethod;
+ productData.value = matchedTemplate.productList || matchedTemplate.productData || [];
+ } else {
+ currentTemplateId.value = null;
+ }
+ };
+
+ const getTemplateList = async () => {
+ const res = await getPurchaseTemplateList();
+ if (res && res.code === 200 && Array.isArray(res.data)) {
+ templateList.value = res.data;
+ }
+ };
+
+ const handleButtonClick = async () => {
+ if (!templateName.value || templateName.value.trim() === "") {
+ ElMessage.warning("璇疯緭鍏ユā鏉垮悕绉�");
+ return;
+ }
+ if (!currentTemplateId.value && checkTemplateNameDuplicate(templateName.value)) {
+ ElMessage.warning("妯℃澘鍚嶇О宸插瓨鍦紝璇锋洿鎹㈡ā鏉垮悕绉�");
+ return;
+ }
+ if (!form.value.supplierId) {
+ ElMessage.warning("璇峰厛閫夋嫨渚涘簲鍟�");
+ return;
+ }
+ if (!productData.value || productData.value.length === 0) {
+ ElMessage.warning("璇峰厛娣诲姞浜у搧淇℃伅");
+ return;
+ }
+
+ const params = {
+ productData: proxy.HaveJson(productData.value),
+ supplierId: form.value.supplierId,
+ paymentMethod: form.value.paymentMethod,
+ recorderId: form.value.recorderId,
+ projectName: form.value.projectName,
+ templateName: templateName.value.trim(),
+ };
+ const res = currentTemplateId.value
+ ? await updatePurchaseTemplate({ id: currentTemplateId.value, ...params })
+ : await addPurchaseTemplate(params);
+ if (res && res.code === 200) {
+ ElMessage.success(currentTemplateId.value ? "妯℃澘鏇存柊鎴愬姛" : "妯℃澘淇濆瓨鎴愬姛");
+ await getTemplateList();
+ templateName.value = "";
+ filterInputValue.value = "";
+ isTemplateNameDuplicate.value = false;
+ currentTemplateId.value = null;
+ } else {
+ ElMessage.error(res?.msg || "妯℃澘淇濆瓨澶辫触");
+ }
+ };
+
+ const productSelected = selectedRows => {
+ productSelectedRows.value = selectedRows;
+ };
+
+ const openProductForm = (type, row, index) => {
+ productOperationType.value = type;
+ productOperationIndex.value = index ?? -1;
+ proxy.resetForm("productFormRef");
+ productForm.value = type === "edit" ? { ...row } : { isChecked: false };
+ productFormVisible.value = true;
+ };
+
+ const handleProductSelect = products => {
+ if (!products || !products.length) return;
+ const p = products[0];
+ productForm.value.productId = p.productId || "";
+ productForm.value.productCategory = p.productName || p.topProductName || "";
+ productForm.value.productModelId = p.id;
+ productForm.value.specificationModel = p.model || "";
+ productForm.value.unit = p.unit || "";
+ showProductSelectDialog.value = false;
+ };
+
+ const closeProductDia = () => {
+ proxy.resetForm("productFormRef");
+ productFormVisible.value = false;
+ };
+
+ const submitProduct = () => {
+ proxy.$refs["productFormRef"].validate(valid => {
+ if (!valid) return;
+ if (productOperationType.value === "add") {
+ productData.value.push({ ...productForm.value });
+ } else if (productOperationIndex.value > -1) {
+ 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 summarizeProTable = param => proxy.summarizeTable(
+ param,
+ ["taxInclusiveUnitPrice", "taxInclusiveTotalPrice", "taxExclusiveTotalPrice"],
+ buildAmountSummaryFormat(["taxInclusiveUnitPrice", "taxInclusiveTotalPrice", "taxExclusiveTotalPrice"])
+ );
+
+ const mathNum = () => {
+ if (!productForm.value.taxRate) {
+ proxy.$modal.msgWarning("璇峰厛閫夋嫨绋庣巼");
+ return;
+ }
+ if (!productForm.value.taxInclusiveUnitPrice || !productForm.value.quantity) return;
+ productForm.value.taxInclusiveTotalPrice = proxy.calculateTaxIncludeTotalPrice(
+ productForm.value.taxInclusiveUnitPrice,
+ productForm.value.quantity
+ );
+ productForm.value.taxExclusiveTotalPrice = proxy.calculateTaxExclusiveTotalPrice(
+ productForm.value.taxInclusiveTotalPrice,
+ productForm.value.taxRate
+ );
+ };
+
+ const reverseMathNum = field => {
+ if (!productForm.value.taxRate) {
+ proxy.$modal.msgWarning("璇峰厛閫夋嫨绋庣巼");
+ return;
+ }
+ const taxRate = Number(productForm.value.taxRate);
+ if (!taxRate) return;
+ if (field === "taxInclusiveTotalPrice") {
+ if (productForm.value.quantity) {
+ productForm.value.taxInclusiveUnitPrice = formatDecimal(
+ Number(productForm.value.taxInclusiveTotalPrice) / Number(productForm.value.quantity)
+ );
+ } else if (productForm.value.taxInclusiveUnitPrice) {
+ productForm.value.quantity = (
+ Number(productForm.value.taxInclusiveTotalPrice) / Number(productForm.value.taxInclusiveUnitPrice)
+ ).toFixed(2);
+ }
+ productForm.value.taxExclusiveTotalPrice = formatDecimal(
+ Number(productForm.value.taxInclusiveTotalPrice) / (1 + taxRate / 100)
+ );
+ } else if (field === "taxExclusiveTotalPrice") {
+ productForm.value.taxInclusiveTotalPrice = formatDecimal(
+ Number(productForm.value.taxExclusiveTotalPrice) * (1 + taxRate / 100)
+ );
+ if (productForm.value.quantity) {
+ productForm.value.taxInclusiveUnitPrice = formatDecimal(
+ Number(productForm.value.taxInclusiveTotalPrice) / Number(productForm.value.quantity)
+ );
+ } else if (productForm.value.taxInclusiveUnitPrice) {
+ productForm.value.quantity = (
+ Number(productForm.value.taxInclusiveTotalPrice) / Number(productForm.value.taxInclusiveUnitPrice)
+ ).toFixed(2);
+ }
+ }
+ };
+
+ const salesLedgerChange = async () => {
+ await querygProductInfoByContractNo();
+ };
+
+ const querygProductInfoByContractNo = async () => {
+ const { code, data } = await getProductInfoByContractNo({
+ contractNo: form.value.salesLedgerId,
+ });
+ if (code === 200) {
+ productData.value = data;
+ }
+ };
+
+ const validateProductRows = () => {
+ if (productData.value.length === 0) {
+ return "璇锋坊鍔犱骇鍝佷俊鎭�";
+ }
+ const requiredFields = [
+ ["productCategory", "浜у搧澶х被"],
+ ["productModelId", "瑙勬牸鍨嬪彿"],
+ ["unit", "鍗曚綅"],
+ ["quantity", "鏁伴噺"],
+ ["taxRate", "绋庣巼"],
+ ["taxInclusiveUnitPrice", "鍚◣鍗曚环"],
+ ["taxInclusiveTotalPrice", "鍚◣鎬讳环"],
+ ["taxExclusiveTotalPrice", "涓嶅惈绋庢�讳环"],
+ ["invoiceType", "鍙戠エ绫诲瀷"],
+ ];
+ for (let i = 0; i < productData.value.length; i++) {
+ const row = productData.value[i];
+ const missing = requiredFields.find(([field]) => row[field] === "" || row[field] === null || row[field] === undefined);
+ if (missing) {
+ return `绗�${i + 1}琛屼骇鍝佷俊鎭己灏�${missing[1]}锛岃缂栬緫瀹屽啀鎻愪氦`;
+ }
+ }
+ return "";
+ };
+
+ const submitForm = () => {
+ proxy.$refs["formRef"].validate(valid => {
+ if (!valid) return;
+ const productMessage = validateProductRows();
+ if (productMessage) {
+ proxy.$modal.msgWarning(productMessage);
+ return;
+ }
+
+ const processedProductData = productData.value.map(product => {
+ const { id, ...rest } = product;
+ return rest;
+ });
+ const submitData = {
+ ...form.value,
+ productData: proxy.HaveJson(processedProductData),
+ storageBlobDTOS: fileList.value,
+ type: 2,
+ };
+ if (!submitData.salesLedgerId) {
+ submitData.salesContractNo = "";
+ }
+ delete submitData.id;
+
+ addOrEditPurchase(submitData).then(() => {
+ proxy.$modal.msgSuccess("鎻愪氦鎴愬姛");
+ closeDia();
+ emit("saved");
+ });
+ });
+ };
+
+ const closeDia = () => {
+ proxy.resetForm("formRef");
+ dialogVisible.value = false;
+ };
+
+ const handleDeleteTemplate = async item => {
+ if (!item.id) {
+ proxy.$modal.msgWarning("鏃犳硶鍒犻櫎璇ユā鏉�");
+ return;
+ }
+ try {
+ await ElMessageBox.confirm(`纭畾瑕佸垹闄ゆā鏉�"${item.templateName}"鍚楋紵`, "鍒犻櫎纭", {
+ confirmButtonText: "纭畾",
+ cancelButtonText: "鍙栨秷",
+ type: "warning",
+ });
+ const res = await delPurchaseTemplate([item.id]);
+ if (res && res.code === 200) {
+ ElMessage.success("鍒犻櫎鎴愬姛");
+ if (templateName.value === item.templateName) {
+ templateName.value = "";
+ filterInputValue.value = "";
+ }
+ await getTemplateList();
+ } else {
+ ElMessage.error(res?.msg || "鍒犻櫎澶辫触");
+ }
+ } catch (error) {
+ if (error !== "cancel") {
+ ElMessage.error("鍒犻櫎澶辫触锛岃绋嶅悗閲嶈瘯");
+ }
+ }
+ };
+</script>
+
+<style scoped lang="scss">
+ .el-row {
+ justify-content: space-between;
+ align-items: center;
+ }
+
+ .select-button-group {
+ display: flex;
+ align-items: center;
+ width: 500px;
+ margin: 20px 0;
+ }
+
+ .template-select {
+ width: 500px;
+ }
+
+ .template-save-button {
+ height: 32px;
+ margin-left: 8px;
+ }
+
+ .template-option {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ }
+
+ .no-arrow-select {
+ --el-select-suffix-icon-color: transparent;
+ }
+
+ .delete-icon {
+ cursor: pointer;
+ color: #f56c6c;
+ font-size: 14px;
+ margin-left: 8px;
+ transition: all 0.3s;
+
+ &:hover {
+ color: #f56c6c !important;
+ transform: scale(1.2);
+ }
+ }
+</style>
--
Gitblit v1.9.3