新增员工确认信息实名认证相关信息板块
This commit is contained in:
@@ -977,7 +977,13 @@ export const onboardingPublicApi = {
|
|||||||
confirmContract(params: { token: string; taskContractId: number }) {
|
confirmContract(params: { token: string; taskContractId: number }) {
|
||||||
return publicHttp.post("confirm-contract", params);
|
return publicHttp.post("confirm-contract", params);
|
||||||
},
|
},
|
||||||
submit(params: { token: string }) {
|
submit(params: {
|
||||||
|
token: string;
|
||||||
|
identityInformationConfirmed: boolean;
|
||||||
|
confirmedEmployeeName: string;
|
||||||
|
confirmedIdCardNumber: string;
|
||||||
|
confirmedPhone: string;
|
||||||
|
}) {
|
||||||
return publicHttp.post("submit", params);
|
return publicHttp.post("submit", params);
|
||||||
},
|
},
|
||||||
prepareContract(params: { token: string; taskContractId: number }) {
|
prepareContract(params: { token: string; taskContractId: number }) {
|
||||||
|
|||||||
@@ -635,6 +635,53 @@
|
|||||||
|
|
||||||
<!-- 步骤 4: 合同查阅与签署 -->
|
<!-- 步骤 4: 合同查阅与签署 -->
|
||||||
<div v-show="currentStep === 3" class="step-content">
|
<div v-show="currentStep === 3" class="step-content">
|
||||||
|
<div v-if="canEdit" id="identity-confirmation-card" class="identity-confirmation-card">
|
||||||
|
<div class="identity-confirmation-heading">
|
||||||
|
<div>
|
||||||
|
<h3 class="identity-confirmation-title">实名认证信息二次确认</h3>
|
||||||
|
<p class="identity-confirmation-desc">
|
||||||
|
以下三项将直接用于电子签约实名认证,请按身份证及本人实名手机号逐项核对。
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<el-tag type="warning" effect="plain">必须全部确认</el-tag>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<el-alert
|
||||||
|
title="任意一项填写错误都可能导致实名认证失败,并需要退回后重新发起流程。"
|
||||||
|
type="warning"
|
||||||
|
show-icon
|
||||||
|
:closable="false"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div class="identity-confirmation-list">
|
||||||
|
<div v-for="field in identityFields" :key="field.key" class="identity-confirmation-item">
|
||||||
|
<div class="identity-field-row">
|
||||||
|
<span class="identity-field-label">{{ field.label }}</span>
|
||||||
|
<span class="identity-field-value">{{ identityDisplayValue(field.key) }}</span>
|
||||||
|
</div>
|
||||||
|
<el-checkbox
|
||||||
|
:model-value="isIdentityFieldConfirmed(field.key)"
|
||||||
|
class="identity-confirmation-checkbox"
|
||||||
|
@change="setIdentityFieldConfirmation(field.key, $event)"
|
||||||
|
>
|
||||||
|
{{ field.confirmText }}
|
||||||
|
</el-checkbox>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="identity-confirmation-footer">
|
||||||
|
<span
|
||||||
|
class="identity-confirmation-progress"
|
||||||
|
:class="{ 'is-complete': isIdentityConfirmationComplete }"
|
||||||
|
>
|
||||||
|
已确认 {{ confirmedIdentityFieldCount }}/{{ identityFields.length }} 项
|
||||||
|
</span>
|
||||||
|
<el-button type="primary" plain @click="returnToIdentityInformation">
|
||||||
|
返回第一步修改实名信息
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="section-header">
|
<div class="section-header">
|
||||||
<h3 class="section-title">入职签署材料查阅与确认</h3>
|
<h3 class="section-title">入职签署材料查阅与确认</h3>
|
||||||
<p class="section-desc">系统已为您代入真实登记信息生成专属签署材料,请认真查阅条款并勾选确认</p>
|
<p class="section-desc">系统已为您代入真实登记信息生成专属签署材料,请认真查阅条款并勾选确认</p>
|
||||||
@@ -742,7 +789,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, nextTick, onBeforeUnmount, onMounted, reactive, ref } from "vue";
|
import { computed, nextTick, onBeforeUnmount, onMounted, reactive, ref, watch } from "vue";
|
||||||
import { useRoute } from "vue-router";
|
import { useRoute } from "vue-router";
|
||||||
import { ElMessage, ElMessageBox, FormInstance, FormRules } from "element-plus";
|
import { ElMessage, ElMessageBox, FormInstance, FormRules } from "element-plus";
|
||||||
import {
|
import {
|
||||||
@@ -819,6 +866,85 @@ const form = reactive<any>({
|
|||||||
otherRemarks: ""
|
otherRemarks: ""
|
||||||
});
|
});
|
||||||
|
|
||||||
|
type IdentityField = "employeeName" | "idCardNumber" | "phone";
|
||||||
|
|
||||||
|
const identityFields: Array<{
|
||||||
|
key: IdentityField;
|
||||||
|
label: string;
|
||||||
|
confirmText: string;
|
||||||
|
}> = [
|
||||||
|
{ key: "employeeName", label: "真实姓名", confirmText: "我已核对姓名,与本人身份证一致" },
|
||||||
|
{ key: "idCardNumber", label: "身份证号码", confirmText: "我已核对身份证号码完整且无误" },
|
||||||
|
{ key: "phone", label: "手机号码", confirmText: "我已核对手机号,可用于本人实名认证" }
|
||||||
|
];
|
||||||
|
|
||||||
|
const identityConfirmations = reactive<Record<IdentityField, boolean>>({
|
||||||
|
employeeName: false,
|
||||||
|
idCardNumber: false,
|
||||||
|
phone: false
|
||||||
|
});
|
||||||
|
const identityConfirmedValues = reactive<Record<IdentityField, string>>({
|
||||||
|
employeeName: "",
|
||||||
|
idCardNumber: "",
|
||||||
|
phone: ""
|
||||||
|
});
|
||||||
|
|
||||||
|
function normalizeIdentityValue(field: IdentityField, value: unknown) {
|
||||||
|
const normalized = String(value ?? "").trim();
|
||||||
|
return field === "idCardNumber" ? normalized.toUpperCase() : normalized;
|
||||||
|
}
|
||||||
|
|
||||||
|
function identityDisplayValue(field: IdentityField) {
|
||||||
|
return normalizeIdentityValue(field, form[field]) || "未填写";
|
||||||
|
}
|
||||||
|
|
||||||
|
function isIdentityFieldConfirmed(field: IdentityField) {
|
||||||
|
return identityConfirmations[field]
|
||||||
|
&& identityConfirmedValues[field] === normalizeIdentityValue(field, form[field]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function setIdentityFieldConfirmation(field: IdentityField, value: unknown) {
|
||||||
|
const checked = Boolean(value);
|
||||||
|
identityConfirmations[field] = checked;
|
||||||
|
identityConfirmedValues[field] = checked
|
||||||
|
? normalizeIdentityValue(field, form[field])
|
||||||
|
: "";
|
||||||
|
}
|
||||||
|
|
||||||
|
function resetIdentityConfirmations() {
|
||||||
|
identityFields.forEach(({ key }) => {
|
||||||
|
identityConfirmations[key] = false;
|
||||||
|
identityConfirmedValues[key] = "";
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const confirmedIdentityFieldCount = computed(() =>
|
||||||
|
identityFields.filter(({ key }) => isIdentityFieldConfirmed(key)).length
|
||||||
|
);
|
||||||
|
const isIdentityConfirmationComplete = computed(() =>
|
||||||
|
confirmedIdentityFieldCount.value === identityFields.length
|
||||||
|
);
|
||||||
|
|
||||||
|
function returnToIdentityInformation() {
|
||||||
|
resetIdentityConfirmations();
|
||||||
|
currentStep.value = 0;
|
||||||
|
window.scrollTo({ top: 0, behavior: "smooth" });
|
||||||
|
nextTick(() => regFormRef.value?.scrollToField("employeeName"));
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => identityFields.map(({ key }) => normalizeIdentityValue(key, form[key])),
|
||||||
|
values => {
|
||||||
|
identityFields.forEach(({ key }, index) => {
|
||||||
|
if (identityConfirmations[key] && identityConfirmedValues[key] !== values[index]) {
|
||||||
|
identityConfirmations[key] = false;
|
||||||
|
identityConfirmedValues[key] = "";
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
{ flush: "sync" }
|
||||||
|
);
|
||||||
|
|
||||||
// 多条动态经历
|
// 多条动态经历
|
||||||
const educationHistory = ref<{ timeRange: string; location: string; content: string; result: string }[]>([]);
|
const educationHistory = ref<{ timeRange: string; location: string; content: string; result: string }[]>([]);
|
||||||
const workHistory = ref<{ timeRange: string; companyName: string; salary: string; position: string; leaveReason: string }[]>([]);
|
const workHistory = ref<{ timeRange: string; companyName: string; salary: string; position: string; leaveReason: string }[]>([]);
|
||||||
@@ -1583,6 +1709,15 @@ function previewAndDownloadContract(c: any) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function submitAll() {
|
async function submitAll() {
|
||||||
|
if (!isIdentityConfirmationComplete.value) {
|
||||||
|
const missingLabels = identityFields
|
||||||
|
.filter(({ key }) => !isIdentityFieldConfirmed(key))
|
||||||
|
.map(item => item.label);
|
||||||
|
ElMessage.warning(`请逐项核对并确认实名认证信息:${missingLabels.join("、")}`);
|
||||||
|
locateValidationTarget("identity-confirmation-card");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const unprepared = contracts.value.filter(c => !isContractPrepared(c.id));
|
const unprepared = contracts.value.filter(c => !isContractPrepared(c.id));
|
||||||
if (unprepared.length) {
|
if (unprepared.length) {
|
||||||
ElMessage.warning(`请先等待签署材料准备完成:${unprepared.map(c => c.contractName).join("、")}`);
|
ElMessage.warning(`请先等待签署材料准备完成:${unprepared.map(c => c.contractName).join("、")}`);
|
||||||
@@ -1605,7 +1740,7 @@ async function submitAll() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const confirmed = await isMessageBoxConfirmed(
|
const confirmed = await isMessageBoxConfirmed(
|
||||||
ElMessageBox.confirm("确认提交全部入职资料与签署材料确认?提交后将进入人事复核建档流程。", "最终提交确认", {
|
ElMessageBox.confirm("请再次确认姓名、身份证号码和手机号码均准确无误。提交后将进入人事复核建档流程,您不能再自行修改。", "最终提交确认", {
|
||||||
type: "success",
|
type: "success",
|
||||||
confirmButtonText: "确认提交",
|
confirmButtonText: "确认提交",
|
||||||
cancelButtonText: "再检查一下"
|
cancelButtonText: "再检查一下"
|
||||||
@@ -1621,7 +1756,13 @@ async function submitAll() {
|
|||||||
for (const c of contracts.value) {
|
for (const c of contracts.value) {
|
||||||
await onboardingPublicApi.confirmContract({ token: token.value, taskContractId: c.id });
|
await onboardingPublicApi.confirmContract({ token: token.value, taskContractId: c.id });
|
||||||
}
|
}
|
||||||
await onboardingPublicApi.submit({ token: token.value });
|
await onboardingPublicApi.submit({
|
||||||
|
token: token.value,
|
||||||
|
identityInformationConfirmed: true,
|
||||||
|
confirmedEmployeeName: identityConfirmedValues.employeeName,
|
||||||
|
confirmedIdCardNumber: identityConfirmedValues.idCardNumber,
|
||||||
|
confirmedPhone: identityConfirmedValues.phone
|
||||||
|
});
|
||||||
ElMessage.success("入职资料已成功提交!请等待人事审核。");
|
ElMessage.success("入职资料已成功提交!请等待人事审核。");
|
||||||
await load();
|
await load();
|
||||||
} finally {
|
} finally {
|
||||||
@@ -1972,6 +2113,112 @@ onBeforeUnmount(() => {
|
|||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.identity-confirmation-card {
|
||||||
|
margin-bottom: 26px;
|
||||||
|
padding: 18px;
|
||||||
|
border: 1px solid #f59e0b;
|
||||||
|
border-radius: 10px;
|
||||||
|
background: #fffbeb;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.identity-confirmation-heading {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 14px;
|
||||||
|
margin-bottom: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.identity-confirmation-title {
|
||||||
|
margin: 0 0 6px;
|
||||||
|
color: #92400e;
|
||||||
|
font-size: 17px;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.identity-confirmation-desc {
|
||||||
|
margin: 0;
|
||||||
|
color: #78350f;
|
||||||
|
font-size: 13px;
|
||||||
|
line-height: 1.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.identity-confirmation-list {
|
||||||
|
display: grid;
|
||||||
|
gap: 10px;
|
||||||
|
margin-top: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.identity-confirmation-item {
|
||||||
|
min-width: 0;
|
||||||
|
padding: 13px 14px;
|
||||||
|
border: 1px solid #fde68a;
|
||||||
|
border-radius: 8px;
|
||||||
|
background: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.identity-field-row {
|
||||||
|
display: flex;
|
||||||
|
align-items: baseline;
|
||||||
|
gap: 14px;
|
||||||
|
margin-bottom: 9px;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.identity-field-label {
|
||||||
|
flex: 0 0 86px;
|
||||||
|
color: #64748b;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.identity-field-value {
|
||||||
|
min-width: 0;
|
||||||
|
color: #0f172a;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 700;
|
||||||
|
letter-spacing: 0.02em;
|
||||||
|
overflow-wrap: anywhere;
|
||||||
|
}
|
||||||
|
|
||||||
|
.identity-confirmation-checkbox {
|
||||||
|
max-width: 100%;
|
||||||
|
height: auto;
|
||||||
|
margin-right: 0;
|
||||||
|
white-space: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
.identity-confirmation-checkbox :deep(.el-checkbox__input) {
|
||||||
|
flex: 0 0 auto;
|
||||||
|
margin-top: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.identity-confirmation-checkbox :deep(.el-checkbox__label) {
|
||||||
|
min-width: 0;
|
||||||
|
padding-left: 8px;
|
||||||
|
white-space: normal;
|
||||||
|
overflow-wrap: anywhere;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.identity-confirmation-footer {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 12px;
|
||||||
|
margin-top: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.identity-confirmation-progress {
|
||||||
|
color: #b45309;
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.identity-confirmation-progress.is-complete {
|
||||||
|
color: #15803d;
|
||||||
|
}
|
||||||
|
|
||||||
.contract-sign-card {
|
.contract-sign-card {
|
||||||
border: 1px solid #e2e8f0;
|
border: 1px solid #e2e8f0;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
@@ -2197,6 +2444,24 @@ onBeforeUnmount(() => {
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
}
|
}
|
||||||
|
.identity-confirmation-card {
|
||||||
|
padding: 14px;
|
||||||
|
}
|
||||||
|
.identity-confirmation-heading,
|
||||||
|
.identity-confirmation-footer {
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: stretch;
|
||||||
|
}
|
||||||
|
.identity-field-row {
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 4px;
|
||||||
|
}
|
||||||
|
.identity-field-label {
|
||||||
|
flex-basis: auto;
|
||||||
|
}
|
||||||
|
.identity-confirmation-footer > .el-button {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
.contract-card-bottom {
|
.contract-card-bottom {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: stretch;
|
align-items: stretch;
|
||||||
|
|||||||
Reference in New Issue
Block a user