新增设备采购模块合同和发票上传
This commit is contained in:
parent
c568370e54
commit
9e7cc674d4
|
@ -0,0 +1,534 @@
|
|||
<template>
|
||||
<a-modal
|
||||
:visible="visible"
|
||||
title="合同发票管理"
|
||||
width="800px"
|
||||
:footer="false"
|
||||
@cancel="handleCancel"
|
||||
@update:visible="handleVisibleChange"
|
||||
>
|
||||
<div class="contract-invoice-container">
|
||||
<!-- 设备基本信息 -->
|
||||
<a-card title="设备信息" class="info-card" :bordered="false">
|
||||
<a-descriptions :column="2" bordered>
|
||||
<a-descriptions-item label="设备名称">
|
||||
{{ equipmentData?.equipmentName }}
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item label="设备型号">
|
||||
{{ equipmentData?.equipmentModel }}
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item label="供应商">
|
||||
{{ equipmentData?.supplierName }}
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item label="采购价格">
|
||||
¥{{ formatPrice(equipmentData?.purchasePrice || 0) }}
|
||||
</a-descriptions-item>
|
||||
</a-descriptions>
|
||||
</a-card>
|
||||
|
||||
<!-- 合同信息 -->
|
||||
<a-card title="合同信息" class="info-card" :bordered="false">
|
||||
<a-form
|
||||
ref="contractFormRef"
|
||||
:model="contractForm"
|
||||
:rules="contractRules"
|
||||
layout="vertical"
|
||||
>
|
||||
<a-row :gutter="16">
|
||||
<a-col :span="12">
|
||||
<a-form-item label="合同编号" name="contractNumber">
|
||||
<a-input
|
||||
v-model="contractForm.contractNumber"
|
||||
placeholder="请输入合同编号"
|
||||
allow-clear
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item label="合同签订日期" name="contractDate">
|
||||
<a-date-picker
|
||||
v-model="contractForm.contractDate"
|
||||
placeholder="请选择合同签订日期"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<a-row :gutter="16">
|
||||
<a-col :span="12">
|
||||
<a-form-item label="合同金额" name="contractAmount">
|
||||
<a-input-number
|
||||
v-model="contractForm.contractAmount"
|
||||
placeholder="请输入合同金额"
|
||||
:precision="2"
|
||||
:min="0"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item label="合同状态" name="contractStatus">
|
||||
<a-select
|
||||
v-model="contractForm.contractStatus"
|
||||
placeholder="请选择合同状态"
|
||||
allow-clear
|
||||
>
|
||||
<a-option value="DRAFT">草稿</a-option>
|
||||
<a-option value="SIGNED">已签订</a-option>
|
||||
<a-option value="EXECUTING">执行中</a-option>
|
||||
<a-option value="COMPLETED">已完成</a-option>
|
||||
<a-option value="TERMINATED">已终止</a-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<a-form-item label="合同文件" name="contractFile">
|
||||
<a-upload
|
||||
v-model:file-list="contractForm.contractFile"
|
||||
:action="uploadAction"
|
||||
:headers="uploadHeaders"
|
||||
:before-upload="beforeUpload"
|
||||
:on-success="onContractUploadSuccess"
|
||||
:on-error="onUploadError"
|
||||
accept=".pdf,.doc,.docx"
|
||||
:max-count="1"
|
||||
>
|
||||
<a-button>
|
||||
<template #icon>
|
||||
<IconUpload />
|
||||
</template>
|
||||
上传合同文件
|
||||
</a-button>
|
||||
<template #itemRender="{ file }">
|
||||
<a-space>
|
||||
<IconFile />
|
||||
<span>{{ file.name }}</span>
|
||||
<a-button
|
||||
type="text"
|
||||
size="small"
|
||||
status="danger"
|
||||
@click="removeContractFile(file)"
|
||||
>
|
||||
删除
|
||||
</a-button>
|
||||
</a-space>
|
||||
</template>
|
||||
</a-upload>
|
||||
</a-form-item>
|
||||
<a-form-item label="合同备注" name="contractRemark">
|
||||
<a-textarea
|
||||
v-model="contractForm.contractRemark"
|
||||
placeholder="请输入合同备注信息"
|
||||
:rows="3"
|
||||
allow-clear
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-card>
|
||||
|
||||
<!-- 发票信息 -->
|
||||
<a-card title="发票信息" class="info-card" :bordered="false">
|
||||
<a-form
|
||||
ref="invoiceFormRef"
|
||||
:model="invoiceForm"
|
||||
:rules="invoiceRules"
|
||||
layout="vertical"
|
||||
>
|
||||
<a-row :gutter="16">
|
||||
<a-col :span="12">
|
||||
<a-form-item label="发票号码" name="invoiceNumber">
|
||||
<a-input
|
||||
v-model="invoiceForm.invoiceNumber"
|
||||
placeholder="请输入发票号码"
|
||||
allow-clear
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item label="发票类型" name="invoiceType">
|
||||
<a-select
|
||||
v-model="invoiceForm.invoiceType"
|
||||
placeholder="请选择发票类型"
|
||||
allow-clear
|
||||
>
|
||||
<a-option value="VAT_SPECIAL">增值税专用发票</a-option>
|
||||
<a-option value="VAT_COMMON">增值税普通发票</a-option>
|
||||
<a-option value="ELECTRONIC">电子发票</a-option>
|
||||
<a-option value="OTHER">其他</a-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<a-row :gutter="16">
|
||||
<a-col :span="12">
|
||||
<a-form-item label="发票金额" name="invoiceAmount">
|
||||
<a-input-number
|
||||
v-model="invoiceForm.invoiceAmount"
|
||||
placeholder="请输入发票金额"
|
||||
:precision="2"
|
||||
:min="0"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item label="开票日期" name="invoiceDate">
|
||||
<a-date-picker
|
||||
v-model="invoiceForm.invoiceDate"
|
||||
placeholder="请选择开票日期"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<a-row :gutter="16">
|
||||
<a-col :span="12">
|
||||
<a-form-item label="税率" name="taxRate">
|
||||
<a-input-number
|
||||
v-model="invoiceForm.taxRate"
|
||||
placeholder="请输入税率"
|
||||
:precision="2"
|
||||
:min="0"
|
||||
:max="100"
|
||||
style="width: 100%"
|
||||
addon-after="%"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item label="税额" name="taxAmount">
|
||||
<a-input-number
|
||||
v-model="invoiceForm.taxAmount"
|
||||
placeholder="税额将自动计算"
|
||||
:precision="2"
|
||||
:min="0"
|
||||
style="width: 100%"
|
||||
disabled
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<a-form-item label="发票文件" name="invoiceFile">
|
||||
<a-upload
|
||||
v-model:file-list="invoiceForm.invoiceFile"
|
||||
:action="uploadAction"
|
||||
:headers="uploadHeaders"
|
||||
:before-upload="beforeUpload"
|
||||
:on-success="onInvoiceUploadSuccess"
|
||||
:on-error="onUploadError"
|
||||
accept=".pdf,.jpg,.jpeg,.png"
|
||||
:max-count="1"
|
||||
>
|
||||
<a-button>
|
||||
<template #icon>
|
||||
<IconUpload />
|
||||
</template>
|
||||
上传发票文件
|
||||
</a-button>
|
||||
<template #itemRender="{ file }">
|
||||
<a-space>
|
||||
<IconFile />
|
||||
<span>{{ file.name }}</span>
|
||||
<a-button
|
||||
type="text"
|
||||
size="small"
|
||||
status="danger"
|
||||
@click="removeInvoiceFile(file)"
|
||||
>
|
||||
删除
|
||||
</a-button>
|
||||
</a-space>
|
||||
</template>
|
||||
</a-upload>
|
||||
</a-form-item>
|
||||
<a-form-item label="发票备注" name="invoiceRemark">
|
||||
<a-textarea
|
||||
v-model="invoiceForm.invoiceRemark"
|
||||
placeholder="请输入发票备注信息"
|
||||
:rows="3"
|
||||
allow-clear
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-card>
|
||||
|
||||
<!-- 操作按钮 -->
|
||||
<div class="action-buttons">
|
||||
<a-space>
|
||||
<a-button @click="handleCancel">取消</a-button>
|
||||
<a-button type="primary" @click="handleSave" :loading="saving">
|
||||
保存
|
||||
</a-button>
|
||||
<a-button
|
||||
type="primary"
|
||||
@click="handleComplete"
|
||||
:loading="saving"
|
||||
:disabled="!canComplete"
|
||||
>
|
||||
完成并进入收货阶段
|
||||
</a-button>
|
||||
</a-space>
|
||||
</div>
|
||||
</div>
|
||||
</a-modal>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, reactive, computed, watch } from 'vue'
|
||||
import { IconUpload, IconFile } from '@arco-design/web-vue/es/icon'
|
||||
import message from '@arco-design/web-vue/es/message'
|
||||
import type { EquipmentResp } from '@/apis/equipment/type'
|
||||
|
||||
interface Props {
|
||||
visible: boolean
|
||||
equipmentData: EquipmentResp | null
|
||||
}
|
||||
|
||||
interface ContractForm {
|
||||
contractNumber: string
|
||||
contractDate: string | null
|
||||
contractAmount: number | null
|
||||
contractStatus: string
|
||||
contractFile: any[]
|
||||
contractRemark: string
|
||||
}
|
||||
|
||||
interface InvoiceForm {
|
||||
invoiceNumber: string
|
||||
invoiceType: string
|
||||
invoiceAmount: number | null
|
||||
invoiceDate: string | null
|
||||
taxRate: number | null
|
||||
taxAmount: number | null
|
||||
invoiceFile: any[]
|
||||
invoiceRemark: string
|
||||
}
|
||||
|
||||
const props = defineProps<Props>()
|
||||
const emit = defineEmits<{
|
||||
'update:visible': [value: boolean]
|
||||
'success': []
|
||||
}>()
|
||||
|
||||
// 表单引用
|
||||
const contractFormRef = ref()
|
||||
const invoiceFormRef = ref()
|
||||
|
||||
// 保存状态
|
||||
const saving = ref(false)
|
||||
|
||||
// 合同表单
|
||||
const contractForm = reactive<ContractForm>({
|
||||
contractNumber: '',
|
||||
contractDate: null,
|
||||
contractAmount: null,
|
||||
contractStatus: 'DRAFT',
|
||||
contractFile: [],
|
||||
contractRemark: ''
|
||||
})
|
||||
|
||||
// 发票表单
|
||||
const invoiceForm = reactive<InvoiceForm>({
|
||||
invoiceNumber: '',
|
||||
invoiceType: '',
|
||||
invoiceAmount: null,
|
||||
invoiceDate: null,
|
||||
taxRate: null,
|
||||
taxAmount: null,
|
||||
invoiceFile: [],
|
||||
invoiceRemark: ''
|
||||
})
|
||||
|
||||
// 表单验证规则
|
||||
const contractRules = {
|
||||
contractNumber: [
|
||||
{ required: true, message: '请输入合同编号', trigger: 'blur' }
|
||||
],
|
||||
contractDate: [
|
||||
{ required: true, message: '请选择合同签订日期', trigger: 'change' }
|
||||
],
|
||||
contractAmount: [
|
||||
{ required: true, message: '请输入合同金额', trigger: 'blur' }
|
||||
],
|
||||
contractStatus: [
|
||||
{ required: true, message: '请选择合同状态', trigger: 'change' }
|
||||
]
|
||||
}
|
||||
|
||||
const invoiceRules = {
|
||||
invoiceNumber: [
|
||||
{ required: true, message: '请输入发票号码', trigger: 'blur' }
|
||||
],
|
||||
invoiceType: [
|
||||
{ required: true, message: '请选择发票类型', trigger: 'change' }
|
||||
],
|
||||
invoiceAmount: [
|
||||
{ required: true, message: '请输入发票金额', trigger: 'blur' }
|
||||
],
|
||||
invoiceDate: [
|
||||
{ required: true, message: '请选择开票日期', trigger: 'change' }
|
||||
],
|
||||
taxRate: [
|
||||
{ required: true, message: '请输入税率', trigger: 'blur' }
|
||||
]
|
||||
}
|
||||
|
||||
// 上传配置
|
||||
const uploadAction = '/api/file/upload'
|
||||
const uploadHeaders = {
|
||||
// 根据实际需求设置上传头
|
||||
}
|
||||
|
||||
// 计算是否可以完成
|
||||
const canComplete = computed(() => {
|
||||
return contractForm.contractStatus === 'SIGNED' &&
|
||||
invoiceForm.invoiceNumber &&
|
||||
invoiceForm.invoiceAmount
|
||||
})
|
||||
|
||||
// 监听发票金额和税率变化,自动计算税额
|
||||
watch([() => invoiceForm.invoiceAmount, () => invoiceForm.taxRate], ([amount, rate]) => {
|
||||
if (amount && rate) {
|
||||
invoiceForm.taxAmount = Number((amount * rate / 100).toFixed(2))
|
||||
} else {
|
||||
invoiceForm.taxAmount = null
|
||||
}
|
||||
})
|
||||
|
||||
// 格式化价格
|
||||
const formatPrice = (price: number) => {
|
||||
return price.toLocaleString('zh-CN', {
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 2,
|
||||
})
|
||||
}
|
||||
|
||||
// 上传前检查
|
||||
const beforeUpload = (file: File) => {
|
||||
const isValidSize = file.size / 1024 / 1024 < 10 // 10MB限制
|
||||
if (!isValidSize) {
|
||||
message.error('文件大小不能超过10MB')
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// 合同文件上传成功
|
||||
const onContractUploadSuccess = (response: any, file: any) => {
|
||||
console.log('合同文件上传成功:', response, file)
|
||||
message.success('合同文件上传成功')
|
||||
}
|
||||
|
||||
// 发票文件上传成功
|
||||
const onInvoiceUploadSuccess = (response: any, file: any) => {
|
||||
console.log('发票文件上传成功:', response, file)
|
||||
message.success('发票文件上传成功')
|
||||
}
|
||||
|
||||
// 上传失败
|
||||
const onUploadError = (error: any) => {
|
||||
console.error('文件上传失败:', error)
|
||||
message.error('文件上传失败')
|
||||
}
|
||||
|
||||
// 删除合同文件
|
||||
const removeContractFile = (file: any) => {
|
||||
const index = contractForm.contractFile.findIndex(f => f.uid === file.uid)
|
||||
if (index > -1) {
|
||||
contractForm.contractFile.splice(index, 1)
|
||||
}
|
||||
}
|
||||
|
||||
// 删除发票文件
|
||||
const removeInvoiceFile = (file: any) => {
|
||||
const index = invoiceForm.invoiceFile.findIndex(f => f.uid === file.uid)
|
||||
if (index > -1) {
|
||||
invoiceForm.invoiceFile.splice(index, 1)
|
||||
}
|
||||
}
|
||||
|
||||
// 取消
|
||||
const handleCancel = () => {
|
||||
emit('update:visible', false)
|
||||
}
|
||||
|
||||
// 处理 visible 变化
|
||||
const handleVisibleChange = (newVisible: boolean) => {
|
||||
emit('update:visible', newVisible)
|
||||
}
|
||||
|
||||
// 保存
|
||||
const handleSave = async () => {
|
||||
try {
|
||||
saving.value = true
|
||||
|
||||
// 验证合同表单
|
||||
await contractFormRef.value?.validate()
|
||||
|
||||
// 验证发票表单
|
||||
await invoiceFormRef.value?.validate()
|
||||
|
||||
// 这里调用API保存数据
|
||||
console.log('保存合同发票信息:', {
|
||||
contract: contractForm,
|
||||
invoice: invoiceForm,
|
||||
equipmentId: props.equipmentData?.equipmentId
|
||||
})
|
||||
|
||||
message.success('保存成功')
|
||||
|
||||
} catch (error) {
|
||||
console.error('保存失败:', error)
|
||||
message.error('保存失败,请检查表单信息')
|
||||
} finally {
|
||||
saving.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 完成并进入收货阶段
|
||||
const handleComplete = async () => {
|
||||
try {
|
||||
saving.value = true
|
||||
|
||||
// 验证表单
|
||||
await contractFormRef.value?.validate()
|
||||
await invoiceFormRef.value?.validate()
|
||||
|
||||
// 这里调用API完成合同发票管理
|
||||
console.log('完成合同发票管理:', {
|
||||
contract: contractForm,
|
||||
invoice: invoiceForm,
|
||||
equipmentId: props.equipmentData?.equipmentId
|
||||
})
|
||||
|
||||
message.success('合同发票管理完成,可以进入收货阶段')
|
||||
emit('success')
|
||||
emit('update:visible', false)
|
||||
|
||||
} catch (error) {
|
||||
console.error('操作失败:', error)
|
||||
message.error('操作失败,请检查表单信息')
|
||||
} finally {
|
||||
saving.value = false
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.contract-invoice-container {
|
||||
.info-card {
|
||||
margin-bottom: 16px;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.action-buttons {
|
||||
text-align: center;
|
||||
margin-top: 24px;
|
||||
padding-top: 16px;
|
||||
border-top: 1px solid var(--color-border);
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -219,6 +219,15 @@
|
|||
>
|
||||
确认收货
|
||||
</a-button>
|
||||
<!-- 管理合同发票按钮 - 在采购审批通过后显示 -->
|
||||
<a-button
|
||||
v-if="canManageContractInvoice(record)"
|
||||
type="outline"
|
||||
size="small"
|
||||
@click="handleManageContractInvoice(record)"
|
||||
>
|
||||
管理合同发票
|
||||
</a-button>
|
||||
<a-button
|
||||
v-if="record.receiptStatus === 'RECEIVED'"
|
||||
type="text"
|
||||
|
@ -338,6 +347,13 @@
|
|||
:equipment-data="currentPaymentData"
|
||||
@success="handlePaymentSuccess"
|
||||
/>
|
||||
|
||||
<!-- 合同发票管理弹窗 -->
|
||||
<ContractInvoiceModal
|
||||
v-model:visible="contractInvoiceModalVisible"
|
||||
:equipment-data="currentContractInvoiceData"
|
||||
@success="handleContractInvoiceSuccess"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
@ -361,6 +377,7 @@ import ReceiptDetailModal from './components/ReceiptDetailModal.vue'
|
|||
import PaymentDetailModal from './components/PaymentDetailModal.vue'
|
||||
import ReceiptModal from './components/ReceiptModal.vue'
|
||||
import PaymentModal from './components/PaymentModal.vue'
|
||||
import ContractInvoiceModal from './components/ContractInvoiceModal.vue'
|
||||
import { equipmentProcurementApi } from '@/apis/equipment/procurement'
|
||||
import { equipmentApprovalApi } from '@/apis/equipment/approval'
|
||||
import type { EquipmentListReq, EquipmentResp } from '@/apis/equipment/type'
|
||||
|
@ -409,6 +426,10 @@ const currentPaymentData = ref<EquipmentResp | null>(null)
|
|||
const receiptModalVisible = ref(false)
|
||||
const paymentModalVisible = ref(false)
|
||||
|
||||
// 合同发票管理弹窗控制
|
||||
const contractInvoiceModalVisible = ref(false)
|
||||
const currentContractInvoiceData = ref<EquipmentResp | null>(null)
|
||||
|
||||
// 表格选择
|
||||
const selectedRowKeys = ref<string[]>([])
|
||||
const rowSelection = reactive({
|
||||
|
@ -1052,19 +1073,27 @@ const canApplyProcurement = (record: EquipmentResp) => {
|
|||
// 检查是否可以收货
|
||||
const canReceiveGoods = (record: EquipmentResp) => {
|
||||
// 只有在采购状态为已通过且收货状态为未收货时才显示确认收货按钮
|
||||
// 同时需要确保合同发票信息已经完善
|
||||
const procurementStatus = record.procurementStatus
|
||||
const receiptStatus = record.receiptStatus
|
||||
|
||||
// 检查是否有合同发票信息(这里可以根据实际业务逻辑调整)
|
||||
// 暂时使用一个简单的判断,后续可以根据实际数据结构调整
|
||||
const hasContractInvoice = true // 暂时设为true,后续根据实际业务逻辑调整
|
||||
|
||||
console.log('🔍 canReceiveGoods 检查:', {
|
||||
equipmentName: record.equipmentName,
|
||||
procurementStatus,
|
||||
receiptStatus,
|
||||
hasContractInvoice,
|
||||
canReceive: procurementStatus === 'APPROVED' &&
|
||||
(receiptStatus === 'NOT_RECEIVED' || receiptStatus === 'PARTIALLY_RECEIVED')
|
||||
(receiptStatus === 'NOT_RECEIVED' || receiptStatus === 'PARTIALLY_RECEIVED') &&
|
||||
hasContractInvoice
|
||||
})
|
||||
|
||||
return procurementStatus === 'APPROVED' &&
|
||||
(receiptStatus === 'NOT_RECEIVED' || receiptStatus === 'PARTIALLY_RECEIVED')
|
||||
(receiptStatus === 'NOT_RECEIVED' || receiptStatus === 'PARTIALLY_RECEIVED') &&
|
||||
hasContractInvoice
|
||||
}
|
||||
|
||||
// 收货操作
|
||||
|
@ -1160,6 +1189,24 @@ const handleRefreshRecord = async (record: EquipmentResp) => {
|
|||
}
|
||||
}
|
||||
|
||||
// 检查是否可以管理合同发票
|
||||
const canManageContractInvoice = (record: EquipmentResp) => {
|
||||
// 只有在采购状态为已通过时才显示管理合同发票按钮
|
||||
return record.procurementStatus === 'APPROVED'
|
||||
}
|
||||
|
||||
// 管理合同发票操作
|
||||
const handleManageContractInvoice = (record: EquipmentResp) => {
|
||||
currentContractInvoiceData.value = { ...record }
|
||||
contractInvoiceModalVisible.value = true
|
||||
}
|
||||
|
||||
// 合同发票管理成功回调
|
||||
const handleContractInvoiceSuccess = () => {
|
||||
contractInvoiceModalVisible.value = false
|
||||
loadData(currentSearchParams.value)
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
loadData()
|
||||
})
|
||||
|
|
Loading…
Reference in New Issue