admin-vue3/src/views/project/acceptance/detail/index.vue

304 lines
10 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<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>