<template>
|
<FormDialog
|
v-model="dialogVisible"
|
title="确认收货"
|
width="70%"
|
operation-type="edit"
|
@close="handleClose"
|
@cancel="handleClose"
|
@confirm="handleConfirm"
|
>
|
<el-form ref="formRef" :model="detailData" label-width="140px" label-position="top">
|
<el-row :gutter="30">
|
<el-col :span="12">
|
<el-form-item label="采购合同号:">
|
<el-input :model-value="detailData.purchaseContractNumber || ''" disabled />
|
</el-form-item>
|
</el-col>
|
<el-col :span="12">
|
<el-form-item label="销售合同号:">
|
<el-input :model-value="detailData.salesContractNo || ''" disabled />
|
</el-form-item>
|
</el-col>
|
</el-row>
|
|
<el-row :gutter="30">
|
<el-col :span="12">
|
<el-form-item label="供应商名称:">
|
<el-input :model-value="detailData.supplierName || ''" disabled />
|
</el-form-item>
|
</el-col>
|
<el-col :span="12">
|
<el-form-item label="项目名称:">
|
<el-input :model-value="detailData.projectName || ''" disabled />
|
</el-form-item>
|
</el-col>
|
</el-row>
|
|
<el-row :gutter="30">
|
<el-col :span="12">
|
<el-form-item label="付款方式:">
|
<el-input :model-value="detailData.paymentMethod || ''" disabled />
|
</el-form-item>
|
</el-col>
|
<el-col :span="12">
|
<el-form-item label="签订日期:">
|
<el-input :model-value="detailData.executionDate || ''" disabled />
|
</el-form-item>
|
</el-col>
|
</el-row>
|
|
<el-row :gutter="30">
|
<el-col :span="12">
|
<el-form-item label="录入人:">
|
<el-input :model-value="detailData.recorderName || ''" disabled />
|
</el-form-item>
|
</el-col>
|
<el-col :span="12">
|
<el-form-item label="录入日期:">
|
<el-input :model-value="detailData.entryDate || ''" disabled />
|
</el-form-item>
|
</el-col>
|
</el-row>
|
|
<el-row :gutter="30">
|
<el-col :span="24">
|
<el-form-item label="审批人选择:">
|
<div class="approver-nodes-container">
|
<div v-for="(name, index) in approverNames" :key="`${name}-${index}`" class="approver-node-item">
|
<div class="approver-node-header">
|
<span class="approver-node-label">审批节点 {{ index + 1 }}</span>
|
</div>
|
<el-input :model-value="name" disabled />
|
</div>
|
<el-empty v-if="!approverNames.length" description="暂无审批人信息" />
|
</div>
|
</el-form-item>
|
</el-col>
|
</el-row>
|
|
<el-form-item label="产品信息:">
|
<el-table :data="detailData.productData" border show-summary :summary-method="summarizeProductTable">
|
<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="90" />
|
<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" width="150" :formatter="formattedNumber" />
|
<el-table-column label="含税总价(元)" prop="taxInclusiveTotalPrice" width="150" :formatter="formattedNumber" />
|
<el-table-column label="不含税总价(元)" prop="taxExclusiveTotalPrice" width="150" :formatter="formattedNumber" />
|
<el-table-column label="是否质检" prop="isChecked" width="100">
|
<template #default="scope">
|
<el-tag :type="scope.row.isChecked ? 'success' : 'info'">
|
{{ scope.row.isChecked ? '是' : '否' }}
|
</el-tag>
|
</template>
|
</el-table-column>
|
<el-table-column label="是否合格" min-width="180">
|
<template #default="scope">
|
<el-form-item
|
:prop="`productData.${scope.$index}.isQualified`"
|
:rules="[{ required: true, message: '请选择是否合格', trigger: 'change' }]"
|
class="inline-form-item"
|
>
|
<el-radio-group v-model="scope.row.isQualified">
|
<el-radio :label="1">合格</el-radio>
|
<el-radio :label="2">不合格</el-radio>
|
</el-radio-group>
|
</el-form-item>
|
</template>
|
</el-table-column>
|
<el-table-column label="不合格原因" min-width="220">
|
<template #default="scope">
|
<el-form-item
|
:prop="`productData.${scope.$index}.reason`"
|
:rules="[{
|
validator: (_rule, value, callback) => validateReason(scope.row, value, callback),
|
trigger: 'blur'
|
}]"
|
class="inline-form-item"
|
>
|
<el-input
|
v-model="scope.row.reason"
|
:disabled="scope.row.isQualified !== 2"
|
placeholder="不合格时请填写原因"
|
clearable
|
/>
|
</el-form-item>
|
</template>
|
</el-table-column>
|
</el-table>
|
</el-form-item>
|
|
<el-row :gutter="30">
|
<el-col :span="24">
|
<el-form-item label="备注:">
|
<el-input :model-value="detailData.remarks || ''" type="textarea" :rows="2" disabled />
|
</el-form-item>
|
</el-col>
|
</el-row>
|
|
<el-row :gutter="30">
|
<el-col :span="24">
|
<el-form-item label="附件材料:">
|
<div class="file-list">
|
<el-tag v-for="file in detailData.salesLedgerFiles" :key="file.id || file.fileName" class="file-tag">
|
{{ file.fileName || file.name }}
|
</el-tag>
|
<el-empty v-if="!detailData.salesLedgerFiles?.length" description="暂无附件" />
|
</div>
|
</el-form-item>
|
</el-col>
|
</el-row>
|
</el-form>
|
</FormDialog>
|
</template>
|
|
<script setup>
|
import { computed, reactive, ref, watch } from 'vue'
|
import { ElMessage } from 'element-plus'
|
import FormDialog from '@/components/Dialog/FormDialog.vue'
|
|
const props = defineProps({
|
modelValue: {
|
type: Boolean,
|
default: false
|
},
|
record: {
|
type: Object,
|
default: () => ({})
|
}
|
})
|
|
const emit = defineEmits(['update:modelValue', 'submit'])
|
|
const formRef = ref()
|
|
const dialogVisible = computed({
|
get: () => props.modelValue,
|
set: (value) => emit('update:modelValue', value)
|
})
|
|
const createDefaultDetail = () => ({
|
id: '',
|
purchaseContractNumber: '',
|
salesContractNo: '',
|
supplierName: '',
|
projectName: '',
|
paymentMethod: '',
|
executionDate: '',
|
recorderName: '',
|
entryDate: '',
|
remarks: '',
|
approveUserIds: '',
|
approverNames: [],
|
salesLedgerFiles: [],
|
productData: []
|
})
|
|
const detailData = reactive(createDefaultDetail())
|
|
const approverNames = computed(() => detailData.approverNames || [])
|
|
const formattedNumber = (_row, _column, cellValue) => {
|
const value = Number(cellValue)
|
return Number.isFinite(value) ? value.toFixed(2) : '0.00'
|
}
|
|
const summarizeProductTable = ({ columns, data }) => {
|
const sumFields = ['quantity', 'taxInclusiveUnitPrice', 'taxInclusiveTotalPrice', 'taxExclusiveTotalPrice']
|
const sums = []
|
columns.forEach((column, index) => {
|
if (index === 0) {
|
sums[index] = '合计'
|
return
|
}
|
if (!sumFields.includes(column.property)) {
|
sums[index] = ''
|
return
|
}
|
const total = data.reduce((sum, item) => sum + Number(item?.[column.property] || 0), 0)
|
sums[index] = Number.isFinite(total) ? total.toFixed(2) : '0.00'
|
})
|
return sums
|
}
|
|
const validateReason = (row, value, callback) => {
|
if (row.isQualified === 2 && (!value || !value.trim())) {
|
callback(new Error('不合格时必须填写原因'))
|
return
|
}
|
callback()
|
}
|
|
watch(
|
() => props.record,
|
(value) => {
|
const nextData = createDefaultDetail()
|
Object.assign(nextData, value || {})
|
nextData.productData = (value?.productData || []).map((item) => ({
|
...item,
|
isQualified: item.isQualified ?? undefined,
|
reason: item.reason || ''
|
}))
|
nextData.salesLedgerFiles = value?.salesLedgerFiles || []
|
nextData.approverNames = value?.approverNames || []
|
Object.assign(detailData, nextData)
|
},
|
{ immediate: true, deep: true }
|
)
|
|
const handleClose = () => {
|
dialogVisible.value = false
|
}
|
|
const handleConfirm = async () => {
|
if (!formRef.value) {
|
return
|
}
|
try {
|
await formRef.value.validate()
|
const unqualifiedRows = detailData.productData.filter((item) => item.isQualified === 2)
|
emit('submit', {
|
id: detailData.id,
|
purchaseContractNumber: detailData.purchaseContractNumber,
|
supplierName: detailData.supplierName,
|
projectName: detailData.projectName,
|
productData: detailData.productData.map((item) => ({
|
id: item.id,
|
specificationModel: item.specificationModel,
|
quantity: item.quantity,
|
isQualified: item.isQualified,
|
reason: item.reason || ''
|
})),
|
unqualifiedQuantity: unqualifiedRows.reduce((sum, item) => sum + Number(item.quantity || 0), 0)
|
})
|
dialogVisible.value = false
|
} catch (_error) {
|
ElMessage.warning('请先完成每条产品的收货判断')
|
}
|
}
|
</script>
|
|
<style scoped lang="scss">
|
.approver-nodes-container {
|
display: flex;
|
flex-wrap: wrap;
|
gap: 16px;
|
width: 100%;
|
}
|
|
.approver-node-item {
|
flex: 0 0 calc(33.333% - 12px);
|
min-width: 220px;
|
padding: 12px;
|
background-color: #fff;
|
border-radius: 4px;
|
border: 1px solid #dcdfe6;
|
}
|
|
.approver-node-header {
|
margin-bottom: 8px;
|
}
|
|
.approver-node-label {
|
font-size: 13px;
|
font-weight: 500;
|
color: #606266;
|
}
|
|
.inline-form-item {
|
margin-bottom: 0;
|
}
|
|
.file-list {
|
display: flex;
|
flex-wrap: wrap;
|
gap: 8px;
|
width: 100%;
|
}
|
|
.file-tag {
|
margin-right: 0;
|
}
|
|
@media (max-width: 1200px) {
|
.approver-node-item {
|
flex: 0 0 calc(50% - 8px);
|
}
|
}
|
|
@media (max-width: 768px) {
|
.approver-node-item {
|
flex: 0 0 100%;
|
}
|
}
|
</style>
|