2025-08-07 16:46:44 +08:00
|
|
|
|
<template>
|
|
|
|
|
<div class="equipment-approval-container">
|
|
|
|
|
<!-- 页面头部 -->
|
|
|
|
|
<div class="page-header">
|
|
|
|
|
<div class="header-content">
|
|
|
|
|
<div class="header-left">
|
|
|
|
|
<div class="page-title">
|
|
|
|
|
<IconCheckCircle style="font-size: 24px; margin-right: 12px; color: var(--color-primary);" />
|
|
|
|
|
<h1>设备审批控制台</h1>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="page-description">
|
|
|
|
|
管理设备采购申请的审批流程,包括待审批、已审批等状态管理
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="header-right">
|
|
|
|
|
<a-space>
|
|
|
|
|
<ApprovalSearch
|
|
|
|
|
:loading="loading"
|
|
|
|
|
@search="handleSearch"
|
|
|
|
|
@reset="handleReset"
|
|
|
|
|
/>
|
|
|
|
|
<a-button type="primary" size="large" @click="refreshData">
|
|
|
|
|
<template #icon>
|
|
|
|
|
<IconRefresh />
|
|
|
|
|
</template>
|
|
|
|
|
刷新
|
|
|
|
|
</a-button>
|
|
|
|
|
</a-space>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- 统计卡片 -->
|
|
|
|
|
<div class="stats-container">
|
|
|
|
|
<a-row :gutter="16">
|
|
|
|
|
<a-col :span="6">
|
|
|
|
|
<a-card class="stat-card" :bordered="false">
|
|
|
|
|
<div class="stat-content">
|
|
|
|
|
<div class="stat-icon" style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);">
|
|
|
|
|
<IconClockCircle />
|
|
|
|
|
</div>
|
|
|
|
|
<div class="stat-info">
|
|
|
|
|
<div class="stat-number">{{ getPendingCount() }}</div>
|
|
|
|
|
<div class="stat-label">待审批</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</a-card>
|
|
|
|
|
</a-col>
|
|
|
|
|
<a-col :span="6">
|
|
|
|
|
<a-card class="stat-card" :bordered="false">
|
|
|
|
|
<div class="stat-content">
|
|
|
|
|
<div class="stat-icon" style="background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);">
|
|
|
|
|
<IconCheckCircle />
|
|
|
|
|
</div>
|
|
|
|
|
<div class="stat-info">
|
|
|
|
|
<div class="stat-number">{{ getApprovedCount() }}</div>
|
|
|
|
|
<div class="stat-label">已通过</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</a-card>
|
|
|
|
|
</a-col>
|
|
|
|
|
<a-col :span="6">
|
|
|
|
|
<a-card class="stat-card" :bordered="false">
|
|
|
|
|
<div class="stat-content">
|
|
|
|
|
<div class="stat-icon" style="background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);">
|
|
|
|
|
<IconCloseCircle />
|
|
|
|
|
</div>
|
|
|
|
|
<div class="stat-info">
|
|
|
|
|
<div class="stat-number">{{ getRejectedCount() }}</div>
|
|
|
|
|
<div class="stat-label">已拒绝</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</a-card>
|
|
|
|
|
</a-col>
|
|
|
|
|
<a-col :span="6">
|
|
|
|
|
<a-card class="stat-card" :bordered="false">
|
|
|
|
|
<div class="stat-content">
|
|
|
|
|
<div class="stat-icon" style="background: linear-gradient(135deg, #fa709a 0%, #fee140 100%);">
|
|
|
|
|
<IconApps />
|
|
|
|
|
</div>
|
|
|
|
|
<div class="stat-info">
|
|
|
|
|
<div class="stat-number">¥{{ getTotalAmount() }}</div>
|
|
|
|
|
<div class="stat-label">审批总额</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</a-card>
|
|
|
|
|
</a-col>
|
|
|
|
|
</a-row>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- 标签页 -->
|
|
|
|
|
<a-card class="table-card" :bordered="false">
|
|
|
|
|
<template #title>
|
|
|
|
|
<div class="card-title">
|
|
|
|
|
<a-tabs v-model:active-key="activeTab" @change="handleTabChange">
|
|
|
|
|
<a-tab-pane key="pending" title="待审批">
|
|
|
|
|
<template #title>
|
|
|
|
|
<span>
|
|
|
|
|
<IconClockCircle style="margin-right: 4px;" />
|
|
|
|
|
待审批 ({{ getPendingCount() }})
|
|
|
|
|
</span>
|
|
|
|
|
</template>
|
|
|
|
|
</a-tab-pane>
|
|
|
|
|
<a-tab-pane key="approved" title="已审批">
|
|
|
|
|
<template #title>
|
|
|
|
|
<span>
|
|
|
|
|
<IconCheckCircle style="margin-right: 4px;" />
|
|
|
|
|
已审批 ({{ getApprovedCount() + getRejectedCount() }})
|
|
|
|
|
</span>
|
|
|
|
|
</template>
|
|
|
|
|
</a-tab-pane>
|
|
|
|
|
</a-tabs>
|
|
|
|
|
</div>
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<a-table
|
|
|
|
|
:columns="columns"
|
|
|
|
|
:data="tableData"
|
|
|
|
|
:loading="loading"
|
|
|
|
|
:pagination="pagination"
|
|
|
|
|
row-key="approvalId"
|
|
|
|
|
@change="handleTableChange"
|
|
|
|
|
>
|
|
|
|
|
<!-- 业务类型 -->
|
|
|
|
|
<template #businessType="{ record }">
|
|
|
|
|
<a-tag :color="getBusinessTypeColor(record.businessType)">
|
|
|
|
|
{{ getBusinessTypeText(record.businessType) }}
|
|
|
|
|
</a-tag>
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<!-- 审批状态 -->
|
|
|
|
|
<template #approvalStatus="{ record }">
|
|
|
|
|
<a-tag :color="getApprovalStatusColor(record.approvalStatus)">
|
|
|
|
|
{{ getApprovalStatusText(record.approvalStatus) }}
|
|
|
|
|
</a-tag>
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<!-- 采购价格 -->
|
|
|
|
|
<template #purchasePrice="{ record }">
|
|
|
|
|
<span v-if="record.purchasePrice" class="price-text">
|
|
|
|
|
¥{{ formatPrice(record.purchasePrice) }}
|
|
|
|
|
</span>
|
|
|
|
|
<span v-else class="no-data">-</span>
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<!-- 总价 -->
|
|
|
|
|
<template #totalPrice="{ record }">
|
|
|
|
|
<span v-if="record.totalPrice" class="price-text">
|
|
|
|
|
¥{{ formatPrice(record.totalPrice) }}
|
|
|
|
|
</span>
|
|
|
|
|
<span v-else class="no-data">-</span>
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<!-- 申请时间 -->
|
|
|
|
|
<template #applyTime="{ record }">
|
|
|
|
|
<span v-if="record.applyTime" class="time-text">
|
|
|
|
|
{{ formatDateTime(record.applyTime) }}
|
|
|
|
|
</span>
|
|
|
|
|
<span v-else class="no-data">-</span>
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<!-- 审批时间 -->
|
|
|
|
|
<template #approvalTime="{ record }">
|
|
|
|
|
<span v-if="record.approvalTime" class="time-text">
|
|
|
|
|
{{ formatDateTime(record.approvalTime) }}
|
|
|
|
|
</span>
|
|
|
|
|
<span v-else class="no-data">-</span>
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<!-- 操作 -->
|
|
|
|
|
<template #action="{ record }">
|
|
|
|
|
<a-space>
|
|
|
|
|
<a-button type="text" size="small" @click="handleView(record)">
|
|
|
|
|
查看详情
|
|
|
|
|
</a-button>
|
|
|
|
|
<a-button
|
|
|
|
|
v-if="record.approvalStatus === ApprovalStatus.PENDING"
|
|
|
|
|
type="primary"
|
|
|
|
|
size="small"
|
|
|
|
|
@click="handleApprove(record)"
|
|
|
|
|
>
|
|
|
|
|
审批通过
|
|
|
|
|
</a-button>
|
|
|
|
|
<a-button
|
|
|
|
|
v-if="record.approvalStatus === ApprovalStatus.PENDING"
|
|
|
|
|
type="text"
|
|
|
|
|
size="small"
|
|
|
|
|
status="danger"
|
|
|
|
|
@click="handleReject(record)"
|
|
|
|
|
>
|
|
|
|
|
审批拒绝
|
|
|
|
|
</a-button>
|
|
|
|
|
</a-space>
|
|
|
|
|
</template>
|
|
|
|
|
</a-table>
|
|
|
|
|
</a-card>
|
|
|
|
|
|
|
|
|
|
<!-- 审批详情弹窗 -->
|
|
|
|
|
<ApprovalDetailModal
|
|
|
|
|
v-model:visible="detailModalVisible"
|
|
|
|
|
:approval-data="currentApproval"
|
|
|
|
|
@success="handleModalSuccess"
|
|
|
|
|
@approve="handleApproveFromDetail"
|
|
|
|
|
@reject="handleRejectFromDetail"
|
|
|
|
|
/>
|
|
|
|
|
|
|
|
|
|
<!-- 审批操作弹窗 -->
|
|
|
|
|
<ApprovalActionModal
|
|
|
|
|
v-model:visible="actionModalVisible"
|
|
|
|
|
:approval-data="currentApproval"
|
|
|
|
|
:action-type="actionType"
|
|
|
|
|
@success="handleModalSuccess"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<script lang="ts" setup>
|
|
|
|
|
import { onBeforeUnmount, onMounted, reactive, ref, watch } from 'vue'
|
|
|
|
|
import { Modal } from '@arco-design/web-vue'
|
|
|
|
|
import {
|
|
|
|
|
IconApps,
|
|
|
|
|
IconCheckCircle,
|
|
|
|
|
IconClockCircle,
|
|
|
|
|
IconCloseCircle,
|
|
|
|
|
IconRefresh,
|
|
|
|
|
} from '@arco-design/web-vue/es/icon'
|
|
|
|
|
import message from '@arco-design/web-vue/es/message'
|
|
|
|
|
import ApprovalDetailModal from './components/ApprovalDetailModal.vue'
|
|
|
|
|
import ApprovalActionModal from './components/ApprovalActionModal.vue'
|
|
|
|
|
import ApprovalSearch from './components/ApprovalSearch.vue'
|
|
|
|
|
import { equipmentApprovalApi } from '@/apis/equipment/approval'
|
|
|
|
|
import type { EquipmentApprovalListReq, EquipmentApprovalResp } from '@/apis/equipment/type'
|
|
|
|
|
import { ApprovalStatus, BusinessType } from '@/apis/equipment/type'
|
|
|
|
|
|
|
|
|
|
defineOptions({ name: 'EquipmentApproval' })
|
|
|
|
|
|
|
|
|
|
// 当前搜索参数
|
|
|
|
|
const currentSearchParams = ref<EquipmentApprovalListReq>({})
|
|
|
|
|
|
|
|
|
|
// 表格数据
|
|
|
|
|
const tableData = ref<EquipmentApprovalResp[]>([])
|
|
|
|
|
const loading = ref(false)
|
|
|
|
|
|
|
|
|
|
// 分页配置
|
|
|
|
|
const pagination = reactive<any>({
|
|
|
|
|
current: 1,
|
|
|
|
|
pageSize: 10,
|
|
|
|
|
total: 0,
|
|
|
|
|
showPageSize: true,
|
|
|
|
|
showJumper: true,
|
|
|
|
|
showTotal: (total: number) => `共 ${total} 条记录`,
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
// 弹窗控制
|
|
|
|
|
const detailModalVisible = ref(false)
|
|
|
|
|
const actionModalVisible = ref(false)
|
|
|
|
|
const currentApproval = ref<EquipmentApprovalResp | null>(null)
|
|
|
|
|
const actionType = ref<'approve' | 'reject'>('approve')
|
|
|
|
|
|
|
|
|
|
// 当前标签页
|
|
|
|
|
const activeTab = ref('pending')
|
|
|
|
|
|
|
|
|
|
// 表格列配置
|
|
|
|
|
const columns = [
|
|
|
|
|
{
|
|
|
|
|
title: '设备名称',
|
|
|
|
|
dataIndex: 'equipmentName',
|
|
|
|
|
key: 'equipmentName',
|
|
|
|
|
width: 150,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
title: '设备类型',
|
|
|
|
|
dataIndex: 'equipmentType',
|
|
|
|
|
key: 'equipmentType',
|
|
|
|
|
width: 120,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
title: '业务类型',
|
|
|
|
|
dataIndex: 'businessType',
|
|
|
|
|
key: 'businessType',
|
|
|
|
|
slotName: 'businessType',
|
|
|
|
|
width: 100,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
title: '设备型号',
|
|
|
|
|
dataIndex: 'equipmentModel',
|
|
|
|
|
key: 'equipmentModel',
|
|
|
|
|
width: 120,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
title: '品牌',
|
|
|
|
|
dataIndex: 'brand',
|
|
|
|
|
key: 'brand',
|
|
|
|
|
width: 100,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
title: '供应商',
|
|
|
|
|
dataIndex: 'supplierName',
|
|
|
|
|
key: 'supplierName',
|
|
|
|
|
width: 150,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
title: '采购价格',
|
|
|
|
|
dataIndex: 'purchasePrice',
|
|
|
|
|
key: 'purchasePrice',
|
|
|
|
|
slotName: 'purchasePrice',
|
|
|
|
|
width: 120,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
title: '总价',
|
|
|
|
|
dataIndex: 'totalPrice',
|
|
|
|
|
key: 'totalPrice',
|
|
|
|
|
slotName: 'totalPrice',
|
|
|
|
|
width: 120,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
title: '数量',
|
|
|
|
|
dataIndex: 'quantity',
|
|
|
|
|
key: 'quantity',
|
|
|
|
|
width: 80,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
title: '申请人',
|
|
|
|
|
dataIndex: 'applicantName',
|
|
|
|
|
key: 'applicantName',
|
|
|
|
|
width: 120,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
title: '申请时间',
|
|
|
|
|
dataIndex: 'applyTime',
|
|
|
|
|
key: 'applyTime',
|
|
|
|
|
slotName: 'applyTime',
|
|
|
|
|
width: 160,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
title: '审批状态',
|
|
|
|
|
dataIndex: 'approvalStatus',
|
|
|
|
|
key: 'approvalStatus',
|
|
|
|
|
slotName: 'approvalStatus',
|
|
|
|
|
width: 120,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
title: '审批人',
|
|
|
|
|
dataIndex: 'approverName',
|
|
|
|
|
key: 'approverName',
|
|
|
|
|
width: 120,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
title: '审批时间',
|
|
|
|
|
dataIndex: 'approvalTime',
|
|
|
|
|
key: 'approvalTime',
|
|
|
|
|
slotName: 'approvalTime',
|
|
|
|
|
width: 160,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
title: '操作',
|
|
|
|
|
key: 'action',
|
|
|
|
|
slotName: 'action',
|
|
|
|
|
width: 200,
|
|
|
|
|
fixed: 'right',
|
|
|
|
|
},
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
// 获取审批状态颜色
|
|
|
|
|
const getApprovalStatusColor = (status: ApprovalStatus) => {
|
|
|
|
|
const colorMap: Record<string, string> = {
|
|
|
|
|
[ApprovalStatus.PENDING]: 'orange',
|
|
|
|
|
[ApprovalStatus.APPROVED]: 'green',
|
|
|
|
|
[ApprovalStatus.REJECTED]: 'red',
|
|
|
|
|
}
|
|
|
|
|
return colorMap[status] || 'blue'
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 获取业务类型颜色
|
|
|
|
|
const getBusinessTypeColor = (type: BusinessType) => {
|
|
|
|
|
const colorMap: Record<string, string> = {
|
|
|
|
|
[BusinessType.PROCUREMENT]: 'blue',
|
|
|
|
|
[BusinessType.BORROW]: 'green',
|
|
|
|
|
[BusinessType.RETURN]: 'orange',
|
|
|
|
|
}
|
|
|
|
|
return colorMap[type] || 'gray'
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 获取业务类型文本
|
|
|
|
|
const getBusinessTypeText = (type: BusinessType) => {
|
|
|
|
|
const textMap: Record<string, string> = {
|
|
|
|
|
[BusinessType.PROCUREMENT]: '采购',
|
|
|
|
|
[BusinessType.BORROW]: '借用',
|
|
|
|
|
[BusinessType.RETURN]: '归还',
|
|
|
|
|
}
|
|
|
|
|
return textMap[type] || '未知'
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 获取审批状态文本
|
|
|
|
|
const getApprovalStatusText = (status: ApprovalStatus) => {
|
|
|
|
|
const textMap: Record<string, string> = {
|
|
|
|
|
[ApprovalStatus.PENDING]: '待审批',
|
|
|
|
|
[ApprovalStatus.APPROVED]: '已通过',
|
|
|
|
|
[ApprovalStatus.REJECTED]: '已拒绝',
|
|
|
|
|
}
|
|
|
|
|
return textMap[status] || '未知'
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 格式化价格
|
|
|
|
|
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 transformBackendData = (data: any[]): EquipmentApprovalResp[] => {
|
|
|
|
|
return data.map((item: any) => ({
|
|
|
|
|
approvalId: item.approvalId || item.id,
|
|
|
|
|
equipmentId: item.equipmentId,
|
|
|
|
|
equipmentName: item.equipmentName,
|
|
|
|
|
equipmentType: item.equipmentType,
|
|
|
|
|
equipmentModel: item.equipmentModel,
|
|
|
|
|
brand: item.brand,
|
|
|
|
|
supplierName: item.supplierName,
|
|
|
|
|
purchasePrice: item.purchasePrice,
|
|
|
|
|
totalPrice: item.totalPrice,
|
|
|
|
|
quantity: item.quantity,
|
|
|
|
|
applicantName: item.applicantName,
|
|
|
|
|
applicantId: item.applicantId,
|
|
|
|
|
applyTime: item.applyTime,
|
|
|
|
|
applyReason: item.applyReason,
|
|
|
|
|
businessType: item.businessType || BusinessType.PROCUREMENT,
|
|
|
|
|
approvalStatus: item.approvalStatus,
|
|
|
|
|
approverName: item.approverName,
|
|
|
|
|
approverId: item.approverId,
|
|
|
|
|
approvalTime: item.approvalTime,
|
|
|
|
|
approvalComment: item.approvalComment,
|
|
|
|
|
createTime: item.createTime,
|
|
|
|
|
updateTime: item.updateTime,
|
|
|
|
|
}))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 加载数据
|
|
|
|
|
const loadData = async (searchParams?: EquipmentApprovalListReq) => {
|
|
|
|
|
console.log('📊 loadData - 开始加载数据')
|
|
|
|
|
console.log('📊 loadData - 接收到的搜索参数:', searchParams)
|
|
|
|
|
console.log('📊 loadData - 当前标签页:', activeTab.value)
|
|
|
|
|
|
|
|
|
|
loading.value = true
|
|
|
|
|
try {
|
2025-08-08 15:44:56 +08:00
|
|
|
|
// 构建完整的请求参数 - 参考设备采购功能的实现
|
2025-08-07 16:46:44 +08:00
|
|
|
|
const params: EquipmentApprovalListReq = {
|
2025-08-08 15:44:56 +08:00
|
|
|
|
pageSize: pagination.pageSize,
|
|
|
|
|
page: pagination.current,
|
2025-08-07 16:46:44 +08:00
|
|
|
|
...(searchParams || {}),
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
console.log('📊 loadData - 构建的完整请求参数:', params)
|
|
|
|
|
|
|
|
|
|
let res: any
|
|
|
|
|
if (activeTab.value === 'pending') {
|
|
|
|
|
console.log('📊 loadData - 调用待审批API')
|
|
|
|
|
res = await equipmentApprovalApi.getPendingApprovals(params)
|
|
|
|
|
} else {
|
|
|
|
|
console.log('📊 loadData - 调用已审批API')
|
|
|
|
|
res = await equipmentApprovalApi.getApprovedApprovals(params)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
console.log('API响应:', res)
|
|
|
|
|
|
|
|
|
|
// 处理不同的响应格式
|
|
|
|
|
let dataList: any[] = []
|
|
|
|
|
let total = 0
|
|
|
|
|
|
|
|
|
|
if (res && res.data) {
|
|
|
|
|
if (Array.isArray(res.data)) {
|
|
|
|
|
dataList = res.data
|
|
|
|
|
total = res.data.length
|
|
|
|
|
} else if (res.data && Array.isArray((res.data as any).records)) {
|
|
|
|
|
dataList = (res.data as any).records
|
|
|
|
|
total = (res.data as any).total || 0
|
|
|
|
|
} else if (res.data && Array.isArray((res.data as any).list)) {
|
|
|
|
|
dataList = (res.data as any).list
|
|
|
|
|
total = (res.data as any).total || 0
|
|
|
|
|
} else if (res.data && Array.isArray((res.data as any).rows)) {
|
|
|
|
|
dataList = (res.data as any).rows
|
|
|
|
|
total = (res.data as any).total || 0
|
|
|
|
|
} else if (res.data && Array.isArray((res.data as any).data)) {
|
|
|
|
|
dataList = (res.data as any).data
|
|
|
|
|
total = (res.data as any).total || 0
|
|
|
|
|
}
|
|
|
|
|
} else if (Array.isArray(res)) {
|
|
|
|
|
dataList = res
|
|
|
|
|
total = res.length
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
console.log('处理后的数据列表:', dataList)
|
|
|
|
|
console.log('总数:', total)
|
|
|
|
|
|
|
|
|
|
if (dataList.length > 0) {
|
|
|
|
|
const transformedData = transformBackendData(dataList)
|
|
|
|
|
console.log('转换后的数据:', transformedData)
|
|
|
|
|
tableData.value = transformedData
|
|
|
|
|
} else {
|
|
|
|
|
console.log('没有数据,设置空数组')
|
|
|
|
|
tableData.value = []
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pagination.total = total
|
|
|
|
|
console.log('设置分页总数:', pagination.total)
|
|
|
|
|
} catch (error: any) {
|
|
|
|
|
console.error('加载数据失败:', error)
|
|
|
|
|
message.error(error?.message || '加载数据失败')
|
|
|
|
|
tableData.value = []
|
|
|
|
|
pagination.total = 0
|
|
|
|
|
} finally {
|
|
|
|
|
loading.value = false
|
|
|
|
|
console.log('📊 loadData - 加载完成')
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 搜索
|
|
|
|
|
const handleSearch = (searchParams: EquipmentApprovalListReq) => {
|
|
|
|
|
console.log('🔍 主组件 - 接收到的搜索参数:', searchParams)
|
|
|
|
|
pagination.current = 1
|
|
|
|
|
currentSearchParams.value = { ...searchParams }
|
|
|
|
|
loadData(searchParams)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 重置
|
|
|
|
|
const handleReset = () => {
|
|
|
|
|
console.log('🔄 主组件 - 重置操作')
|
|
|
|
|
pagination.current = 1
|
|
|
|
|
currentSearchParams.value = {}
|
|
|
|
|
loadData({}) // 传递空对象而不是 undefined
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 表格变化
|
|
|
|
|
const handleTableChange = (pag: any) => {
|
|
|
|
|
pagination.current = pag.current || 1
|
|
|
|
|
pagination.pageSize = pag.pageSize || 10
|
|
|
|
|
loadData(currentSearchParams.value || {})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 标签页变化
|
|
|
|
|
const handleTabChange = (key: string) => {
|
|
|
|
|
activeTab.value = key
|
|
|
|
|
pagination.current = 1
|
|
|
|
|
loadData(currentSearchParams.value || {})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 查看详情
|
|
|
|
|
const handleView = (record: EquipmentApprovalResp) => {
|
|
|
|
|
currentApproval.value = { ...record }
|
|
|
|
|
detailModalVisible.value = true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 审批通过
|
|
|
|
|
const handleApprove = (record: EquipmentApprovalResp) => {
|
|
|
|
|
currentApproval.value = { ...record }
|
|
|
|
|
actionType.value = 'approve'
|
|
|
|
|
actionModalVisible.value = true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 审批拒绝
|
|
|
|
|
const handleReject = (record: EquipmentApprovalResp) => {
|
|
|
|
|
currentApproval.value = { ...record }
|
|
|
|
|
actionType.value = 'reject'
|
|
|
|
|
actionModalVisible.value = true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 从详情弹窗审批通过
|
|
|
|
|
const handleApproveFromDetail = (record: EquipmentApprovalResp) => {
|
|
|
|
|
currentApproval.value = { ...record }
|
|
|
|
|
actionType.value = 'approve'
|
|
|
|
|
actionModalVisible.value = true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 从详情弹窗审批拒绝
|
|
|
|
|
const handleRejectFromDetail = (record: EquipmentApprovalResp) => {
|
|
|
|
|
currentApproval.value = { ...record }
|
|
|
|
|
actionType.value = 'reject'
|
|
|
|
|
actionModalVisible.value = true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 弹窗成功回调
|
|
|
|
|
const handleModalSuccess = () => {
|
|
|
|
|
detailModalVisible.value = false
|
|
|
|
|
actionModalVisible.value = false
|
|
|
|
|
loadData(currentSearchParams.value || {})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 刷新数据
|
|
|
|
|
const refreshData = () => {
|
|
|
|
|
loadData(currentSearchParams.value || {})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 统计函数
|
|
|
|
|
const getPendingCount = () => {
|
|
|
|
|
return tableData.value.filter(item =>
|
|
|
|
|
item.approvalStatus === ApprovalStatus.PENDING
|
|
|
|
|
).length
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const getApprovedCount = () => {
|
|
|
|
|
return tableData.value.filter(item =>
|
|
|
|
|
item.approvalStatus === ApprovalStatus.APPROVED
|
|
|
|
|
).length
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const getRejectedCount = () => {
|
|
|
|
|
return tableData.value.filter(item =>
|
|
|
|
|
item.approvalStatus === ApprovalStatus.REJECTED
|
|
|
|
|
).length
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const getTotalAmount = () => {
|
|
|
|
|
const total = tableData.value.reduce((sum, item) => {
|
|
|
|
|
return sum + (item.totalPrice || 0)
|
|
|
|
|
}, 0)
|
|
|
|
|
return formatPrice(total)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
onMounted(() => {
|
|
|
|
|
loadData({}) // 传递空对象而不是 undefined
|
|
|
|
|
})
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<style scoped lang="scss">
|
|
|
|
|
.equipment-approval-container {
|
|
|
|
|
.page-header {
|
|
|
|
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
|
|
|
border-radius: 12px;
|
|
|
|
|
padding: 24px;
|
|
|
|
|
margin-bottom: 24px;
|
|
|
|
|
|
|
|
|
|
.header-content {
|
|
|
|
|
display: flex;
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
|
|
|
|
.header-left {
|
|
|
|
|
.page-title {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
margin-bottom: 8px;
|
|
|
|
|
|
|
|
|
|
h1 {
|
|
|
|
|
margin: 0;
|
|
|
|
|
color: white;
|
|
|
|
|
font-size: 28px;
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
background: linear-gradient(135deg, #ffffff 0%, #f0f0f0 100%);
|
|
|
|
|
-webkit-background-clip: text;
|
|
|
|
|
-webkit-text-fill-color: transparent;
|
|
|
|
|
background-clip: text;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.page-description {
|
|
|
|
|
color: rgba(255, 255, 255, 0.8);
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.header-right {
|
|
|
|
|
.arco-btn {
|
|
|
|
|
border-radius: 8px;
|
|
|
|
|
font-weight: 500;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.stats-container {
|
|
|
|
|
margin-bottom: 24px;
|
|
|
|
|
|
|
|
|
|
.stat-card {
|
|
|
|
|
border-radius: 12px;
|
|
|
|
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
|
|
|
|
transition: all 0.3s ease;
|
|
|
|
|
|
|
|
|
|
&:hover {
|
|
|
|
|
transform: translateY(-4px);
|
|
|
|
|
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.15);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.stat-content {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
|
|
|
|
.stat-icon {
|
|
|
|
|
width: 60px;
|
|
|
|
|
height: 60px;
|
|
|
|
|
border-radius: 12px;
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
margin-right: 16px;
|
|
|
|
|
|
|
|
|
|
.arco-icon {
|
|
|
|
|
font-size: 24px;
|
|
|
|
|
color: white;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.stat-info {
|
|
|
|
|
flex: 1;
|
|
|
|
|
|
|
|
|
|
.stat-number {
|
|
|
|
|
font-size: 24px;
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
color: var(--color-text-1);
|
|
|
|
|
margin-bottom: 4px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.stat-label {
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
color: var(--color-text-3);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.table-card {
|
|
|
|
|
border-radius: 12px;
|
|
|
|
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
|
|
|
|
|
|
|
|
|
.card-title {
|
|
|
|
|
.arco-tabs {
|
|
|
|
|
.arco-tabs-nav {
|
|
|
|
|
background: transparent;
|
|
|
|
|
|
|
|
|
|
.arco-tabs-tab {
|
|
|
|
|
border-radius: 8px;
|
|
|
|
|
margin-right: 8px;
|
|
|
|
|
|
|
|
|
|
&.arco-tabs-tab-active {
|
|
|
|
|
background: var(--color-primary-light-1);
|
|
|
|
|
color: var(--color-primary);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.arco-table {
|
|
|
|
|
.arco-table-th {
|
|
|
|
|
background-color: var(--color-fill-2);
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.arco-table-tr:hover {
|
|
|
|
|
background-color: var(--color-fill-1);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.price-text {
|
|
|
|
|
color: #f56c6c;
|
|
|
|
|
font-weight: 500;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.time-text {
|
|
|
|
|
color: var(--color-text-2);
|
|
|
|
|
font-size: 12px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.no-data {
|
|
|
|
|
color: var(--color-text-4);
|
|
|
|
|
font-style: italic;
|
|
|
|
|
}
|
2025-08-07 17:38:41 +08:00
|
|
|
|
|
|
|
|
|
// 确保搜索区域有良好的对比度
|
|
|
|
|
.approval-search-container {
|
|
|
|
|
background: #ffffff;
|
|
|
|
|
padding: 16px;
|
|
|
|
|
border-radius: 8px;
|
|
|
|
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
|
|
|
|
margin-bottom: 16px;
|
|
|
|
|
|
|
|
|
|
.search-form {
|
|
|
|
|
.arco-form-item-label {
|
|
|
|
|
color: #333 !important;
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-08-07 16:46:44 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 响应式设计
|
|
|
|
|
@media (max-width: 768px) {
|
|
|
|
|
.equipment-approval-container {
|
|
|
|
|
.page-header {
|
|
|
|
|
.header-content {
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
align-items: flex-start;
|
|
|
|
|
|
|
|
|
|
.header-right {
|
|
|
|
|
margin-top: 16px;
|
|
|
|
|
width: 100%;
|
|
|
|
|
|
|
|
|
|
.arco-space {
|
|
|
|
|
width: 100%;
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.stats-container {
|
|
|
|
|
.arco-col {
|
|
|
|
|
margin-bottom: 16px;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
</style>
|