<template>
|
<div>
|
<el-dialog
|
v-model="isShow"
|
title="新增采购退货"
|
width="1600"
|
@close="closeModal"
|
>
|
<el-form label-width="140px" :model="formState" label-position="top" ref="formRef" :inline="true">
|
<div class="section-title">
|
<span class="title-dot"></span>
|
<span class="title-text">基本信息</span>
|
</div>
|
<el-form-item
|
label="退料单号"
|
prop="no"
|
:rules="[
|
{
|
required: !formState.isDefaultNo,
|
message: '请输入退料单号',
|
trigger: 'blur',
|
}
|
]"
|
>
|
<el-input
|
v-model="formState.no"
|
:placeholder="formState.isDefaultNo ? '使用系统编号' : '请输入退料单号'"
|
:disabled="formState.isDefaultNo"
|
style="width: 240px"
|
>
|
<template #append>
|
<el-checkbox v-model="formState.isDefaultNo" size="large" @change="handleChangeIsDefaultNo" />
|
</template>
|
</el-input>
|
</el-form-item>
|
|
<el-form-item
|
label="退货方式"
|
prop="returnType"
|
:rules="[
|
{
|
required: true,
|
message: '请选择退货方式',
|
trigger: 'change',
|
}
|
]"
|
>
|
<el-select
|
v-model="formState.returnType"
|
placeholder="请选择退货方式"
|
style="width: 240px"
|
>
|
<el-option label="退货退款" :value="0" />
|
<el-option label="拒收" :value="1" />
|
</el-select>
|
</el-form-item>
|
|
<el-form-item
|
label="供应商名称"
|
prop="supplierId"
|
:rules="[
|
{
|
required: true,
|
message: '请选择供应商',
|
trigger: 'change',
|
}
|
]"
|
>
|
<el-select
|
v-model="formState.supplierId"
|
placeholder="请选择供应商"
|
style="width: 240px"
|
@focus="fetchSupplierOptions"
|
@change="handleChangeSupplierId"
|
>
|
<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="项目阶段"
|
prop="projectPhase"
|
>
|
<el-select
|
v-model="formState.projectPhase"
|
placeholder="请选择项目阶段"
|
style="width: 240px"
|
>
|
<el-option
|
v-for="item in projectStageOptions"
|
:key="item.value"
|
:label="item.label"
|
:value="item.value"
|
/>
|
</el-select>
|
</el-form-item>
|
|
<el-form-item
|
label="制作日期"
|
prop="preparedAt"
|
:rules="[
|
{
|
required: true,
|
message: '请选择制作日期',
|
trigger: 'change',
|
}
|
]"
|
>
|
<el-date-picker
|
v-model="formState.preparedAt"
|
value-format="YYYY-MM-DD"
|
format="YYYY-MM-DD"
|
type="date"
|
placeholder="请选择制作日期"
|
style="width: 240px"
|
clearable />
|
</el-form-item>
|
|
<el-form-item
|
label="制单人:"
|
prop="preparedUserId"
|
:rules="[
|
{
|
required: true,
|
message: '请选择制单人',
|
trigger: 'change',
|
}
|
]"
|
>
|
<el-select
|
v-model="formState.preparedUserId"
|
placeholder="请选择"
|
clearable
|
filterable
|
default-first-option
|
:reserve-keyword="false"
|
style="width: 240px"
|
@focus="fetchUserOptions"
|
@change="formState.preparedUserName = userOptions.find(item => item.userId === formState.preparedUserId)?.nickName || ''"
|
>
|
<el-option
|
v-for="item in userOptions"
|
:key="item.userId"
|
:label="item.nickName"
|
:value="item.userId"
|
/>
|
</el-select>
|
</el-form-item>
|
|
<el-form-item
|
label="退料人:"
|
prop="returnUserId"
|
:rules="[
|
{
|
required: true,
|
message: '请选择退料人',
|
trigger: 'change',
|
}
|
]"
|
>
|
<el-select
|
v-model="formState.returnUserId"
|
placeholder="请选择"
|
clearable
|
filterable
|
default-first-option
|
style="width: 240px"
|
:reserve-keyword="false"
|
@focus="fetchUserOptions"
|
@change="formState.returnUserName = userOptions.find(item => item.userId === formState.returnUserId)?.nickName || ''"
|
>
|
<el-option
|
v-for="item in userOptions"
|
:key="item.userId"
|
:label="item.nickName"
|
:value="item.userId"
|
/>
|
</el-select>
|
</el-form-item>
|
|
<el-form-item
|
label="采购合同号:"
|
prop="purchaseLedgerId"
|
:rules="[
|
{
|
required: true,
|
message: '请选择采购合同号',
|
trigger: 'change',
|
}
|
]"
|
>
|
<el-select
|
v-model="formState.purchaseLedgerId"
|
placeholder="请选择"
|
clearable
|
filterable
|
default-first-option
|
style="width: 240px"
|
:reserve-keyword="false"
|
@change="handleChangePurchaseLedgerId"
|
>
|
<el-option
|
v-for="item in purchaseLedgerOptions"
|
:key="item.id"
|
:label="item.purchaseContractNumber"
|
:value="item.id"
|
/>
|
</el-select>
|
</el-form-item>
|
|
<el-form-item
|
label="备注:"
|
prop="remark"
|
>
|
<el-input style="width: 240px" v-model="formState.remark" :rows="1" type="textarea" placeholder="请输入备注"/>
|
</el-form-item>
|
|
<div style="margin: 20px 0;">
|
<div class="section-title">
|
<span class="title-dot"></span>
|
<span class="title-text">产品列表</span>
|
</div>
|
<el-button type="primary" size="small" style="margin-bottom: 20px" @click="isShowProductsModal = true" :disabled="!formState.purchaseLedgerId">添加产品</el-button>
|
<el-table :data="formState.purchaseReturnOrderProductsDtos"
|
border
|
max-height="400"
|
:scroll-y="true"
|
show-summary
|
:summary-method="summarizeChildrenTable">
|
<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="100" />
|
<el-table-column label="可退货数量"
|
prop="availableQuality"
|
width="130" />
|
<el-table-column label="退货数量"
|
prop="returnQuantity"
|
width="180">
|
<template #default="scope">
|
<el-input-number v-model="scope.row.returnQuantity"
|
controls-position="right"
|
:step="1"
|
:min="0"
|
:max="getReturnQtyMax(scope.row)"
|
:disabled="getReturnQtyMax(scope.row) <= 0"
|
@change="syncReturnTotal(scope.row)"
|
required
|
placeholder="请输入退货数量" />
|
</template>
|
</el-table-column>
|
<el-table-column label="库存预警数量"
|
prop="warnNum"
|
width="120"
|
show-overflow-tooltip />
|
<el-table-column label="税率(%)"
|
prop="taxRate"
|
width="80" />
|
<el-table-column label="含税单价(元)"
|
prop="taxInclusiveUnitPrice"
|
:formatter="formattedNumber"
|
width="150" />
|
<el-table-column label="退货总价(元)"
|
prop="taxInclusiveTotalPrice"
|
width="180">
|
<template #default="scope">
|
{{ formatAmount(getReturnTotal(scope.row)) || '--' }}
|
</template>
|
</el-table-column>
|
<el-table-column label="是否质检"
|
prop="isChecked"
|
width="150">
|
<template #default="scope">
|
<el-tag :type="scope.row.isChecked ? 'success' : 'info'">
|
{{ scope.row.isChecked ? '是' : '否' }}
|
</el-tag>
|
</template>
|
</el-table-column>
|
<el-table-column fixed="right"
|
label="操作"
|
width="100"
|
align="center">
|
<template #default="scope">
|
<el-button
|
link
|
type="danger"
|
size="small"
|
@click="delProduct(scope.$index)"
|
>
|
删除
|
</el-button>
|
</template>
|
</el-table-column>
|
</el-table>
|
</div>
|
|
<div class="section-title">
|
<span class="title-dot"></span>
|
<span class="title-text">费用信息</span>
|
</div>
|
|
<el-form-item
|
label="整单折扣额:"
|
prop="totalDiscountAmount"
|
>
|
<el-input-number v-model="formState.totalDiscountAmount"
|
controls-position="right"
|
:step="0.01"
|
:precision="2"
|
style="width: 100%;"
|
@change="handleChangeTotalDiscountAmount"
|
placeholder="请输入整单折扣额"/>
|
</el-form-item>
|
|
<el-form-item
|
label="整单折扣率:"
|
prop="totalDiscountAmount"
|
>
|
<el-input-number v-model="formState.totalDiscountRate"
|
controls-position="right"
|
:step="0.01"
|
:precision="2"
|
style="width: 100%;"
|
placeholder="请输入整单折扣率"/>
|
</el-form-item>
|
|
<el-form-item
|
label="成交金额:"
|
prop="totalAmount"
|
:rules="[
|
{
|
required: true,
|
message: '请输入成交金额',
|
trigger: 'change',
|
}
|
]"
|
>
|
<el-input-number v-model="formState.totalAmount"
|
controls-position="right"
|
:step="0.01"
|
:precision="2"
|
style="width: 100%;"
|
placeholder="请输入成交金额"/>
|
</el-form-item>
|
<el-form-item label="收款方式" prop="incomeType" :rules="[
|
{
|
required: true,
|
message: '请选择收款方式',
|
trigger: 'change',
|
}
|
]">
|
<el-select
|
style="width: 240px;"
|
v-model="formState.incomeType"
|
placeholder="请选择"
|
clearable
|
|
>
|
<el-option :label="item.label" :value="item.value" v-for="(item,index) in payment_methods" :key="index" />
|
</el-select>
|
</el-form-item>
|
</el-form>
|
<template #footer>
|
<div class="dialog-footer">
|
<el-button type="primary" @click="handleSubmit">确认</el-button>
|
<el-button @click="closeModal">取消</el-button>
|
</div>
|
</template>
|
</el-dialog>
|
|
<ProductList
|
v-if="isShowProductsModal"
|
v-model:visible="isShowProductsModal"
|
:purchase-ledger-id="formState.purchaseLedgerId"
|
@completed="handleAddProduct"
|
/>
|
|
</div>
|
</template>
|
|
<script setup>
|
import {ref, computed, getCurrentInstance, watch, defineAsyncComponent} from "vue";
|
import {createPurchaseReturnOrder} from "@/api/procurementManagement/purchase_return_order.js";
|
import {getOptions, purchaseList} from "@/api/procurementManagement/procurementLedger.js";
|
import {userListNoPageByTenantId} from "@/api/system/user.js";
|
const ProductList = defineAsyncComponent(() => import("@/views/procurementManagement/purchaseReturnOrder/ProductList.vue"));
|
import {
|
productList,
|
} from "@/api/procurementManagement/procurementLedger.js";
|
const props = defineProps({
|
visible: {
|
type: Boolean,
|
required: true,
|
}
|
});
|
let { proxy } = getCurrentInstance()
|
const { payment_methods } = proxy.useDict("payment_methods");
|
const emit = defineEmits(['update:visible', 'completed']);
|
|
// 响应式数据(替代选项式的 data)
|
const formState = ref({
|
no: '',
|
isDefaultNo: true,
|
returnType: 0,
|
incomeType: undefined,
|
remark: '',
|
supplierId: undefined,
|
projectId: undefined,
|
projectPhase: undefined,
|
preparedAt: undefined,
|
preparedUserId: undefined,
|
returnUserId: undefined,
|
purchaseLedgerId: undefined,
|
purchaseReturnOrderProductsDtos: [],
|
totalDiscountAmount: 0,
|
totalDiscountRate: undefined,
|
totalAmount: 0,
|
});
|
// 供应商选项
|
const supplierOptions = ref([])
|
// 项目阶段选项
|
const projectStageOptions = ref([
|
{
|
label: '立项',
|
value: 0,
|
},
|
{
|
label: '设计',
|
value: 1,
|
},
|
{
|
label: '采购',
|
value: 2,
|
},
|
{
|
label: '生产',
|
value: 3,
|
},
|
{
|
label: '出货',
|
value: 4,
|
}
|
])
|
// 用户选项
|
const userOptions = ref([])
|
// 采购台账选项
|
const purchaseLedgerOptions = ref([])
|
// 是否展示产品列表数据
|
const isShowProductsModal = ref(false)
|
|
const isShow = computed({
|
get() {
|
return props.visible;
|
},
|
set(val) {
|
emit('update:visible', val);
|
},
|
});
|
|
const formattedNumber = (row, column, cellValue) => {
|
return parseFloat(cellValue).toFixed(2);
|
};
|
|
const formatAmount = (value) => {
|
if (value === null || value === undefined || value === '') {
|
return '--'
|
}
|
const num = Number(value)
|
if (Number.isNaN(num)) {
|
return '--'
|
}
|
return num.toFixed(2)
|
}
|
|
const toNumber = (val) => {
|
const num = Number(val)
|
return Number.isNaN(num) ? 0 : num
|
}
|
|
const getReturnTotal = (row) => {
|
const qty = toNumber(row?.returnQuantity)
|
const unitPrice = toNumber(row?.taxInclusiveUnitPrice)
|
const total = qty * unitPrice
|
return Number(total.toFixed(2))
|
}
|
|
const syncReturnTotal = (row) => {
|
if (!row) {
|
return
|
}
|
row.taxInclusiveTotalPrice = getReturnTotal(row)
|
}
|
|
const getReturnQtyMax = (row) => {
|
const max = Number(row?.availableQuality)
|
if (Number.isNaN(max) || max < 0) {
|
return 0
|
}
|
return max
|
}
|
|
const closeModal = () => {
|
isShow.value = false;
|
};
|
|
const summarizeChildrenTable = (param) => {
|
return proxy.summarizeTable(
|
param,
|
[
|
"quantity",
|
"availableQuality",
|
"returnQuantity",
|
"taxInclusiveUnitPrice",
|
"taxInclusiveTotalPrice",
|
"taxExclusiveTotalPrice",
|
],
|
{
|
quantity: { noDecimal: true }, // 不保留小数
|
returnQuantity: { noDecimal: true }, // 不保留小数
|
availableQuality: { noDecimal: true }, // 不保留小数
|
}
|
);
|
};
|
|
const handleChangeTotalDiscountAmount= () => {
|
syncTotalAmount()
|
}
|
|
const syncTotalAmount = () => {
|
const rows = formState.value.purchaseReturnOrderProductsDtos || []
|
const baseAmount = rows.reduce((sum, item) => {
|
return sum + toNumber(item.taxInclusiveTotalPrice)
|
}, 0)
|
const discount = toNumber(formState.value.totalDiscountAmount)
|
// 成交金额 = 产品退货总价合计 - 折扣额
|
formState.value.totalAmount = Number((baseAmount - discount).toFixed(2))
|
}
|
|
// 获取供应商选项
|
const fetchSupplierOptions = () => {
|
if (supplierOptions.value.length > 0) {
|
return
|
}
|
getOptions().then((res) => {
|
supplierOptions.value = res.data;
|
});
|
}
|
|
|
// 获取用户选项
|
const fetchUserOptions = () => {
|
if (userOptions.value.length > 0) {
|
return
|
}
|
userListNoPageByTenantId().then((res) => {
|
userOptions.value = res.data;
|
});
|
}
|
|
// 处理改变供应商数据
|
const handleChangeSupplierId = () => {
|
formState.value.purchaseLedgerId = undefined
|
formState.value.supplierName = supplierOptions.value.find(item => item.id === formState.value.supplierId)?.supplierName || ''
|
fetchPurchaseLedgerOptions()
|
}
|
|
// 获取采购台账选项
|
const fetchPurchaseLedgerOptions = () => {
|
purchaseLedgerOptions.value = []
|
if (formState.value.supplierId) {
|
purchaseList({supplierId: formState.value.supplierId,approvalStatus:3}).then((res) => {
|
purchaseLedgerOptions.value = res.rows;
|
});
|
}
|
}
|
|
// 处理改变采购台账数据
|
const handleChangePurchaseLedgerId = async () => {
|
const res = await productList({ salesLedgerId: formState.value.purchaseLedgerId, type: 2 });
|
formState.value.purchaseReturnOrderProductsDtos = res.data.map(item => ({
|
...item,
|
returnQuantity: undefined,
|
taxInclusiveTotalPrice: 0,
|
salesLedgerProductId: item.id,
|
}))
|
syncTotalAmount()
|
}
|
|
// 处理改变是否默认编号
|
const handleChangeIsDefaultNo = (checked) => {
|
if (checked) {
|
formState.value.no = ''
|
}
|
}
|
|
// 增加产品
|
const handleAddProduct = (selectedRows) => {
|
const existingIds = new Set(formState.value.purchaseReturnOrderProductsDtos.map(item => item.id));
|
const newProducts = selectedRows.filter(item => !existingIds.has(item.id)).map(item => ({
|
...item,
|
returnQuantity: undefined,
|
taxInclusiveTotalPrice: 0,
|
salesLedgerProductId: item.id,
|
}));
|
formState.value.purchaseReturnOrderProductsDtos.push(...newProducts);
|
syncTotalAmount()
|
}
|
|
// 删除单项产品
|
const delProduct = (index) => {
|
formState.value.purchaseReturnOrderProductsDtos.splice(index, 1)
|
syncTotalAmount()
|
}
|
|
// 提交表单
|
const handleSubmit = () => {
|
const productList = formState.value.purchaseReturnOrderProductsDtos || []
|
|
productList.forEach(syncReturnTotal)
|
|
if (productList.length === 0) {
|
proxy.$modal.msgError("请先选择产品")
|
return
|
}
|
|
// 逐行校验退货数量:任意一行未填/非法/超限都不允许提交
|
const invalidRowIndex = productList.findIndex((item) => {
|
const qty = Number(item.returnQuantity)
|
const maxQty = Number(item.availableQuality)
|
|
if (item.returnQuantity === null || item.returnQuantity === undefined || item.returnQuantity === "") {
|
return true
|
}
|
if (Number.isNaN(qty) || qty <= 0) {
|
return true
|
}
|
if (!Number.isNaN(maxQty) && maxQty > 0 && qty > maxQty) {
|
return true
|
}
|
return false
|
})
|
|
if (invalidRowIndex !== -1) {
|
proxy.$modal.msgError(`第${invalidRowIndex + 1}行退货数量未填写或不合法`)
|
return
|
}
|
|
proxy.$refs["formRef"].validate(valid => {
|
if (valid) {
|
createPurchaseReturnOrder(formState.value).then(res => {
|
// 关闭模态框
|
isShow.value = false;
|
// 告知父组件已完成
|
emit('completed');
|
proxy.$modal.msgSuccess("提交成功");
|
})
|
}
|
})
|
};
|
|
watch(
|
() => formState.value.purchaseReturnOrderProductsDtos,
|
(rows) => {
|
(rows || []).forEach(syncReturnTotal)
|
syncTotalAmount()
|
},
|
{ deep: true }
|
)
|
|
defineExpose({
|
closeModal,
|
handleSubmit,
|
isShow,
|
});
|
</script>
|
|
<style scoped lang="scss">
|
.section-title {
|
display: flex;
|
align-items: center;
|
margin-bottom: 20px;
|
font-size: 16px;
|
font-weight: 600;
|
color: #303133;
|
width: 100%;
|
clear: both;
|
}
|
|
.title-dot {
|
display: inline-block;
|
width: 8px;
|
height: 8px;
|
background-color: #409EFF;
|
border-radius: 50%;
|
margin-right: 8px;
|
}
|
</style>
|