340 lines
8.6 KiB
Vue
340 lines
8.6 KiB
Vue
|
<template>
|
||
|
<a-modal
|
||
|
v-model:visible="visible"
|
||
|
:title="modalTitle"
|
||
|
width="600px"
|
||
|
@ok="handleSubmit"
|
||
|
@cancel="handleCancel"
|
||
|
:confirm-loading="loading"
|
||
|
>
|
||
|
<div class="approval-action" v-if="approvalData">
|
||
|
<!-- 审批信息预览 -->
|
||
|
<div class="preview-section">
|
||
|
<h4 class="preview-title">
|
||
|
<IconInfoCircle style="margin-right: 8px; color: var(--color-primary);" />
|
||
|
审批信息预览
|
||
|
</h4>
|
||
|
<a-descriptions :column="1" bordered size="small">
|
||
|
<a-descriptions-item label="设备名称">
|
||
|
{{ approvalData.equipmentName }}
|
||
|
</a-descriptions-item>
|
||
|
<a-descriptions-item label="设备类型">
|
||
|
{{ approvalData.equipmentType }}
|
||
|
</a-descriptions-item>
|
||
|
<a-descriptions-item label="设备型号">
|
||
|
{{ approvalData.equipmentModel }}
|
||
|
</a-descriptions-item>
|
||
|
<a-descriptions-item label="申请人">
|
||
|
{{ approvalData.applicantName }}
|
||
|
</a-descriptions-item>
|
||
|
<a-descriptions-item label="申请时间">
|
||
|
{{ formatDateTime(approvalData.applyTime) }}
|
||
|
</a-descriptions-item>
|
||
|
<a-descriptions-item label="总价">
|
||
|
<span v-if="approvalData.totalPrice" class="price-text">
|
||
|
¥{{ formatPrice(approvalData.totalPrice) }}
|
||
|
</span>
|
||
|
<span v-else class="no-data">-</span>
|
||
|
</a-descriptions-item>
|
||
|
</a-descriptions>
|
||
|
</div>
|
||
|
|
||
|
<!-- 审批表单 -->
|
||
|
<div class="form-section">
|
||
|
<h4 class="form-title">
|
||
|
<IconEdit style="margin-right: 8px; color: var(--color-warning);" />
|
||
|
审批意见
|
||
|
</h4>
|
||
|
<a-form
|
||
|
ref="formRef"
|
||
|
:model="formData"
|
||
|
:rules="rules"
|
||
|
layout="vertical"
|
||
|
>
|
||
|
<a-form-item field="approvalComment" label="审批意见" class="form-item">
|
||
|
<a-textarea
|
||
|
v-model="formData.approvalComment"
|
||
|
:placeholder="actionType === 'approve' ? '请输入审批通过的意见(可选)' : '请输入审批拒绝的原因(必填)'"
|
||
|
:rows="4"
|
||
|
class="form-textarea"
|
||
|
allow-clear
|
||
|
/>
|
||
|
</a-form-item>
|
||
|
</a-form>
|
||
|
</div>
|
||
|
|
||
|
<!-- 确认提示 -->
|
||
|
<div class="confirm-section">
|
||
|
<a-alert
|
||
|
:type="actionType === 'approve' ? 'success' : 'error'"
|
||
|
:title="actionType === 'approve' ? '审批通过确认' : '审批拒绝确认'"
|
||
|
:description="actionType === 'approve' ? '确认通过此设备采购申请吗?通过后该申请将进入采购流程。' : '确认拒绝此设备采购申请吗?拒绝后申请人将收到通知。'"
|
||
|
show-icon
|
||
|
/>
|
||
|
</div>
|
||
|
</div>
|
||
|
</a-modal>
|
||
|
</template>
|
||
|
|
||
|
<script lang="ts" setup>
|
||
|
import { computed, reactive, ref, watch } from 'vue'
|
||
|
import { IconInfoCircle, IconEdit } from '@arco-design/web-vue/es/icon'
|
||
|
import message from '@arco-design/web-vue/es/message'
|
||
|
import type { EquipmentApprovalResp, EquipmentApprovalReq } from '@/apis/equipment/type'
|
||
|
import { equipmentApprovalApi } from '@/apis/equipment/approval'
|
||
|
|
||
|
defineOptions({ name: 'ApprovalActionModal' })
|
||
|
|
||
|
// Props
|
||
|
interface Props {
|
||
|
visible: boolean
|
||
|
approvalData: EquipmentApprovalResp | null
|
||
|
actionType: 'approve' | 'reject'
|
||
|
}
|
||
|
|
||
|
const props = defineProps<Props>()
|
||
|
|
||
|
// Emits
|
||
|
const emit = defineEmits<{
|
||
|
'update:visible': [value: boolean]
|
||
|
success: []
|
||
|
}>()
|
||
|
|
||
|
// 计算属性
|
||
|
const visible = computed({
|
||
|
get: () => props.visible,
|
||
|
set: (value) => emit('update:visible', value)
|
||
|
})
|
||
|
|
||
|
const modalTitle = computed(() => {
|
||
|
return props.actionType === 'approve' ? '审批通过' : '审批拒绝'
|
||
|
})
|
||
|
|
||
|
// 表单相关
|
||
|
const formRef = ref()
|
||
|
const loading = ref(false)
|
||
|
const formData = reactive<EquipmentApprovalReq>({
|
||
|
approvalComment: '',
|
||
|
approvalResult: props.actionType === 'approve' ? 'APPROVED' : 'REJECTED',
|
||
|
approverName: '当前用户', // 这里应该从用户认证系统获取
|
||
|
approverId: 'current-user-id' // 这里应该从用户认证系统获取
|
||
|
})
|
||
|
|
||
|
// 表单验证规则
|
||
|
const rules = computed(() => ({
|
||
|
approvalComment: props.actionType === 'reject' ? [
|
||
|
{ required: true, message: '请输入审批拒绝的原因' }
|
||
|
] : []
|
||
|
}))
|
||
|
|
||
|
// 格式化价格
|
||
|
const formatPrice = (price: number) => {
|
||
|
return price.toLocaleString('zh-CN', {
|
||
|
minimumFractionDigits: 2,
|
||
|
maximumFractionDigits: 2,
|
||
|
})
|
||
|
}
|
||
|
|
||
|
// 格式化日期时间
|
||
|
const formatDateTime = (dateTime: string) => {
|
||
|
if (!dateTime) return '-'
|
||
|
const date = new Date(dateTime)
|
||
|
return date.toLocaleString('zh-CN', {
|
||
|
year: 'numeric',
|
||
|
month: '2-digit',
|
||
|
day: '2-digit',
|
||
|
hour: '2-digit',
|
||
|
minute: '2-digit',
|
||
|
})
|
||
|
}
|
||
|
|
||
|
// 提交审批
|
||
|
const handleSubmit = async () => {
|
||
|
try {
|
||
|
await formRef.value.validate()
|
||
|
|
||
|
if (!props.approvalData) {
|
||
|
message.error('审批数据不存在')
|
||
|
return
|
||
|
}
|
||
|
|
||
|
loading.value = true
|
||
|
|
||
|
const approvalId = props.approvalData.approvalId
|
||
|
const submitData: EquipmentApprovalReq = {
|
||
|
approvalComment: formData.approvalComment,
|
||
|
approvalResult: props.actionType === 'approve' ? 'APPROVED' : 'REJECTED',
|
||
|
approverName: formData.approverName,
|
||
|
approverId: formData.approverId
|
||
|
}
|
||
|
|
||
|
console.log('提交审批数据:', submitData)
|
||
|
|
||
|
if (props.actionType === 'approve') {
|
||
|
await equipmentApprovalApi.approve(approvalId, submitData)
|
||
|
message.success('审批通过成功')
|
||
|
} else {
|
||
|
await equipmentApprovalApi.reject(approvalId, submitData)
|
||
|
message.success('审批拒绝成功')
|
||
|
}
|
||
|
|
||
|
emit('success')
|
||
|
visible.value = false
|
||
|
resetForm()
|
||
|
} catch (error: any) {
|
||
|
console.error('审批操作失败:', error)
|
||
|
message.error(error?.message || '审批操作失败')
|
||
|
} finally {
|
||
|
loading.value = false
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// 取消
|
||
|
const handleCancel = () => {
|
||
|
visible.value = false
|
||
|
resetForm()
|
||
|
}
|
||
|
|
||
|
// 重置表单
|
||
|
const resetForm = () => {
|
||
|
Object.assign(formData, {
|
||
|
approvalComment: '',
|
||
|
approvalResult: props.actionType === 'approve' ? 'APPROVED' : 'REJECTED',
|
||
|
approverName: '当前用户',
|
||
|
approverId: 'current-user-id'
|
||
|
})
|
||
|
formRef.value?.resetFields()
|
||
|
}
|
||
|
|
||
|
// 监听弹窗显示状态,重置表单
|
||
|
watch(() => props.visible, (newVal) => {
|
||
|
if (newVal) {
|
||
|
resetForm()
|
||
|
}
|
||
|
})
|
||
|
</script>
|
||
|
|
||
|
<style scoped lang="scss">
|
||
|
.approval-action {
|
||
|
.preview-section {
|
||
|
margin-bottom: 24px;
|
||
|
|
||
|
.preview-title {
|
||
|
display: flex;
|
||
|
align-items: center;
|
||
|
margin-bottom: 16px;
|
||
|
font-size: 14px;
|
||
|
font-weight: 600;
|
||
|
color: var(--color-text-1);
|
||
|
}
|
||
|
|
||
|
.arco-descriptions {
|
||
|
.arco-descriptions-item-label {
|
||
|
font-weight: 500;
|
||
|
color: var(--color-text-1);
|
||
|
background-color: var(--color-fill-1);
|
||
|
}
|
||
|
|
||
|
.arco-descriptions-item-value {
|
||
|
color: var(--color-text-2);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
.form-section {
|
||
|
margin-bottom: 24px;
|
||
|
|
||
|
.form-title {
|
||
|
display: flex;
|
||
|
align-items: center;
|
||
|
margin-bottom: 16px;
|
||
|
font-size: 14px;
|
||
|
font-weight: 600;
|
||
|
color: var(--color-text-1);
|
||
|
}
|
||
|
|
||
|
.form-item {
|
||
|
.arco-form-item-label {
|
||
|
font-weight: 500;
|
||
|
color: var(--color-text-1);
|
||
|
margin-bottom: 8px;
|
||
|
}
|
||
|
|
||
|
.form-textarea {
|
||
|
border-radius: 6px;
|
||
|
border: 1px solid var(--color-border);
|
||
|
transition: all 0.2s ease;
|
||
|
|
||
|
&:hover {
|
||
|
border-color: var(--color-primary-light-3);
|
||
|
}
|
||
|
|
||
|
&:focus,
|
||
|
&.arco-textarea-focus {
|
||
|
border-color: var(--color-primary);
|
||
|
box-shadow: 0 0 0 2px rgba(var(--primary-6), 0.1);
|
||
|
}
|
||
|
|
||
|
.arco-textarea-inner {
|
||
|
padding: 12px;
|
||
|
font-size: 14px;
|
||
|
line-height: 1.6;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
.confirm-section {
|
||
|
.arco-alert {
|
||
|
border-radius: 6px;
|
||
|
|
||
|
.arco-alert-title {
|
||
|
font-weight: 600;
|
||
|
}
|
||
|
|
||
|
.arco-alert-description {
|
||
|
color: var(--color-text-2);
|
||
|
line-height: 1.5;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
.price-text {
|
||
|
color: #f56c6c;
|
||
|
font-weight: 500;
|
||
|
}
|
||
|
|
||
|
.no-data {
|
||
|
color: var(--color-text-4);
|
||
|
font-style: italic;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// 响应式设计
|
||
|
@media (max-width: 768px) {
|
||
|
.approval-action {
|
||
|
.preview-section,
|
||
|
.form-section {
|
||
|
.arco-descriptions {
|
||
|
.arco-descriptions-item {
|
||
|
display: block;
|
||
|
|
||
|
.arco-descriptions-item-label {
|
||
|
display: block;
|
||
|
width: 100%;
|
||
|
text-align: left;
|
||
|
padding: 8px 12px;
|
||
|
}
|
||
|
|
||
|
.arco-descriptions-item-value {
|
||
|
display: block;
|
||
|
width: 100%;
|
||
|
padding: 8px 12px;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
</style>
|