| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <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> |