<script setup lang="ts">
|
import { reactive, ref } from 'vue'
|
import { useRouter } from 'vue-router'
|
import { ElMessage } from 'element-plus'
|
import type { FormInstance, FormRules } from 'element-plus'
|
import { createTalent, getTalentServiceStaff, updateTalentServiceStaff } from '../api/admin'
|
import type { AdminStaff, TalentPayload } from '../types/admin'
|
|
const router = useRouter()
|
const saving = ref(false)
|
const formRef = ref<FormInstance>()
|
const cardFormRef = ref<FormInstance>()
|
const staff = ref<AdminStaff[]>([])
|
const talentLevelConfirmed = ref(false)
|
const cardDialogVisible = ref(false)
|
const pendingTalentLevel = ref('')
|
const pendingSourceAreaType = ref('LOCAL')
|
const pendingTalentCardNo = ref('')
|
const cardForm = reactive({ talentLevel: '', talentCardNo: '' })
|
|
const levels = ['卓越人才卡', '杰出人才卡', '菁英人才卡', '普通游客']
|
|
const form = reactive<TalentPayload & { serviceStaff?: number | string }>({
|
name: '',
|
mobile: '',
|
idCardNo: '',
|
gender: undefined,
|
workUnit: '',
|
talentLevel: '',
|
talentCardNo: '',
|
sourceAreaType: 'LOCAL',
|
workAddress: '',
|
contactAddress: '',
|
serviceStaff: undefined,
|
})
|
|
const rules: FormRules = {
|
mobile: [
|
{ required: true, message: '请输入手机号码', trigger: 'blur' },
|
{ pattern: /^1\d{10}$/, message: '请输入11位数字手机号码', trigger: 'blur' },
|
],
|
name: [{ required: true, message: '请输入持卡人姓名', trigger: 'blur' }],
|
idCardNo: [
|
{ required: true, message: '请输入身份证号', trigger: 'blur' },
|
{ pattern: /^[0-9A-Z]{18}$/, message: '身份证号须为18位数字或大写英文字母', trigger: 'blur' },
|
],
|
contactAddress: [{ required: true, message: '请输入联系地址', trigger: 'blur' }],
|
workUnit: [{ required: true, message: '请输入工作单位', trigger: 'blur' }],
|
gender: [{ required: true, message: '请选择性别', trigger: 'change' }],
|
talentCardNo: [{
|
validator: (_rule, value, callback) => talentLevelConfirmed.value && String(value || '').trim()
|
? callback()
|
: callback(new Error('请先确认人才等级、来源地和人才卡号')),
|
trigger: 'change',
|
}],
|
serviceStaff: [{ required: true, message: '请选择专属服务人员', trigger: 'change' }],
|
}
|
const cardRules: FormRules = {
|
talentLevel: [{ required: true, message: '请选择人才等级', trigger: 'change' }],
|
talentCardNo: [{ required: true, message: '请输入人才卡号', trigger: 'blur' }],
|
}
|
|
function normalizeIdCardNo(value: string) {
|
form.idCardNo = value.toUpperCase().replace(/[^0-9A-Z]/g, '').slice(0, 18)
|
}
|
|
function normalizeMobile(value: string) {
|
form.mobile = value.replace(/\D/g, '').slice(0, 11)
|
}
|
|
function openCardDialog() {
|
cardForm.talentLevel = form.talentLevel || ''
|
cardForm.talentCardNo = form.talentCardNo || ''
|
pendingTalentLevel.value = cardForm.talentLevel
|
pendingSourceAreaType.value = form.sourceAreaType || 'LOCAL'
|
pendingTalentCardNo.value = cardForm.talentCardNo
|
cardDialogVisible.value = true
|
}
|
|
function normalizeTalentCardNo(value: string) {
|
cardForm.talentCardNo = value.toUpperCase().replace(/[^0-9A-Z-]/g, '').slice(0, 64)
|
pendingTalentCardNo.value = cardForm.talentCardNo
|
}
|
|
async function confirmTalentLevel() {
|
const valid = await cardFormRef.value?.validate().catch(() => false)
|
if (!valid) return
|
form.talentLevel = cardForm.talentLevel
|
form.sourceAreaType = pendingSourceAreaType.value
|
form.talentCardNo = cardForm.talentCardNo
|
talentLevelConfirmed.value = true
|
cardDialogVisible.value = false
|
formRef.value?.validateField('talentCardNo').catch(() => undefined)
|
ElMessage.success('人才等级已确认')
|
}
|
|
async function submitCreate() {
|
const valid = await formRef.value?.validate().catch(() => false)
|
if (!valid || !talentLevelConfirmed.value || !form.talentCardNo) {
|
if (valid && (!talentLevelConfirmed.value || !form.talentCardNo)) {
|
ElMessage.warning('请先确认人才等级、来源地和人才卡号')
|
}
|
return
|
}
|
|
saving.value = true
|
try {
|
// 后端仍保留 workAddress 历史字段;新建时同步写入,保证新旧端均可回显。
|
const { serviceStaff, ...talentForm } = form
|
const talent = await createTalent({ ...talentForm, workAddress: talentForm.workUnit })
|
await updateTalentServiceStaff(talent.id, serviceStaff)
|
ElMessage.success('持卡人已保存')
|
router.push('/talents')
|
} finally {
|
saving.value = false
|
}
|
}
|
|
async function loadServiceStaff() {
|
staff.value = await getTalentServiceStaff()
|
}
|
|
loadServiceStaff()
|
</script>
|
|
<template>
|
<div class="talent-detail-page talent-create-page">
|
<el-form ref="formRef" :model="form" :rules="rules" label-width="104px">
|
<section class="detail-block user-summary create-mobile-summary">
|
<el-form-item label="手机号码:" prop="mobile" required>
|
<el-input
|
v-model="form.mobile"
|
placeholder="请输入"
|
inputmode="numeric"
|
maxlength="11"
|
@input="normalizeMobile"
|
/>
|
</el-form-item>
|
</section>
|
|
<section class="detail-block">
|
<h3>持卡人认证</h3>
|
<div class="identity-edit-card">
|
<div class="identity-edit-form">
|
<el-form-item label="持卡人姓名:" prop="name" required>
|
<el-input v-model="form.name" placeholder="请输入" />
|
</el-form-item>
|
<el-form-item label="身份证号:" prop="idCardNo" required>
|
<el-input
|
v-model="form.idCardNo"
|
placeholder="请输入18位身份证号"
|
maxlength="18"
|
@input="normalizeIdCardNo"
|
/>
|
</el-form-item>
|
<el-form-item label="联系方式:" prop="mobile" required>
|
<el-input
|
v-model="form.mobile"
|
placeholder="请输入"
|
inputmode="numeric"
|
maxlength="11"
|
@input="normalizeMobile"
|
/>
|
</el-form-item>
|
<el-form-item label="联系地址:" prop="contactAddress" required>
|
<el-input v-model="form.contactAddress" placeholder="请输入" />
|
</el-form-item>
|
<el-form-item label="工作单位:" prop="workUnit" required>
|
<el-input v-model="form.workUnit" placeholder="请输入工作单位" />
|
</el-form-item>
|
<el-form-item label="性别:" prop="gender" required>
|
<el-select v-model="form.gender" placeholder="选择">
|
<el-option label="男" :value="1" />
|
<el-option label="女" :value="2" />
|
</el-select>
|
</el-form-item>
|
</div>
|
</div>
|
</section>
|
|
<section class="detail-block">
|
<h3>人才卡管理</h3>
|
<el-form-item prop="talentCardNo" required class="create-card-form-item" label-width="0">
|
<div class="card-manage-panel">
|
<div class="talent-card-stack">
|
<button type="button" class="talent-card-preview create-card-preview" @click="openCardDialog">
|
{{ form.talentLevel || '请选择' }}
|
</button>
|
<strong v-if="form.talentCardNo" class="card-number">卡片编号 {{ form.talentCardNo }}</strong>
|
</div>
|
<el-button type="primary" @click="openCardDialog">
|
{{ talentLevelConfirmed ? '修改人才等级' : '选择人才等级' }}
|
</el-button>
|
</div>
|
</el-form-item>
|
</section>
|
|
<section class="detail-block">
|
<h3>专属服务人员</h3>
|
<el-form-item prop="serviceStaff" class="service-staff-form-item" required label-width="0">
|
<div class="service-staff-panel">
|
<el-select v-model="form.serviceStaff" clearable placeholder="请选择专属服务人员">
|
<el-option
|
v-for="item in staff"
|
:key="item.id"
|
:label="item.displayName"
|
:value="item.id"
|
/>
|
</el-select>
|
</div>
|
</el-form-item>
|
<el-button type="primary" class="detail-save-button" :loading="saving" @click="submitCreate">保存</el-button>
|
</section>
|
|
</el-form>
|
|
<el-dialog
|
v-model="cardDialogVisible"
|
title="选择人才等级"
|
width="650px"
|
class="talent-level-dialog"
|
destroy-on-close
|
:close-on-click-modal="false"
|
>
|
<el-form ref="cardFormRef" :model="cardForm" :rules="cardRules" label-width="100px">
|
<el-form-item label="人才等级" prop="talentLevel" required>
|
<div class="talent-level-options">
|
<button
|
v-for="level in levels"
|
:key="level"
|
type="button"
|
:class="{ selected: pendingTalentLevel === level }"
|
@click="cardForm.talentLevel = pendingTalentLevel = level"
|
>
|
<span class="talent-level-radio"></span>
|
<strong>{{ level }}</strong>
|
</button>
|
</div>
|
</el-form-item>
|
|
<el-radio-group v-model="pendingSourceAreaType" class="talent-area-options">
|
<el-radio value="LOCAL">本地</el-radio>
|
<el-radio value="OUTSIDE">外地</el-radio>
|
</el-radio-group>
|
|
<div class="talent-card-number-field">
|
<el-form-item label="人才卡号" prop="talentCardNo" required>
|
<el-input
|
v-model="cardForm.talentCardNo"
|
maxlength="64"
|
placeholder="请输入"
|
@input="normalizeTalentCardNo"
|
/>
|
</el-form-item>
|
</div>
|
|
<el-button type="primary" class="talent-level-confirm" @click="confirmTalentLevel">确认人才等级</el-button>
|
</el-form>
|
</el-dialog>
|
</div>
|
</template>
|