入职任务预览,入职任务cope,验证填入的信息
This commit is contained in:
@@ -3,6 +3,7 @@ import type { ReqId, ResPage } from "@/api/interface";
|
|||||||
|
|
||||||
const taskHttp = moduleRequest("/biz/onboarding/task/");
|
const taskHttp = moduleRequest("/biz/onboarding/task/");
|
||||||
const auditHttp = moduleRequest("/biz/onboarding/audit/");
|
const auditHttp = moduleRequest("/biz/onboarding/audit/");
|
||||||
|
const contractDocumentHttp = moduleRequest("/biz/onboarding/contract-document/");
|
||||||
const fileHttp = moduleRequest("/sys/dev/file/");
|
const fileHttp = moduleRequest("/sys/dev/file/");
|
||||||
|
|
||||||
export interface OnboardingTask {
|
export interface OnboardingTask {
|
||||||
@@ -21,6 +22,33 @@ export interface OnboardingTask {
|
|||||||
linkExpireAt?: string;
|
linkExpireAt?: string;
|
||||||
submitCount: number;
|
submitCount: number;
|
||||||
createTime: string;
|
createTime: string;
|
||||||
|
remark?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 当前查询条件下、不受状态筛选影响的任务统计。 */
|
||||||
|
export interface OnboardingTaskStatistics {
|
||||||
|
pendingFill: number;
|
||||||
|
reviewing: number;
|
||||||
|
rejected: number;
|
||||||
|
approved: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 带状态统计的入职任务分页结果。 */
|
||||||
|
export interface OnboardingTaskPage extends ResPage<OnboardingTask> {
|
||||||
|
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 = {
|
export const onboardingTaskApi = {
|
||||||
page(params: Record<string, unknown>) {
|
page(params: Record<string, unknown>) {
|
||||||
return taskHttp.get<ResPage<OnboardingTask>>("page", params);
|
return taskHttp.get<OnboardingTaskPage>("page", params);
|
||||||
},
|
},
|
||||||
|
|
||||||
detail(params: ReqId) {
|
detail(params: ReqId) {
|
||||||
@@ -254,10 +282,62 @@ export const onboardingTaskApi = {
|
|||||||
return response;
|
return response;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/** 创建任务前生成并下载单份临时 DOCX 合同预览。 */
|
||||||
|
async previewContractBeforeCreate(params: Record<string, unknown>) {
|
||||||
|
let response: any;
|
||||||
|
try {
|
||||||
|
response = await contractDocumentHttp.post<any>(
|
||||||
|
"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<string, unknown>) {
|
create(params: Record<string, unknown>) {
|
||||||
return taskHttp.post("create", params);
|
return taskHttp.post("create", params);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
copy(params: {
|
||||||
|
sourceTaskId: number;
|
||||||
|
count: number;
|
||||||
|
items: Array<{ remark: string }>;
|
||||||
|
}) {
|
||||||
|
return taskHttp.post<OnboardingTaskCopyResult>("copy", params);
|
||||||
|
},
|
||||||
|
|
||||||
approve(params: {
|
approve(params: {
|
||||||
taskId: number;
|
taskId: number;
|
||||||
auditOpinion?: string;
|
auditOpinion?: string;
|
||||||
|
|||||||
@@ -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}`;
|
||||||
|
}
|
||||||
@@ -514,6 +514,8 @@ const positions = ref<OnboardingPositionOption[]>([]);
|
|||||||
const searchCompanies = ref<OnboardingCompanyOption[]>([]);
|
const searchCompanies = ref<OnboardingCompanyOption[]>([]);
|
||||||
const searchDepartments = ref<OnboardingDepartmentOption[]>([]);
|
const searchDepartments = ref<OnboardingDepartmentOption[]>([]);
|
||||||
const searchPositions = ref<OnboardingPositionOption[]>([]);
|
const searchPositions = ref<OnboardingPositionOption[]>([]);
|
||||||
|
const allSearchDepartments = ref<OnboardingDepartmentOption[]>([]);
|
||||||
|
const allSearchPositions = ref<OnboardingPositionOption[]>([]);
|
||||||
|
|
||||||
const companySearchOptions = computed(() =>
|
const companySearchOptions = computed(() =>
|
||||||
searchCompanies.value.map(c => ({
|
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<OnboardingContractVersion[]>([]);
|
const versions = ref<OnboardingContractVersion[]>([]);
|
||||||
const versionVisible = ref(false);
|
const versionVisible = ref(false);
|
||||||
const variablesVisible = ref(false);
|
const variablesVisible = ref(false);
|
||||||
@@ -605,7 +659,12 @@ const templateColumns: ColumnProps<OnboardingContractTemplate>[] = [
|
|||||||
enum: companySearchOptions,
|
enum: companySearchOptions,
|
||||||
search: {
|
search: {
|
||||||
el: "select",
|
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<OnboardingContractTemplate>[] = [
|
|||||||
enum: departmentSearchOptions,
|
enum: departmentSearchOptions,
|
||||||
search: {
|
search: {
|
||||||
el: "select",
|
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() {
|
async function loadSearchOptions() {
|
||||||
await loadCompanies();
|
try {
|
||||||
searchCompanies.value = companies.value;
|
await loadCompanies();
|
||||||
|
searchCompanies.value = companies.value;
|
||||||
|
|
||||||
const deptList: OnboardingDepartmentOption[] = [];
|
const deptList: OnboardingDepartmentOption[] = [];
|
||||||
const posList: OnboardingPositionOption[] = [];
|
const posList: OnboardingPositionOption[] = [];
|
||||||
|
|
||||||
if (companies.value && companies.value.length > 0) {
|
if (companies.value && companies.value.length > 0) {
|
||||||
const promises = companies.value.map(async comp => {
|
const promises = companies.value.map(async comp => {
|
||||||
const [depRes, posRes] = await Promise.all([
|
const [depRes, posRes] = await Promise.all([
|
||||||
onboardingTaskApi.listDepartments(comp.id),
|
onboardingTaskApi.listDepartments(comp.id).catch(() => ({ data: [] })),
|
||||||
onboardingTaskApi.listPositions(comp.id)
|
onboardingTaskApi.listPositions(comp.id).catch(() => ({ data: [] }))
|
||||||
]);
|
]);
|
||||||
if (depRes.data) deptList.push(...depRes.data);
|
if (depRes.data) deptList.push(...depRes.data);
|
||||||
if (posRes.data) posList.push(...posRes.data);
|
if (posRes.data) posList.push(...posRes.data);
|
||||||
});
|
});
|
||||||
await Promise.all(promises);
|
await Promise.all(promises);
|
||||||
|
}
|
||||||
|
|
||||||
|
const uniqueDeptMap = new Map<number, OnboardingDepartmentOption>();
|
||||||
|
deptList.forEach(d => uniqueDeptMap.set(d.id, d));
|
||||||
|
const finalDepts = Array.from(uniqueDeptMap.values());
|
||||||
|
allSearchDepartments.value = finalDepts;
|
||||||
|
searchDepartments.value = finalDepts;
|
||||||
|
|
||||||
|
const uniquePosMap = new Map<number, OnboardingPositionOption>();
|
||||||
|
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<number, OnboardingDepartmentOption>();
|
|
||||||
deptList.forEach(d => uniqueDeptMap.set(d.id, d));
|
|
||||||
searchDepartments.value = Array.from(uniqueDeptMap.values());
|
|
||||||
|
|
||||||
const uniquePosMap = new Map<number, OnboardingPositionOption>();
|
|
||||||
posList.forEach(p => uniquePosMap.set(p.id, p));
|
|
||||||
searchPositions.value = Array.from(uniquePosMap.values());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(loadSearchOptions);
|
onMounted(loadSearchOptions);
|
||||||
|
|||||||
@@ -213,17 +213,103 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts" name="onboardingEmployee">
|
<script setup lang="ts" name="onboardingEmployee">
|
||||||
import { computed, ref } from "vue";
|
import { computed, ref, onMounted } from "vue";
|
||||||
import { UserFilled, Download } from "@element-plus/icons-vue";
|
import { UserFilled, Download } from "@element-plus/icons-vue";
|
||||||
import { ElMessage } from "element-plus";
|
import { ElMessage } from "element-plus";
|
||||||
import { onboardingTaskApi, type OnboardingEmployee } from "@/api/modules/biz/onboarding";
|
import {
|
||||||
import type { ColumnProps } from "@/components/ProTable/interface";
|
onboardingTaskApi,
|
||||||
|
type OnboardingEmployee,
|
||||||
|
type OnboardingCompanyOption,
|
||||||
|
type OnboardingDepartmentOption,
|
||||||
|
type OnboardingPositionOption
|
||||||
|
} from "@/api/modules/biz/onboarding";
|
||||||
|
import type { ColumnProps, ProTableInstance } from "@/components/ProTable/interface";
|
||||||
|
|
||||||
|
const proTable = ref<ProTableInstance>();
|
||||||
const detailVisible = ref(false);
|
const detailVisible = ref(false);
|
||||||
const activeTab = ref("basic");
|
const activeTab = ref("basic");
|
||||||
const detail = ref<any>();
|
const detail = ref<any>();
|
||||||
const employeeInfo = computed<Record<string, any>>(() => detail.value?.employeeInfo || {});
|
const employeeInfo = computed<Record<string, any>>(() => detail.value?.employeeInfo || {});
|
||||||
|
|
||||||
|
const searchCompanies = ref<OnboardingCompanyOption[]>([]);
|
||||||
|
const searchDepartments = ref<OnboardingDepartmentOption[]>([]);
|
||||||
|
const searchPositions = ref<OnboardingPositionOption[]>([]);
|
||||||
|
const allSearchDepartments = ref<OnboardingDepartmentOption[]>([]);
|
||||||
|
const allSearchPositions = ref<OnboardingPositionOption[]>([]);
|
||||||
|
|
||||||
|
const companySearchOptions = computed(() =>
|
||||||
|
searchCompanies.value.map(c => ({
|
||||||
|
label: c.companyName,
|
||||||
|
value: c.id
|
||||||
|
}))
|
||||||
|
);
|
||||||
|
|
||||||
|
const departmentSearchOptions = computed(() =>
|
||||||
|
searchDepartments.value.map(d => ({
|
||||||
|
label: d.departmentName,
|
||||||
|
value: d.id
|
||||||
|
}))
|
||||||
|
);
|
||||||
|
|
||||||
|
const positionSearchOptions = computed(() =>
|
||||||
|
searchPositions.value.map(p => ({
|
||||||
|
label: p.positionName,
|
||||||
|
value: p.id
|
||||||
|
}))
|
||||||
|
);
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
proTable.value?.enumMap.set("departmentId", departmentSearchOptions.value);
|
||||||
|
proTable.value?.enumMap.set("positionId", positionSearchOptions.value);
|
||||||
|
|
||||||
|
if (proTable.value?.searchParam) {
|
||||||
|
const currentDept = proTable.value.searchParam.departmentId;
|
||||||
|
if (currentDept && !searchDepartments.value.some(d => d.id === currentDept)) {
|
||||||
|
proTable.value.searchParam.departmentId = undefined;
|
||||||
|
}
|
||||||
|
const currentPos = proTable.value.searchParam.positionId;
|
||||||
|
if (currentPos && !searchPositions.value.some(p => p.id === currentPos)) {
|
||||||
|
proTable.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 = proTable.value?.searchParam?.companyId;
|
||||||
|
if (selectedCompanyId) {
|
||||||
|
const posRes = await onboardingTaskApi.listPositions(selectedCompanyId).catch(() => ({ data: [] }));
|
||||||
|
searchPositions.value = posRes.data || [];
|
||||||
|
} else {
|
||||||
|
searchPositions.value = allSearchPositions.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
proTable.value?.enumMap.set("positionId", positionSearchOptions.value);
|
||||||
|
|
||||||
|
if (proTable.value?.searchParam) {
|
||||||
|
const currentPos = proTable.value.searchParam.positionId;
|
||||||
|
if (currentPos && !searchPositions.value.some(p => p.id === currentPos)) {
|
||||||
|
proTable.value.searchParam.positionId = undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function parseJsonList(value?: string) {
|
function parseJsonList(value?: string) {
|
||||||
if (!value) return [];
|
if (!value) return [];
|
||||||
|
|
||||||
@@ -240,6 +326,46 @@ const workHistory = computed(() => parseJsonList(employeeInfo.value.workHistoryJ
|
|||||||
const familyMembers = computed(() => parseJsonList(employeeInfo.value.familyMembersJson));
|
const familyMembers = computed(() => parseJsonList(employeeInfo.value.familyMembersJson));
|
||||||
|
|
||||||
const columns: ColumnProps<OnboardingEmployee>[] = [
|
const columns: ColumnProps<OnboardingEmployee>[] = [
|
||||||
|
{
|
||||||
|
prop: "companyId",
|
||||||
|
label: "所属公司",
|
||||||
|
isShow: false,
|
||||||
|
enum: companySearchOptions,
|
||||||
|
search: {
|
||||||
|
el: "select",
|
||||||
|
props: {
|
||||||
|
filterable: true,
|
||||||
|
clearable: true,
|
||||||
|
placeholder: "请选择所属公司",
|
||||||
|
onChange: (val?: number) => onSearchCompanyChange(val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
prop: "departmentId",
|
||||||
|
label: "所属部门",
|
||||||
|
isShow: false,
|
||||||
|
enum: departmentSearchOptions,
|
||||||
|
search: {
|
||||||
|
el: "select",
|
||||||
|
props: {
|
||||||
|
filterable: true,
|
||||||
|
clearable: true,
|
||||||
|
placeholder: "请选择所属部门",
|
||||||
|
onChange: (val?: number) => onSearchDepartmentChange(val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
prop: "positionId",
|
||||||
|
label: "入职岗位",
|
||||||
|
isShow: false,
|
||||||
|
enum: positionSearchOptions,
|
||||||
|
search: {
|
||||||
|
el: "select",
|
||||||
|
props: { filterable: true, clearable: true, placeholder: "请选择入职岗位" }
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
prop: "employeeName",
|
prop: "employeeName",
|
||||||
label: "姓名",
|
label: "姓名",
|
||||||
@@ -353,6 +479,48 @@ async function download(fileId: number, fallbackFileName?: string) {
|
|||||||
ElMessage.error(error?.msg || error?.message || "签署材料文件下载失败");
|
ElMessage.error(error?.msg || error?.message || "签署材料文件下载失败");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function loadSearchOptions() {
|
||||||
|
try {
|
||||||
|
const { data } = await onboardingTaskApi.listCompanies().catch(() => ({ data: [] }));
|
||||||
|
searchCompanies.value = data || [];
|
||||||
|
|
||||||
|
const deptList: OnboardingDepartmentOption[] = [];
|
||||||
|
const posList: OnboardingPositionOption[] = [];
|
||||||
|
|
||||||
|
if (data && data.length > 0) {
|
||||||
|
const promises = data.map(async (comp: any) => {
|
||||||
|
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<number, OnboardingDepartmentOption>();
|
||||||
|
deptList.forEach(d => uniqueDeptMap.set(d.id, d));
|
||||||
|
const finalDepts = Array.from(uniqueDeptMap.values());
|
||||||
|
allSearchDepartments.value = finalDepts;
|
||||||
|
searchDepartments.value = finalDepts;
|
||||||
|
|
||||||
|
const uniquePosMap = new Map<number, OnboardingPositionOption>();
|
||||||
|
posList.forEach(p => uniquePosMap.set(p.id, p));
|
||||||
|
const finalPositions = Array.from(uniquePosMap.values());
|
||||||
|
allSearchPositions.value = finalPositions;
|
||||||
|
searchPositions.value = finalPositions;
|
||||||
|
|
||||||
|
proTable.value?.enumMap.set("companyId", companySearchOptions.value);
|
||||||
|
proTable.value?.enumMap.set("departmentId", departmentSearchOptions.value);
|
||||||
|
proTable.value?.enumMap.set("positionId", positionSearchOptions.value);
|
||||||
|
} catch (e) {
|
||||||
|
console.error("加载员工档案搜索选项失败", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(loadSearchOptions);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -441,11 +441,46 @@
|
|||||||
/>
|
/>
|
||||||
<el-row :gutter="16">
|
<el-row :gutter="16">
|
||||||
<el-col v-for="v in extraVariables" :key="v.variableCode" :xs="24" :sm="12">
|
<el-col v-for="v in extraVariables" :key="v.variableCode" :xs="24" :sm="12">
|
||||||
<el-form-item :label="v.variableName || v.variableCode" :required="v.required">
|
<el-form-item
|
||||||
<el-input
|
:label="v.variableName || v.variableCode"
|
||||||
|
:required="v.required"
|
||||||
|
:error="extraVariableErrors[v.variableCode]"
|
||||||
|
>
|
||||||
|
<el-select
|
||||||
|
v-if="getVariableOptions(v).length"
|
||||||
v-model="customVariables[v.variableCode]"
|
v-model="customVariables[v.variableCode]"
|
||||||
|
:placeholder="`请选择${v.variableName}`"
|
||||||
|
:disabled="!canEdit"
|
||||||
|
class="full-width"
|
||||||
|
clearable
|
||||||
|
@change="clearExtraVariableError(v.variableCode)"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="option in getVariableOptions(v)"
|
||||||
|
:key="option.value"
|
||||||
|
:label="option.label"
|
||||||
|
:value="option.value"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
<el-date-picker
|
||||||
|
v-else-if="getVariableDataType(v) === 'date'"
|
||||||
|
v-model="customVariables[v.variableCode]"
|
||||||
|
type="date"
|
||||||
|
value-format="YYYY-MM-DD"
|
||||||
|
:placeholder="`请选择${v.variableName}`"
|
||||||
|
:disabled="!canEdit"
|
||||||
|
class="full-width"
|
||||||
|
@change="clearExtraVariableError(v.variableCode)"
|
||||||
|
/>
|
||||||
|
<el-input
|
||||||
|
v-else
|
||||||
|
v-model="customVariables[v.variableCode]"
|
||||||
|
:type="getVariableDataType(v) === 'number' ? 'number' : 'text'"
|
||||||
|
:inputmode="getVariableDataType(v) === 'number' ? 'decimal' : 'text'"
|
||||||
:placeholder="v.defaultValue || `请输入${v.variableName}`"
|
:placeholder="v.defaultValue || `请输入${v.variableName}`"
|
||||||
:disabled="!canEdit"
|
:disabled="!canEdit"
|
||||||
|
clearable
|
||||||
|
@input="clearExtraVariableError(v.variableCode)"
|
||||||
/>
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
@@ -680,6 +715,17 @@ import {
|
|||||||
OnboardingTask,
|
OnboardingTask,
|
||||||
onboardingPublicApi
|
onboardingPublicApi
|
||||||
} from "@/api/modules/biz/onboarding";
|
} from "@/api/modules/biz/onboarding";
|
||||||
|
import {
|
||||||
|
getIdCardBirthday,
|
||||||
|
getIdCardValidationMessage,
|
||||||
|
isBankCardNumber,
|
||||||
|
isContactPhone,
|
||||||
|
isEmail,
|
||||||
|
isMobilePhone,
|
||||||
|
isNumberInRange,
|
||||||
|
normalizeBankCardNumber,
|
||||||
|
normalizeIdCardNumber
|
||||||
|
} from "@/utils/onboardingValidation";
|
||||||
|
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const token = ref((route.query.token as string) || "");
|
const token = ref((route.query.token as string) || "");
|
||||||
@@ -745,14 +791,96 @@ const familyMembers = ref<{ name: string; relationship: string; age: string; com
|
|||||||
// 2. 特殊合同增量变量 (差集)
|
// 2. 特殊合同增量变量 (差集)
|
||||||
const customVariables = reactive<Record<string, string>>({});
|
const customVariables = reactive<Record<string, string>>({});
|
||||||
|
|
||||||
// 表单验证规则
|
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 = {
|
const regRules: FormRules = {
|
||||||
employeeName: [{ required: true, message: "请输入真实姓名", trigger: "blur" }],
|
employeeName: [{ required: true, validator: validateEmployeeName, trigger: "blur" }],
|
||||||
gender: [{ required: true, message: "请选择性别", trigger: "change" }],
|
gender: [{ required: true, message: "请选择性别", trigger: "change" }],
|
||||||
phone: [{ required: true, message: "请输入联系电话", trigger: "blur" }],
|
birthday: [{ validator: validateBirthday, trigger: "change" }],
|
||||||
idCardNumber: [{ required: true, message: "请输入身份证号码", trigger: "blur" }],
|
height: [{ validator: validateOptionalHeight, trigger: "blur" }],
|
||||||
bankCardNumber: [{ required: true, message: "请输入工资银行卡号", trigger: "blur" }],
|
weight: [{ validator: validateOptionalWeight, trigger: "blur" }],
|
||||||
bankName: [{ required: true, message: "请输入开户银行名称", 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(() => {
|
const canEdit = computed(() => {
|
||||||
@@ -810,6 +938,157 @@ function addFamily() {
|
|||||||
familyMembers.value.push({ name: "", relationship: "", age: "", company: "", phone: "" });
|
familyMembers.value.push({ name: "", relationship: "", age: "", company: "", phone: "" });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const extraVariableErrors = reactive<Record<string, string>>({});
|
||||||
|
|
||||||
|
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) {
|
function getUploadedMaterials(matId: number) {
|
||||||
return uploadedMaterials.value.filter(item => item.materialTypeId === matId);
|
return uploadedMaterials.value.filter(item => item.materialTypeId === matId);
|
||||||
}
|
}
|
||||||
@@ -915,6 +1194,11 @@ async function load(silent = false) {
|
|||||||
materialRequirements.value = data.materialRequirements || [];
|
materialRequirements.value = data.materialRequirements || [];
|
||||||
uploadedMaterials.value = data.materials || [];
|
uploadedMaterials.value = data.materials || [];
|
||||||
contractVariables.value = data.contractVariables || [];
|
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) {
|
if (data.employeeInfo) {
|
||||||
@@ -942,7 +1226,13 @@ async function load(silent = false) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function saveCurrentEmployeeInfo() {
|
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 = {
|
const payload = {
|
||||||
...form,
|
...form,
|
||||||
token: token.value,
|
token: token.value,
|
||||||
@@ -957,11 +1247,17 @@ async function saveCurrentEmployeeInfo() {
|
|||||||
async function nextStep() {
|
async function nextStep() {
|
||||||
if (currentStep.value === 0) {
|
if (currentStep.value === 0) {
|
||||||
await regFormRef.value?.validate();
|
await regFormRef.value?.validate();
|
||||||
|
if (!validateRegistrationLists()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (canEdit.value) {
|
if (canEdit.value) {
|
||||||
await saveCurrentEmployeeInfo();
|
await saveCurrentEmployeeInfo();
|
||||||
ElMessage.success("入职登记表已临时保存");
|
ElMessage.success("入职登记表已临时保存");
|
||||||
}
|
}
|
||||||
} else if (currentStep.value === 1) {
|
} else if (currentStep.value === 1) {
|
||||||
|
if (!validateExtraVariables()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (canEdit.value) {
|
if (canEdit.value) {
|
||||||
await saveCurrentEmployeeInfo();
|
await saveCurrentEmployeeInfo();
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user