304 lines
10 KiB
Vue
304 lines
10 KiB
Vue
<template>
|
||
<div class="p-4 bg-gray-50 min-h-screen">
|
||
<div v-loading="loading">
|
||
<!-- 共享头部:项目信息 + V4 流程步骤 -->
|
||
<AcceptanceHeader :acceptance="acceptance" :projectInfo="projectInfo" />
|
||
|
||
<!-- 角色视图分发 -->
|
||
<LiaisonView
|
||
v-if="userRole === 'liaison'"
|
||
:acceptance="acceptance"
|
||
:materials="materials"
|
||
:opinions="opinions"
|
||
@preview="handlePreview"
|
||
@download="handleDownload"
|
||
@show-versions="showVersionHistory"
|
||
@upload-success="handleUploadSuccess"
|
||
@submit-material="handleSubmitMaterial"
|
||
@submit-rectify="handleSubmitRectify"
|
||
@submit-final="handleSubmitFinal"
|
||
@submit-rectify-manual="handleSubmitRectifyManual"
|
||
@submit-expert-reupload="handleSubmitExpertReupload"
|
||
@go-back="router.back()"
|
||
/>
|
||
|
||
<AdminView
|
||
v-else-if="userRole === 'admin'"
|
||
:acceptance="acceptance"
|
||
:projectInfo="projectInfo"
|
||
:preMaterials="preMaterials"
|
||
:finalMaterials="finalMaterials"
|
||
:opinions="opinions"
|
||
@preview="handlePreview"
|
||
@download="handleDownload"
|
||
@show-versions="showVersionHistory"
|
||
@audit="auditDialogVisible = true"
|
||
@organize-meeting="handleOrganizeMeeting"
|
||
@go-back="router.back()"
|
||
/>
|
||
|
||
<ExpertView
|
||
v-else-if="userRole === 'expert'"
|
||
:acceptance="acceptance"
|
||
:preMaterials="preMaterials"
|
||
:finalMaterials="finalMaterials"
|
||
:opinions="opinions"
|
||
@preview="handlePreview"
|
||
@download="handleDownload"
|
||
@expert-check="auditDialogVisible = true"
|
||
@go-back="router.back()"
|
||
/>
|
||
|
||
<ReviewerView
|
||
v-else
|
||
:acceptance="acceptance"
|
||
:projectInfo="projectInfo"
|
||
:preMaterials="preMaterials"
|
||
:finalMaterials="finalMaterials"
|
||
:opinions="opinions"
|
||
@preview="handlePreview"
|
||
@download="handleDownload"
|
||
@show-versions="showVersionHistory"
|
||
@audit="auditDialogVisible = true"
|
||
@go-back="router.back()"
|
||
/>
|
||
</div>
|
||
|
||
<!-- 共享对话框 -->
|
||
<AuditDialog v-model="auditDialogVisible" :acceptance="acceptance" @success="getDetail" />
|
||
<VersionHistoryDialog v-model="versionHistoryVisible" :materialName="currentMaterialName" :versions="currentVersions" />
|
||
</div>
|
||
</template>
|
||
|
||
<script lang="ts" setup>
|
||
import * as AcceptanceApi from '@/api/project/acceptance'
|
||
import * as ProjectApi from '@/api/project/project'
|
||
import * as AcceptanceMaterialApi from '@/api/project/acceptanceMaterial'
|
||
import * as AcceptanceMaterialDefApi from '@/api/project/acceptanceMaterialDef'
|
||
import * as AcceptanceOpinionApi from '@/api/project/acceptanceOpinion'
|
||
import { useUserStore } from '@/store/modules/user'
|
||
|
||
import AcceptanceHeader from './components/AcceptanceHeader.vue'
|
||
import AuditDialog from './components/AuditDialog.vue'
|
||
import VersionHistoryDialog from './components/VersionHistoryDialog.vue'
|
||
import LiaisonView from './LiaisonView.vue'
|
||
import ReviewerView from './ReviewerView.vue'
|
||
import AdminView from './AdminView.vue'
|
||
import ExpertView from './ExpertView.vue'
|
||
|
||
defineOptions({ name: 'AcceptanceDetail' })
|
||
|
||
const route = useRoute()
|
||
const router = useRouter()
|
||
const message = useMessage()
|
||
|
||
// ==================== 状态 ====================
|
||
const loading = ref(true)
|
||
const acceptance = ref<AcceptanceApi.AcceptanceVO>({} as AcceptanceApi.AcceptanceVO)
|
||
const projectInfo = ref<any>({})
|
||
|
||
const materials = ref<any[]>([]) // 联络人:当前阶段材料
|
||
const preMaterials = ref<any[]>([]) // 审核人/专家/管理员:预验收材料
|
||
const finalMaterials = ref<any[]>([]) // 审核人/专家/管理员:终验材料
|
||
const opinions = ref<any[]>([])
|
||
|
||
const auditDialogVisible = ref(false)
|
||
const versionHistoryVisible = ref(false)
|
||
const currentVersions = ref<any[]>([])
|
||
const currentMaterialName = ref('')
|
||
|
||
// ==================== 角色判断(4角色) ====================
|
||
/**
|
||
* 角色分发优先级:
|
||
* 1. liaison — 项目联络人 (liaisonUserId === currentUserId)
|
||
* 2. admin — 管理员:终验初审(40)、组织会议(45)、会议评审(50)、整改审核(61)
|
||
* 3. expert — 专家:专家复核(62)
|
||
* 4. reviewer — 对口人/组长:预验收阶段 (10, 20) 及其他默认
|
||
*/
|
||
const userRole = computed<'liaison' | 'admin' | 'expert' | 'reviewer'>(() => {
|
||
const userId = useUserStore().getUser.id
|
||
if (projectInfo.value.liaisonUserId === userId) return 'liaison'
|
||
const s = acceptance.value.status
|
||
if (['40', '45', '50', '61'].includes(s)) return 'admin'
|
||
if (s === '62') return 'expert'
|
||
return 'reviewer'
|
||
})
|
||
|
||
// ==================== 操作方法 ====================
|
||
const showVersionHistory = (item: any) => {
|
||
currentMaterialName.value = item.materialName
|
||
currentVersions.value = item.versions || []
|
||
versionHistoryVisible.value = true
|
||
}
|
||
|
||
const getFileUrl = (url: string) => {
|
||
if (!url) return ''
|
||
if (url.startsWith('http')) return url
|
||
return import.meta.env.VITE_BASE_URL + url
|
||
}
|
||
|
||
const handlePreview = (row: any) => {
|
||
const url = getFileUrl(row.fileUrl)
|
||
if (url) window.open(url, '_blank')
|
||
}
|
||
|
||
const handleDownload = (row: any) => {
|
||
const url = getFileUrl(row.fileUrl)
|
||
if (url) {
|
||
const link = document.createElement('a')
|
||
link.href = url
|
||
link.download = row.fileName || row.materialName
|
||
link.click()
|
||
}
|
||
}
|
||
|
||
const handleUploadSuccess = async (response: any, file: any, row: any) => {
|
||
if (response.code === 0) {
|
||
try {
|
||
await AcceptanceMaterialApi.createAcceptanceMaterial({
|
||
acceptanceId: row.acceptanceId,
|
||
materialCode: row.materialCode,
|
||
fileName: file.name,
|
||
fileUrl: response.data,
|
||
fileSize: file.size || 0,
|
||
fileType: file.name.split('.').pop(),
|
||
version: 1,
|
||
remark: ''
|
||
} as any)
|
||
message.success('上传成功')
|
||
getDetail()
|
||
} catch {
|
||
message.error('保存材料记录失败')
|
||
}
|
||
} else {
|
||
message.error('上传失败: ' + response.msg)
|
||
}
|
||
}
|
||
|
||
const handleSubmitMaterial = async () => {
|
||
try {
|
||
const missing = materials.value.filter((m: any) => m.isRequired && !m.fileUrl)
|
||
if (missing.length > 0) {
|
||
message.warning(`请先上传:${missing.map((m: any) => m.materialName).join('、')}`)
|
||
return
|
||
}
|
||
await message.confirm('确定提交材料吗?提交后将进入审核流程。')
|
||
await AcceptanceApi.submitMaterials(acceptance.value.id)
|
||
message.success('提交成功')
|
||
await getDetail()
|
||
} catch {}
|
||
}
|
||
|
||
const handleSubmitRectify = async () => {
|
||
try {
|
||
await message.confirm('确定提交整改吗?')
|
||
if (acceptance.value.acceptanceType === 'PRE') {
|
||
await AcceptanceApi.submitPreRectify(acceptance.value.id)
|
||
} else {
|
||
await AcceptanceApi.submitFinalRectify(acceptance.value.id)
|
||
}
|
||
message.success('提交成功')
|
||
await getDetail()
|
||
} catch {}
|
||
}
|
||
|
||
const handleSubmitFinal = async () => {
|
||
try {
|
||
const missing = materials.value.filter((m: any) => m.isRequired && !m.fileUrl)
|
||
if (missing.length > 0) {
|
||
message.warning(`请先上传终验必填材料:${missing.map((m: any) => m.materialName).join('、')}`)
|
||
return
|
||
}
|
||
await message.confirm('确定提交终验申请吗?')
|
||
await AcceptanceApi.submitFinalAcceptance({ acceptanceId: acceptance.value.id })
|
||
message.success('终验申请提交成功')
|
||
await getDetail()
|
||
} catch {}
|
||
}
|
||
|
||
const handleSubmitRectifyManual = async () => {
|
||
try {
|
||
await message.confirm('确定提交整改说明书吗?')
|
||
await AcceptanceApi.submitRectifyManual(acceptance.value.id)
|
||
message.success('整改说明书提交成功')
|
||
await getDetail()
|
||
} catch {}
|
||
}
|
||
|
||
const handleSubmitExpertReupload = async () => {
|
||
try {
|
||
await message.confirm('确定重新提交终验材料吗?')
|
||
await AcceptanceApi.submitExpertReupload(acceptance.value.id)
|
||
message.success('材料提交成功')
|
||
await getDetail()
|
||
} catch {}
|
||
}
|
||
|
||
/** 跳转到会议管理页 */
|
||
const handleOrganizeMeeting = () => {
|
||
router.push({
|
||
path: '/project/acceptance-meeting',
|
||
query: { acceptanceId: String(acceptance.value.id) }
|
||
})
|
||
}
|
||
|
||
// ==================== 数据加载 ====================
|
||
const getDetail = async () => {
|
||
loading.value = true
|
||
try {
|
||
const id = Number(route.params.id)
|
||
acceptance.value = await AcceptanceApi.getAcceptance(id)
|
||
|
||
if (acceptance.value.projectId) {
|
||
projectInfo.value = await ProjectApi.getProject(acceptance.value.projectId)
|
||
}
|
||
|
||
const uploads = await AcceptanceMaterialApi.getListByAcceptanceId(id) as unknown as any[]
|
||
|
||
const mergeMaterials = (defs: any[]) => {
|
||
return defs.map((def: any) => {
|
||
const allUploads = uploads.filter((u: any) => u.materialCode === def.materialCode)
|
||
const sortedUploads = allUploads.sort((a: any, b: any) => b.id - a.id)
|
||
const upload = sortedUploads[0]
|
||
return {
|
||
...def,
|
||
materialType: def.requiredFlag ? '必填' : '可选',
|
||
isRequired: def.requiredFlag,
|
||
fileUrl: upload?.fileUrl,
|
||
fileName: upload?.fileName,
|
||
fileSize: upload?.fileSize,
|
||
uploadId: upload?.id,
|
||
acceptanceId: id,
|
||
versions: sortedUploads,
|
||
versionCount: sortedUploads.length
|
||
}
|
||
})
|
||
}
|
||
|
||
const currentUserId = useUserStore().getUser.id
|
||
const isLiaison = projectInfo.value.liaisonUserId === currentUserId
|
||
|
||
if (isLiaison) {
|
||
const materialType = acceptance.value.status === '30' ? 'FINAL' : acceptance.value.acceptanceType
|
||
const defs = await AcceptanceMaterialDefApi.getListByType(materialType)
|
||
materials.value = mergeMaterials(defs)
|
||
} else {
|
||
const preDefs = await AcceptanceMaterialDefApi.getListByType('PRE')
|
||
const finalDefs = await AcceptanceMaterialDefApi.getListByType('FINAL')
|
||
preMaterials.value = mergeMaterials(preDefs)
|
||
finalMaterials.value = mergeMaterials(finalDefs)
|
||
}
|
||
|
||
const res = await AcceptanceOpinionApi.getListByAcceptanceId(id)
|
||
opinions.value = res.sort((a: any, b: any) => new Date(b.createTime).getTime() - new Date(a.createTime).getTime())
|
||
} catch (error) {
|
||
console.error(error)
|
||
message.error('获取验收详情失败')
|
||
} finally {
|
||
loading.value = false
|
||
}
|
||
}
|
||
|
||
onMounted(() => getDetail())
|
||
</script>
|