设备采购模块添加收货状态和支付状态

This commit is contained in:
Mr.j 2025-08-13 11:25:55 +08:00
parent bac2e99f0d
commit da6e158cc3
3 changed files with 644 additions and 0 deletions

View File

@ -0,0 +1,249 @@
<template>
<a-modal
:visible="visible"
title="支付详情"
width="800px"
@cancel="handleCancel"
:footer="false"
>
<div class="payment-detail">
<!-- 基本信息 -->
<a-card title="基本信息" class="detail-card" :bordered="false">
<a-descriptions :column="2" bordered>
<a-descriptions-item label="设备名称">
{{ paymentData?.equipmentName || '-' }}
</a-descriptions-item>
<a-descriptions-item label="设备类型">
{{ paymentData?.equipmentType || '-' }}
</a-descriptions-item>
<a-descriptions-item label="设备型号">
{{ paymentData?.equipmentModel || '-' }}
</a-descriptions-item>
<a-descriptions-item label="品牌">
{{ paymentData?.brand || '-' }}
</a-descriptions-item>
<a-descriptions-item label="供应商">
{{ paymentData?.supplierName || '-' }}
</a-descriptions-item>
<a-descriptions-item label="采购订单">
{{ paymentData?.purchaseOrder || '-' }}
</a-descriptions-item>
</a-descriptions>
</a-card>
<!-- 采购信息 -->
<a-card title="采购信息" class="detail-card" :bordered="false">
<a-descriptions :column="2" bordered>
<a-descriptions-item label="采购价格">
¥{{ paymentData?.purchasePrice || '-' }}
</a-descriptions-item>
<a-descriptions-item label="采购数量">
{{ paymentData?.quantity || '-' }}
</a-descriptions-item>
<a-descriptions-item label="总金额">
¥{{ paymentData?.totalPrice || '-' }}
</a-descriptions-item>
<a-descriptions-item label="采购日期">
{{ paymentData?.purchaseTime || '-' }}
</a-descriptions-item>
</a-descriptions>
</a-card>
<!-- 支付信息 -->
<a-card title="支付信息" class="detail-card" :bordered="false">
<a-descriptions :column="2" bordered>
<a-descriptions-item label="支付状态">
<a-tag :color="getPaymentStatusColor(paymentData?.paymentStatus)">
{{ getPaymentStatusText(paymentData?.paymentStatus) }}
</a-tag>
</a-descriptions-item>
<a-descriptions-item label="支付方式">
{{ paymentData?.paymentMethod || '-' }}
</a-descriptions-item>
<a-descriptions-item label="支付金额">
¥{{ paymentData?.paymentAmount || '-' }}
</a-descriptions-item>
<a-descriptions-item label="支付时间">
{{ paymentData?.paymentTime || '-' }}
</a-descriptions-item>
<a-descriptions-item label="支付人">
{{ paymentData?.paymentPerson || '-' }}
</a-descriptions-item>
<a-descriptions-item label="支付备注">
{{ paymentData?.paymentRemark || '-' }}
</a-descriptions-item>
</a-descriptions>
</a-card>
<!-- 发票信息 -->
<a-card title="发票信息" class="detail-card" :bordered="false">
<a-descriptions :column="2" bordered>
<a-descriptions-item label="发票状态">
<a-tag :color="getInvoiceStatusColor(paymentData?.invoiceStatus)">
{{ getInvoiceStatusText(paymentData?.invoiceStatus) }}
</a-tag>
</a-descriptions-item>
<a-descriptions-item label="发票类型">
{{ paymentData?.invoiceType || '-' }}
</a-descriptions-item>
<a-descriptions-item label="发票号码">
{{ paymentData?.invoiceNumber || '-' }}
</a-descriptions-item>
<a-descriptions-item label="开票日期">
{{ paymentData?.invoiceDate || '-' }}
</a-descriptions-item>
<a-descriptions-item label="发票金额">
¥{{ paymentData?.invoiceAmount || '-' }}
</a-descriptions-item>
<a-descriptions-item label="税率">
{{ paymentData?.taxRate || '-' }}
</a-descriptions-item>
<a-descriptions-item label="税额">
¥{{ paymentData?.taxAmount || '-' }}
</a-descriptions-item>
<a-descriptions-item label="不含税金额">
¥{{ paymentData?.amountWithoutTax || '-' }}
</a-descriptions-item>
</a-descriptions>
</a-card>
<!-- 合同信息 -->
<a-card title="合同信息" class="detail-card" :bordered="false">
<a-descriptions :column="2" bordered>
<a-descriptions-item label="合同编号">
{{ paymentData?.contractNumber || '-' }}
</a-descriptions-item>
<a-descriptions-item label="合同状态">
<a-tag :color="getContractStatusColor(paymentData?.contractStatus)">
{{ getContractStatusText(paymentData?.contractStatus) }}
</a-tag>
</a-descriptions-item>
<a-descriptions-item label="合同金额">
¥{{ paymentData?.contractAmount || '-' }}
</a-descriptions-item>
<a-descriptions-item label="签订日期">
{{ paymentData?.contractDate || '-' }}
</a-descriptions-item>
<a-descriptions-item label="付款条件">
{{ paymentData?.paymentTerms || '-' }}
</a-descriptions-item>
<a-descriptions-item label="付款期限">
{{ paymentData?.paymentDeadline || '-' }}
</a-descriptions-item>
</a-descriptions>
</a-card>
</div>
</a-modal>
</template>
<script setup lang="ts">
import type { EquipmentResp } from '@/apis/equipment/type'
interface Props {
visible: boolean
paymentData?: EquipmentResp | null
}
const props = withDefaults(defineProps<Props>(), {
visible: false,
paymentData: null,
})
const emit = defineEmits<{
'update:visible': [value: boolean]
}>()
//
const getPaymentStatusColor = (status?: string) => {
const colorMap: Record<string, string> = {
NOT_PAID: 'gray',
PAID: 'green',
PARTIALLY_PAID: 'orange',
REJECTED: 'red',
}
return colorMap[status || ''] || 'blue'
}
//
const getPaymentStatusText = (status?: string) => {
const textMap: Record<string, string> = {
NOT_PAID: '未支付',
PAID: '已支付',
PARTIALLY_PAID: '部分支付',
REJECTED: '已拒付',
}
return textMap[status || ''] || '未知'
}
//
const getInvoiceStatusColor = (status?: string) => {
const colorMap: Record<string, string> = {
NOT_ISSUED: 'gray',
ISSUED: 'green',
PENDING: 'orange',
REJECTED: 'red',
}
return colorMap[status || ''] || 'blue'
}
//
const getInvoiceStatusText = (status?: string) => {
const textMap: Record<string, string> = {
NOT_ISSUED: '未开票',
ISSUED: '已开票',
PENDING: '待开票',
REJECTED: '已拒开',
}
return textMap[status || ''] || '未知'
}
//
const getContractStatusColor = (status?: string) => {
const colorMap: Record<string, string> = {
DRAFT: 'gray',
SIGNED: 'green',
EXECUTING: 'blue',
COMPLETED: 'purple',
TERMINATED: 'red',
}
return colorMap[status || ''] || 'blue'
}
//
const getContractStatusText = (status?: string) => {
const textMap: Record<string, string> = {
DRAFT: '草稿',
SIGNED: '已签订',
EXECUTING: '执行中',
COMPLETED: '已完成',
TERMINATED: '已终止',
}
return textMap[status || ''] || '未知'
}
//
const handleCancel = () => {
emit('update:visible', false)
}
</script>
<style scoped lang="scss">
.payment-detail {
.detail-card {
margin-bottom: 16px;
&:last-child {
margin-bottom: 0;
}
}
.arco-descriptions-item-label {
font-weight: 600;
color: var(--color-text-1);
}
.arco-descriptions-item-value {
color: var(--color-text-2);
}
}
</style>

View File

@ -0,0 +1,211 @@
<template>
<a-modal
:visible="visible"
title="收货详情"
width="800px"
@cancel="handleCancel"
:footer="false"
>
<div class="receipt-detail">
<!-- 基本信息 -->
<a-card title="基本信息" class="detail-card" :bordered="false">
<a-descriptions :column="2" bordered>
<a-descriptions-item label="设备名称">
{{ receiptData?.equipmentName || '-' }}
</a-descriptions-item>
<a-descriptions-item label="设备类型">
{{ receiptData?.equipmentType || '-' }}
</a-descriptions-item>
<a-descriptions-item label="设备型号">
{{ receiptData?.equipmentModel || '-' }}
</a-descriptions-item>
<a-descriptions-item label="品牌">
{{ receiptData?.brand || '-' }}
</a-descriptions-item>
<a-descriptions-item label="供应商">
{{ receiptData?.supplierName || '-' }}
</a-descriptions-item>
<a-descriptions-item label="采购订单">
{{ receiptData?.purchaseOrder || '-' }}
</a-descriptions-item>
</a-descriptions>
</a-card>
<!-- 收货信息 -->
<a-card title="收货信息" class="detail-card" :bordered="false">
<a-descriptions :column="2" bordered>
<a-descriptions-item label="收货状态">
<a-tag :color="getReceiptStatusColor(receiptData?.receiptStatus)">
{{ getReceiptStatusText(receiptData?.receiptStatus) }}
</a-tag>
</a-descriptions-item>
<a-descriptions-item label="收货时间">
{{ receiptData?.receiptTime || '-' }}
</a-descriptions-item>
<a-descriptions-item label="收货人">
{{ receiptData?.receiptPerson || '-' }}
</a-descriptions-item>
<a-descriptions-item label="收货数量">
{{ receiptData?.receiptQuantity || '-' }}
</a-descriptions-item>
<a-descriptions-item label="收货备注" :span="2">
{{ receiptData?.receiptRemark || '-' }}
</a-descriptions-item>
</a-descriptions>
</a-card>
<!-- 质量检查 -->
<a-card title="质量检查" class="detail-card" :bordered="false">
<a-descriptions :column="2" bordered>
<a-descriptions-item label="外观检查">
{{ receiptData?.appearanceCheck || '-' }}
</a-descriptions-item>
<a-descriptions-item label="功能测试">
{{ receiptData?.functionTest || '-' }}
</a-descriptions-item>
<a-descriptions-item label="包装完整性">
{{ receiptData?.packageIntegrity || '-' }}
</a-descriptions-item>
<a-descriptions-item label="配件完整性">
{{ receiptData?.accessoryIntegrity || '-' }}
</a-descriptions-item>
<a-descriptions-item label="检查结果" :span="2">
<a-tag :color="getCheckResultColor(receiptData?.checkResult)">
{{ getCheckResultText(receiptData?.checkResult) }}
</a-tag>
</a-descriptions-item>
<a-descriptions-item label="检查备注" :span="2">
{{ receiptData?.checkRemark || '-' }}
</a-descriptions-item>
</a-descriptions>
</a-card>
<!-- 入库信息 -->
<a-card title="入库信息" class="detail-card" :bordered="false">
<a-descriptions :column="2" bordered>
<a-descriptions-item label="入库状态">
<a-tag :color="getStorageStatusColor(receiptData?.storageStatus)">
{{ getStorageStatusText(receiptData?.storageStatus) }}
</a-tag>
</a-descriptions-item>
<a-descriptions-item label="入库时间">
{{ receiptData?.storageTime || '-' }}
</a-descriptions-item>
<a-descriptions-item label="入库位置">
{{ receiptData?.storageLocation || '-' }}
</a-descriptions-item>
<a-descriptions-item label="库管员">
{{ receiptData?.storageManager || '-' }}
</a-descriptions-item>
</a-descriptions>
</a-card>
</div>
</a-modal>
</template>
<script setup lang="ts">
import { computed } from 'vue'
import type { EquipmentResp } from '@/apis/equipment/type'
interface Props {
visible: boolean
receiptData?: EquipmentResp | null
}
const props = withDefaults(defineProps<Props>(), {
visible: false,
receiptData: null,
})
const emit = defineEmits<{
'update:visible': [value: boolean]
}>()
//
const getReceiptStatusColor = (status?: string) => {
const colorMap: Record<string, string> = {
NOT_RECEIVED: 'gray',
RECEIVED: 'green',
PARTIALLY_RECEIVED: 'orange',
REJECTED: 'red',
}
return colorMap[status || ''] || 'blue'
}
//
const getReceiptStatusText = (status?: string) => {
const textMap: Record<string, string> = {
NOT_RECEIVED: '未收货',
RECEIVED: '已收货',
PARTIALLY_RECEIVED: '部分收货',
REJECTED: '已拒收',
}
return textMap[status || ''] || '未知'
}
//
const getCheckResultColor = (result?: string) => {
const colorMap: Record<string, string> = {
PASS: 'green',
FAIL: 'red',
CONDITIONAL: 'orange',
}
return colorMap[result || ''] || 'blue'
}
//
const getCheckResultText = (result?: string) => {
const textMap: Record<string, string> = {
PASS: '通过',
FAIL: '不通过',
CONDITIONAL: '有条件通过',
}
return textMap[result || ''] || '未知'
}
//
const getStorageStatusColor = (status?: string) => {
const colorMap: Record<string, string> = {
NOT_STORED: 'gray',
STORED: 'green',
PARTIALLY_STORED: 'orange',
}
return colorMap[status || ''] || 'blue'
}
//
const getStorageStatusText = (status?: string) => {
const textMap: Record<string, string> = {
NOT_STORED: '未入库',
STORED: '已入库',
PARTIALLY_STORED: '部分入库',
}
return textMap[status || ''] || '未知'
}
//
const handleCancel = () => {
emit('update:visible', false)
}
</script>
<style scoped lang="scss">
.receipt-detail {
.detail-card {
margin-bottom: 16px;
&:last-child {
margin-bottom: 0;
}
}
.arco-descriptions-item-label {
font-weight: 600;
color: var(--color-text-1);
}
.arco-descriptions-item-value {
color: var(--color-text-2);
}
}
</style>

View File

@ -178,6 +178,20 @@
<span v-else class="no-data">-</span>
</template>
<!-- 收货状态 -->
<template #receiptStatus="{ record }">
<a-tag :color="getReceiptStatusColor(record.receiptStatus)">
{{ getReceiptStatusText(record.receiptStatus) }}
</a-tag>
</template>
<!-- 支付状态 -->
<template #paymentStatus="{ record }">
<a-tag :color="getPaymentStatusColor(record.paymentStatus)">
{{ getPaymentStatusText(record.paymentStatus) }}
</a-tag>
</template>
<!-- 操作 -->
<template #action="{ record }">
<a-space>
@ -196,6 +210,40 @@
>
申请采购
</a-button>
<!-- 收货操作按钮 -->
<a-button
v-if="canReceiveGoods(record)"
type="primary"
size="small"
@click="handleReceiveGoods(record)"
>
确认收货
</a-button>
<a-button
v-if="record.receiptStatus === 'RECEIVED'"
type="text"
size="small"
@click="handleViewReceipt(record)"
>
查看收货
</a-button>
<!-- 支付操作按钮 -->
<a-button
v-if="canMakePayment(record)"
type="outline"
size="small"
@click="handleMakePayment(record)"
>
付款
</a-button>
<a-button
v-if="record.paymentStatus === 'PAID'"
type="text"
size="small"
@click="handleViewPayment(record)"
>
查看支付详情
</a-button>
<!-- 显示采购状态 - 优先显示采购状态 -->
<a-tag
v-if="record.procurementStatus && record.procurementStatus !== 'NOT_STARTED'"
@ -252,6 +300,18 @@
:equipment-data="currentApplicationData"
@success="handleApplicationSuccess"
/>
<!-- 收货详情弹窗 -->
<ReceiptDetailModal
v-model:visible="receiptDetailModalVisible"
:receipt-data="currentReceiptData"
/>
<!-- 支付详情弹窗 -->
<PaymentDetailModal
v-model:visible="paymentDetailModalVisible"
:payment-data="currentPaymentData"
/>
</div>
</template>
@ -271,6 +331,8 @@ import message from '@arco-design/web-vue/es/message'
import ProcurementModal from './components/ProcurementModal.vue'
import ProcurementSearch from './components/ProcurementSearch.vue'
import ProcurementApplicationModal from './components/ProcurementApplicationModal.vue'
import ReceiptDetailModal from './components/ReceiptDetailModal.vue'
import PaymentDetailModal from './components/PaymentDetailModal.vue'
import { equipmentProcurementApi } from '@/apis/equipment/procurement'
import { equipmentApprovalApi } from '@/apis/equipment/approval'
import type { EquipmentListReq, EquipmentResp } from '@/apis/equipment/type'
@ -307,6 +369,14 @@ const modalMode = ref<'add' | 'edit' | 'view'>('add')
const applicationModalVisible = ref(false)
const currentApplicationData = ref<EquipmentResp | null>(null)
//
const receiptDetailModalVisible = ref(false)
const currentReceiptData = ref<EquipmentResp | null>(null)
//
const paymentDetailModalVisible = ref(false)
const currentPaymentData = ref<EquipmentResp | null>(null)
//
const selectedRowKeys = ref<string[]>([])
const rowSelection = reactive({
@ -418,6 +488,20 @@ const columns = [
slotName: 'createTime',
width: 160,
},
{
title: '收货状态',
dataIndex: 'receiptStatus',
key: 'receiptStatus',
slotName: 'receiptStatus',
width: 120,
},
{
title: '支付状态',
dataIndex: 'paymentStatus',
key: 'paymentStatus',
slotName: 'paymentStatus',
width: 120,
},
{
title: '操作',
key: 'action',
@ -527,6 +611,50 @@ const getHealthStatusText = (status: string) => {
return textMap[status] || '未知'
}
//
const getReceiptStatusColor = (status: string) => {
const colorMap: Record<string, string> = {
NOT_RECEIVED: 'gray',
RECEIVED: 'green',
PARTIALLY_RECEIVED: 'orange',
REJECTED: 'red',
}
return colorMap[status] || 'blue'
}
//
const getReceiptStatusText = (status: string) => {
const textMap: Record<string, string> = {
NOT_RECEIVED: '未收货',
RECEIVED: '已收货',
PARTIALLY_RECEIVED: '部分收货',
REJECTED: '已拒收',
}
return textMap[status] || '未知'
}
//
const getPaymentStatusColor = (status: string) => {
const colorMap: Record<string, string> = {
NOT_PAID: 'gray',
PAID: 'green',
PARTIALLY_PAID: 'orange',
REJECTED: 'red',
}
return colorMap[status] || 'blue'
}
//
const getPaymentStatusText = (status: string) => {
const textMap: Record<string, string> = {
NOT_PAID: '未支付',
PAID: '已支付',
PARTIALLY_PAID: '部分支付',
REJECTED: '已拒付',
}
return textMap[status] || '未知'
}
//
const formatPrice = (price: number) => {
return price.toLocaleString('zh-CN', {
@ -601,6 +729,8 @@ const transformBackendData = (data: any[]): EquipmentResp[] => {
inventoryBasis: item.inventoryBasis,
dynamicRecord: item.dynamicRecord,
procurementStatus: item.procurementStatus,
receiptStatus: item.receiptStatus || 'NOT_RECEIVED',
paymentStatus: item.paymentStatus || 'NOT_PAID',
}))
}
@ -850,6 +980,60 @@ const canApplyProcurement = (record: EquipmentResp) => {
return canApply
}
//
const canReceiveGoods = (record: EquipmentResp) => {
const receiptStatus = (record as any).receiptStatus
return receiptStatus === 'NOT_RECEIVED' || receiptStatus === 'PARTIALLY_RECEIVED'
}
//
const handleReceiveGoods = async (record: EquipmentResp) => {
try {
// API
// await equipmentProcurementApi.receiveGoods(record.equipmentId)
message.success('收货成功')
//
(record as any).receiptStatus = 'RECEIVED'
loadData(currentSearchParams.value)
} catch (error: any) {
console.error('收货失败:', error)
message.error(error?.message || '收货失败')
}
}
//
const handleViewReceipt = (record: EquipmentResp) => {
currentReceiptData.value = { ...record }
receiptDetailModalVisible.value = true
}
//
const canMakePayment = (record: EquipmentResp) => {
const paymentStatus = (record as any).paymentStatus
return paymentStatus === 'NOT_PAID' || paymentStatus === 'PARTIALLY_PAID'
}
//
const handleMakePayment = async (record: EquipmentResp) => {
try {
// API
// await equipmentProcurementApi.makePayment(record.equipmentId)
message.success('付款成功')
//
(record as any).paymentStatus = 'PAID'
loadData(currentSearchParams.value)
} catch (error: any) {
console.error('付款失败:', error)
message.error(error?.message || '付款失败')
}
}
//
const handleViewPayment = (record: EquipmentResp) => {
currentPaymentData.value = { ...record }
paymentDetailModalVisible.value = true
}
//
const getApprovalStatusColor = (status: string) => {
const colorMap: Record<string, string> = {