yeg
2026-09-18 70ea0911ce7519ca802266078624e9fed3559a2c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
<script setup lang="ts">
import { computed, onMounted, ref } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { ElMessage, ElMessageBox } from 'element-plus'
import { auditUpgradeApplication, getUpgradeApplicationDetail } from '../api/admin'
import type { UpgradeApplication, UpgradeApplicationHonor } from '../types/admin'
 
const route = useRoute()
const router = useRouter()
const detail = ref<UpgradeApplication>()
const loading = ref(false)
const processing = ref(false)
const pending = computed(() => detail.value?.status === '待审核')
 
function text(value: unknown) {
  return value === undefined || value === null || value === '' ? '-' : String(value)
}
function genderText(value: unknown) {
  return Number(value) === 1 ? '男' : Number(value) === 2 ? '女' : '-'
}
function formatDate(value: unknown) {
  return typeof value === 'string' ? value.replace('T', ' ').slice(0, 19) : text(value)
}
function imageUrl(honor: UpgradeApplicationHonor) {
  const url = honor.proofUrl || honor.proofFileUrl || honor.proofFileId
  return typeof url === 'string' ? url : ''
}
function resolveImageUrl(url: string) {
  if (!url || /^https?:\/\//i.test(url)) return url
  const base = import.meta.env.VITE_API_BASE_URL || ''
  return base ? `${base.replace(/\/$/, '')}${url}` : url
}
 
async function load() {
  loading.value = true
  try {
    detail.value = await getUpgradeApplicationDetail(String(route.params.id))
  } finally {
    loading.value = false
  }
}
 
async function approve() {
  if (!detail.value) return
  const target = detail.value.targetLevel ? `「${detail.value.targetLevel}」` : '申请等级'
  await ElMessageBox.confirm(
    `确认通过该升级申请?系统不会自动修改持卡人等级,通过后请到「持卡人管理」手动把该持卡人等级改为${target}。`,
    '审核通过',
    { type: 'warning' },
  )
  await process('已通过')
}
 
async function reject() {
  if (!detail.value) return
  const result = await ElMessageBox.prompt('请输入驳回原因', '审核不通过', { inputValidator: value => !!value.trim(), inputErrorMessage: '请输入驳回原因' })
  await process('已驳回', result.value)
}
 
async function process(status: string, remark = '') {
  if (!detail.value) return
  processing.value = true
  try {
    detail.value = await auditUpgradeApplication(detail.value.id, status, remark)
    ElMessage.success(
      status === '已通过'
        ? '升级申请已通过,请到「持卡人管理」手动修改该持卡人的等级'
        : '升级申请已驳回',
    )
  } finally {
    processing.value = false
  }
}
 
onMounted(load)
</script>
 
<template>
  <div v-loading="loading" class="upgrade-application-detail-page">
    <div class="detail-toolbar"><el-button @click="router.back">返回</el-button><span>升级申请详情</span></div>
    <section v-if="detail" class="upgrade-detail-panel">
      <h3>持卡人信息</h3>
      <div class="upgrade-detail-grid">
        <div>持卡人姓名:<strong>{{ text(detail.talentName) }}</strong></div>
        <div>身份证号:<strong>{{ text(detail.talentIdCardNo) }}</strong></div>
        <div>联系方式:<strong>{{ text(detail.talentMobile) }}</strong></div>
        <div>联系地址:<strong>{{ text(detail.contactAddress) }}</strong></div>
        <div>工作地址:<strong>{{ text(detail.workAddress) }}</strong></div>
        <div>性别:<strong>{{ genderText(detail.gender) }}</strong></div>
        <div>人才等级:<strong>{{ text(detail.currentLevel) }}</strong></div>
        <div>人才卡号:<strong>{{ text(detail.talentCardNo) }}</strong></div>
      </div>
    </section>
    <section v-if="detail" class="upgrade-detail-panel">
      <h3>申请详情</h3>
      <div class="upgrade-detail-grid">
        <div>申请编号:<strong>{{ text(detail.applicationNo) }}</strong></div>
        <div>申请等级:<strong>{{ text(detail.targetLevel) }}</strong></div>
        <div>申请时间:<strong>{{ formatDate(detail.applyTime) }}</strong></div>
        <div class="full-line">申请理由:<strong>{{ text(detail.applyReason) }}</strong></div>
      </div>
      <div v-for="honor in (detail.honors || [])" :key="honor.id" class="upgrade-detail-grid honor-block">
        <div>新获得的荣誉称号/大奖项:<strong>{{ text(honor.honorName) }}</strong></div>
        <div>获得时间:<strong>{{ text(honor.obtainedDate) }}</strong></div>
        <div class="honor-images full-line">
          <el-image
            v-if="imageUrl(honor)"
            :src="resolveImageUrl(imageUrl(honor))"
            :preview-src-list="[resolveImageUrl(imageUrl(honor))]"
            preview-teleported
            fit="cover"
            class="honor-image"
            alt="荣誉材料"
          />
          <span v-else>暂无材料图片</span>
        </div>
      </div>
    </section>
    <section v-if="detail" class="upgrade-detail-actions">
      <template v-if="pending">
        <el-button v-permission="'admin.upgrade-applications.audit'" type="primary" :loading="processing" @click="approve">处理</el-button>
        <el-button v-permission="'admin.upgrade-applications.audit'" type="danger" :loading="processing" @click="reject">审核不通过</el-button>
      </template>
      <span v-else>审核结果:{{ text(detail.status) }}<template v-if="detail.auditRemark">({{ detail.auditRemark }})</template></span>
    </section>
  </div>
</template>
 
<style scoped>
.upgrade-application-detail-page { padding: 20px; }
.detail-toolbar { display: flex; align-items: center; gap: 16px; margin-bottom: 16px; font-size: 18px; font-weight: 600; }
.upgrade-detail-panel { padding: 18px 20px; margin-bottom: 14px; background: #fff; border: 1px solid #ebeef5; }
.upgrade-detail-panel h3 { margin: 0 0 16px; font-size: 16px; }
.upgrade-detail-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 18px 36px; padding: 18px; background: #f5f6f8; color: #606266; }
.upgrade-detail-grid strong { color: #303133; font-weight: 500; }
.full-line { grid-column: 1 / -1; }
.honor-block { margin-top: 14px; }
.honor-images { display: flex; gap: 20px; align-items: flex-start; }
.honor-image { width: 150px; height: 110px; background: #e9eaec; cursor: zoom-in; }
.honor-images span { display: flex; align-items: center; justify-content: center; width: 150px; height: 110px; color: #909399; background: #e9eaec; }
.upgrade-detail-actions { display: flex; align-items: center; gap: 12px; padding: 18px 0; }
@media (max-width: 800px) { .upgrade-detail-grid { grid-template-columns: 1fr; } .full-line { grid-column: auto; } }
</style>