<script setup lang="ts">
|
import { computed, onMounted, ref } from 'vue'
|
import { useRoute } from 'vue-router'
|
import { ElMessage } from 'element-plus'
|
import type { FormInstance, FormRules } from 'element-plus'
|
import {
|
getTalentServiceStaff,
|
getTalentCards,
|
getTalentDetail,
|
updateTalentServiceStaff,
|
updateTalentCertification,
|
updateTalentLevel,
|
updateTalentMobile,
|
} from '../api/admin'
|
import type { AdminStaff, Talent, TalentCard } from '../types/admin'
|
|
const route = useRoute()
|
const loading = ref(false)
|
const talent = ref<Talent>()
|
const cards = ref<TalentCard[]>([])
|
const serviceStaff = ref<number | string | undefined>()
|
const staff = ref<AdminStaff[]>([])
|
const serviceStaffSaving = ref(false)
|
const mobileDialogVisible = ref(false)
|
const mobileSaving = ref(false)
|
const newMobile = ref('')
|
const mobileError = ref('')
|
const certificationDialogVisible = ref(false)
|
const certificationSaving = ref(false)
|
const certificationError = ref('')
|
const certificationForm = ref({
|
name: '',
|
idCardNo: '',
|
gender: undefined as number | undefined,
|
workUnit: '',
|
workAddress: '',
|
contactAddress: '',
|
})
|
const levelDialogVisible = ref(false)
|
const levelSaving = ref(false)
|
const pendingTalentLevel = ref('')
|
const levelFormRef = ref<FormInstance>()
|
const levelRules: FormRules = {
|
talentLevel: [{ required: true, message: '请选择人才等级', trigger: 'change' }],
|
}
|
const talentLevels = ['卓越人才卡', '杰出人才卡', '菁英人才卡', '普通游客']
|
|
const primaryCard = computed(() => cards.value[0])
|
|
function genderText(gender?: number) {
|
if (gender === 2) return '女'
|
if (gender === 1) return '男'
|
return '-'
|
}
|
|
function mask(value?: string, start = 3, end = 4) {
|
if (!value) return '-'
|
if (value.length <= start + end) return value
|
return `${value.slice(0, start)}****${value.slice(-end)}`
|
}
|
|
function formatDateTime(value?: string) {
|
return value ? value.replace('T', ' ') : '-'
|
}
|
|
function cardLevel() {
|
return primaryCard.value?.cardLevel || talent.value?.talentLevel || '精英卡'
|
}
|
|
function cardNo() {
|
return primaryCard.value?.cardNo || talent.value?.talentCardNo || '-'
|
}
|
|
function openMobileDialog() {
|
newMobile.value = talent.value?.mobile || ''
|
mobileError.value = ''
|
mobileDialogVisible.value = true
|
}
|
|
function normalizeMobile(value: string) {
|
newMobile.value = value.replace(/\D/g, '').slice(0, 11)
|
if (mobileError.value) mobileError.value = ''
|
}
|
|
async function submitMobileUpdate() {
|
if (!/^1\d{10}$/.test(newMobile.value)) {
|
mobileError.value = '请输入正确的11位手机号'
|
return
|
}
|
if (!talent.value?.id) return
|
mobileSaving.value = true
|
try {
|
talent.value = await updateTalentMobile(talent.value.id, newMobile.value)
|
mobileDialogVisible.value = false
|
ElMessage.success('手机号已更新')
|
} finally {
|
mobileSaving.value = false
|
}
|
}
|
|
function openCertificationDialog() {
|
certificationForm.value = {
|
name: talent.value?.name || '',
|
idCardNo: talent.value?.idCardNo || '',
|
gender: talent.value?.gender,
|
workUnit: talent.value?.workUnit || '',
|
workAddress: talent.value?.workAddress || '',
|
contactAddress: talent.value?.contactAddress || '',
|
}
|
certificationError.value = ''
|
certificationDialogVisible.value = true
|
}
|
|
function normalizeIdCard(value: string) {
|
certificationForm.value.idCardNo = value.toUpperCase().replace(/[^0-9A-Z]/g, '').slice(0, 18)
|
if (certificationError.value) certificationError.value = ''
|
}
|
|
async function submitCertificationUpdate() {
|
const form = certificationForm.value
|
form.name = form.name.trim()
|
if (!form.name) {
|
certificationError.value = '请输入持卡人姓名'
|
return
|
}
|
if (form.idCardNo && !/^[0-9A-Z]{18}$/.test(form.idCardNo)) {
|
certificationError.value = '请输入18位数字或大写英文字母的身份证号'
|
return
|
}
|
if (!talent.value?.id) return
|
certificationSaving.value = true
|
try {
|
talent.value = await updateTalentCertification(talent.value.id, form)
|
certificationDialogVisible.value = false
|
ElMessage.success('认证信息已更新')
|
} finally {
|
certificationSaving.value = false
|
}
|
}
|
|
function openLevelDialog() {
|
pendingTalentLevel.value = cardLevel()
|
levelDialogVisible.value = true
|
}
|
|
async function submitLevelUpdate() {
|
const valid = await levelFormRef.value?.validate().catch(() => false)
|
if (!valid) return
|
if (!talent.value?.id) return
|
levelSaving.value = true
|
try {
|
talent.value = await updateTalentLevel(talent.value.id, pendingTalentLevel.value)
|
const cardResult = await getTalentCards(talent.value.id)
|
cards.value = cardResult.records
|
levelDialogVisible.value = false
|
ElMessage.success('人才等级已更新')
|
} finally {
|
levelSaving.value = false
|
}
|
}
|
|
async function saveServiceStaff() {
|
if (!talent.value?.id) return
|
serviceStaffSaving.value = true
|
try {
|
talent.value = await updateTalentServiceStaff(talent.value.id, serviceStaff.value)
|
ElMessage.success(serviceStaff.value ? '专属服务人员已保存' : '已取消专属服务人员绑定')
|
} finally {
|
serviceStaffSaving.value = false
|
}
|
}
|
|
async function loadData() {
|
loading.value = true
|
try {
|
const id = String(route.params.id)
|
const [detail, cardResult, advisorResult] = await Promise.all([
|
getTalentDetail(id),
|
getTalentCards(id),
|
getTalentServiceStaff(),
|
])
|
talent.value = detail
|
cards.value = cardResult.records
|
staff.value = advisorResult
|
serviceStaff.value = talent.value.serviceStaffId
|
} finally {
|
loading.value = false
|
}
|
}
|
|
onMounted(loadData)
|
</script>
|
|
<template>
|
<div v-loading="loading" class="talent-detail-page">
|
<section class="detail-block user-summary">
|
<span>用户ID:</span>
|
<strong>{{ talent?.id || '-' }}</strong>
|
<span>手机号码:</span>
|
<strong>{{ talent?.mobile || '-' }}</strong>
|
<el-button link type="primary" @click="openMobileDialog">修改</el-button>
|
<span>注册时间:</span>
|
<strong>{{ formatDateTime(talent?.registerTime) }}</strong>
|
</section>
|
|
<section class="detail-block">
|
<div class="detail-section-title">
|
<h3>持卡人认证</h3>
|
<el-button link type="primary" @click="openCertificationDialog">修改认证信息</el-button>
|
</div>
|
<div class="identity-card">
|
<div>
|
<span>持卡人姓名:</span>
|
<strong>{{ talent?.name || '-' }}</strong>
|
</div>
|
<div>
|
<span>性别:</span>
|
<strong>{{ genderText(talent?.gender) }}</strong>
|
</div>
|
<div>
|
<span>联系方式:</span>
|
<strong>{{ mask(talent?.mobile) }}</strong>
|
</div>
|
<div>
|
<span>身份证号:</span>
|
<strong>{{ mask(talent?.idCardNo, 6, 4) }}</strong>
|
</div>
|
<div>
|
<span>工作地址:</span>
|
<strong>{{ talent?.workAddress || talent?.workUnit || '-' }}</strong>
|
</div>
|
<div>
|
<span>联系地址:</span>
|
<strong>{{ talent?.contactAddress || '-' }}</strong>
|
</div>
|
</div>
|
</section>
|
|
<section class="detail-block">
|
<h3>人才卡管理</h3>
|
<div class="card-manage-panel">
|
<div class="talent-card-stack">
|
<div class="talent-card-preview">
|
<span>{{ cardLevel() }}</span>
|
</div>
|
<strong class="card-number">卡片编号 {{ cardNo() }}</strong>
|
</div>
|
<el-button type="primary" @click="openLevelDialog">修改人才等级</el-button>
|
</div>
|
</section>
|
|
<section class="detail-block">
|
<h3>专属服务人员</h3>
|
<div class="service-staff-panel">
|
<el-select v-model="serviceStaff" clearable placeholder="请选择专属服务人员">
|
<el-option
|
v-for="item in staff"
|
:key="item.id"
|
:label="item.displayName"
|
:value="item.id"
|
/>
|
</el-select>
|
</div>
|
<el-button type="primary" class="detail-save-button" :loading="serviceStaffSaving" @click="saveServiceStaff">保存</el-button>
|
</section>
|
|
<el-dialog v-model="mobileDialogVisible" title="修改手机号" width="420px" destroy-on-close :close-on-click-modal="false">
|
<div class="talent-mobile-dialog">
|
<label for="talent-mobile-input">手机号</label>
|
<el-input
|
id="talent-mobile-input"
|
v-model="newMobile"
|
:class="{ 'is-error': mobileError }"
|
maxlength="11"
|
inputmode="numeric"
|
placeholder="请输入11位手机号"
|
@input="normalizeMobile"
|
@keyup.enter="submitMobileUpdate"
|
/>
|
<p v-if="mobileError">{{ mobileError }}</p>
|
</div>
|
<template #footer>
|
<el-button @click="mobileDialogVisible = false">取消</el-button>
|
<el-button type="primary" :loading="mobileSaving" @click="submitMobileUpdate">保存</el-button>
|
</template>
|
</el-dialog>
|
|
<el-dialog v-model="certificationDialogVisible" title="修改认证信息" width="560px" destroy-on-close :close-on-click-modal="false">
|
<div class="talent-certification-dialog">
|
<div class="talent-certification-grid">
|
<label>
|
<span>持卡人姓名</span>
|
<el-input v-model="certificationForm.name" maxlength="64" placeholder="请输入持卡人姓名" />
|
</label>
|
<label>
|
<span>性别</span>
|
<el-select v-model="certificationForm.gender" clearable placeholder="请选择性别">
|
<el-option label="男" :value="1" />
|
<el-option label="女" :value="2" />
|
</el-select>
|
</label>
|
<label>
|
<span>身份证号</span>
|
<el-input
|
v-model="certificationForm.idCardNo"
|
:class="{ 'is-error': certificationError.includes('身份证') }"
|
maxlength="18"
|
placeholder="请输入18位身份证号"
|
@input="normalizeIdCard"
|
/>
|
</label>
|
<label>
|
<span>工作单位</span>
|
<el-input v-model="certificationForm.workUnit" maxlength="128" placeholder="请输入工作单位" />
|
</label>
|
<label>
|
<span>工作地址</span>
|
<el-input v-model="certificationForm.workAddress" maxlength="255" placeholder="请输入工作地址" />
|
</label>
|
<label>
|
<span>联系地址</span>
|
<el-input v-model="certificationForm.contactAddress" maxlength="255" placeholder="请输入联系地址" />
|
</label>
|
</div>
|
<p v-if="certificationError" class="talent-certification-error">{{ certificationError }}</p>
|
</div>
|
<template #footer>
|
<el-button @click="certificationDialogVisible = false">取消</el-button>
|
<el-button type="primary" :loading="certificationSaving" @click="submitCertificationUpdate">保存</el-button>
|
</template>
|
</el-dialog>
|
|
<el-dialog
|
v-model="levelDialogVisible"
|
title="修改人才等级"
|
width="650px"
|
class="talent-level-dialog"
|
destroy-on-close
|
:close-on-click-modal="false"
|
>
|
<el-form ref="levelFormRef" :model="{ talentLevel: pendingTalentLevel }" :rules="levelRules" label-width="88px">
|
<el-form-item label="人才等级" prop="talentLevel">
|
<div class="talent-level-options">
|
<button
|
v-for="level in talentLevels"
|
:key="level"
|
type="button"
|
:class="{ selected: pendingTalentLevel === level }"
|
@click="pendingTalentLevel = level"
|
>
|
<span class="talent-level-radio"></span>
|
<strong>{{ level }}</strong>
|
</button>
|
</div>
|
</el-form-item>
|
</el-form>
|
<template #footer>
|
<el-button @click="levelDialogVisible = false">取消</el-button>
|
<el-button type="primary" :loading="levelSaving" @click="submitLevelUpdate">确认修改</el-button>
|
</template>
|
</el-dialog>
|
</div>
|
</template>
|