2025-08-08 15:44:56 +08:00
|
|
|
<template>
|
|
|
|
<a-modal
|
|
|
|
:visible="visible"
|
|
|
|
:title="modalTitle"
|
|
|
|
width="800px"
|
|
|
|
@ok="handleSubmit"
|
|
|
|
@cancel="handleCancel"
|
|
|
|
@update:visible="(value) => emit('update:visible', value)"
|
|
|
|
:confirm-loading="submitLoading"
|
|
|
|
>
|
|
|
|
<a-form
|
|
|
|
ref="formRef"
|
|
|
|
:model="formData"
|
|
|
|
:rules="rules"
|
|
|
|
layout="vertical"
|
|
|
|
class="procurement-application-form"
|
|
|
|
>
|
|
|
|
<a-row :gutter="16">
|
|
|
|
<a-col :span="12">
|
|
|
|
<a-form-item label="设备名称" field="equipmentName">
|
|
|
|
<a-input
|
|
|
|
v-model="formData.equipmentName"
|
|
|
|
placeholder="请输入设备名称"
|
|
|
|
allow-clear
|
|
|
|
/>
|
|
|
|
</a-form-item>
|
|
|
|
</a-col>
|
|
|
|
<a-col :span="12">
|
|
|
|
<a-form-item label="设备类型" field="equipmentType">
|
|
|
|
<a-select
|
|
|
|
v-model="formData.equipmentType"
|
|
|
|
placeholder="请选择设备类型"
|
|
|
|
allow-clear
|
|
|
|
>
|
|
|
|
<a-option value="服务器">服务器</a-option>
|
|
|
|
<a-option value="网络设备">网络设备</a-option>
|
|
|
|
<a-option value="办公设备">办公设备</a-option>
|
|
|
|
<a-option value="计算机">计算机</a-option>
|
|
|
|
<a-option value="安防设备">安防设备</a-option>
|
|
|
|
<a-option value="显示设备">显示设备</a-option>
|
|
|
|
<a-option value="测试设备">测试设备</a-option>
|
|
|
|
</a-select>
|
|
|
|
</a-form-item>
|
|
|
|
</a-col>
|
|
|
|
</a-row>
|
|
|
|
|
|
|
|
<a-row :gutter="16">
|
|
|
|
<a-col :span="12">
|
|
|
|
<a-form-item label="设备型号" field="equipmentModel">
|
|
|
|
<a-input
|
|
|
|
v-model="formData.equipmentModel"
|
|
|
|
placeholder="请输入设备型号"
|
|
|
|
allow-clear
|
|
|
|
/>
|
|
|
|
</a-form-item>
|
|
|
|
</a-col>
|
|
|
|
<a-col :span="12">
|
|
|
|
<a-form-item label="品牌" field="brand">
|
|
|
|
<a-input
|
|
|
|
v-model="formData.brand"
|
|
|
|
placeholder="请输入品牌"
|
|
|
|
allow-clear
|
|
|
|
/>
|
|
|
|
</a-form-item>
|
|
|
|
</a-col>
|
|
|
|
</a-row>
|
|
|
|
|
|
|
|
<a-row :gutter="16">
|
|
|
|
<a-col :span="12">
|
|
|
|
<a-form-item label="供应商名称" field="supplierName">
|
|
|
|
<a-input
|
|
|
|
v-model="formData.supplierName"
|
|
|
|
placeholder="请输入供应商名称"
|
|
|
|
allow-clear
|
|
|
|
/>
|
|
|
|
</a-form-item>
|
|
|
|
</a-col>
|
|
|
|
<a-col :span="12">
|
|
|
|
<a-form-item label="数量" field="quantity">
|
|
|
|
<a-input-number
|
|
|
|
v-model="formData.quantity"
|
|
|
|
placeholder="请输入数量"
|
|
|
|
:min="1"
|
|
|
|
:precision="0"
|
|
|
|
style="width: 100%"
|
|
|
|
/>
|
|
|
|
</a-form-item>
|
|
|
|
</a-col>
|
|
|
|
</a-row>
|
|
|
|
|
|
|
|
<a-row :gutter="16">
|
|
|
|
<a-col :span="12">
|
|
|
|
<a-form-item label="预算金额" field="budgetAmount">
|
|
|
|
<a-input-number
|
|
|
|
v-model="formData.budgetAmount"
|
|
|
|
placeholder="请输入预算金额"
|
|
|
|
:min="0"
|
|
|
|
:precision="2"
|
|
|
|
style="width: 100%"
|
|
|
|
/>
|
|
|
|
</a-form-item>
|
|
|
|
</a-col>
|
|
|
|
<a-col :span="12">
|
|
|
|
<a-form-item label="紧急程度" field="urgencyLevel">
|
|
|
|
<a-select
|
|
|
|
v-model="formData.urgencyLevel"
|
|
|
|
placeholder="请选择紧急程度"
|
|
|
|
allow-clear
|
|
|
|
>
|
|
|
|
<a-option value="LOW">低</a-option>
|
|
|
|
<a-option value="NORMAL">普通</a-option>
|
|
|
|
<a-option value="HIGH">高</a-option>
|
|
|
|
<a-option value="URGENT">紧急</a-option>
|
|
|
|
</a-select>
|
|
|
|
</a-form-item>
|
|
|
|
</a-col>
|
|
|
|
</a-row>
|
|
|
|
|
|
|
|
<a-row :gutter="16">
|
|
|
|
<a-col :span="12">
|
|
|
|
<a-form-item label="采购类型" field="procurementType">
|
|
|
|
<a-select
|
|
|
|
v-model="formData.procurementType"
|
|
|
|
placeholder="请选择采购类型"
|
|
|
|
allow-clear
|
|
|
|
>
|
|
|
|
<a-option value="NEW_PURCHASE">新采购</a-option>
|
|
|
|
<a-option value="REPLACEMENT">更换</a-option>
|
|
|
|
<a-option value="UPGRADE">升级</a-option>
|
|
|
|
</a-select>
|
|
|
|
</a-form-item>
|
|
|
|
</a-col>
|
|
|
|
<a-col :span="12">
|
|
|
|
<a-form-item label="预期交付日期" field="expectedDeliveryDate">
|
|
|
|
<a-date-picker
|
|
|
|
v-model="formData.expectedDeliveryDate"
|
|
|
|
placeholder="请选择预期交付日期"
|
|
|
|
style="width: 100%"
|
|
|
|
/>
|
|
|
|
</a-form-item>
|
|
|
|
</a-col>
|
|
|
|
</a-row>
|
|
|
|
|
|
|
|
<a-form-item label="申请原因" field="applyReason">
|
|
|
|
<a-textarea
|
|
|
|
v-model="formData.applyReason"
|
|
|
|
placeholder="请详细说明采购原因"
|
|
|
|
:rows="4"
|
|
|
|
allow-clear
|
|
|
|
/>
|
|
|
|
</a-form-item>
|
|
|
|
|
|
|
|
<a-form-item label="技术要求" field="technicalRequirements">
|
|
|
|
<a-textarea
|
|
|
|
v-model="formData.technicalRequirements"
|
|
|
|
placeholder="请描述设备的技术要求(可选)"
|
|
|
|
:rows="3"
|
|
|
|
allow-clear
|
|
|
|
/>
|
|
|
|
</a-form-item>
|
|
|
|
|
|
|
|
<a-form-item label="业务合理性说明" field="businessJustification">
|
|
|
|
<a-textarea
|
|
|
|
v-model="formData.businessJustification"
|
|
|
|
placeholder="请说明采购的业务合理性(可选)"
|
|
|
|
:rows="3"
|
|
|
|
allow-clear
|
|
|
|
/>
|
|
|
|
</a-form-item>
|
|
|
|
</a-form>
|
|
|
|
</a-modal>
|
|
|
|
</template>
|
|
|
|
|
|
|
|
<script setup lang="ts">
|
|
|
|
import { ref, reactive, watch } from 'vue'
|
|
|
|
import { Message } from '@arco-design/web-vue'
|
|
|
|
import { equipmentApprovalApi } from '@/apis/equipment/approval'
|
2025-08-11 10:54:45 +08:00
|
|
|
import notificationService from '@/services/notificationService'
|
2025-08-08 15:44:56 +08:00
|
|
|
|
|
|
|
interface Props {
|
|
|
|
visible: boolean
|
|
|
|
equipmentData?: any
|
|
|
|
}
|
|
|
|
|
|
|
|
interface Emits {
|
|
|
|
(e: 'update:visible', value: boolean): void
|
|
|
|
(e: 'success'): void
|
|
|
|
}
|
|
|
|
|
|
|
|
const props = defineProps<Props>()
|
|
|
|
const emit = defineEmits<Emits>()
|
|
|
|
|
|
|
|
const modalTitle = ref('申请采购')
|
|
|
|
const submitLoading = ref(false)
|
|
|
|
const formRef = ref()
|
|
|
|
|
|
|
|
// 表单数据
|
|
|
|
const formData = reactive({
|
|
|
|
equipmentId: '', // 添加设备ID字段
|
|
|
|
equipmentName: '',
|
|
|
|
equipmentType: '',
|
|
|
|
equipmentModel: '',
|
|
|
|
brand: '',
|
|
|
|
supplierName: '',
|
|
|
|
quantity: 1,
|
|
|
|
budgetAmount: undefined,
|
|
|
|
urgencyLevel: 'NORMAL',
|
|
|
|
procurementType: 'NEW_PURCHASE',
|
|
|
|
expectedDeliveryDate: null,
|
|
|
|
applyReason: '',
|
|
|
|
technicalRequirements: '',
|
|
|
|
businessJustification: ''
|
|
|
|
})
|
|
|
|
|
|
|
|
// 表单验证规则
|
|
|
|
const rules = {
|
|
|
|
equipmentId: [{ required: true, message: '设备ID不能为空' }],
|
|
|
|
equipmentName: [{ required: true, message: '请输入设备名称' }],
|
|
|
|
equipmentType: [{ required: true, message: '请选择设备类型' }],
|
|
|
|
quantity: [{ required: true, message: '请输入数量' }],
|
|
|
|
budgetAmount: [{ required: true, message: '请输入预算金额' }],
|
|
|
|
urgencyLevel: [{ required: true, message: '请选择紧急程度' }],
|
|
|
|
procurementType: [{ required: true, message: '请选择采购类型' }],
|
|
|
|
applyReason: [
|
|
|
|
{ required: true, message: '请输入申请原因' },
|
|
|
|
{
|
|
|
|
validator: (value: string) => {
|
|
|
|
if (!value || value.trim() === '') {
|
|
|
|
return new Error('申请原因不能为空')
|
|
|
|
}
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
]
|
|
|
|
}
|
|
|
|
|
|
|
|
// 监听设备数据变化,填充表单
|
|
|
|
watch(() => props.equipmentData, (newData) => {
|
|
|
|
if (newData) {
|
|
|
|
// 填充设备数据到表单
|
|
|
|
Object.assign(formData, {
|
|
|
|
equipmentId: newData.equipmentId || '', // 添加设备ID
|
|
|
|
equipmentName: newData.equipmentName || '',
|
|
|
|
equipmentType: newData.equipmentType || '',
|
|
|
|
equipmentModel: newData.equipmentModel || '',
|
|
|
|
brand: newData.brand || '',
|
|
|
|
supplierName: newData.supplierName || '',
|
|
|
|
quantity: newData.quantity || 1,
|
|
|
|
budgetAmount: newData.unitPrice || undefined,
|
|
|
|
urgencyLevel: 'NORMAL',
|
|
|
|
procurementType: 'NEW_PURCHASE',
|
|
|
|
expectedDeliveryDate: null,
|
|
|
|
applyReason: '', // 申请原因需要用户填写
|
|
|
|
technicalRequirements: '',
|
|
|
|
businessJustification: ''
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}, { immediate: true })
|
|
|
|
|
|
|
|
// 监听模态框显示状态
|
|
|
|
watch(() => props.visible, (visible) => {
|
|
|
|
if (visible) {
|
|
|
|
// 模态框打开时,如果有设备数据则填充,否则重置表单
|
|
|
|
if (props.equipmentData) {
|
|
|
|
// 设备数据会在上面的 watch 中处理
|
|
|
|
} else {
|
|
|
|
// 没有设备数据时重置表单
|
|
|
|
formRef.value?.resetFields()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
// 提交表单
|
|
|
|
const handleSubmit = async () => {
|
|
|
|
try {
|
|
|
|
// 手动验证 applyReason 字段
|
|
|
|
if (!formData.applyReason || formData.applyReason.trim() === '') {
|
|
|
|
Message.error('请输入申请原因')
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
await formRef.value.validate()
|
|
|
|
submitLoading.value = true
|
|
|
|
|
|
|
|
const submitData = {
|
|
|
|
...formData,
|
|
|
|
applyReason: formData.applyReason.trim(), // 确保去除空格
|
|
|
|
expectedDeliveryDate: formData.expectedDeliveryDate ?
|
|
|
|
(formData.expectedDeliveryDate as any).format('YYYY-MM-DD') : null
|
|
|
|
}
|
|
|
|
|
|
|
|
// 调试信息:打印提交的数据
|
|
|
|
console.log('提交的表单数据:', submitData)
|
|
|
|
console.log('applyReason 值:', submitData.applyReason)
|
|
|
|
console.log('applyReason 类型:', typeof submitData.applyReason)
|
|
|
|
console.log('applyReason 长度:', submitData.applyReason?.length)
|
|
|
|
|
|
|
|
await equipmentApprovalApi.submitProcurementApplication(submitData)
|
|
|
|
|
2025-08-11 10:54:45 +08:00
|
|
|
// 添加采购申请通知
|
|
|
|
const currentUser = JSON.parse(localStorage.getItem('userInfo') || '{}')
|
|
|
|
const applicantName = currentUser.nickname || currentUser.username || '未知用户'
|
|
|
|
|
|
|
|
notificationService.addProcurementNotification(
|
|
|
|
submitData.equipmentName,
|
|
|
|
applicantName
|
|
|
|
)
|
|
|
|
|
2025-08-08 15:44:56 +08:00
|
|
|
Message.success('采购申请提交成功')
|
|
|
|
emit('success')
|
|
|
|
handleCancel()
|
|
|
|
} catch (error: any) {
|
|
|
|
console.error('提交采购申请失败:', error)
|
|
|
|
Message.error(error?.message || '提交失败')
|
|
|
|
} finally {
|
|
|
|
submitLoading.value = false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// 取消
|
|
|
|
const handleCancel = () => {
|
|
|
|
emit('update:visible', false)
|
|
|
|
// 清空用户输入的数据,保留设备基本信息
|
|
|
|
formData.equipmentId = ''
|
|
|
|
formData.applyReason = ''
|
|
|
|
formData.technicalRequirements = ''
|
|
|
|
formData.businessJustification = ''
|
|
|
|
formData.expectedDeliveryDate = null
|
|
|
|
formRef.value?.resetFields()
|
|
|
|
}
|
|
|
|
</script>
|
|
|
|
|
|
|
|
<style scoped lang="scss">
|
|
|
|
.procurement-application-form {
|
|
|
|
.arco-form-item {
|
|
|
|
margin-bottom: 16px;
|
|
|
|
}
|
|
|
|
|
|
|
|
.arco-input,
|
|
|
|
.arco-select,
|
|
|
|
.arco-input-number,
|
|
|
|
.arco-date-picker {
|
|
|
|
border-radius: 6px;
|
|
|
|
}
|
|
|
|
|
|
|
|
.arco-textarea {
|
|
|
|
border-radius: 6px;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
</style>
|