From 058727982b97b33ba64147657f7635ed1748bd3c Mon Sep 17 00:00:00 2001 From: fuenmao <980740792@qq.com> Date: Sat, 29 Aug 2026 17:26:38 +0800 Subject: [PATCH] =?UTF-8?q?=E5=85=A5=E8=81=8C=E4=BB=BB=E5=8A=A1=E9=A2=84?= =?UTF-8?q?=E8=A7=88=EF=BC=8C=E5=85=A5=E8=81=8C=E4=BB=BB=E5=8A=A1cope?= =?UTF-8?q?=EF=BC=8C=E9=AA=8C=E8=AF=81=E5=A1=AB=E5=85=A5=E7=9A=84=E4=BF=A1?= =?UTF-8?q?=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/modules/biz/onboarding.ts | 82 +- src/utils/onboardingValidation.ts | 107 +++ src/views/biz/onboarding/contract/index.vue | 124 ++- src/views/biz/onboarding/employee/index.vue | 174 +++- src/views/biz/onboarding/task/index.vue | 859 +++++++++++++++++--- src/views/onboarding/fill/index.vue | 314 ++++++- 6 files changed, 1498 insertions(+), 162 deletions(-) create mode 100644 src/utils/onboardingValidation.ts diff --git a/src/api/modules/biz/onboarding.ts b/src/api/modules/biz/onboarding.ts index 79717fb..e69608d 100644 --- a/src/api/modules/biz/onboarding.ts +++ b/src/api/modules/biz/onboarding.ts @@ -3,6 +3,7 @@ import type { ReqId, ResPage } from "@/api/interface"; const taskHttp = moduleRequest("/biz/onboarding/task/"); const auditHttp = moduleRequest("/biz/onboarding/audit/"); +const contractDocumentHttp = moduleRequest("/biz/onboarding/contract-document/"); const fileHttp = moduleRequest("/sys/dev/file/"); export interface OnboardingTask { @@ -21,6 +22,33 @@ export interface OnboardingTask { linkExpireAt?: string; submitCount: number; createTime: string; + remark?: string; +} + +/** 当前查询条件下、不受状态筛选影响的任务统计。 */ +export interface OnboardingTaskStatistics { + pendingFill: number; + reviewing: number; + rejected: number; + approved: number; +} + +/** 带状态统计的入职任务分页结果。 */ +export interface OnboardingTaskPage extends ResPage { + statistics: OnboardingTaskStatistics; +} + +/** 批量复制任务返回的单条新任务链接。 */ +export interface OnboardingTaskCreatedResult { + taskId: number; + taskNo: string; + linkToken: string; + expiresAt: string; +} + +/** 批量复制任务结果。 */ +export interface OnboardingTaskCopyResult { + tasks: OnboardingTaskCreatedResult[]; } /** 员工端完成合同准备后返回的当前文件版本。 */ @@ -218,7 +246,7 @@ export interface OnboardingTemplate { } export const onboardingTaskApi = { page(params: Record) { - return taskHttp.get>("page", params); + return taskHttp.get("page", params); }, detail(params: ReqId) { @@ -254,10 +282,62 @@ export const onboardingTaskApi = { return response; }, + /** 创建任务前生成并下载单份临时 DOCX 合同预览。 */ + async previewContractBeforeCreate(params: Record) { + let response: any; + try { + response = await contractDocumentHttp.post( + "preview-before-create", + params, + { responseType: "blob", loading: false, cancel: false } + ); + } catch (error: any) { + const errorBlob = error?.response?.data; + if (errorBlob instanceof Blob) { + try { + const result = JSON.parse(await errorBlob.text()); + throw new Error(result?.msg || "合同预览生成失败"); + } catch (parseError) { + if (!(parseError instanceof SyntaxError)) throw parseError; + } + } + throw error; + } + + const blob = response?.data; + const contentType = String( + response?.headers?.["content-type"] || blob?.type || "" + ); + if ( + blob instanceof Blob && + contentType.toLowerCase().includes("application/json") + ) { + try { + const result = JSON.parse(await blob.text()); + throw new Error(result?.msg || "合同预览生成失败"); + } catch (error) { + if (error instanceof SyntaxError) { + throw new Error("合同预览生成失败"); + } + throw error; + } + } + + return response; + }, + create(params: Record) { return taskHttp.post("create", params); }, + copy(params: { + sourceTaskId: number; + count: number; + items: Array<{ remark: string }>; + }) { + return taskHttp.post("copy", params); + }, + approve(params: { taskId: number; auditOpinion?: string; diff --git a/src/utils/onboardingValidation.ts b/src/utils/onboardingValidation.ts new file mode 100644 index 0000000..8c37f65 --- /dev/null +++ b/src/utils/onboardingValidation.ts @@ -0,0 +1,107 @@ +/** + * 入职登记表固定字段校验工具。 + * + * 前端校验用于即时提示,后端仍会执行同样的核心校验,避免绕过页面直接提交非法数据。 + */ + +const mobilePhonePattern = /^1[3-9]\d{9}$/; +const landlinePhonePattern = /^0\d{2,3}-?\d{7,8}$/; +const emailPattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; +const bankCardPattern = /^\d{12,19}$/; + +const idCardWeights = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2]; +const idCardCheckCodes = ["1", "0", "X", "9", "8", "7", "6", "5", "4", "3", "2"]; + +export function normalizeIdCardNumber(value: unknown) { + return String(value ?? "").trim().toUpperCase(); +} + +export function normalizeBankCardNumber(value: unknown) { + return String(value ?? "").replace(/[\s-]/g, ""); +} + +export function isMobilePhone(value: unknown) { + return mobilePhonePattern.test(String(value ?? "").trim()); +} + +export function isContactPhone(value: unknown) { + const phone = String(value ?? "").trim(); + return mobilePhonePattern.test(phone) || landlinePhonePattern.test(phone); +} + +export function isEmail(value: unknown) { + return emailPattern.test(String(value ?? "").trim()); +} + +export function isBankCardNumber(value: unknown) { + return bankCardPattern.test(normalizeBankCardNumber(value)); +} + +/** + * 校验中国大陆 18 位居民身份证,包括出生日期和校验位。 + * 返回空字符串表示校验通过,否则返回适合直接展示给用户的错误信息。 + */ +export function getIdCardValidationMessage(value: unknown) { + const idCardNumber = normalizeIdCardNumber(value); + if (!/^\d{17}[0-9X]$/.test(idCardNumber)) { + return "请输入正确的18位身份证号码"; + } + + const birthday = getIdCardBirthday(idCardNumber); + if (!birthday || !isValidDateText(birthday) || birthday > getTodayText()) { + return "身份证号码中的出生日期不正确"; + } + + const total = idCardWeights.reduce( + (sum, weight, index) => sum + Number(idCardNumber[index]) * weight, + 0 + ); + if (idCardCheckCodes[total % 11] !== idCardNumber[17]) { + return "身份证号码校验位不正确"; + } + + return ""; +} + +export function getIdCardBirthday(value: unknown) { + const idCardNumber = normalizeIdCardNumber(value); + if (!/^\d{17}[0-9X]$/.test(idCardNumber)) { + return ""; + } + + const text = idCardNumber.slice(6, 14); + return `${text.slice(0, 4)}-${text.slice(4, 6)}-${text.slice(6, 8)}`; +} + +export function isNumberInRange(value: unknown, min: number, max: number) { + const text = String(value ?? "").trim(); + if (!/^\d+(\.\d+)?$/.test(text)) { + return false; + } + + const number = Number(text); + return Number.isFinite(number) && number >= min && number <= max; +} + +function isValidDateText(value: string) { + const match = /^(\d{4})-(\d{2})-(\d{2})$/.exec(value); + if (!match) { + return false; + } + + const year = Number(match[1]); + const month = Number(match[2]); + const day = Number(match[3]); + const date = new Date(year, month - 1, day); + return date.getFullYear() === year + && date.getMonth() === month - 1 + && date.getDate() === day; +} + +function getTodayText() { + const now = new Date(); + const year = now.getFullYear(); + const month = String(now.getMonth() + 1).padStart(2, "0"); + const day = String(now.getDate()).padStart(2, "0"); + return `${year}-${month}-${day}`; +} diff --git a/src/views/biz/onboarding/contract/index.vue b/src/views/biz/onboarding/contract/index.vue index f07c56c..df8e98b 100644 --- a/src/views/biz/onboarding/contract/index.vue +++ b/src/views/biz/onboarding/contract/index.vue @@ -514,6 +514,8 @@ const positions = ref([]); const searchCompanies = ref([]); const searchDepartments = ref([]); const searchPositions = ref([]); +const allSearchDepartments = ref([]); +const allSearchPositions = ref([]); const companySearchOptions = computed(() => searchCompanies.value.map(c => ({ @@ -536,6 +538,58 @@ const positionSearchOptions = computed(() => })) ); +async function onSearchCompanyChange(companyId?: number) { + if (companyId) { + const [depRes, posRes] = await Promise.all([ + onboardingTaskApi.listDepartments(companyId).catch(() => ({ data: [] })), + onboardingTaskApi.listPositions(companyId).catch(() => ({ data: [] })) + ]); + searchDepartments.value = depRes.data || []; + searchPositions.value = posRes.data || []; + } else { + searchDepartments.value = allSearchDepartments.value; + searchPositions.value = allSearchPositions.value; + } + + templateTable.value?.enumMap.set("departmentId", departmentSearchOptions.value); + templateTable.value?.enumMap.set("positionId", positionSearchOptions.value); + + if (templateTable.value?.searchParam) { + const currentDept = templateTable.value.searchParam.departmentId; + if (currentDept && !searchDepartments.value.some(d => d.id === currentDept)) { + templateTable.value.searchParam.departmentId = undefined; + } + const currentPos = templateTable.value.searchParam.positionId; + if (currentPos && !searchPositions.value.some(p => p.id === currentPos)) { + templateTable.value.searchParam.positionId = undefined; + } + } +} + +async function onSearchDepartmentChange(departmentId?: number) { + if (departmentId) { + const posRes = await onboardingTaskApi.listPositionsByDepartment(departmentId).catch(() => ({ data: [] })); + searchPositions.value = posRes.data || []; + } else { + const selectedCompanyId = templateTable.value?.searchParam?.companyId; + if (selectedCompanyId) { + const posRes = await onboardingTaskApi.listPositions(selectedCompanyId).catch(() => ({ data: [] })); + searchPositions.value = posRes.data || []; + } else { + searchPositions.value = allSearchPositions.value; + } + } + + templateTable.value?.enumMap.set("positionId", positionSearchOptions.value); + + if (templateTable.value?.searchParam) { + const currentPos = templateTable.value.searchParam.positionId; + if (currentPos && !searchPositions.value.some(p => p.id === currentPos)) { + templateTable.value.searchParam.positionId = undefined; + } + } +} + const versions = ref([]); const versionVisible = ref(false); const variablesVisible = ref(false); @@ -605,7 +659,12 @@ const templateColumns: ColumnProps[] = [ enum: companySearchOptions, search: { el: "select", - props: { filterable: true, clearable: true, placeholder: "请选择所属公司" } + props: { + filterable: true, + clearable: true, + placeholder: "请选择所属公司", + onChange: (val?: number) => onSearchCompanyChange(val) + } } }, { @@ -615,7 +674,12 @@ const templateColumns: ColumnProps[] = [ enum: departmentSearchOptions, search: { el: "select", - props: { filterable: true, clearable: true, placeholder: "请选择所属部门" } + props: { + filterable: true, + clearable: true, + placeholder: "请选择所属部门", + onChange: (val?: number) => onSearchDepartmentChange(val) + } } }, { @@ -910,31 +974,43 @@ async function saveVariable() { } async function loadSearchOptions() { - await loadCompanies(); - searchCompanies.value = companies.value; + try { + await loadCompanies(); + searchCompanies.value = companies.value; - const deptList: OnboardingDepartmentOption[] = []; - const posList: OnboardingPositionOption[] = []; + const deptList: OnboardingDepartmentOption[] = []; + const posList: OnboardingPositionOption[] = []; - if (companies.value && companies.value.length > 0) { - const promises = companies.value.map(async comp => { - const [depRes, posRes] = await Promise.all([ - onboardingTaskApi.listDepartments(comp.id), - onboardingTaskApi.listPositions(comp.id) - ]); - if (depRes.data) deptList.push(...depRes.data); - if (posRes.data) posList.push(...posRes.data); - }); - await Promise.all(promises); + if (companies.value && companies.value.length > 0) { + const promises = companies.value.map(async comp => { + const [depRes, posRes] = await Promise.all([ + onboardingTaskApi.listDepartments(comp.id).catch(() => ({ data: [] })), + onboardingTaskApi.listPositions(comp.id).catch(() => ({ data: [] })) + ]); + if (depRes.data) deptList.push(...depRes.data); + if (posRes.data) posList.push(...posRes.data); + }); + await Promise.all(promises); + } + + const uniqueDeptMap = new Map(); + deptList.forEach(d => uniqueDeptMap.set(d.id, d)); + const finalDepts = Array.from(uniqueDeptMap.values()); + allSearchDepartments.value = finalDepts; + searchDepartments.value = finalDepts; + + const uniquePosMap = new Map(); + posList.forEach(p => uniquePosMap.set(p.id, p)); + const finalPositions = Array.from(uniquePosMap.values()); + allSearchPositions.value = finalPositions; + searchPositions.value = finalPositions; + + templateTable.value?.enumMap.set("companyId", companySearchOptions.value); + templateTable.value?.enumMap.set("departmentId", departmentSearchOptions.value); + templateTable.value?.enumMap.set("positionId", positionSearchOptions.value); + } catch (e) { + console.error("加载签署材料搜索选项失败", e); } - - const uniqueDeptMap = new Map(); - deptList.forEach(d => uniqueDeptMap.set(d.id, d)); - searchDepartments.value = Array.from(uniqueDeptMap.values()); - - const uniquePosMap = new Map(); - posList.forEach(p => uniquePosMap.set(p.id, p)); - searchPositions.value = Array.from(uniquePosMap.values()); } onMounted(loadSearchOptions); diff --git a/src/views/biz/onboarding/employee/index.vue b/src/views/biz/onboarding/employee/index.vue index d77b51b..025bacf 100644 --- a/src/views/biz/onboarding/employee/index.vue +++ b/src/views/biz/onboarding/employee/index.vue @@ -213,17 +213,103 @@ diff --git a/src/views/onboarding/fill/index.vue b/src/views/onboarding/fill/index.vue index 72b98c8..91ffe4a 100644 --- a/src/views/onboarding/fill/index.vue +++ b/src/views/onboarding/fill/index.vue @@ -441,11 +441,46 @@ /> - - + + + + + @@ -680,6 +715,17 @@ import { OnboardingTask, onboardingPublicApi } from "@/api/modules/biz/onboarding"; +import { + getIdCardBirthday, + getIdCardValidationMessage, + isBankCardNumber, + isContactPhone, + isEmail, + isMobilePhone, + isNumberInRange, + normalizeBankCardNumber, + normalizeIdCardNumber +} from "@/utils/onboardingValidation"; const route = useRoute(); const token = ref((route.query.token as string) || ""); @@ -745,14 +791,96 @@ const familyMembers = ref<{ name: string; relationship: string; age: string; com // 2. 特殊合同增量变量 (差集) const customVariables = reactive>({}); -// 表单验证规则 +function validateEmployeeName(_rule: any, value: unknown, callback: (error?: Error) => void) { + const employeeName = String(value ?? "").trim(); + if (!employeeName) { + return callback(new Error("请输入真实姓名")); + } + if (employeeName.length > 100) { + return callback(new Error("姓名不能超过100个字符")); + } + callback(); +} + +function validatePhone(_rule: any, value: unknown, callback: (error?: Error) => void) { + if (!isMobilePhone(value)) { + return callback(new Error("请输入正确的11位手机号码")); + } + callback(); +} + +function validateIdCardNumber(_rule: any, value: unknown, callback: (error?: Error) => void) { + const message = getIdCardValidationMessage(value); + if (message) { + return callback(new Error(message)); + } + callback(); +} + +function validateBirthday(_rule: any, value: unknown, callback: (error?: Error) => void) { + if (!value) { + return callback(); + } + + const birthday = String(value); + const idCardBirthday = getIdCardBirthday(form.idCardNumber); + if (idCardBirthday && birthday !== idCardBirthday) { + return callback(new Error("出生日期与身份证号码不一致")); + } + callback(); +} + +function validateBankCardNumber(_rule: any, value: unknown, callback: (error?: Error) => void) { + if (!isBankCardNumber(value)) { + return callback(new Error("请输入12至19位银行卡号")); + } + callback(); +} + +function validateOptionalEmail(_rule: any, value: unknown, callback: (error?: Error) => void) { + if (value && !isEmail(value)) { + return callback(new Error("请输入正确的电子邮箱")); + } + callback(); +} + +function validateOptionalHeight(_rule: any, value: unknown, callback: (error?: Error) => void) { + if (value && !isNumberInRange(value, 100, 250)) { + return callback(new Error("身高应为100至250厘米之间的数字")); + } + callback(); +} + +function validateOptionalWeight(_rule: any, value: unknown, callback: (error?: Error) => void) { + if (value && !isNumberInRange(value, 30, 300)) { + return callback(new Error("体重应为30至300公斤之间的数字")); + } + callback(); +} + +function validateOptionalContactPhone(_rule: any, value: unknown, callback: (error?: Error) => void) { + if (value && !isContactPhone(value)) { + return callback(new Error("请输入正确的手机号码或座机号码")); + } + callback(); +} + +// 固定登记表字段在前端即时校验;后端仍会执行相同的核心规则作为安全兜底。 const regRules: FormRules = { - employeeName: [{ required: true, message: "请输入真实姓名", trigger: "blur" }], + employeeName: [{ required: true, validator: validateEmployeeName, trigger: "blur" }], gender: [{ required: true, message: "请选择性别", trigger: "change" }], - phone: [{ required: true, message: "请输入联系电话", trigger: "blur" }], - idCardNumber: [{ required: true, message: "请输入身份证号码", trigger: "blur" }], - bankCardNumber: [{ required: true, message: "请输入工资银行卡号", trigger: "blur" }], - bankName: [{ required: true, message: "请输入开户银行名称", trigger: "blur" }] + birthday: [{ validator: validateBirthday, trigger: "change" }], + height: [{ validator: validateOptionalHeight, trigger: "blur" }], + weight: [{ validator: validateOptionalWeight, trigger: "blur" }], + phone: [{ required: true, validator: validatePhone, trigger: "blur" }], + email: [{ validator: validateOptionalEmail, trigger: "blur" }], + idCardNumber: [{ required: true, validator: validateIdCardNumber, trigger: "blur" }], + bankCardNumber: [{ required: true, validator: validateBankCardNumber, trigger: "blur" }], + bankName: [ + { required: true, message: "请输入开户银行名称", trigger: "blur" }, + { max: 100, message: "开户银行名称不能超过100个字符", trigger: "blur" } + ], + emergencyPhone: [{ validator: validateOptionalContactPhone, trigger: "blur" }] }; const canEdit = computed(() => { @@ -810,6 +938,157 @@ function addFamily() { familyMembers.value.push({ name: "", relationship: "", age: "", company: "", phone: "" }); } +const extraVariableErrors = reactive>({}); + +function getVariableDataType(variable: any) { + return String(variable?.dataType || "string").trim().toLowerCase(); +} + +function getVariableOptions(variable: any) { + if (!variable?.optionsJson) { + return []; + } + + try { + const options = JSON.parse(variable.optionsJson); + if (!Array.isArray(options)) { + return []; + } + + return options + .map((option: any) => { + if (option && typeof option === "object") { + const value = option.value ?? option.label; + const label = option.label ?? option.value; + return value == null + ? null + : { value: String(value), label: String(label ?? value) }; + } + + return option == null + ? null + : { value: String(option), label: String(option) }; + }) + .filter(Boolean) as { value: string; label: string }[]; + } catch { + return []; + } +} + +function clearExtraVariableError(variableCode: string) { + extraVariableErrors[variableCode] = ""; +} + +function isIsoDate(value: string) { + const match = /^(\d{4})-(\d{2})-(\d{2})$/.exec(value); + if (!match) { + return false; + } + + const date = new Date( + Number(match[1]), + Number(match[2]) - 1, + Number(match[3]) + ); + return date.getFullYear() === Number(match[1]) + && date.getMonth() === Number(match[2]) - 1 + && date.getDate() === Number(match[3]); +} + +function validateExtraVariables() { + let firstMessage = ""; + + for (const variable of extraVariables.value) { + const variableCode = variable.variableCode; + const variableName = variable.variableName || variableCode; + const value = String(customVariables[variableCode] ?? "").trim(); + const options = getVariableOptions(variable); + const dataType = getVariableDataType(variable); + customVariables[variableCode] = value; + extraVariableErrors[variableCode] = ""; + + if (!value && variable.required) { + extraVariableErrors[variableCode] = `请填写${variableName}`; + } else if (value && options.length && !options.some(option => option.value === value)) { + extraVariableErrors[variableCode] = `${variableName}必须从配置选项中选择`; + } else if (value && dataType === "number" && !/^-?\d+(\.\d+)?$/.test(value)) { + extraVariableErrors[variableCode] = `${variableName}必须填写数字`; + } else if (value && dataType === "date" && !isIsoDate(value)) { + extraVariableErrors[variableCode] = `${variableName}必须填写正确日期`; + } + + if (!firstMessage && extraVariableErrors[variableCode]) { + firstMessage = extraVariableErrors[variableCode]; + } + } + + if (!firstMessage) { + return true; + } + + ElMessage.warning(firstMessage); + return false; +} + +function validateRegistrationLists() { + for (let index = 0; index < educationHistory.value.length; index++) { + const row = educationHistory.value[index]; + const requiredFields = [ + ["timeRange", "时间范围"], + ["location", "学校或机构"], + ["content", "专业或学习内容"], + ["result", "结果或学位"] + ] as const; + const missing = requiredFields.find(([field]) => !String(row[field] || "").trim()); + if (missing) { + ElMessage.warning(`第 ${index + 1} 条教育经历请填写${missing[1]},或删除整条经历`); + return false; + } + } + + for (let index = 0; index < workHistory.value.length; index++) { + const row = workHistory.value[index]; + const requiredFields = [ + ["timeRange", "起止时间"], + ["companyName", "工作单位"], + ["position", "担任职务"], + ["leaveReason", "离职原因"] + ] as const; + const missing = requiredFields.find(([field]) => !String(row[field] || "").trim()); + if (missing) { + ElMessage.warning(`第 ${index + 1} 条工作经历请填写${missing[1]},或删除整条经历`); + return false; + } + } + + for (let index = 0; index < familyMembers.value.length; index++) { + const row = familyMembers.value[index]; + const requiredFields = [ + ["name", "姓名"], + ["relationship", "关系"], + ["age", "年龄"], + ["phone", "联系电话"] + ] as const; + const missing = requiredFields.find(([field]) => !String(row[field] || "").trim()); + if (missing) { + ElMessage.warning(`第 ${index + 1} 条家庭成员请填写${missing[1]},或删除整条记录`); + return false; + } + + const age = Number(row.age); + if (!Number.isInteger(age) || age < 0 || age > 120) { + ElMessage.warning(`第 ${index + 1} 条家庭成员的年龄应为0至120之间的整数`); + return false; + } + if (!isContactPhone(row.phone)) { + ElMessage.warning(`第 ${index + 1} 条家庭成员的联系电话格式不正确`); + return false; + } + } + + return true; +} + function getUploadedMaterials(matId: number) { return uploadedMaterials.value.filter(item => item.materialTypeId === matId); } @@ -915,6 +1194,11 @@ async function load(silent = false) { materialRequirements.value = data.materialRequirements || []; uploadedMaterials.value = data.materials || []; contractVariables.value = data.contractVariables || []; + contractVariables.value.forEach((variable: any) => { + if (!(variable.variableCode in customVariables) && variable.defaultValue != null) { + customVariables[variable.variableCode] = String(variable.defaultValue); + } + }); // 回填入职登记表 if (data.employeeInfo) { @@ -942,7 +1226,13 @@ async function load(silent = false) { } async function saveCurrentEmployeeInfo() { - form.bankAccountName = form.employeeName; // 开户名自动对齐姓名 + form.employeeName = String(form.employeeName || "").trim(); + form.phone = String(form.phone || "").trim(); + form.email = String(form.email || "").trim(); + form.idCardNumber = normalizeIdCardNumber(form.idCardNumber); + form.bankCardNumber = normalizeBankCardNumber(form.bankCardNumber); + form.bankName = String(form.bankName || "").trim(); + form.bankAccountName = form.employeeName; // 开户名始终与员工姓名保持一致 const payload = { ...form, token: token.value, @@ -957,11 +1247,17 @@ async function saveCurrentEmployeeInfo() { async function nextStep() { if (currentStep.value === 0) { await regFormRef.value?.validate(); + if (!validateRegistrationLists()) { + return; + } if (canEdit.value) { await saveCurrentEmployeeInfo(); ElMessage.success("入职登记表已临时保存"); } } else if (currentStep.value === 1) { + if (!validateExtraVariables()) { + return; + } if (canEdit.value) { await saveCurrentEmployeeInfo(); }