逻辑调整
This commit is contained in:
@@ -80,16 +80,6 @@ export interface OnboardingTemplateVariable {
|
||||
remark?: string;
|
||||
}
|
||||
|
||||
export interface OnboardingContractScope {
|
||||
id: number;
|
||||
companyId: number;
|
||||
positionId?: number;
|
||||
templateId: number;
|
||||
templateVersionId: number;
|
||||
required: boolean;
|
||||
sortCode?: number;
|
||||
}
|
||||
|
||||
export interface OnboardingTaskContractVariableItem {
|
||||
id: number;
|
||||
variableCode: string;
|
||||
@@ -142,24 +132,43 @@ export interface OnboardingMaterialType {
|
||||
status: string;
|
||||
}
|
||||
|
||||
export interface OnboardingFormRequirement {
|
||||
id: number;
|
||||
companyId: number;
|
||||
positionId?: number;
|
||||
export interface OnboardingTemplateContractConfig {
|
||||
contractTemplateId: number;
|
||||
contractTemplateVersionId: number;
|
||||
required: boolean;
|
||||
printCopies: number;
|
||||
sortCode?: number;
|
||||
}
|
||||
|
||||
export interface OnboardingTemplateFieldConfig {
|
||||
fieldId: number;
|
||||
required: boolean;
|
||||
sortCode?: number;
|
||||
}
|
||||
|
||||
export interface OnboardingMaterialRequirement {
|
||||
id: number;
|
||||
companyId: number;
|
||||
positionId?: number;
|
||||
export interface OnboardingTemplateMaterialConfig {
|
||||
materialTypeId: number;
|
||||
required: boolean;
|
||||
sortCode?: number;
|
||||
}
|
||||
|
||||
export interface OnboardingTemplate {
|
||||
id: number;
|
||||
templateCode: string;
|
||||
templateName: string;
|
||||
companyId: number;
|
||||
companyName?: string;
|
||||
positionIds: number[];
|
||||
positionNames?: string[];
|
||||
contracts: OnboardingTemplateContractConfig[];
|
||||
formFields: OnboardingTemplateFieldConfig[];
|
||||
materials: OnboardingTemplateMaterialConfig[];
|
||||
contractCount?: number;
|
||||
formFieldCount?: number;
|
||||
materialCount?: number;
|
||||
status: string;
|
||||
description?: string;
|
||||
}
|
||||
export const onboardingTaskApi = {
|
||||
page(params: Record<string, unknown>) {
|
||||
return taskHttp.get<ResPage<OnboardingTask>>("page", params);
|
||||
@@ -291,6 +300,40 @@ export const onboardingTaskApi = {
|
||||
);
|
||||
},
|
||||
|
||||
onboardingTemplatePage(params: Record<string, unknown>) {
|
||||
return moduleRequest("/biz/onboarding/template/").get<
|
||||
ResPage<OnboardingTemplate>
|
||||
>("page", params);
|
||||
},
|
||||
|
||||
onboardingTemplateDetail(params: ReqId) {
|
||||
return moduleRequest("/biz/onboarding/template/").get<OnboardingTemplate>(
|
||||
"detail",
|
||||
params
|
||||
);
|
||||
},
|
||||
|
||||
saveOnboardingTemplate(params: Partial<OnboardingTemplate>) {
|
||||
return moduleRequest("/biz/onboarding/template/").post<number>(
|
||||
"save",
|
||||
params
|
||||
);
|
||||
},
|
||||
|
||||
deleteOnboardingTemplate(ids: number[]) {
|
||||
return moduleRequest("/biz/onboarding/template/").post(
|
||||
"delete",
|
||||
{ ids }
|
||||
);
|
||||
},
|
||||
|
||||
listEnabledOnboardingTemplates(companyId: number, positionId: number) {
|
||||
return moduleRequest("/biz/onboarding/template/").get<OnboardingTemplate[]>(
|
||||
"list-enabled",
|
||||
{ companyId, positionId }
|
||||
);
|
||||
},
|
||||
|
||||
templatePage(params: Record<string, unknown>) {
|
||||
return moduleRequest("/biz/onboarding/contract-template/").get<
|
||||
ResPage<OnboardingContractTemplate>
|
||||
@@ -357,47 +400,13 @@ export const onboardingTaskApi = {
|
||||
>("variable/page", params);
|
||||
},
|
||||
|
||||
saveVariable(
|
||||
params: Partial<OnboardingTemplateVariable>,
|
||||
edit = false
|
||||
) {
|
||||
editVariable(params: Partial<OnboardingTemplateVariable>) {
|
||||
return moduleRequest("/biz/onboarding/contract-template/").post(
|
||||
edit ? "variable/edit" : "variable/add",
|
||||
"variable/edit",
|
||||
params
|
||||
);
|
||||
},
|
||||
|
||||
deleteVariable(ids: number[]) {
|
||||
return moduleRequest("/biz/onboarding/contract-template/").post(
|
||||
"variable/delete",
|
||||
{ ids }
|
||||
);
|
||||
},
|
||||
|
||||
scopePage(params: {
|
||||
templateId: number;
|
||||
pageNum: number;
|
||||
pageSize: number;
|
||||
}) {
|
||||
return moduleRequest("/biz/onboarding/contract-template/").get<
|
||||
ResPage<OnboardingContractScope>
|
||||
>("scope/page", params);
|
||||
},
|
||||
|
||||
addScope(params: Partial<OnboardingContractScope>) {
|
||||
return moduleRequest("/biz/onboarding/contract-template/").post(
|
||||
"scope/add",
|
||||
params
|
||||
);
|
||||
},
|
||||
|
||||
deleteScope(ids: number[]) {
|
||||
return moduleRequest("/biz/onboarding/contract-template/").post(
|
||||
"scope/delete",
|
||||
{ ids }
|
||||
);
|
||||
},
|
||||
|
||||
uploadDocx(params: FormData) {
|
||||
return moduleRequest("/biz/onboarding/contract-template/").post<number>(
|
||||
"file/upload-docx",
|
||||
@@ -475,47 +484,5 @@ export const onboardingTaskApi = {
|
||||
"material-type/delete",
|
||||
{ ids }
|
||||
);
|
||||
},
|
||||
|
||||
listFormRequirements(companyId: number) {
|
||||
return moduleRequest("/biz/onboarding/entry-config/").get<
|
||||
OnboardingFormRequirement[]
|
||||
>("form-requirement/list", { companyId });
|
||||
},
|
||||
|
||||
saveFormRequirement(params: Partial<OnboardingFormRequirement>) {
|
||||
return moduleRequest("/biz/onboarding/entry-config/").post(
|
||||
"form-requirement/save",
|
||||
params
|
||||
);
|
||||
},
|
||||
|
||||
deleteFormRequirements(ids: number[]) {
|
||||
return moduleRequest("/biz/onboarding/entry-config/").post(
|
||||
"form-requirement/delete",
|
||||
{ ids }
|
||||
);
|
||||
},
|
||||
|
||||
listMaterialRequirements(companyId: number) {
|
||||
return moduleRequest("/biz/onboarding/entry-config/").get<
|
||||
OnboardingMaterialRequirement[]
|
||||
>("material-requirement/list", { companyId });
|
||||
},
|
||||
|
||||
saveMaterialRequirement(
|
||||
params: Partial<OnboardingMaterialRequirement>
|
||||
) {
|
||||
return moduleRequest("/biz/onboarding/entry-config/").post(
|
||||
"material-requirement/save",
|
||||
params
|
||||
);
|
||||
},
|
||||
|
||||
deleteMaterialRequirements(ids: number[]) {
|
||||
return moduleRequest("/biz/onboarding/entry-config/").post(
|
||||
"material-requirement/delete",
|
||||
{ ids }
|
||||
);
|
||||
}
|
||||
};
|
||||
@@ -179,104 +179,6 @@
|
||||
</el-table>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
|
||||
<!-- 2. 适用范围配置 -->
|
||||
<el-tab-pane label="适用范围 (公司与岗位)" name="scopes">
|
||||
<div class="tab-pane-content">
|
||||
<el-card shadow="never" class="scope-add-card">
|
||||
<template #header>
|
||||
<span class="font-bold">为公司/岗位分配此合同模板</span>
|
||||
</template>
|
||||
<el-form :model="scopeForm" inline class="scope-inline-form">
|
||||
<el-form-item label="所属公司" required>
|
||||
<el-select
|
||||
v-model="scopeForm.companyId"
|
||||
placeholder="请选择公司"
|
||||
filterable
|
||||
style="width: 200px"
|
||||
@change="onScopeCompanyChange"
|
||||
>
|
||||
<el-option
|
||||
v-for="company in companies"
|
||||
:key="company.id"
|
||||
:label="company.companyName"
|
||||
:value="company.id"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="适用岗位">
|
||||
<el-select
|
||||
v-model="scopeForm.positionId"
|
||||
placeholder="全公司所有岗位"
|
||||
clearable
|
||||
style="width: 200px"
|
||||
>
|
||||
<el-option
|
||||
v-for="position in scopePositions"
|
||||
:key="position.id"
|
||||
:label="position.positionName"
|
||||
:value="position.id"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="绑定版本" required>
|
||||
<el-select
|
||||
v-model="scopeForm.templateVersionId"
|
||||
placeholder="请选择合同版本"
|
||||
style="width: 180px"
|
||||
>
|
||||
<el-option
|
||||
v-for="ver in enabledVersions"
|
||||
:key="ver.id"
|
||||
:label="`版本 ${ver.versionNo}`"
|
||||
:value="ver.id"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="Plus" @click="saveScope">
|
||||
添加适用范围
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-card>
|
||||
|
||||
<el-table :data="scopes" border stripe class="mt-4">
|
||||
<el-table-column label="所属公司" min-width="180">
|
||||
<template #default="scope">
|
||||
<el-tag effect="plain" type="primary">
|
||||
{{ companyName(scope.row.companyId) }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column label="适用岗位" min-width="180">
|
||||
<template #default="scope">
|
||||
{{ positionName(scope.row.positionId) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column label="生效版本" width="140" align="center">
|
||||
<template #default="scope">
|
||||
<el-tag size="small" type="success">
|
||||
版本 {{ versionName(scope.row.templateVersionId) }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column label="操作" width="110" align="center">
|
||||
<template #default="scope">
|
||||
<el-button link type="danger" icon="Delete" @click="removeScope(scope.row)">
|
||||
移除
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</div>
|
||||
|
||||
@@ -294,7 +196,7 @@
|
||||
append-to-body
|
||||
>
|
||||
<el-alert
|
||||
title="仅支持 .docx 文件。请在 Word 文档中使用 {{变量编码}}(如 {{EmployeeName}}、{{Salary}})作为占位符。"
|
||||
title="仅支持 .docx。占位符按来源编写:{{employee.EmployeeName}}、{{company.CompanyName}}、{{hr.MonthlySalary}}、{{system.SignDate}};保存版本时系统会自动识别。"
|
||||
type="info"
|
||||
:closable="false"
|
||||
style="margin-bottom: 16px"
|
||||
@@ -350,18 +252,12 @@
|
||||
destroy-on-close
|
||||
append-to-body
|
||||
>
|
||||
<div class="toolbar-flex">
|
||||
<el-button type="primary" icon="Plus" @click="openVariableCreate">
|
||||
新增变量定义
|
||||
</el-button>
|
||||
<el-alert
|
||||
title="在 DOCX 模板中使用 {{变量编码}} 作为占位符。点击变量编码旁复制按钮可直接复制占位符。"
|
||||
type="info"
|
||||
show-icon
|
||||
:closable="false"
|
||||
style="flex: 1; margin: 0 0 0 16px;"
|
||||
/>
|
||||
</div>
|
||||
<el-alert
|
||||
title="变量已从 DOCX 自动识别。请逐项检查来源、显示名称、数据类型和取值字段;未识别变量调整完成后才能启用版本。"
|
||||
type="info"
|
||||
show-icon
|
||||
:closable="false"
|
||||
/>
|
||||
|
||||
<el-table :data="variables" border stripe class="mt-3">
|
||||
<el-table-column prop="variableCode" label="变量编码 (DOCX占位符)" min-width="220">
|
||||
@@ -410,11 +306,7 @@
|
||||
<el-table-column label="操作" width="150" fixed="right">
|
||||
<template #default="scope">
|
||||
<el-button link type="primary" icon="Edit" @click="openVariableEdit(scope.row)">
|
||||
编辑
|
||||
</el-button>
|
||||
|
||||
<el-button link type="danger" icon="Delete" @click="removeVariable(scope.row)">
|
||||
删除
|
||||
检查调整
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
@@ -424,14 +316,14 @@
|
||||
<!-- 新增 / 编辑单个变量 -->
|
||||
<el-dialog
|
||||
v-model="variableVisible"
|
||||
:title="variableEditing ? '编辑变量' : '新增变量'"
|
||||
title="检查并调整变量"
|
||||
width="640px"
|
||||
destroy-on-close
|
||||
append-to-body
|
||||
>
|
||||
<el-form :model="variableForm" label-width="130px">
|
||||
<el-form-item label="变量编码" required>
|
||||
<el-input v-model="variableForm.variableCode" placeholder="如:EMPLOYEE_NAME" />
|
||||
<el-input v-model="variableForm.variableCode" disabled />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="变量名称" required>
|
||||
@@ -456,7 +348,7 @@
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item v-if="variableForm.variableType === 'employee'" label="员工字段">
|
||||
<el-form-item v-if="['employee', 'company'].includes(variableForm.variableType || '')" label="取值字段">
|
||||
<el-input v-model="variableForm.employeeField" placeholder="如:EmployeeName、IdCardNumber、Phone" />
|
||||
</el-form-item>
|
||||
|
||||
@@ -490,44 +382,30 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="onboardingContract">
|
||||
import { ref, reactive, computed, onMounted } from "vue";
|
||||
import { reactive, ref } from "vue";
|
||||
import { ElMessage, ElMessageBox } from "element-plus";
|
||||
import { Plus, Edit, Delete, Files, Upload, Setting, Download, Check, CopyDocument } from "@element-plus/icons-vue";
|
||||
import { Check } from "@element-plus/icons-vue";
|
||||
import {
|
||||
onboardingTaskApi,
|
||||
type OnboardingCompanyOption,
|
||||
type OnboardingContractScope,
|
||||
type OnboardingContractTemplate,
|
||||
type OnboardingContractVersion,
|
||||
type OnboardingPositionOption,
|
||||
type OnboardingTemplateVariable
|
||||
} from "@/api/modules/biz/onboarding";
|
||||
import type { ColumnProps, ProTableInstance } from "@/components/ProTable/interface";
|
||||
|
||||
const templateTable = ref<ProTableInstance>();
|
||||
|
||||
const companies = ref<OnboardingCompanyOption[]>([]);
|
||||
const scopePositions = ref<OnboardingPositionOption[]>([]);
|
||||
|
||||
const selectedTemplate = ref<OnboardingContractTemplate>();
|
||||
const selectedVersion = ref<OnboardingContractVersion>();
|
||||
|
||||
const drawerVisible = ref(false);
|
||||
const drawerActiveTab = ref("versions");
|
||||
|
||||
const templateVisible = ref(false);
|
||||
const templateEditing = ref(false);
|
||||
|
||||
const versions = ref<OnboardingContractVersion[]>([]);
|
||||
const versionVisible = ref(false);
|
||||
|
||||
const variablesVisible = ref(false);
|
||||
const variableEditing = ref(false);
|
||||
const variables = ref<OnboardingTemplateVariable[]>([]);
|
||||
const variableVisible = ref(false);
|
||||
|
||||
const scopes = ref<OnboardingContractScope[]>([]);
|
||||
|
||||
const emptyTemplate = (): Partial<OnboardingContractTemplate> => ({
|
||||
templateCode: "",
|
||||
templateName: "",
|
||||
@@ -547,50 +425,24 @@ const emptyVersion = (): Partial<OnboardingContractVersion> => ({
|
||||
const emptyVariable = (): Partial<OnboardingTemplateVariable> => ({
|
||||
variableCode: "",
|
||||
variableName: "",
|
||||
variableType: "employee",
|
||||
variableType: "unresolved",
|
||||
dataType: "string",
|
||||
employeeField: "",
|
||||
defaultValue: "",
|
||||
sortCode: 10,
|
||||
required: true,
|
||||
hrEditable: true,
|
||||
hrEditable: false,
|
||||
remark: ""
|
||||
});
|
||||
|
||||
const emptyScope = (): Partial<OnboardingContractScope> => ({
|
||||
companyId: undefined,
|
||||
positionId: undefined,
|
||||
templateVersionId: undefined
|
||||
});
|
||||
|
||||
const templateForm = reactive<Partial<OnboardingContractTemplate>>(emptyTemplate());
|
||||
const versionForm = reactive<Partial<OnboardingContractVersion>>(emptyVersion());
|
||||
const variableForm = reactive<Partial<OnboardingTemplateVariable>>(emptyVariable());
|
||||
const scopeForm = reactive<Partial<OnboardingContractScope>>(emptyScope());
|
||||
|
||||
const enabledVersions = computed(() =>
|
||||
versions.value.filter(item => item.versionStatus === "enabled")
|
||||
);
|
||||
|
||||
const templateColumns: ColumnProps<OnboardingContractTemplate>[] = [
|
||||
{
|
||||
prop: "templateCode",
|
||||
label: "模板编码",
|
||||
minWidth: 160,
|
||||
search: { el: "input" }
|
||||
},
|
||||
{
|
||||
prop: "templateName",
|
||||
label: "模板名称",
|
||||
minWidth: 200,
|
||||
search: { el: "input" }
|
||||
},
|
||||
{
|
||||
prop: "contractType",
|
||||
label: "合同类型",
|
||||
width: 140,
|
||||
search: { el: "input" }
|
||||
},
|
||||
{ prop: "templateCode", label: "模板编码", minWidth: 160, search: { el: "input" } },
|
||||
{ prop: "templateName", label: "模板名称", minWidth: 200, search: { el: "input" } },
|
||||
{ prop: "contractType", label: "合同类型", width: 140, search: { el: "input" } },
|
||||
{
|
||||
prop: "status",
|
||||
label: "状态",
|
||||
@@ -602,24 +454,10 @@ const templateColumns: ColumnProps<OnboardingContractTemplate>[] = [
|
||||
],
|
||||
search: { el: "select" }
|
||||
},
|
||||
{
|
||||
prop: "description",
|
||||
label: "模板说明",
|
||||
minWidth: 200
|
||||
},
|
||||
{
|
||||
prop: "operation",
|
||||
label: "操作",
|
||||
fixed: "right",
|
||||
width: 260
|
||||
}
|
||||
{ prop: "description", label: "模板说明", minWidth: 200 },
|
||||
{ prop: "operation", label: "操作", fixed: "right", width: 260 }
|
||||
];
|
||||
|
||||
function formatTime(timeStr?: string) {
|
||||
if (!timeStr) return "-";
|
||||
return timeStr.replace("T", " ").substring(0, 19);
|
||||
}
|
||||
|
||||
function resetTemplate() {
|
||||
Object.assign(templateForm, emptyTemplate());
|
||||
}
|
||||
@@ -628,19 +466,6 @@ function resetVersion() {
|
||||
Object.assign(versionForm, emptyVersion());
|
||||
}
|
||||
|
||||
function resetVariable() {
|
||||
Object.assign(variableForm, emptyVariable());
|
||||
}
|
||||
|
||||
function resetScope() {
|
||||
Object.assign(scopeForm, emptyScope());
|
||||
}
|
||||
|
||||
async function loadCompanies() {
|
||||
const { data } = await onboardingTaskApi.listCompanies();
|
||||
companies.value = data;
|
||||
}
|
||||
|
||||
async function loadVersions() {
|
||||
if (!selectedTemplate.value) return;
|
||||
const { data } = await onboardingTaskApi.versionPage({
|
||||
@@ -648,7 +473,7 @@ async function loadVersions() {
|
||||
pageNum: 1,
|
||||
pageSize: 100
|
||||
});
|
||||
versions.value = data.list;
|
||||
versions.value = data?.list || [];
|
||||
}
|
||||
|
||||
async function loadVariables() {
|
||||
@@ -658,23 +483,13 @@ async function loadVariables() {
|
||||
pageNum: 1,
|
||||
pageSize: 100
|
||||
});
|
||||
variables.value = data.list;
|
||||
}
|
||||
|
||||
async function loadScopes() {
|
||||
if (!selectedTemplate.value) return;
|
||||
const { data } = await onboardingTaskApi.scopePage({
|
||||
templateId: selectedTemplate.value.id,
|
||||
pageNum: 1,
|
||||
pageSize: 100
|
||||
});
|
||||
scopes.value = data.list;
|
||||
variables.value = data?.list || [];
|
||||
}
|
||||
|
||||
async function openManagementDrawer(row: OnboardingContractTemplate) {
|
||||
selectedTemplate.value = row;
|
||||
drawerActiveTab.value = "versions";
|
||||
await Promise.all([loadVersions(), loadScopes()]);
|
||||
await loadVersions();
|
||||
drawerVisible.value = true;
|
||||
}
|
||||
|
||||
@@ -695,7 +510,6 @@ async function saveTemplate() {
|
||||
if (!templateForm.templateCode || !templateForm.templateName || !templateForm.contractType) {
|
||||
return ElMessage.warning("请填写模板编码、名称和合同类型");
|
||||
}
|
||||
|
||||
await onboardingTaskApi.saveTemplate(templateForm, templateEditing.value);
|
||||
templateVisible.value = false;
|
||||
templateTable.value?.refresh();
|
||||
@@ -723,10 +537,10 @@ function beforeDocxUpload(file: File) {
|
||||
}
|
||||
|
||||
async function uploadDocx(options: any) {
|
||||
const form = new FormData();
|
||||
form.append("file", options.file);
|
||||
const formData = new FormData();
|
||||
formData.append("file", options.file);
|
||||
try {
|
||||
const { data } = await onboardingTaskApi.uploadDocx(form);
|
||||
const { data } = await onboardingTaskApi.uploadDocx(formData);
|
||||
versionForm.sourceDocxFileId = data;
|
||||
options.onSuccess?.(data);
|
||||
ElMessage.success("DOCX 文件上传成功");
|
||||
@@ -737,17 +551,12 @@ async function uploadDocx(options: any) {
|
||||
|
||||
async function saveVersion() {
|
||||
if (!selectedTemplate.value || !versionForm.versionNo || !versionForm.sourceDocxFileId) {
|
||||
return ElMessage.warning("请填写版本号并上传DOCX文件");
|
||||
return ElMessage.warning("请填写版本号并上传 DOCX 文件");
|
||||
}
|
||||
|
||||
await onboardingTaskApi.addVersion({
|
||||
...versionForm,
|
||||
templateId: selectedTemplate.value.id
|
||||
});
|
||||
|
||||
await onboardingTaskApi.addVersion({ ...versionForm, templateId: selectedTemplate.value.id });
|
||||
versionVisible.value = false;
|
||||
await loadVersions();
|
||||
ElMessage.success("版本已保存");
|
||||
ElMessage.success("版本已保存,DOCX 变量已自动识别,请进入变量列表检查后再启用");
|
||||
}
|
||||
|
||||
async function changeVersionStatus(row: OnboardingContractVersion, status: string) {
|
||||
@@ -757,7 +566,7 @@ async function changeVersionStatus(row: OnboardingContractVersion, status: strin
|
||||
}
|
||||
|
||||
function downloadDocx(fileId?: number) {
|
||||
if (!fileId) return ElMessage.warning("暂无DOCX源文件");
|
||||
if (!fileId) return ElMessage.warning("暂无 DOCX 源文件");
|
||||
window.open(`/api/sys/dev/file/download?id=${fileId}`, "_blank");
|
||||
}
|
||||
|
||||
@@ -776,7 +585,8 @@ function variableTypeTag(type: string): EpTagType {
|
||||
employee: "primary",
|
||||
company: "success",
|
||||
contract: "warning",
|
||||
system: "info"
|
||||
system: "info",
|
||||
unresolved: "danger"
|
||||
};
|
||||
return map[type] || "info";
|
||||
}
|
||||
@@ -786,7 +596,8 @@ function variableTypeText(type: string) {
|
||||
employee: "员工填写",
|
||||
company: "公司信息",
|
||||
contract: "人事合同参数",
|
||||
system: "系统自动"
|
||||
system: "系统自动",
|
||||
unresolved: "待确认"
|
||||
};
|
||||
return map[type] || type;
|
||||
}
|
||||
@@ -806,87 +617,22 @@ async function openVariables(row: OnboardingContractVersion) {
|
||||
variablesVisible.value = true;
|
||||
}
|
||||
|
||||
function openVariableCreate() {
|
||||
variableEditing.value = false;
|
||||
resetVariable();
|
||||
variableVisible.value = true;
|
||||
}
|
||||
|
||||
function openVariableEdit(row: OnboardingTemplateVariable) {
|
||||
variableEditing.value = true;
|
||||
Object.assign(variableForm, row);
|
||||
Object.assign(variableForm, emptyVariable(), row);
|
||||
variableVisible.value = true;
|
||||
}
|
||||
|
||||
async function saveVariable() {
|
||||
if (
|
||||
!selectedVersion.value ||
|
||||
!variableForm.variableCode ||
|
||||
!variableForm.variableName ||
|
||||
!variableForm.variableType ||
|
||||
!variableForm.dataType
|
||||
) {
|
||||
return ElMessage.warning("请填写变量编码、名称、类型和数据类型");
|
||||
if (!selectedVersion.value || !variableForm.id || !variableForm.variableCode || !variableForm.variableName || !variableForm.variableType || !variableForm.dataType) {
|
||||
return ElMessage.warning("请完整确认变量名称、来源类型和数据类型");
|
||||
}
|
||||
|
||||
await onboardingTaskApi.saveVariable(
|
||||
{
|
||||
...variableForm,
|
||||
templateVersionId: selectedVersion.value.id
|
||||
},
|
||||
variableEditing.value
|
||||
await onboardingTaskApi.editVariable(
|
||||
{ ...variableForm, templateVersionId: selectedVersion.value.id }
|
||||
);
|
||||
|
||||
variableVisible.value = false;
|
||||
await loadVariables();
|
||||
ElMessage.success("变量已保存");
|
||||
ElMessage.success("变量已确认并保存");
|
||||
}
|
||||
|
||||
async function removeVariable(row: OnboardingTemplateVariable) {
|
||||
await ElMessageBox.confirm(`确认删除变量“${row.variableName}”?`, "删除确认", { type: "warning" });
|
||||
await onboardingTaskApi.deleteVariable([row.id]);
|
||||
await loadVariables();
|
||||
ElMessage.success("删除成功");
|
||||
}
|
||||
|
||||
async function onScopeCompanyChange(companyId: number) {
|
||||
scopeForm.positionId = undefined;
|
||||
const { data } = await onboardingTaskApi.listPositions(companyId);
|
||||
scopePositions.value = data;
|
||||
}
|
||||
|
||||
async function saveScope() {
|
||||
if (!selectedTemplate.value || !scopeForm.companyId || !scopeForm.templateVersionId) {
|
||||
return ElMessage.warning("请选择公司和已启用的合同版本");
|
||||
}
|
||||
|
||||
await onboardingTaskApi.addScope({
|
||||
...scopeForm,
|
||||
templateId: selectedTemplate.value.id
|
||||
});
|
||||
|
||||
await loadScopes();
|
||||
resetScope();
|
||||
ElMessage.success("适用范围已添加");
|
||||
}
|
||||
|
||||
async function removeScope(row: OnboardingContractScope) {
|
||||
await ElMessageBox.confirm("确认删除该适用范围?", "删除确认", { type: "warning" });
|
||||
await onboardingTaskApi.deleteScope([row.id]);
|
||||
await loadScopes();
|
||||
ElMessage.success("删除成功");
|
||||
}
|
||||
|
||||
const companyName = (id: number) => companies.value.find(c => c.id === id)?.companyName || id;
|
||||
|
||||
const positionName = (id?: number) => {
|
||||
if (!id) return "全公司 (所有岗位)";
|
||||
return scopePositions.value.find(p => p.id === id)?.positionName || id;
|
||||
};
|
||||
|
||||
const versionName = (id: number) => versions.value.find(v => v.id === id)?.versionNo || id;
|
||||
|
||||
onMounted(loadCompanies);
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
@@ -0,0 +1,451 @@
|
||||
<template>
|
||||
<div class="template-panel">
|
||||
<div class="toolbar">
|
||||
<el-input
|
||||
v-model="query.keyword"
|
||||
clearable
|
||||
placeholder="搜索模板编码或名称"
|
||||
class="keyword-input"
|
||||
@keyup.enter="loadTemplates"
|
||||
/>
|
||||
<el-select v-model="query.companyId" clearable filterable placeholder="全部公司" class="company-filter">
|
||||
<el-option v-for="item in companies" :key="item.id" :label="item.companyName" :value="item.id" />
|
||||
</el-select>
|
||||
<el-button type="primary" @click="loadTemplates">查询</el-button>
|
||||
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
|
||||
<div class="toolbar-spacer" />
|
||||
<el-button type="primary" icon="Plus" @click="openCreate">新增入职模板</el-button>
|
||||
</div>
|
||||
|
||||
<el-alert
|
||||
title="入职模板统一管理公司、岗位、合同、员工补充字段和上传材料;创建任务后会冻结本次配置。"
|
||||
type="info"
|
||||
:closable="false"
|
||||
show-icon
|
||||
class="tip"
|
||||
/>
|
||||
|
||||
<el-table v-loading="loading" :data="rows" border>
|
||||
<el-table-column prop="templateCode" label="模板编码" min-width="150" />
|
||||
<el-table-column prop="templateName" label="模板名称" min-width="180" />
|
||||
<el-table-column prop="companyName" label="所属公司" min-width="160" />
|
||||
<el-table-column label="适用岗位" min-width="220">
|
||||
<template #default="scope">
|
||||
<el-space wrap>
|
||||
<el-tag v-for="name in scope.row.positionNames" :key="name" size="small">{{ name }}</el-tag>
|
||||
</el-space>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="配置内容" min-width="230">
|
||||
<template #default="scope">
|
||||
合同 {{ scope.row.contractCount || 0 }} 份 / 补充字段 {{ scope.row.formFieldCount || 0 }} 项 / 材料 {{ scope.row.materialCount || 0 }} 项
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="status" label="状态" width="90" align="center">
|
||||
<template #default="scope">
|
||||
<el-tag :type="scope.row.status === 'enable' ? 'success' : 'info'">
|
||||
{{ scope.row.status === "enable" ? "启用" : "停用" }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="150" fixed="right">
|
||||
<template #default="scope">
|
||||
<el-button link type="primary" @click="openEdit(scope.row)">编辑</el-button>
|
||||
<el-button link type="danger" @click="removeTemplate(scope.row)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<div class="pagination">
|
||||
<el-pagination
|
||||
v-model:current-page="query.pageNum"
|
||||
v-model:page-size="query.pageSize"
|
||||
layout="total, sizes, prev, pager, next"
|
||||
:total="total"
|
||||
@current-change="loadTemplates"
|
||||
@size-change="loadTemplates"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<el-dialog
|
||||
v-model="visible"
|
||||
:title="form.id ? '编辑入职模板' : '新增入职模板'"
|
||||
width="1080px"
|
||||
top="5vh"
|
||||
destroy-on-close
|
||||
>
|
||||
<el-form label-width="110px">
|
||||
<el-divider content-position="left">基本信息</el-divider>
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="8">
|
||||
<el-form-item label="模板编码" required>
|
||||
<el-input v-model="form.templateCode" placeholder="例如 FULLTIME_DEFAULT" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="模板名称" required>
|
||||
<el-input v-model="form.templateName" placeholder="例如 全职员工入职模板" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="状态" required>
|
||||
<el-radio-group v-model="form.status">
|
||||
<el-radio value="enable">启用</el-radio>
|
||||
<el-radio value="disable">停用</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="10">
|
||||
<el-form-item label="所属公司" required>
|
||||
<el-select v-model="form.companyId" filterable class="full-width" @change="onCompanyChange">
|
||||
<el-option v-for="item in companies" :key="item.id" :label="item.companyName" :value="item.id" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="14">
|
||||
<el-form-item label="适用岗位" required>
|
||||
<el-select v-model="form.positionIds" multiple filterable collapse-tags class="full-width" :disabled="!form.companyId">
|
||||
<el-option v-for="item in positions" :key="item.id" :label="item.positionName" :value="item.id" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-form-item label="模板说明">
|
||||
<el-input v-model="form.description" type="textarea" :rows="2" />
|
||||
</el-form-item>
|
||||
|
||||
<el-divider content-position="left">合同文件</el-divider>
|
||||
<div class="section-action">
|
||||
<span>合同变量由上传 DOCX 时自动识别,这里只选择已确认并启用的版本。</span>
|
||||
<el-button type="primary" plain icon="Plus" @click="addContract">添加合同</el-button>
|
||||
</div>
|
||||
<el-table :data="form.contracts" border empty-text="请至少添加一份合同">
|
||||
<el-table-column label="合同模板" min-width="210">
|
||||
<template #default="scope">
|
||||
<el-select v-model="scope.row.contractTemplateId" filterable class="full-width" @change="onContractTemplateChange(scope.row)">
|
||||
<el-option v-for="item in contractTemplates" :key="item.id" :label="item.templateName" :value="item.id" />
|
||||
</el-select>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="启用版本" min-width="180">
|
||||
<template #default="scope">
|
||||
<el-select v-model="scope.row.contractTemplateVersionId" class="full-width" :disabled="!scope.row.contractTemplateId">
|
||||
<el-option
|
||||
v-for="item in versionOptions[scope.row.contractTemplateId] || []"
|
||||
:key="item.id"
|
||||
:label="item.versionNo"
|
||||
:value="item.id"
|
||||
/>
|
||||
</el-select>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="必须确认" width="100" align="center">
|
||||
<template #default="scope"><el-switch v-model="scope.row.required" /></template>
|
||||
</el-table-column>
|
||||
<el-table-column label="打印份数" width="130">
|
||||
<template #default="scope"><el-input-number v-model="scope.row.printCopies" :min="1" :max="10" /></template>
|
||||
</el-table-column>
|
||||
<el-table-column label="排序" width="110">
|
||||
<template #default="scope"><el-input-number v-model="scope.row.sortCode" :min="0" /></template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="80" align="center">
|
||||
<template #default="scope"><el-button link type="danger" @click="form.contracts.splice(scope.$index, 1)">移除</el-button></template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<el-divider content-position="left">员工补充字段</el-divider>
|
||||
<el-alert title="姓名、手机号、身份证号等基本信息固定展示;这里只选择额外需要员工填写的字段。" type="info" :closable="false" class="section-tip" />
|
||||
<el-table :data="formFields" border max-height="260">
|
||||
<el-table-column label="选择" width="70" align="center">
|
||||
<template #default="scope">
|
||||
<el-checkbox :model-value="hasField(scope.row.id)" @change="value => toggleField(scope.row.id, Boolean(value))" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="fieldCode" label="字段编码" min-width="170" />
|
||||
<el-table-column prop="fieldName" label="字段名称" min-width="180" />
|
||||
<el-table-column prop="dataType" label="数据类型" width="110" />
|
||||
<el-table-column label="必填" width="100" align="center">
|
||||
<template #default="scope">
|
||||
<el-switch v-if="hasField(scope.row.id)" v-model="fieldConfig(scope.row.id)!.required" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="排序" width="120">
|
||||
<template #default="scope">
|
||||
<el-input-number v-if="hasField(scope.row.id)" v-model="fieldConfig(scope.row.id)!.sortCode" :min="0" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<el-divider content-position="left">员工上传材料</el-divider>
|
||||
<el-table :data="materialTypes" border max-height="260">
|
||||
<el-table-column label="选择" width="70" align="center">
|
||||
<template #default="scope">
|
||||
<el-checkbox :model-value="hasMaterial(scope.row.id)" @change="value => toggleMaterial(scope.row.id, Boolean(value))" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="materialCode" label="材料编码" min-width="170" />
|
||||
<el-table-column prop="materialName" label="材料名称" min-width="200" />
|
||||
<el-table-column prop="maxFileCount" label="最多文件" width="100" align="center" />
|
||||
<el-table-column label="必传" width="100" align="center">
|
||||
<template #default="scope">
|
||||
<el-switch v-if="hasMaterial(scope.row.id)" v-model="materialConfig(scope.row.id)!.required" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="排序" width="120">
|
||||
<template #default="scope">
|
||||
<el-input-number v-if="hasMaterial(scope.row.id)" v-model="materialConfig(scope.row.id)!.sortCode" :min="0" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-form>
|
||||
|
||||
<template #footer>
|
||||
<el-button @click="visible = false">取消</el-button>
|
||||
<el-button type="primary" :loading="saving" @click="save">保存</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { onMounted, reactive, ref } from "vue";
|
||||
import { ElMessage, ElMessageBox } from "element-plus";
|
||||
import {
|
||||
onboardingTaskApi,
|
||||
type OnboardingCompanyOption,
|
||||
type OnboardingContractTemplate,
|
||||
type OnboardingContractVersion,
|
||||
type OnboardingFormField,
|
||||
type OnboardingMaterialType,
|
||||
type OnboardingPositionOption,
|
||||
type OnboardingTemplate,
|
||||
type OnboardingTemplateContractConfig,
|
||||
type OnboardingTemplateFieldConfig,
|
||||
type OnboardingTemplateMaterialConfig
|
||||
} from "@/api/modules/biz/onboarding";
|
||||
|
||||
const loading = ref(false);
|
||||
const saving = ref(false);
|
||||
const visible = ref(false);
|
||||
const total = ref(0);
|
||||
const rows = ref<OnboardingTemplate[]>([]);
|
||||
const companies = ref<OnboardingCompanyOption[]>([]);
|
||||
const positions = ref<OnboardingPositionOption[]>([]);
|
||||
const contractTemplates = ref<OnboardingContractTemplate[]>([]);
|
||||
const formFields = ref<OnboardingFormField[]>([]);
|
||||
const materialTypes = ref<OnboardingMaterialType[]>([]);
|
||||
const versionOptions = reactive<Record<number, OnboardingContractVersion[]>>({});
|
||||
|
||||
const query = reactive({
|
||||
keyword: "",
|
||||
companyId: undefined as number | undefined,
|
||||
pageNum: 1,
|
||||
pageSize: 20
|
||||
});
|
||||
|
||||
const emptyForm = (): OnboardingTemplate => ({
|
||||
id: undefined as unknown as number,
|
||||
templateCode: "",
|
||||
templateName: "",
|
||||
companyId: undefined as unknown as number,
|
||||
positionIds: [],
|
||||
contracts: [],
|
||||
formFields: [],
|
||||
materials: [],
|
||||
status: "enable",
|
||||
description: ""
|
||||
});
|
||||
const form = reactive<OnboardingTemplate>(emptyForm());
|
||||
|
||||
async function loadBaseData() {
|
||||
try {
|
||||
const [companyRes, contractRes, fieldRes, materialRes] = await Promise.all([
|
||||
onboardingTaskApi.listCompanies().catch(() => ({ data: [] })),
|
||||
onboardingTaskApi.templatePage({ pageNum: 1, pageSize: 100, status: "enable" }).catch(() => ({ data: { list: [] } })),
|
||||
onboardingTaskApi.listFormFields().catch(() => ({ data: [] })),
|
||||
onboardingTaskApi.listMaterialTypes().catch(() => ({ data: [] }))
|
||||
]);
|
||||
companies.value = companyRes.data || [];
|
||||
contractTemplates.value = contractRes.data?.list || [];
|
||||
formFields.value = (fieldRes.data || []).filter(item => item.status === "enable");
|
||||
materialTypes.value = (materialRes.data || []).filter(item => item.status === "enable");
|
||||
} catch (e) {
|
||||
console.error("加载基础数据失败", e);
|
||||
}
|
||||
}
|
||||
|
||||
async function loadTemplates() {
|
||||
loading.value = true;
|
||||
try {
|
||||
const { data } = await onboardingTaskApi.onboardingTemplatePage(query);
|
||||
rows.value = data?.list || [];
|
||||
total.value = data?.total || 0;
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
function resetQuery() {
|
||||
Object.assign(query, { keyword: "", companyId: undefined, pageNum: 1 });
|
||||
loadTemplates();
|
||||
}
|
||||
|
||||
async function loadPositions(companyId: number) {
|
||||
const { data } = await onboardingTaskApi.listPositions(companyId);
|
||||
positions.value = data || [];
|
||||
}
|
||||
|
||||
async function onCompanyChange(companyId: number) {
|
||||
form.positionIds = [];
|
||||
positions.value = [];
|
||||
if (companyId) await loadPositions(companyId);
|
||||
}
|
||||
|
||||
function addContract() {
|
||||
form.contracts.push({
|
||||
contractTemplateId: undefined as unknown as number,
|
||||
contractTemplateVersionId: undefined as unknown as number,
|
||||
required: true,
|
||||
printCopies: 1,
|
||||
sortCode: (form.contracts.length + 1) * 10
|
||||
});
|
||||
}
|
||||
|
||||
async function loadVersions(templateId: number) {
|
||||
if (!templateId || versionOptions[templateId]) return;
|
||||
const { data } = await onboardingTaskApi.versionPage({ templateId, pageNum: 1, pageSize: 100 });
|
||||
versionOptions[templateId] = (data?.list || []).filter(item => item.versionStatus === "enabled");
|
||||
}
|
||||
|
||||
async function onContractTemplateChange(row: OnboardingTemplateContractConfig) {
|
||||
row.contractTemplateVersionId = undefined as unknown as number;
|
||||
await loadVersions(row.contractTemplateId);
|
||||
}
|
||||
|
||||
function hasField(fieldId: number) {
|
||||
return form.formFields.some(item => item.fieldId === fieldId);
|
||||
}
|
||||
|
||||
function fieldConfig(fieldId: number) {
|
||||
return form.formFields.find(item => item.fieldId === fieldId);
|
||||
}
|
||||
|
||||
function toggleField(fieldId: number, selected: boolean) {
|
||||
if (selected && !hasField(fieldId)) {
|
||||
form.formFields.push({ fieldId, required: true, sortCode: (form.formFields.length + 1) * 10 });
|
||||
} else if (!selected) {
|
||||
form.formFields = form.formFields.filter(item => item.fieldId !== fieldId);
|
||||
}
|
||||
}
|
||||
|
||||
function hasMaterial(materialTypeId: number) {
|
||||
return form.materials.some(item => item.materialTypeId === materialTypeId);
|
||||
}
|
||||
|
||||
function materialConfig(materialTypeId: number) {
|
||||
return form.materials.find(item => item.materialTypeId === materialTypeId);
|
||||
}
|
||||
|
||||
function toggleMaterial(materialTypeId: number, selected: boolean) {
|
||||
if (selected && !hasMaterial(materialTypeId)) {
|
||||
form.materials.push({ materialTypeId, required: true, sortCode: (form.materials.length + 1) * 10 });
|
||||
} else if (!selected) {
|
||||
form.materials = form.materials.filter(item => item.materialTypeId !== materialTypeId);
|
||||
}
|
||||
}
|
||||
|
||||
function openCreate() {
|
||||
Object.assign(form, emptyForm());
|
||||
positions.value = [];
|
||||
visible.value = true;
|
||||
}
|
||||
|
||||
async function openEdit(row: OnboardingTemplate) {
|
||||
const { data } = await onboardingTaskApi.onboardingTemplateDetail({ id: row.id! });
|
||||
Object.assign(form, emptyForm(), data);
|
||||
await loadPositions(form.companyId);
|
||||
await Promise.all(form.contracts.map(item => loadVersions(item.contractTemplateId)));
|
||||
visible.value = true;
|
||||
}
|
||||
|
||||
async function save() {
|
||||
if (!form.templateCode.trim() || !form.templateName.trim() || !form.companyId) {
|
||||
return ElMessage.warning("请填写模板编码、模板名称和所属公司");
|
||||
}
|
||||
if (!form.positionIds.length) return ElMessage.warning("请至少选择一个适用岗位");
|
||||
if (!form.contracts.length || form.contracts.some(item => !item.contractTemplateId || !item.contractTemplateVersionId)) {
|
||||
return ElMessage.warning("请完整选择至少一份合同模板及版本");
|
||||
}
|
||||
|
||||
saving.value = true;
|
||||
try {
|
||||
await onboardingTaskApi.saveOnboardingTemplate(form);
|
||||
ElMessage.success("入职模板保存成功");
|
||||
visible.value = false;
|
||||
await loadTemplates();
|
||||
} finally {
|
||||
saving.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
async function removeTemplate(row: OnboardingTemplate) {
|
||||
await ElMessageBox.confirm(`确认删除入职模板“${row.templateName}”吗?`, "删除确认", { type: "warning" });
|
||||
await onboardingTaskApi.deleteOnboardingTemplate([row.id!]);
|
||||
ElMessage.success("删除成功");
|
||||
await loadTemplates();
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
await Promise.all([loadBaseData(), loadTemplates()]);
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.template-panel {
|
||||
padding-top: 4px;
|
||||
}
|
||||
|
||||
.toolbar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
margin-bottom: 14px;
|
||||
}
|
||||
|
||||
.keyword-input {
|
||||
width: 260px;
|
||||
}
|
||||
|
||||
.company-filter {
|
||||
width: 220px;
|
||||
}
|
||||
|
||||
.toolbar-spacer {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.tip,
|
||||
.section-tip {
|
||||
margin-bottom: 14px;
|
||||
}
|
||||
|
||||
.pagination {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
.full-width {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.section-action {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 10px;
|
||||
color: var(--el-text-color-secondary);
|
||||
}
|
||||
</style>
|
||||
@@ -82,103 +82,9 @@
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane label="公司岗位要求" name="requirements">
|
||||
<el-tab-pane label="入职模板" name="templates">
|
||||
<div class="tab-card-wrapper">
|
||||
<el-alert title="请先选择公司,再为全公司或某个岗位分别配置员工必填信息和上传资料。" type="info" :closable="false" class="hint" />
|
||||
|
||||
<el-select v-model="selectedCompanyId" placeholder="请选择公司" filterable @change="loadRequirements">
|
||||
<el-option v-for="company in companies" :key="company.id" :label="company.companyName" :value="company.id" />
|
||||
</el-select>
|
||||
|
||||
<template v-if="selectedCompanyId">
|
||||
<el-divider />
|
||||
|
||||
<el-tabs v-model="requirementTab">
|
||||
<el-tab-pane label="填写字段要求" name="form">
|
||||
<div class="toolbar">
|
||||
<el-button type="primary" @click="openRequirementCreate('form')">
|
||||
新增填写要求
|
||||
</el-button>
|
||||
</div>
|
||||
|
||||
<el-table :data="formRequirements" border>
|
||||
<el-table-column label="字段">
|
||||
<template #default="scope">
|
||||
{{ fieldName(scope.row.fieldId) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column label="岗位">
|
||||
<template #default="scope">
|
||||
{{ positionName(scope.row.positionId) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column label="必填">
|
||||
<template #default="scope">
|
||||
{{ scope.row.required ? "是" : "否" }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column prop="sortCode" label="排序" />
|
||||
|
||||
<el-table-column label="操作" width="140">
|
||||
<template #default="scope">
|
||||
<el-button link type="primary" @click="openRequirementEdit('form', scope.row)">
|
||||
编辑
|
||||
</el-button>
|
||||
|
||||
<el-button link type="danger" @click="removeRequirement('form', scope.row)">
|
||||
删除
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane label="上传资料要求" name="material">
|
||||
<div class="toolbar">
|
||||
<el-button type="primary" @click="openRequirementCreate('material')">
|
||||
新增资料要求
|
||||
</el-button>
|
||||
</div>
|
||||
|
||||
<el-table :data="materialRequirements" border>
|
||||
<el-table-column label="资料类型">
|
||||
<template #default="scope">
|
||||
{{ materialName(scope.row.materialTypeId) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column label="岗位">
|
||||
<template #default="scope">
|
||||
{{ positionName(scope.row.positionId) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column label="必传">
|
||||
<template #default="scope">
|
||||
{{ scope.row.required ? "是" : "否" }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column prop="sortCode" label="排序" />
|
||||
|
||||
<el-table-column label="操作" width="140">
|
||||
<template #default="scope">
|
||||
<el-button link type="primary" @click="openRequirementEdit('material', scope.row)">
|
||||
编辑
|
||||
</el-button>
|
||||
|
||||
<el-button link type="danger" @click="removeRequirement('material', scope.row)">
|
||||
删除
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</template>
|
||||
<OnboardingTemplatePanel />
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
@@ -275,133 +181,32 @@
|
||||
</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog v-model="requirementVisible" :title="requirementKind === 'form' ? '填写字段要求' : '上传资料要求'" width="560px"
|
||||
destroy-on-close>
|
||||
<el-form :model="requirementForm" label-width="110px">
|
||||
<el-form-item label="所属公司">
|
||||
<el-select v-model="requirementForm.companyId" disabled>
|
||||
<el-option v-for="company in companies" :key="company.id" :label="company.companyName"
|
||||
:value="company.id" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="适用岗位">
|
||||
<el-select v-model="requirementForm.positionId" clearable placeholder="不选表示全公司">
|
||||
<el-option v-for="position in positions" :key="position.id" :label="position.positionName"
|
||||
:value="position.id" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item v-if="requirementKind === 'form'" label="填写字段" required>
|
||||
<el-select v-model="requirementForm.fieldId">
|
||||
<el-option v-for="field in enabledFields" :key="field.id" :label="field.fieldName" :value="field.id" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item v-else label="资料类型" required>
|
||||
<el-select v-model="requirementForm.materialTypeId">
|
||||
<el-option v-for="material in enabledMaterials" :key="material.id" :label="material.materialName"
|
||||
:value="material.id" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="是否必填/必传">
|
||||
<el-switch v-model="requirementForm.required" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="排序">
|
||||
<el-input-number v-model="requirementForm.sortCode" :min="0" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<template #footer>
|
||||
<el-button @click="requirementVisible = false">
|
||||
取消
|
||||
</el-button>
|
||||
|
||||
<el-button type="primary" @click="saveRequirement">
|
||||
保存
|
||||
</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="onboardingEntryConfig">
|
||||
import {
|
||||
onboardingTaskApi,
|
||||
type OnboardingCompanyOption,
|
||||
type OnboardingFormField,
|
||||
type OnboardingFormRequirement,
|
||||
type OnboardingMaterialRequirement,
|
||||
type OnboardingMaterialType,
|
||||
type OnboardingPositionOption
|
||||
type OnboardingMaterialType
|
||||
} from "@/api/modules/biz/onboarding";
|
||||
import OnboardingTemplatePanel from "./components/OnboardingTemplatePanel.vue";
|
||||
|
||||
const activeTab = ref("fields");
|
||||
const requirementTab = ref("form");
|
||||
|
||||
const fields = ref<OnboardingFormField[]>([]);
|
||||
const materialTypes = ref<OnboardingMaterialType[]>([]);
|
||||
const companies = ref<OnboardingCompanyOption[]>([]);
|
||||
const positions = ref<OnboardingPositionOption[]>([]);
|
||||
|
||||
const formRequirements = ref<OnboardingFormRequirement[]>([]);
|
||||
const materialRequirements = ref<OnboardingMaterialRequirement[]>([]);
|
||||
|
||||
const selectedCompanyId = ref<number>();
|
||||
|
||||
const fieldVisible = ref(false);
|
||||
const materialVisible = ref(false);
|
||||
const requirementVisible = ref(false);
|
||||
|
||||
const requirementKind = ref<"form" | "material">("form");
|
||||
|
||||
const fieldForm = reactive<Partial<OnboardingFormField>>({});
|
||||
const materialForm = reactive<Partial<OnboardingMaterialType>>({});
|
||||
const requirementForm = reactive<any>({});
|
||||
|
||||
const enabledFields = computed(() =>
|
||||
fields.value.filter(item => item.status === "enable")
|
||||
);
|
||||
|
||||
const enabledMaterials = computed(() =>
|
||||
materialTypes.value.filter(item => item.status === "enable")
|
||||
);
|
||||
|
||||
async function loadBase() {
|
||||
const [
|
||||
fieldResult,
|
||||
materialResult,
|
||||
companyResult
|
||||
] = await Promise.all([
|
||||
const [fieldResult, materialResult] = await Promise.all([
|
||||
onboardingTaskApi.listFormFields(),
|
||||
onboardingTaskApi.listMaterialTypes(),
|
||||
onboardingTaskApi.listCompanies()
|
||||
onboardingTaskApi.listMaterialTypes()
|
||||
]);
|
||||
|
||||
fields.value = fieldResult.data;
|
||||
materialTypes.value = materialResult.data;
|
||||
companies.value = companyResult.data;
|
||||
}
|
||||
|
||||
async function loadRequirements() {
|
||||
if (!selectedCompanyId.value) return;
|
||||
|
||||
const [
|
||||
formResult,
|
||||
materialResult,
|
||||
positionResult
|
||||
] = await Promise.all([
|
||||
onboardingTaskApi.listFormRequirements(selectedCompanyId.value),
|
||||
onboardingTaskApi.listMaterialRequirements(selectedCompanyId.value),
|
||||
onboardingTaskApi.listPositions(selectedCompanyId.value)
|
||||
]);
|
||||
|
||||
formRequirements.value = formResult.data;
|
||||
materialRequirements.value = materialResult.data;
|
||||
positions.value = positionResult.data;
|
||||
fields.value = fieldResult.data || [];
|
||||
materialTypes.value = materialResult.data || [];
|
||||
}
|
||||
|
||||
function resetField() {
|
||||
@@ -440,20 +245,12 @@ function openFieldEdit(row: OnboardingFormField) {
|
||||
}
|
||||
|
||||
async function saveField() {
|
||||
if (
|
||||
!fieldForm.fieldCode ||
|
||||
!fieldForm.fieldName ||
|
||||
!fieldForm.dataType
|
||||
) {
|
||||
return ElMessage.warning(
|
||||
"请填写字段编码、名称和数据类型"
|
||||
);
|
||||
if (!fieldForm.fieldCode || !fieldForm.fieldName || !fieldForm.dataType) {
|
||||
return ElMessage.warning("请填写字段编码、名称和数据类型");
|
||||
}
|
||||
|
||||
await onboardingTaskApi.saveFormField(fieldForm);
|
||||
|
||||
fieldVisible.value = false;
|
||||
|
||||
ElMessage.success("保存成功");
|
||||
await loadBase();
|
||||
}
|
||||
|
||||
@@ -468,140 +265,29 @@ function openMaterialEdit(row: OnboardingMaterialType) {
|
||||
}
|
||||
|
||||
async function saveMaterial() {
|
||||
if (
|
||||
!materialForm.materialCode ||
|
||||
!materialForm.materialName
|
||||
) {
|
||||
return ElMessage.warning(
|
||||
"请填写资料编码和名称"
|
||||
);
|
||||
if (!materialForm.materialCode || !materialForm.materialName) {
|
||||
return ElMessage.warning("请填写资料编码和名称");
|
||||
}
|
||||
|
||||
await onboardingTaskApi.saveMaterialType(materialForm);
|
||||
|
||||
materialVisible.value = false;
|
||||
|
||||
ElMessage.success("保存成功");
|
||||
await loadBase();
|
||||
}
|
||||
|
||||
async function removeField(row: OnboardingFormField) {
|
||||
await ElMessageBox.confirm(
|
||||
`确认删除字段“${row.fieldName}”?`,
|
||||
"删除确认"
|
||||
);
|
||||
|
||||
await ElMessageBox.confirm(`确认删除字段“${row.fieldName}”?`, "删除确认");
|
||||
await onboardingTaskApi.deleteFormFields([row.id]);
|
||||
|
||||
ElMessage.success("删除成功");
|
||||
await loadBase();
|
||||
}
|
||||
|
||||
async function removeMaterial(row: OnboardingMaterialType) {
|
||||
await ElMessageBox.confirm(
|
||||
`确认删除资料类型“${row.materialName}”?`,
|
||||
"删除确认"
|
||||
);
|
||||
|
||||
await ElMessageBox.confirm(`确认删除资料类型“${row.materialName}”?`, "删除确认");
|
||||
await onboardingTaskApi.deleteMaterialTypes([row.id]);
|
||||
|
||||
ElMessage.success("删除成功");
|
||||
await loadBase();
|
||||
}
|
||||
|
||||
function resetRequirement() {
|
||||
Object.assign(requirementForm, {
|
||||
id: undefined,
|
||||
companyId: selectedCompanyId.value,
|
||||
positionId: undefined,
|
||||
fieldId: undefined,
|
||||
materialTypeId: undefined,
|
||||
required: true,
|
||||
sortCode: 0
|
||||
});
|
||||
}
|
||||
|
||||
function openRequirementCreate(
|
||||
kind: "form" | "material"
|
||||
) {
|
||||
requirementKind.value = kind;
|
||||
|
||||
resetRequirement();
|
||||
|
||||
requirementVisible.value = true;
|
||||
}
|
||||
|
||||
function openRequirementEdit(
|
||||
kind: "form" | "material",
|
||||
row: any
|
||||
) {
|
||||
requirementKind.value = kind;
|
||||
|
||||
Object.assign(requirementForm, row);
|
||||
|
||||
requirementVisible.value = true;
|
||||
}
|
||||
|
||||
async function saveRequirement() {
|
||||
if (
|
||||
requirementKind.value === "form" &&
|
||||
!requirementForm.fieldId
|
||||
) {
|
||||
return ElMessage.warning("请选择填写字段");
|
||||
}
|
||||
|
||||
if (
|
||||
requirementKind.value === "material" &&
|
||||
!requirementForm.materialTypeId
|
||||
) {
|
||||
return ElMessage.warning("请选择资料类型");
|
||||
}
|
||||
|
||||
if (requirementKind.value === "form") {
|
||||
await onboardingTaskApi.saveFormRequirement(
|
||||
requirementForm
|
||||
);
|
||||
} else {
|
||||
await onboardingTaskApi.saveMaterialRequirement(
|
||||
requirementForm
|
||||
);
|
||||
}
|
||||
|
||||
requirementVisible.value = false;
|
||||
|
||||
await loadRequirements();
|
||||
}
|
||||
|
||||
async function removeRequirement(
|
||||
kind: "form" | "material",
|
||||
row: any
|
||||
) {
|
||||
await ElMessageBox.confirm(
|
||||
"确认删除该要求?",
|
||||
"删除确认"
|
||||
);
|
||||
|
||||
if (kind === "form") {
|
||||
await onboardingTaskApi.deleteFormRequirements([
|
||||
row.id
|
||||
]);
|
||||
} else {
|
||||
await onboardingTaskApi.deleteMaterialRequirements([
|
||||
row.id
|
||||
]);
|
||||
}
|
||||
|
||||
await loadRequirements();
|
||||
}
|
||||
|
||||
const fieldName = (id: number) =>
|
||||
fields.value.find(item => item.id === id)?.fieldName || id;
|
||||
|
||||
const materialName = (id: number) =>
|
||||
materialTypes.value.find(item => item.id === id)?.materialName || id;
|
||||
|
||||
const positionName = (id?: number) =>
|
||||
!id
|
||||
? "全公司"
|
||||
: positions.value.find(item => item.id === id)?.positionName || id;
|
||||
|
||||
onMounted(loadBase);
|
||||
</script>
|
||||
|
||||
|
||||
@@ -111,7 +111,7 @@
|
||||
</div>
|
||||
|
||||
<!-- 发起任务弹窗 -->
|
||||
<el-dialog v-model="createVisible" title="发起入职任务" width="520px" destroy-on-close>
|
||||
<el-dialog v-model="createVisible" title="发起入职任务" width="760px" destroy-on-close>
|
||||
<el-form :model="form" label-width="90px">
|
||||
<el-form-item label="所属公司" required>
|
||||
<el-select
|
||||
@@ -132,11 +132,55 @@
|
||||
placeholder="请选择岗位"
|
||||
class="full-width"
|
||||
:disabled="!form.companyId"
|
||||
@change="onPositionChange"
|
||||
>
|
||||
<el-option v-for="item in positions" :key="item.id" :label="item.positionName" :value="item.id" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="入职模板" required>
|
||||
<el-select
|
||||
v-model="form.onboardingTemplateId"
|
||||
filterable
|
||||
placeholder="请选择入职模板"
|
||||
class="full-width"
|
||||
:disabled="!form.positionId"
|
||||
@change="onOnboardingTemplateChange"
|
||||
>
|
||||
<el-option v-for="item in onboardingTemplates" :key="item.id" :label="item.templateName" :value="item.id" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<template v-if="form.contractVariables.length">
|
||||
<el-divider content-position="left">合同人事参数</el-divider>
|
||||
<el-alert
|
||||
title="以下内容会在候选人查看合同时自动替换到 DOCX 中,请在发送链接前确认。"
|
||||
type="warning"
|
||||
:closable="false"
|
||||
class="contract-variable-tip"
|
||||
/>
|
||||
<el-form-item
|
||||
v-for="item in form.contractVariables"
|
||||
:key="`${item.contractTemplateVersionId}-${item.variableCode}`"
|
||||
:label="item.variableName"
|
||||
:required="item.required"
|
||||
>
|
||||
<el-date-picker
|
||||
v-if="item.dataType === 'date'"
|
||||
v-model="item.variableValue"
|
||||
type="date"
|
||||
value-format="YYYY-MM-DD"
|
||||
placeholder="请选择日期"
|
||||
class="full-width"
|
||||
/>
|
||||
<el-input
|
||||
v-else
|
||||
v-model="item.variableValue"
|
||||
:placeholder="`${item.contractName} · ${item.variableCode}`"
|
||||
/>
|
||||
</el-form-item>
|
||||
</template>
|
||||
|
||||
<el-form-item label="人事备注">
|
||||
<el-input v-model="form.remark" type="textarea" :rows="3" placeholder="填写该候选人入职的相关备注(选填)" />
|
||||
</el-form-item>
|
||||
@@ -548,6 +592,8 @@ import {
|
||||
onboardingTaskApi,
|
||||
type OnboardingCompanyOption,
|
||||
type OnboardingPositionOption,
|
||||
type OnboardingTemplate,
|
||||
type OnboardingContractTemplate,
|
||||
type OnboardingTask,
|
||||
type OnboardingTaskContractVariableGroup
|
||||
} from "@/api/modules/biz/onboarding";
|
||||
@@ -557,6 +603,8 @@ const proTable = ref<ProTableInstance>();
|
||||
|
||||
const companies = ref<OnboardingCompanyOption[]>([]);
|
||||
const positions = ref<OnboardingPositionOption[]>([]);
|
||||
const onboardingTemplates = ref<OnboardingTemplate[]>([]);
|
||||
const contractTemplates = ref<OnboardingContractTemplate[]>([]);
|
||||
|
||||
const createVisible = ref(false);
|
||||
const creating = ref(false);
|
||||
@@ -582,6 +630,16 @@ const statusCounts = reactive({
|
||||
const form = reactive({
|
||||
companyId: undefined as number | undefined,
|
||||
positionId: undefined as number | undefined,
|
||||
onboardingTemplateId: undefined as number | undefined,
|
||||
contractVariables: [] as Array<{
|
||||
contractTemplateVersionId: number;
|
||||
variableCode: string;
|
||||
variableName: string;
|
||||
variableValue?: string;
|
||||
dataType: string;
|
||||
required: boolean;
|
||||
contractName: string;
|
||||
}>,
|
||||
remark: ""
|
||||
});
|
||||
|
||||
@@ -724,25 +782,88 @@ async function getTableData(params: any) {
|
||||
}
|
||||
|
||||
async function loadCompanies() {
|
||||
const { data } = await onboardingTaskApi.listCompanies();
|
||||
companies.value = data;
|
||||
try {
|
||||
const [companyResult, contractResult] = await Promise.all([
|
||||
onboardingTaskApi.listCompanies().catch(() => ({ data: [] })),
|
||||
onboardingTaskApi.templatePage({ pageNum: 1, pageSize: 100, status: "enable" }).catch(() => ({ data: { list: [] } }))
|
||||
]);
|
||||
companies.value = companyResult.data || [];
|
||||
contractTemplates.value = contractResult.data?.list || [];
|
||||
} catch (e) {
|
||||
console.error("加载公司与合同数据失败", e);
|
||||
}
|
||||
}
|
||||
|
||||
async function onCompanyChange(companyId: number) {
|
||||
form.positionId = undefined;
|
||||
form.onboardingTemplateId = undefined;
|
||||
form.contractVariables = [];
|
||||
onboardingTemplates.value = [];
|
||||
const { data } = await onboardingTaskApi.listPositions(companyId);
|
||||
positions.value = data;
|
||||
positions.value = data || [];
|
||||
}
|
||||
|
||||
async function onPositionChange(positionId: number) {
|
||||
form.onboardingTemplateId = undefined;
|
||||
form.contractVariables = [];
|
||||
onboardingTemplates.value = [];
|
||||
if (!form.companyId || !positionId) return;
|
||||
const { data } = await onboardingTaskApi.listEnabledOnboardingTemplates(form.companyId, positionId);
|
||||
onboardingTemplates.value = data || [];
|
||||
if (onboardingTemplates.value.length === 1) {
|
||||
form.onboardingTemplateId = onboardingTemplates.value[0].id;
|
||||
await onOnboardingTemplateChange(form.onboardingTemplateId!);
|
||||
}
|
||||
}
|
||||
|
||||
async function onOnboardingTemplateChange(templateId: number) {
|
||||
form.contractVariables = [];
|
||||
if (!templateId) return;
|
||||
const { data: template } = await onboardingTaskApi.onboardingTemplateDetail({ id: templateId });
|
||||
const variableResults = await Promise.all(
|
||||
template.contracts.map(contract => onboardingTaskApi.variablePage({
|
||||
templateVersionId: contract.contractTemplateVersionId,
|
||||
pageNum: 1,
|
||||
pageSize: 100
|
||||
}))
|
||||
);
|
||||
|
||||
form.contractVariables = template.contracts.flatMap((contract, index) => {
|
||||
const contractName = contractTemplates.value.find(item => item.id === contract.contractTemplateId)?.templateName || "合同";
|
||||
return (variableResults[index].data?.list || [])
|
||||
.filter(variable => variable.hrEditable && ["contract", "company"].includes(variable.variableType))
|
||||
.map(variable => ({
|
||||
contractTemplateVersionId: contract.contractTemplateVersionId,
|
||||
variableCode: variable.variableCode,
|
||||
variableName: variable.variableName,
|
||||
variableValue: variable.defaultValue,
|
||||
dataType: variable.dataType,
|
||||
required: variable.required,
|
||||
contractName
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
function openCreate() {
|
||||
Object.assign(form, { companyId: undefined, positionId: undefined, remark: "" });
|
||||
Object.assign(form, {
|
||||
companyId: undefined,
|
||||
positionId: undefined,
|
||||
onboardingTemplateId: undefined,
|
||||
contractVariables: [],
|
||||
remark: ""
|
||||
});
|
||||
positions.value = [];
|
||||
onboardingTemplates.value = [];
|
||||
createVisible.value = true;
|
||||
}
|
||||
|
||||
async function createTask() {
|
||||
if (!form.companyId || !form.positionId) {
|
||||
return ElMessage.warning("请选择所属公司和入职岗位");
|
||||
if (!form.companyId || !form.positionId || !form.onboardingTemplateId) {
|
||||
return ElMessage.warning("请选择所属公司、入职岗位和入职模板");
|
||||
}
|
||||
const missingVariable = form.contractVariables.find(item => item.required && !String(item.variableValue || "").trim());
|
||||
if (missingVariable) {
|
||||
return ElMessage.warning(`请填写合同人事参数“${missingVariable.variableName}”`);
|
||||
}
|
||||
|
||||
creating.value = true;
|
||||
|
||||
Reference in New Issue
Block a user