<template>
|
<el-dialog :title="modalOptions.title" v-model="visible" width="70%">
|
<el-form
|
ref="formRef"
|
:model="form"
|
:rules="rules"
|
label-width="120px"
|
label-position="top"
|
>
|
<el-row :gutter="30">
|
<el-col :span="12">
|
<el-form-item label="采购合同号:" prop="purchaseLedgerNo">
|
<el-input v-model="form.purchaseLedgerNo" disabled placeholder="多合同批量处理(具体合同号见产品列表)" />
|
</el-form-item>
|
</el-col>
|
<el-col :span="12">
|
<el-form-item label="销售合同号:" prop="salesContractNo">
|
<el-input
|
v-model="form.salesContractNo"
|
placeholder="自动填充"
|
clearable
|
disabled
|
/>
|
</el-form-item>
|
</el-col>
|
<el-col :span="12">
|
<el-form-item label="供应商名称:" prop="supplierName">
|
<el-input
|
v-model="form.supplierName"
|
placeholder="自动填充"
|
clearable
|
disabled
|
/>
|
</el-form-item>
|
</el-col>
|
<!-- <el-col :span="12">-->
|
<!-- <el-form-item label="项目名称:" prop="projectName">-->
|
<!-- <el-input-->
|
<!-- v-model="form.projectName"-->
|
<!-- placeholder="自动填充"-->
|
<!-- clearable-->
|
<!-- disabled-->
|
<!-- />-->
|
<!-- </el-form-item>-->
|
<!-- </el-col>-->
|
<el-col :span="12">
|
<el-form-item label="发票号:" prop="invoiceNumber">
|
<el-input
|
v-model="form.invoiceNumber"
|
placeholder="请输入"
|
clearable
|
/>
|
</el-form-item>
|
</el-col>
|
<el-col :span="12">
|
<el-form-item label="发票金额(元):" prop="invoiceAmount">
|
<el-input-number :step="0.01" :min="0" style="width: 100%"
|
v-model="form.invoiceAmount"
|
placeholder="请输入发票金额"
|
clearable
|
/>
|
</el-form-item>
|
</el-col>
|
<el-col :span="12">
|
<el-form-item label="录入人:" prop="issUer">
|
<el-input
|
v-model="form.issUer"
|
placeholder="请输入"
|
clearable
|
disabled
|
/>
|
</el-form-item>
|
</el-col>
|
<el-col :span="12">
|
<el-form-item label="开票日期:" prop="entryDate">
|
<el-date-picker
|
style="width: 100%"
|
v-model="form.entryDate"
|
type="date"
|
value-format="YYYY-MM-DD"
|
format="YYYY-MM-DD"
|
clearable
|
/>
|
</el-form-item>
|
</el-col>
|
<el-col :span="12">
|
<el-form-item label="录入日期:" prop="enterDate">
|
<el-date-picker
|
style="width: 100%"
|
v-model="form.enterDate"
|
type="date"
|
value-format="YYYY-MM-DD"
|
format="YYYY-MM-DD"
|
clearable
|
/>
|
</el-form-item>
|
</el-col>
|
<el-col :span="12">
|
<el-form-item label="上传附件">
|
<FileUpload
|
:showTip="false"
|
accept="*"
|
:autoUpload="true"
|
:action="action"
|
:headers="{
|
Authorization: 'Bearer ' + getToken(),
|
}"
|
:limit="10"
|
@success="uploadSuccess"
|
@remove="removeFile"
|
/>
|
</el-form-item>
|
</el-col>
|
|
</el-row>
|
<el-form-item label="产品信息:"> </el-form-item>
|
<el-table
|
:data="form.productData"
|
border
|
show-summary
|
:summary-method="summarizeChildrenTable"
|
>
|
<el-table-column align="center" label="序号" type="index" width="60" />
|
<el-table-column label="所属合同" prop="purchaseLedgerNo" width="200">
|
<template #default="{ row }">
|
<el-tag type="primary">{{ row.purchaseLedgerNo }}</el-tag>
|
</template>
|
</el-table-column>
|
<el-table-column label="产品大类" prop="productCategory" />
|
<el-table-column label="规格型号" prop="specificationModel" width="150" />
|
<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"
|
/>
|
<el-table-column
|
label="含税总价(元)"
|
prop="taxInclusiveTotalPrice"
|
:formatter="formattedNumber"
|
/>
|
<el-table-column
|
label="不含税总价(元)"
|
prop="taxExclusiveTotalPrice"
|
:formatter="formattedNumber"
|
/>
|
<el-table-column label="本次开票数" prop="ticketsNum" width="180">
|
<template #default="scope">
|
<el-input-number :step="0.1" :min="0" style="width: 100%"
|
:precision="2"
|
v-model="scope.row.ticketsNum"
|
@change="invoiceNumBlur(scope.row)"
|
/>
|
</template>
|
</el-table-column>
|
<el-table-column
|
label="本次开票金额(元)"
|
prop="ticketsAmount"
|
width="180"
|
>
|
<template #default="scope">
|
<el-input-number :step="0.01" :min="0" style="width: 100%"
|
:precision="2"
|
v-model="scope.row.ticketsAmount"
|
@change="invoiceAmountBlur(scope.row)"
|
/>
|
</template>
|
</el-table-column>
|
<el-table-column
|
label="未来票数"
|
prop="futureTickets"
|
:formatter="formattedNumber"
|
/>
|
<el-table-column
|
label="本次来票金额(元)"
|
prop="ticketsAmount"
|
:formatter="formattedNumber"
|
/>
|
<el-table-column
|
label="未来票数"
|
prop="futureTickets"
|
:formatter="formattedNumber"
|
/>
|
<el-table-column
|
label="未来票金额(元)"
|
prop="futureTicketsAmount"
|
:formatter="formattedNumber"
|
/>
|
</el-table>
|
</el-form>
|
<template #footer>
|
<el-button type="primary" :loading="modalLoading" @click="submitForm">
|
确认
|
</el-button>
|
<el-button @click="closeModal">取消</el-button>
|
</template>
|
</el-dialog>
|
</template>
|
|
<script setup>
|
import { ref, getCurrentInstance } from "vue";
|
import { defineEmits } from 'vue';
|
import { useModal } from "@/hooks/useModal";
|
import useFormData from "@/hooks/useFormData";
|
import FileUpload from "@/components/Upload/FileUpload.vue";
|
import {
|
getPurchaseNoById,
|
getInfo,
|
addOrUpdateRegistration,
|
} from "@/api/procurementManagement/invoiceEntry.js";
|
import { getPurchaseById } from "@/api/procurementManagement/procurementLedger.js";
|
import { getToken } from "@/utils/auth";
|
import useUserStore from "@/store/modules/user";
|
import dayjs from "dayjs";
|
|
defineOptions({
|
name: "来票登记模态框",
|
});
|
|
const userStore = useUserStore();
|
const action = import.meta.env.VITE_APP_BASE_API + "/file/upload";
|
const formRef = ref();
|
const { proxy } = getCurrentInstance();
|
const { form } = useFormData({
|
purchaseLedgerNo: undefined, // 采购合同号
|
salesContractNo: undefined, // 销售合同号
|
supplierName: undefined, // 供应商名称
|
projectName: undefined, // 项目名称
|
invoiceNumber: undefined, // 发票号
|
invoiceAmount: undefined, // 发票金额(元)
|
issUerId: userStore.id, // 录入人
|
issUer: userStore.nickName, // 录入人
|
entryDate: undefined, // 开票日期
|
salesContractNoId: undefined, // 开票日期
|
enterDate: dayjs().format("YYYY-MM-DD"),
|
productData: [], // 表格
|
tempFileIds: [], // 文件
|
});
|
|
const selectedContracts = ref([]); // 存储选中的合同数据
|
|
const rules = ref({
|
invoiceNumber: [
|
{ required: true, message: "请输入发票号", trigger: "blur" },
|
{ type: "string" },
|
],
|
invoiceAmount: [
|
{ required: true, message: "请输入发票金额", trigger: "blur" },
|
],
|
entryDate: [{ required: true, message: "请选择开票日期", trigger: "change" }],
|
enterDate: [{ required: true, message: "请选择录入日期", trigger: "change" }],
|
});
|
|
const {
|
id,
|
visible,
|
loading: modalLoading,
|
openModal,
|
modalOptions,
|
handleConfirm,
|
closeModal,
|
} = useModal({
|
title: "来票登记",
|
});
|
|
const emit = defineEmits(['refreshList']);
|
|
const columns = [
|
{
|
label: "产品大类",
|
prop: "productCategory",
|
width: 120,
|
},
|
{
|
label: "规格型号",
|
prop: "specificationModel",
|
width: 120,
|
},
|
{
|
label: "单位",
|
prop: "unit",
|
width: 80,
|
},
|
{
|
label: "数量",
|
prop: "quantity",
|
width: 80,
|
},
|
{
|
label: "税率(%)",
|
prop: "taxRate",
|
width: 80,
|
},
|
{
|
label: "录入日期",
|
prop: "registerDate",
|
width: 120,
|
},
|
{
|
label: "含税单价(元)",
|
prop: "taxInclusiveUnitPrice",
|
width: 150,
|
formatData: (val) => {
|
return val ? parseFloat(val).toFixed(2) : 0;
|
},
|
},
|
{
|
label: "含税总价(元)",
|
prop: "taxInclusiveTotalPrice",
|
width: 150,
|
formatData: (val) => {
|
return parseFloat(val).toFixed(2) ?? 0;
|
},
|
},
|
{
|
label: "不含税总价(元)",
|
prop: "taxExclusiveTotalPrice",
|
width: 150,
|
formatData: (val) => {
|
return parseFloat(val).toFixed(2) ?? 0;
|
},
|
},
|
{
|
label: "本次来票数",
|
prop: "ticketsNum",
|
dataType: "slot",
|
slot: "ticketsNumRef",
|
width: 180,
|
align: "center",
|
},
|
{
|
label: "本次来票金额(元)",
|
prop: "ticketsAmount",
|
dataType: "slot",
|
slot: "ticketsAmountRef",
|
width: 180,
|
align: "center",
|
},
|
{
|
label: "未来票数",
|
prop: "futureTickets",
|
width: 100,
|
},
|
{
|
label: "未来票金额(元)",
|
prop: "futureTicketsAmount",
|
width: 200,
|
},
|
];
|
const formattedNumber = (row, column, cellValue) => {
|
if (cellValue == 0) {
|
return parseFloat(cellValue).toFixed(2);
|
}
|
if (cellValue) {
|
return parseFloat(cellValue).toFixed(2);
|
} else {
|
return cellValue;
|
}
|
};
|
const getTableData = async (type, selectedRows) => {
|
if (type == "add") {
|
// 检查所有选择的合同是否具有相同的供应商名称
|
const firstRow = selectedRows[0];
|
const isSameSupplier = selectedRows.every(row =>
|
row.supplierName === firstRow.supplierName
|
);
|
|
if (!isSameSupplier) {
|
proxy.$modal.msgError("请选择相同供应商名称的合同");
|
return;
|
}
|
|
// 允许不同的采购合同号批量处理,无需检查重复
|
|
// 清空表单数据
|
Object.keys(form).forEach(key => {
|
if (key !== 'productData') {
|
form[key] = undefined;
|
}
|
});
|
form.productData = [];
|
|
// 加载所有选中合同的产品数据
|
const promises = selectedRows.map(row =>
|
getInfo({ id: row.id })
|
);
|
|
Promise.all(promises).then(results => {
|
// 合并所有合同的产品数据,并为每个产品添加对应的合同信息
|
const allProductData = [];
|
results.forEach((result, index) => {
|
const contract = selectedRows[index];
|
const contractId = contract.id;
|
if (result.data && result.data.productData) {
|
result.data.productData.forEach(item => {
|
allProductData.push({
|
...item,
|
id: contractId, // 明确设置合同ID
|
purchaseLedgerNo: contract.purchaseContractNumber, // 添加采购合同号
|
supplierName: contract.supplierName, // 添加供应商名称
|
projectName: contract.projectName // 添加项目名称
|
});
|
});
|
}
|
});
|
|
// 设置表单数据(使用第一个合同的基本信息,采购合同号留空)
|
form.purchaseLedgerNo = ""; // 采购合同号留空,因为会在产品表格中分别显示
|
form.invoiceAmount = 0;
|
form.invoiceNumber = "";
|
form.entryDate = dayjs().format("YYYY-MM-DD");
|
form.enterDate = dayjs().format("YYYY-MM-DD");
|
form.salesContractNo = results[0].data.salesContractNo;
|
form.projectName = results[0].data.projectName;
|
form.supplierName = results[0].data.supplierName;
|
// 保留录入人信息
|
form.issUerId = userStore.id;
|
form.issUer = userStore.nickName;
|
|
form.productData = allProductData;
|
|
// 存储选中的合同数据
|
selectedContracts.value = selectedRows;
|
});
|
} else if (type == "edit") {
|
const id = Array.isArray(selectedRows) ? selectedRows[0].id : selectedRows;
|
const data = await getPurchaseById({ id, type: 2 });
|
form.purchaseLedgerNo = data.purchaseContractNumber;
|
form.invoiceAmount = data.invoiceAmount;
|
form.invoiceNumber = data.invoiceNumber;
|
form.salesContractNo = data.salesContractNo;
|
form.projectName = data.projectName;
|
form.supplierName = data.supplierName;
|
form.entryDate = data.entryDate;
|
form.productData = data.productData;
|
}
|
};
|
// 子表合计方法
|
const summarizeChildrenTable = (param) => {
|
return proxy.summarizeTable(param, [
|
"taxInclusiveUnitPrice",
|
"taxInclusiveTotalPrice",
|
"taxExclusiveTotalPrice",
|
"ticketsNum",
|
"ticketsAmount",
|
"ticketsAmountRef",
|
"futureTickets",
|
"futureTicketsAmount",
|
]);
|
};
|
//本次来票数失焦操作
|
const invoiceNumBlur = (row) => {
|
if (!row.ticketsNum || row.ticketsNum === "") {
|
row.ticketsNum = 0;
|
}
|
if (Number(row.ticketsNum) > Number(row.tempFutureTickets)) {
|
proxy.$modal.msgWarning("本次开票数不得大于未开票数");
|
row.ticketsNum = 0;
|
return;
|
}
|
// 计算本次来票金额
|
row.ticketsAmount = (row.ticketsNum * row.taxInclusiveUnitPrice).toFixed(2)
|
// 计算未来票数
|
row.futureTickets = (row.tempFutureTickets - row.ticketsNum).toFixed(2)
|
// 计算未来票金额
|
row.futureTicketsAmount = (row.tempFutureTicketsAmount - row.ticketsAmount).toFixed(2)
|
calculateinvoiceAmount();
|
};
|
|
// 本次来票金额失焦操作
|
const invoiceAmountBlur = (row) => {
|
if (!row.ticketsAmount) {
|
row.ticketsAmount = 0;
|
}
|
// 计算是否超过来票总金额
|
if (row.ticketsAmount > row.tempFutureTicketsAmount) {
|
proxy.$modal.msgWarning("本次来票金额不得大于未来票金额");
|
row.ticketsAmount = 0;
|
}
|
// 计算本次来票数
|
row.ticketsNum = Number(
|
(row.ticketsAmount / row.taxInclusiveUnitPrice).toFixed(2)
|
);
|
// 计算未来票数
|
row.futureTickets = (row.tempFutureTickets - row.ticketsNum).toFixed(2)
|
// 计算未来票金额
|
row.futureTicketsAmount = (row.tempFutureTicketsAmount - row.ticketsAmount).toFixed(2)
|
calculateinvoiceAmount();
|
};
|
|
const calculateinvoiceAmount = () => {
|
let invoiceAmountTotal = 0;
|
form.productData.forEach((item) => {
|
if (item.ticketsAmount) {
|
invoiceAmountTotal += Number(item.ticketsAmount);
|
}
|
});
|
form.invoiceAmount = invoiceAmountTotal.toFixed(2);
|
};
|
|
const open = async (type, selectedRows) => {
|
visible.value = true;
|
|
// 如果是批量操作,设置标题
|
if (Array.isArray(selectedRows) && selectedRows.length > 1) {
|
modalOptions.title = `批量新增 (${selectedRows.length}条)`;
|
} else {
|
modalOptions.title = type == "add" ? "新增" : "编辑";
|
}
|
|
// 如果是单个操作,获取id
|
if (!Array.isArray(selectedRows) || selectedRows.length === 1) {
|
const idValue = Array.isArray(selectedRows) ? selectedRows[0].id : selectedRows;
|
id.value = idValue;
|
}
|
|
await getTableData(type, selectedRows);
|
};
|
|
const uploadSuccess = (response) => {
|
form.tempFileIds.push(response.data.tempId);
|
console.log(form);
|
};
|
|
const removeFile = (file) => {
|
const { tempId } = file.response.data;
|
form.tempFileIds = form.tempFileIds.filter((item) => item !== tempId);
|
};
|
|
const closeAndRefresh = () => {
|
closeModal();
|
emit('refreshList');
|
};
|
|
const submitForm = () => {
|
proxy.$refs["formRef"].validate((valid) => {
|
if (valid) {
|
// 如果是批量操作,将所有合同的数据放在一个数组里,只调用一次接口
|
if (selectedContracts.value.length > 1) {
|
// 创建包含所有合同数据的数组
|
const batchData = selectedContracts.value.map(contract => {
|
// 筛选出属于当前合同的产品数据
|
const contractProductData = form.productData.filter(item =>
|
item.id === contract.id
|
);
|
|
// 为每个采购合同创建独立的对象
|
return {
|
// 基础表单数据
|
invoiceNumber: form.invoiceNumber,
|
invoiceAmount: form.invoiceAmount,
|
entryDate: form.entryDate,
|
enterDate: form.enterDate,
|
issUerId: form.issUerId, // 录入人id
|
issUer: form.issUer, // 录入人
|
tempFileIds: form.tempFileIds,
|
|
// 合同实际信息
|
purchaseLedgerId: contract.id, // 使用id作为字段名,值为purchaseLedgerId
|
purchaseContractNumber: contract.purchaseContractNumber, // 使用实际的采购合同号
|
salesContractNo: contract.salesContractNo, // 使用实际的销售合同号
|
supplierName: contract.supplierName, // 使用实际的供应商名称
|
projectName: contract.projectName, // 使用实际的项目名称
|
|
// 产品数据
|
productData: proxy.HaveJson(contractProductData),
|
|
// 批量标识
|
isBatch: true,
|
type: 4
|
};
|
});
|
|
// 只调用一次接口,传递包含所有合同数据的数组
|
modalLoading.value = true;
|
addOrUpdateRegistration(batchData).then((res) => {
|
modalLoading.value = false;
|
if (res.code === 200) {
|
proxy.$modal.msgSuccess("批量登记成功");
|
closeAndRefresh();
|
}
|
}).catch(() => {
|
modalLoading.value = false;
|
proxy.$modal.msgError("批量登记失败");
|
});
|
} else {
|
// 单个合同提交逻辑
|
const singleContract = selectedContracts.value[0];
|
const singleForm = {
|
// 基础表单数据
|
invoiceNumber: form.invoiceNumber,
|
invoiceAmount: form.invoiceAmount,
|
entryDate: form.entryDate,
|
enterDate: form.enterDate,
|
issUerId: form.issUerId, // 录入人id
|
issUer: form.issUer, // 录入人
|
tempFileIds: form.tempFileIds,
|
|
// 合同实际信息
|
purchaseLedgerId: singleContract.id, // 使用id作为字段名,值为purchaseLedgerId
|
purchaseContractNumber: singleContract.purchaseContractNumber, // 使用实际的采购合同号
|
salesContractNo: singleContract.salesContractNo, // 使用实际的销售合同号
|
supplierName: singleContract.supplierName, // 使用实际的供应商名称
|
projectName: singleContract.projectName, // 使用实际的项目名称
|
|
// 产品数据
|
productData: proxy.HaveJson(form.productData),
|
|
// 批量标识
|
isBatch: false,
|
type: 4
|
};
|
|
modalLoading.value = true;
|
addOrUpdateRegistration(singleForm).then((res) => {
|
modalLoading.value = false;
|
if (res.code === 200) {
|
proxy.$modal.msgSuccess("登记成功");
|
closeAndRefresh();
|
}
|
}).catch(() => {
|
modalLoading.value = false;
|
proxy.$modal.msgError("登记失败");
|
});
|
}
|
}
|
});
|
};
|
|
defineExpose({
|
open,
|
closeAndRefresh,
|
});
|
</script>
|
|
<style lang="scss" scoped></style>
|