Compare commits
4
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f1a910c25a | ||
|
|
eab359d9fa | ||
|
|
aec6c1f36c | ||
|
|
49a1a3ee6a |
@@ -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 fileHttp = moduleRequest("/sys/dev/file/");
|
||||||
|
|
||||||
export interface OnboardingTask {
|
export interface OnboardingTask {
|
||||||
id: number;
|
id: number;
|
||||||
@@ -80,16 +81,6 @@ export interface OnboardingTemplateVariable {
|
|||||||
remark?: string;
|
remark?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface OnboardingContractScope {
|
|
||||||
id: number;
|
|
||||||
companyId: number;
|
|
||||||
positionId?: number;
|
|
||||||
templateId: number;
|
|
||||||
templateVersionId: number;
|
|
||||||
required: boolean;
|
|
||||||
sortCode?: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface OnboardingTaskContractVariableItem {
|
export interface OnboardingTaskContractVariableItem {
|
||||||
id: number;
|
id: number;
|
||||||
variableCode: string;
|
variableCode: string;
|
||||||
@@ -142,24 +133,43 @@ export interface OnboardingMaterialType {
|
|||||||
status: string;
|
status: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface OnboardingFormRequirement {
|
export interface OnboardingTemplateContractConfig {
|
||||||
id: number;
|
contractTemplateId: number;
|
||||||
companyId: number;
|
contractTemplateVersionId: number;
|
||||||
positionId?: number;
|
required: boolean;
|
||||||
|
printCopies: number;
|
||||||
|
sortCode?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface OnboardingTemplateFieldConfig {
|
||||||
fieldId: number;
|
fieldId: number;
|
||||||
required: boolean;
|
required: boolean;
|
||||||
sortCode?: number;
|
sortCode?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface OnboardingMaterialRequirement {
|
export interface OnboardingTemplateMaterialConfig {
|
||||||
id: number;
|
|
||||||
companyId: number;
|
|
||||||
positionId?: number;
|
|
||||||
materialTypeId: number;
|
materialTypeId: number;
|
||||||
required: boolean;
|
required: boolean;
|
||||||
sortCode?: number;
|
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 = {
|
export const onboardingTaskApi = {
|
||||||
page(params: Record<string, unknown>) {
|
page(params: Record<string, unknown>) {
|
||||||
return taskHttp.get<ResPage<OnboardingTask>>("page", params);
|
return taskHttp.get<ResPage<OnboardingTask>>("page", params);
|
||||||
@@ -169,6 +179,15 @@ export const onboardingTaskApi = {
|
|||||||
return taskHttp.get("detail", params);
|
return taskHttp.get("detail", params);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/** 使用后台登录令牌读取文件流,供入职材料下载和图片预览使用。 */
|
||||||
|
downloadFile(fileId: number) {
|
||||||
|
return fileHttp.get<any>(
|
||||||
|
"download",
|
||||||
|
{ id: fileId },
|
||||||
|
{ responseType: "blob", loading: false, cancel: false }
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
create(params: Record<string, unknown>) {
|
create(params: Record<string, unknown>) {
|
||||||
return taskHttp.post("create", params);
|
return taskHttp.post("create", params);
|
||||||
},
|
},
|
||||||
@@ -208,6 +227,10 @@ export const onboardingTaskApi = {
|
|||||||
return taskHttp.post("revoke", params);
|
return taskHttp.post("revoke", params);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
deleteTask(id: number) {
|
||||||
|
return taskHttp.post("delete", { id });
|
||||||
|
},
|
||||||
|
|
||||||
reviewMaterial(params: {
|
reviewMaterial(params: {
|
||||||
taskMaterialId: number;
|
taskMaterialId: number;
|
||||||
reviewStatus: string;
|
reviewStatus: string;
|
||||||
@@ -291,6 +314,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>) {
|
templatePage(params: Record<string, unknown>) {
|
||||||
return moduleRequest("/biz/onboarding/contract-template/").get<
|
return moduleRequest("/biz/onboarding/contract-template/").get<
|
||||||
ResPage<OnboardingContractTemplate>
|
ResPage<OnboardingContractTemplate>
|
||||||
@@ -347,6 +404,13 @@ export const onboardingTaskApi = {
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
deleteVersion(id: number) {
|
||||||
|
return moduleRequest("/biz/onboarding/contract-template/").post(
|
||||||
|
"version/delete",
|
||||||
|
{ id }
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
variablePage(params: {
|
variablePage(params: {
|
||||||
templateVersionId: number;
|
templateVersionId: number;
|
||||||
pageNum: number;
|
pageNum: number;
|
||||||
@@ -357,47 +421,13 @@ export const onboardingTaskApi = {
|
|||||||
>("variable/page", params);
|
>("variable/page", params);
|
||||||
},
|
},
|
||||||
|
|
||||||
saveVariable(
|
editVariable(params: Partial<OnboardingTemplateVariable>) {
|
||||||
params: Partial<OnboardingTemplateVariable>,
|
|
||||||
edit = false
|
|
||||||
) {
|
|
||||||
return moduleRequest("/biz/onboarding/contract-template/").post(
|
return moduleRequest("/biz/onboarding/contract-template/").post(
|
||||||
edit ? "variable/edit" : "variable/add",
|
"variable/edit",
|
||||||
params
|
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) {
|
uploadDocx(params: FormData) {
|
||||||
return moduleRequest("/biz/onboarding/contract-template/").post<number>(
|
return moduleRequest("/biz/onboarding/contract-template/").post<number>(
|
||||||
"file/upload-docx",
|
"file/upload-docx",
|
||||||
@@ -477,45 +507,50 @@ export const onboardingTaskApi = {
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
listFormRequirements(companyId: number) {
|
// 合同变量字典定义
|
||||||
return moduleRequest("/biz/onboarding/entry-config/").get<
|
variableDefinitionPage(params: { keyword?: string; variableCategory?: string; pageNum: number; pageSize: number }) {
|
||||||
OnboardingFormRequirement[]
|
return moduleRequest("/biz/onboarding/variable-definition/").get<ResPage<any>>("page", params);
|
||||||
>("form-requirement/list", { companyId });
|
|
||||||
},
|
},
|
||||||
|
|
||||||
saveFormRequirement(params: Partial<OnboardingFormRequirement>) {
|
variableDefinitionList() {
|
||||||
return moduleRequest("/biz/onboarding/entry-config/").post(
|
return moduleRequest("/biz/onboarding/variable-definition/").get<any[]>("list");
|
||||||
"form-requirement/save",
|
|
||||||
params
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
deleteFormRequirements(ids: number[]) {
|
addVariableDefinition(params: Record<string, unknown>) {
|
||||||
return moduleRequest("/biz/onboarding/entry-config/").post(
|
return moduleRequest("/biz/onboarding/variable-definition/").post("add", params);
|
||||||
"form-requirement/delete",
|
|
||||||
{ ids }
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
listMaterialRequirements(companyId: number) {
|
editVariableDefinition(params: Record<string, unknown>) {
|
||||||
return moduleRequest("/biz/onboarding/entry-config/").get<
|
return moduleRequest("/biz/onboarding/variable-definition/").post("edit", params);
|
||||||
OnboardingMaterialRequirement[]
|
|
||||||
>("material-requirement/list", { companyId });
|
|
||||||
},
|
},
|
||||||
|
|
||||||
saveMaterialRequirement(
|
deleteVariableDefinitions(ids: number[]) {
|
||||||
params: Partial<OnboardingMaterialRequirement>
|
return moduleRequest("/biz/onboarding/variable-definition/").post("delete", { ids });
|
||||||
) {
|
}
|
||||||
return moduleRequest("/biz/onboarding/entry-config/").post(
|
};
|
||||||
"material-requirement/save",
|
|
||||||
params
|
const publicHttp = moduleRequest("/open/onboarding/");
|
||||||
);
|
|
||||||
|
export const onboardingPublicApi = {
|
||||||
|
detail(params: { token: string }) {
|
||||||
|
return publicHttp.post<any>("detail", params);
|
||||||
|
},
|
||||||
|
saveEmployeeInfo(params: Record<string, unknown>) {
|
||||||
|
return publicHttp.post("save-employee-info", params);
|
||||||
|
},
|
||||||
|
saveMaterial(params: { token: string; materialTypeId: number; fileId: number }) {
|
||||||
|
return publicHttp.post("save-material", params);
|
||||||
},
|
},
|
||||||
|
confirmContract(params: { token: string; taskContractId: number }) {
|
||||||
deleteMaterialRequirements(ids: number[]) {
|
return publicHttp.post("confirm-contract", params);
|
||||||
return moduleRequest("/biz/onboarding/entry-config/").post(
|
},
|
||||||
"material-requirement/delete",
|
submit(params: { token: string }) {
|
||||||
{ ids }
|
return publicHttp.post("submit", params);
|
||||||
);
|
},
|
||||||
|
contractSourceUrl(params: { token: string; taskContractId: number }) {
|
||||||
|
return `/api/open/onboarding/contract-source?token=${encodeURIComponent(params.token)}&taskContractId=${params.taskContractId}`;
|
||||||
|
},
|
||||||
|
deleteMaterial(params: { token: string; taskMaterialId: number }) {
|
||||||
|
return publicHttp.post("delete-material", params);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -82,7 +82,7 @@
|
|||||||
<el-drawer
|
<el-drawer
|
||||||
v-model="drawerVisible"
|
v-model="drawerVisible"
|
||||||
:title="`合同模板管理 · ${selectedTemplate?.templateName || ''}`"
|
:title="`合同模板管理 · ${selectedTemplate?.templateName || ''}`"
|
||||||
size="960px"
|
size="1150px"
|
||||||
destroy-on-close
|
destroy-on-close
|
||||||
>
|
>
|
||||||
<div v-if="selectedTemplate" class="drawer-content-box">
|
<div v-if="selectedTemplate" class="drawer-content-box">
|
||||||
@@ -135,143 +135,56 @@
|
|||||||
<el-table-column prop="expiryDate" label="失效日期" width="130" align="center" />
|
<el-table-column prop="expiryDate" label="失效日期" width="130" align="center" />
|
||||||
<el-table-column prop="remark" label="版本说明" min-width="150" />
|
<el-table-column prop="remark" label="版本说明" min-width="150" />
|
||||||
|
|
||||||
<el-table-column label="操作" width="280" fixed="right">
|
<el-table-column label="操作" width="340" fixed="right" align="center">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<el-button
|
<div class="table-actions">
|
||||||
link
|
<el-button
|
||||||
type="primary"
|
link
|
||||||
icon="Setting"
|
type="primary"
|
||||||
@click="openVariables(scope.row)"
|
icon="Setting"
|
||||||
>
|
@click="openVariables(scope.row)"
|
||||||
变量
|
>
|
||||||
</el-button>
|
变量
|
||||||
|
</el-button>
|
||||||
|
|
||||||
<el-button
|
<el-button
|
||||||
link
|
link
|
||||||
type="primary"
|
type="primary"
|
||||||
icon="Download"
|
icon="Download"
|
||||||
@click="downloadDocx(scope.row.sourceDocxFileId)"
|
@click="downloadDocx(scope.row.sourceDocxFileId)"
|
||||||
>
|
>
|
||||||
下载源文件
|
下载源文件
|
||||||
</el-button>
|
</el-button>
|
||||||
|
|
||||||
<el-button
|
<el-button
|
||||||
v-if="scope.row.versionStatus !== 'enabled'"
|
v-if="scope.row.versionStatus !== 'enabled'"
|
||||||
link
|
link
|
||||||
type="success"
|
type="success"
|
||||||
icon="Check"
|
icon="Check"
|
||||||
@click="changeVersionStatus(scope.row, 'enabled')"
|
@click="changeVersionStatus(scope.row, 'enabled')"
|
||||||
>
|
>
|
||||||
启用
|
启用
|
||||||
</el-button>
|
</el-button>
|
||||||
|
|
||||||
<el-button
|
<el-button
|
||||||
v-else
|
v-else
|
||||||
link
|
link
|
||||||
type="warning"
|
type="warning"
|
||||||
icon="Close"
|
icon="Close"
|
||||||
@click="changeVersionStatus(scope.row, 'disabled')"
|
@click="changeVersionStatus(scope.row, 'disabled')"
|
||||||
>
|
>
|
||||||
停用
|
停用
|
||||||
</el-button>
|
</el-button>
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
</el-table>
|
|
||||||
</div>
|
|
||||||
</el-tab-pane>
|
|
||||||
|
|
||||||
<!-- 2. 适用范围配置 -->
|
<el-button
|
||||||
<el-tab-pane label="适用范围 (公司与岗位)" name="scopes">
|
link
|
||||||
<div class="tab-pane-content">
|
type="danger"
|
||||||
<el-card shadow="never" class="scope-add-card">
|
icon="Delete"
|
||||||
<template #header>
|
@click="removeVersion(scope.row)"
|
||||||
<span class="font-bold">为公司/岗位分配此合同模板</span>
|
>
|
||||||
</template>
|
删除
|
||||||
<el-form :model="scopeForm" inline class="scope-inline-form">
|
</el-button>
|
||||||
<el-form-item label="所属公司" required>
|
</div>
|
||||||
<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>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</el-table>
|
</el-table>
|
||||||
@@ -294,7 +207,7 @@
|
|||||||
append-to-body
|
append-to-body
|
||||||
>
|
>
|
||||||
<el-alert
|
<el-alert
|
||||||
title="仅支持 .docx 文件。请在 Word 文档中使用 {{变量编码}}(如 {{EmployeeName}}、{{Salary}})作为占位符。"
|
title="仅支持 .docx。占位符按来源编写:{{employee.EmployeeName}}、{{company.CompanyName}}、{{hr.MonthlySalary}}、{{system.SignDate}};保存版本时系统会自动识别。"
|
||||||
type="info"
|
type="info"
|
||||||
:closable="false"
|
:closable="false"
|
||||||
style="margin-bottom: 16px"
|
style="margin-bottom: 16px"
|
||||||
@@ -346,92 +259,100 @@
|
|||||||
<el-dialog
|
<el-dialog
|
||||||
v-model="variablesVisible"
|
v-model="variablesVisible"
|
||||||
:title="`合同变量配置 · 版本 ${selectedVersion?.versionNo || ''}`"
|
:title="`合同变量配置 · 版本 ${selectedVersion?.versionNo || ''}`"
|
||||||
width="1050px"
|
width="1220px"
|
||||||
|
top="4vh"
|
||||||
destroy-on-close
|
destroy-on-close
|
||||||
append-to-body
|
append-to-body
|
||||||
|
class="variables-dialog"
|
||||||
>
|
>
|
||||||
<div class="toolbar-flex">
|
<el-alert
|
||||||
<el-button type="primary" icon="Plus" @click="openVariableCreate">
|
title="变量已从 DOCX 自动识别与解析。请核对来源分类、显示名称及数据类型;确保全部配置正确后即可启用版本。"
|
||||||
新增变量定义
|
type="info"
|
||||||
</el-button>
|
show-icon
|
||||||
<el-alert
|
:closable="false"
|
||||||
title="在 DOCX 模板中使用 {{变量编码}} 作为占位符。点击变量编码旁复制按钮可直接复制占位符。"
|
class="mb-3"
|
||||||
type="info"
|
/>
|
||||||
show-icon
|
|
||||||
:closable="false"
|
<!-- 分组 Tabs 切换 -->
|
||||||
style="flex: 1; margin: 0 0 0 16px;"
|
<el-tabs v-model="variableTab" type="card" class="variable-filter-tabs">
|
||||||
/>
|
<el-tab-pane :label="`全部变量 (${variableCounts.all})`" name="all" />
|
||||||
|
<el-tab-pane :label="`员工填写 (${variableCounts.employee})`" name="employee" />
|
||||||
|
<el-tab-pane :label="`人事合同参数 (${variableCounts.hr})`" name="hr" />
|
||||||
|
<el-tab-pane :label="`公司信息 (${variableCounts.company})`" name="company" />
|
||||||
|
<el-tab-pane :label="`系统预置 (${variableCounts.system})`" name="system" />
|
||||||
|
</el-tabs>
|
||||||
|
|
||||||
|
<div class="variables-table-wrapper">
|
||||||
|
<el-table :data="filteredVariables" border stripe height="500px">
|
||||||
|
<el-table-column prop="variableCode" label="变量全编码 (DOCX占位符)" min-width="260">
|
||||||
|
<template #default="scope">
|
||||||
|
<div class="variable-code-cell">
|
||||||
|
<code class="code-font">{{ formatPlaceholder(scope.row.variableCode) }}</code>
|
||||||
|
<el-button
|
||||||
|
size="small"
|
||||||
|
link
|
||||||
|
type="primary"
|
||||||
|
icon="CopyDocument"
|
||||||
|
@click="copyPlaceholder(scope.row.variableCode)"
|
||||||
|
>
|
||||||
|
复制
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
|
||||||
|
<el-table-column prop="variableName" label="变量显示名称" min-width="170" />
|
||||||
|
|
||||||
|
<el-table-column prop="variableType" label="来源板块" width="130" align="center">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-tag size="small" :type="variableTypeTag(scope.row.variableType)">
|
||||||
|
{{ variableTypeText(scope.row.variableType) }}
|
||||||
|
</el-tag>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
|
||||||
|
<el-table-column prop="dataType" label="数据类型" width="100" align="center">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-tag size="small" type="info">{{ scope.row.dataType || 'string' }}</el-tag>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
|
||||||
|
<el-table-column prop="employeeField" label="对应字段" min-width="160" show-overflow-tooltip />
|
||||||
|
|
||||||
|
<el-table-column label="必填" width="80" align="center">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-tag size="small" :type="scope.row.required ? 'danger' : 'info'">
|
||||||
|
{{ scope.row.required ? "是" : "否" }}
|
||||||
|
</el-tag>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
|
||||||
|
<el-table-column label="操作" width="120" align="center" fixed="right">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-button link type="primary" icon="Edit" @click="openVariableEdit(scope.row)">
|
||||||
|
检查调整
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<el-table :data="variables" border stripe class="mt-3">
|
<template #footer>
|
||||||
<el-table-column prop="variableCode" label="变量编码 (DOCX占位符)" min-width="220">
|
<el-button type="primary" @click="variablesVisible = false">关闭</el-button>
|
||||||
<template #default="scope">
|
</template>
|
||||||
<div style="display: flex; align-items: center; justify-content: space-between;">
|
|
||||||
<code style="font-weight: 600; color: #2563eb;">{{ formatPlaceholder(scope.row.variableCode) }}</code>
|
|
||||||
<el-button
|
|
||||||
size="small"
|
|
||||||
link
|
|
||||||
type="primary"
|
|
||||||
icon="CopyDocument"
|
|
||||||
@click="copyPlaceholder(scope.row.variableCode)"
|
|
||||||
>
|
|
||||||
复制
|
|
||||||
</el-button>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
|
|
||||||
<el-table-column prop="variableName" label="变量显示名称" min-width="160" />
|
|
||||||
|
|
||||||
<el-table-column prop="variableType" label="来源类型" width="130" align="center">
|
|
||||||
<template #default="scope">
|
|
||||||
<el-tag size="small" :type="variableTypeTag(scope.row.variableType)">
|
|
||||||
{{ variableTypeText(scope.row.variableType) }}
|
|
||||||
</el-tag>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
|
|
||||||
<el-table-column prop="dataType" label="数据类型" width="100" align="center">
|
|
||||||
<template #default="scope">
|
|
||||||
<el-tag size="small" type="info">{{ scope.row.dataType }}</el-tag>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
|
|
||||||
<el-table-column prop="employeeField" label="对应员工字段" min-width="140" />
|
|
||||||
|
|
||||||
<el-table-column label="必填" width="80" align="center">
|
|
||||||
<template #default="scope">
|
|
||||||
<el-tag size="small" :type="scope.row.required ? 'danger' : 'info'">
|
|
||||||
{{ scope.row.required ? "是" : "否" }}
|
|
||||||
</el-tag>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
|
|
||||||
<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>
|
|
||||||
</el-table>
|
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
|
|
||||||
<!-- 新增 / 编辑单个变量 -->
|
<!-- 新增 / 编辑单个变量 -->
|
||||||
<el-dialog
|
<el-dialog
|
||||||
v-model="variableVisible"
|
v-model="variableVisible"
|
||||||
:title="variableEditing ? '编辑变量' : '新增变量'"
|
title="检查并调整变量"
|
||||||
width="640px"
|
width="640px"
|
||||||
destroy-on-close
|
destroy-on-close
|
||||||
append-to-body
|
append-to-body
|
||||||
>
|
>
|
||||||
<el-form :model="variableForm" label-width="130px">
|
<el-form :model="variableForm" label-width="130px">
|
||||||
<el-form-item label="变量编码" required>
|
<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>
|
||||||
|
|
||||||
<el-form-item label="变量名称" required>
|
<el-form-item label="变量名称" required>
|
||||||
@@ -456,7 +377,7 @@
|
|||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</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-input v-model="variableForm.employeeField" placeholder="如:EmployeeName、IdCardNumber、Phone" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
|
||||||
@@ -490,43 +411,48 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts" name="onboardingContract">
|
<script setup lang="ts" name="onboardingContract">
|
||||||
import { ref, reactive, computed, onMounted } from "vue";
|
import { computed, reactive, ref } from "vue";
|
||||||
import { ElMessage, ElMessageBox } from "element-plus";
|
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 {
|
import {
|
||||||
onboardingTaskApi,
|
onboardingTaskApi,
|
||||||
type OnboardingCompanyOption,
|
|
||||||
type OnboardingContractScope,
|
|
||||||
type OnboardingContractTemplate,
|
type OnboardingContractTemplate,
|
||||||
type OnboardingContractVersion,
|
type OnboardingContractVersion,
|
||||||
type OnboardingPositionOption,
|
|
||||||
type OnboardingTemplateVariable
|
type OnboardingTemplateVariable
|
||||||
} from "@/api/modules/biz/onboarding";
|
} from "@/api/modules/biz/onboarding";
|
||||||
import type { ColumnProps, ProTableInstance } from "@/components/ProTable/interface";
|
import type { ColumnProps, ProTableInstance } from "@/components/ProTable/interface";
|
||||||
|
|
||||||
const templateTable = ref<ProTableInstance>();
|
const templateTable = ref<ProTableInstance>();
|
||||||
|
|
||||||
const companies = ref<OnboardingCompanyOption[]>([]);
|
|
||||||
const scopePositions = ref<OnboardingPositionOption[]>([]);
|
|
||||||
|
|
||||||
const selectedTemplate = ref<OnboardingContractTemplate>();
|
const selectedTemplate = ref<OnboardingContractTemplate>();
|
||||||
const selectedVersion = ref<OnboardingContractVersion>();
|
const selectedVersion = ref<OnboardingContractVersion>();
|
||||||
|
|
||||||
const drawerVisible = ref(false);
|
const drawerVisible = ref(false);
|
||||||
const drawerActiveTab = ref("versions");
|
const drawerActiveTab = ref("versions");
|
||||||
|
|
||||||
const templateVisible = ref(false);
|
const templateVisible = ref(false);
|
||||||
const templateEditing = ref(false);
|
const templateEditing = ref(false);
|
||||||
|
|
||||||
const versions = ref<OnboardingContractVersion[]>([]);
|
const versions = ref<OnboardingContractVersion[]>([]);
|
||||||
const versionVisible = ref(false);
|
const versionVisible = ref(false);
|
||||||
|
|
||||||
const variablesVisible = ref(false);
|
const variablesVisible = ref(false);
|
||||||
const variableEditing = ref(false);
|
|
||||||
const variables = ref<OnboardingTemplateVariable[]>([]);
|
const variables = ref<OnboardingTemplateVariable[]>([]);
|
||||||
const variableVisible = ref(false);
|
const variableVisible = ref(false);
|
||||||
|
const variableTab = ref("all");
|
||||||
|
|
||||||
const scopes = ref<OnboardingContractScope[]>([]);
|
const filteredVariables = computed(() => {
|
||||||
|
if (variableTab.value === "all") return variables.value;
|
||||||
|
if (variableTab.value === "employee") return variables.value.filter(v => v.variableType === "employee");
|
||||||
|
if (variableTab.value === "hr") return variables.value.filter(v => v.variableType === "hr" || v.variableType === "contract" || v.variableCode.startsWith("hr."));
|
||||||
|
if (variableTab.value === "company") return variables.value.filter(v => v.variableType === "company");
|
||||||
|
if (variableTab.value === "system") return variables.value.filter(v => v.variableType === "system");
|
||||||
|
return variables.value;
|
||||||
|
});
|
||||||
|
|
||||||
|
const variableCounts = computed(() => {
|
||||||
|
const all = variables.value.length;
|
||||||
|
const employee = variables.value.filter(v => v.variableType === "employee").length;
|
||||||
|
const hr = variables.value.filter(v => v.variableType === "hr" || v.variableType === "contract" || v.variableCode.startsWith("hr.")).length;
|
||||||
|
const company = variables.value.filter(v => v.variableType === "company").length;
|
||||||
|
const system = variables.value.filter(v => v.variableType === "system").length;
|
||||||
|
return { all, employee, hr, company, system };
|
||||||
|
});
|
||||||
|
|
||||||
const emptyTemplate = (): Partial<OnboardingContractTemplate> => ({
|
const emptyTemplate = (): Partial<OnboardingContractTemplate> => ({
|
||||||
templateCode: "",
|
templateCode: "",
|
||||||
@@ -547,50 +473,24 @@ const emptyVersion = (): Partial<OnboardingContractVersion> => ({
|
|||||||
const emptyVariable = (): Partial<OnboardingTemplateVariable> => ({
|
const emptyVariable = (): Partial<OnboardingTemplateVariable> => ({
|
||||||
variableCode: "",
|
variableCode: "",
|
||||||
variableName: "",
|
variableName: "",
|
||||||
variableType: "employee",
|
variableType: "unresolved",
|
||||||
dataType: "string",
|
dataType: "string",
|
||||||
employeeField: "",
|
employeeField: "",
|
||||||
defaultValue: "",
|
defaultValue: "",
|
||||||
sortCode: 10,
|
sortCode: 10,
|
||||||
required: true,
|
required: true,
|
||||||
hrEditable: true,
|
hrEditable: false,
|
||||||
remark: ""
|
remark: ""
|
||||||
});
|
});
|
||||||
|
|
||||||
const emptyScope = (): Partial<OnboardingContractScope> => ({
|
|
||||||
companyId: undefined,
|
|
||||||
positionId: undefined,
|
|
||||||
templateVersionId: undefined
|
|
||||||
});
|
|
||||||
|
|
||||||
const templateForm = reactive<Partial<OnboardingContractTemplate>>(emptyTemplate());
|
const templateForm = reactive<Partial<OnboardingContractTemplate>>(emptyTemplate());
|
||||||
const versionForm = reactive<Partial<OnboardingContractVersion>>(emptyVersion());
|
const versionForm = reactive<Partial<OnboardingContractVersion>>(emptyVersion());
|
||||||
const variableForm = reactive<Partial<OnboardingTemplateVariable>>(emptyVariable());
|
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>[] = [
|
const templateColumns: ColumnProps<OnboardingContractTemplate>[] = [
|
||||||
{
|
{ prop: "templateCode", label: "模板编码", minWidth: 160, search: { el: "input" } },
|
||||||
prop: "templateCode",
|
{ prop: "templateName", label: "模板名称", minWidth: 200, search: { el: "input" } },
|
||||||
label: "模板编码",
|
{ prop: "contractType", label: "合同类型", width: 140, search: { el: "input" } },
|
||||||
minWidth: 160,
|
|
||||||
search: { el: "input" }
|
|
||||||
},
|
|
||||||
{
|
|
||||||
prop: "templateName",
|
|
||||||
label: "模板名称",
|
|
||||||
minWidth: 200,
|
|
||||||
search: { el: "input" }
|
|
||||||
},
|
|
||||||
{
|
|
||||||
prop: "contractType",
|
|
||||||
label: "合同类型",
|
|
||||||
width: 140,
|
|
||||||
search: { el: "input" }
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
prop: "status",
|
prop: "status",
|
||||||
label: "状态",
|
label: "状态",
|
||||||
@@ -602,24 +502,10 @@ const templateColumns: ColumnProps<OnboardingContractTemplate>[] = [
|
|||||||
],
|
],
|
||||||
search: { el: "select" }
|
search: { el: "select" }
|
||||||
},
|
},
|
||||||
{
|
{ prop: "description", label: "模板说明", minWidth: 200 },
|
||||||
prop: "description",
|
{ prop: "operation", label: "操作", fixed: "right", width: 260 }
|
||||||
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() {
|
function resetTemplate() {
|
||||||
Object.assign(templateForm, emptyTemplate());
|
Object.assign(templateForm, emptyTemplate());
|
||||||
}
|
}
|
||||||
@@ -628,19 +514,6 @@ function resetVersion() {
|
|||||||
Object.assign(versionForm, emptyVersion());
|
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() {
|
async function loadVersions() {
|
||||||
if (!selectedTemplate.value) return;
|
if (!selectedTemplate.value) return;
|
||||||
const { data } = await onboardingTaskApi.versionPage({
|
const { data } = await onboardingTaskApi.versionPage({
|
||||||
@@ -648,7 +521,7 @@ async function loadVersions() {
|
|||||||
pageNum: 1,
|
pageNum: 1,
|
||||||
pageSize: 100
|
pageSize: 100
|
||||||
});
|
});
|
||||||
versions.value = data.list;
|
versions.value = data?.list || [];
|
||||||
}
|
}
|
||||||
|
|
||||||
async function loadVariables() {
|
async function loadVariables() {
|
||||||
@@ -658,23 +531,13 @@ async function loadVariables() {
|
|||||||
pageNum: 1,
|
pageNum: 1,
|
||||||
pageSize: 100
|
pageSize: 100
|
||||||
});
|
});
|
||||||
variables.value = data.list;
|
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function openManagementDrawer(row: OnboardingContractTemplate) {
|
async function openManagementDrawer(row: OnboardingContractTemplate) {
|
||||||
selectedTemplate.value = row;
|
selectedTemplate.value = row;
|
||||||
drawerActiveTab.value = "versions";
|
drawerActiveTab.value = "versions";
|
||||||
await Promise.all([loadVersions(), loadScopes()]);
|
await loadVersions();
|
||||||
drawerVisible.value = true;
|
drawerVisible.value = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -695,7 +558,6 @@ async function saveTemplate() {
|
|||||||
if (!templateForm.templateCode || !templateForm.templateName || !templateForm.contractType) {
|
if (!templateForm.templateCode || !templateForm.templateName || !templateForm.contractType) {
|
||||||
return ElMessage.warning("请填写模板编码、名称和合同类型");
|
return ElMessage.warning("请填写模板编码、名称和合同类型");
|
||||||
}
|
}
|
||||||
|
|
||||||
await onboardingTaskApi.saveTemplate(templateForm, templateEditing.value);
|
await onboardingTaskApi.saveTemplate(templateForm, templateEditing.value);
|
||||||
templateVisible.value = false;
|
templateVisible.value = false;
|
||||||
templateTable.value?.refresh();
|
templateTable.value?.refresh();
|
||||||
@@ -723,10 +585,10 @@ function beforeDocxUpload(file: File) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function uploadDocx(options: any) {
|
async function uploadDocx(options: any) {
|
||||||
const form = new FormData();
|
const formData = new FormData();
|
||||||
form.append("file", options.file);
|
formData.append("file", options.file);
|
||||||
try {
|
try {
|
||||||
const { data } = await onboardingTaskApi.uploadDocx(form);
|
const { data } = await onboardingTaskApi.uploadDocx(formData);
|
||||||
versionForm.sourceDocxFileId = data;
|
versionForm.sourceDocxFileId = data;
|
||||||
options.onSuccess?.(data);
|
options.onSuccess?.(data);
|
||||||
ElMessage.success("DOCX 文件上传成功");
|
ElMessage.success("DOCX 文件上传成功");
|
||||||
@@ -737,17 +599,12 @@ async function uploadDocx(options: any) {
|
|||||||
|
|
||||||
async function saveVersion() {
|
async function saveVersion() {
|
||||||
if (!selectedTemplate.value || !versionForm.versionNo || !versionForm.sourceDocxFileId) {
|
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;
|
versionVisible.value = false;
|
||||||
await loadVersions();
|
await loadVersions();
|
||||||
ElMessage.success("版本已保存");
|
ElMessage.success("版本已保存,DOCX 变量已自动识别,请进入变量列表检查后再启用");
|
||||||
}
|
}
|
||||||
|
|
||||||
async function changeVersionStatus(row: OnboardingContractVersion, status: string) {
|
async function changeVersionStatus(row: OnboardingContractVersion, status: string) {
|
||||||
@@ -756,8 +613,15 @@ async function changeVersionStatus(row: OnboardingContractVersion, status: strin
|
|||||||
ElMessage.success(`版本状态已更新为:${versionText(status)}`);
|
ElMessage.success(`版本状态已更新为:${versionText(status)}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function removeVersion(row: OnboardingContractVersion) {
|
||||||
|
await ElMessageBox.confirm(`确认删除合同版本“${row.versionNo}”?此操作不可撤销。`, "删除确认", { type: "warning" });
|
||||||
|
await onboardingTaskApi.deleteVersion(row.id);
|
||||||
|
await loadVersions();
|
||||||
|
ElMessage.success("版本删除成功");
|
||||||
|
}
|
||||||
|
|
||||||
function downloadDocx(fileId?: number) {
|
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");
|
window.open(`/api/sys/dev/file/download?id=${fileId}`, "_blank");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -776,7 +640,8 @@ function variableTypeTag(type: string): EpTagType {
|
|||||||
employee: "primary",
|
employee: "primary",
|
||||||
company: "success",
|
company: "success",
|
||||||
contract: "warning",
|
contract: "warning",
|
||||||
system: "info"
|
system: "info",
|
||||||
|
unresolved: "danger"
|
||||||
};
|
};
|
||||||
return map[type] || "info";
|
return map[type] || "info";
|
||||||
}
|
}
|
||||||
@@ -786,7 +651,8 @@ function variableTypeText(type: string) {
|
|||||||
employee: "员工填写",
|
employee: "员工填写",
|
||||||
company: "公司信息",
|
company: "公司信息",
|
||||||
contract: "人事合同参数",
|
contract: "人事合同参数",
|
||||||
system: "系统自动"
|
system: "系统自动",
|
||||||
|
unresolved: "待确认"
|
||||||
};
|
};
|
||||||
return map[type] || type;
|
return map[type] || type;
|
||||||
}
|
}
|
||||||
@@ -806,87 +672,22 @@ async function openVariables(row: OnboardingContractVersion) {
|
|||||||
variablesVisible.value = true;
|
variablesVisible.value = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
function openVariableCreate() {
|
|
||||||
variableEditing.value = false;
|
|
||||||
resetVariable();
|
|
||||||
variableVisible.value = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
function openVariableEdit(row: OnboardingTemplateVariable) {
|
function openVariableEdit(row: OnboardingTemplateVariable) {
|
||||||
variableEditing.value = true;
|
Object.assign(variableForm, emptyVariable(), row);
|
||||||
Object.assign(variableForm, row);
|
|
||||||
variableVisible.value = true;
|
variableVisible.value = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function saveVariable() {
|
async function saveVariable() {
|
||||||
if (
|
if (!selectedVersion.value || !variableForm.id || !variableForm.variableCode || !variableForm.variableName || !variableForm.variableType || !variableForm.dataType) {
|
||||||
!selectedVersion.value ||
|
return ElMessage.warning("请完整确认变量名称、来源类型和数据类型");
|
||||||
!variableForm.variableCode ||
|
|
||||||
!variableForm.variableName ||
|
|
||||||
!variableForm.variableType ||
|
|
||||||
!variableForm.dataType
|
|
||||||
) {
|
|
||||||
return ElMessage.warning("请填写变量编码、名称、类型和数据类型");
|
|
||||||
}
|
}
|
||||||
|
await onboardingTaskApi.editVariable(
|
||||||
await onboardingTaskApi.saveVariable(
|
{ ...variableForm, templateVersionId: selectedVersion.value.id }
|
||||||
{
|
|
||||||
...variableForm,
|
|
||||||
templateVersionId: selectedVersion.value.id
|
|
||||||
},
|
|
||||||
variableEditing.value
|
|
||||||
);
|
);
|
||||||
|
|
||||||
variableVisible.value = false;
|
variableVisible.value = false;
|
||||||
await loadVariables();
|
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>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
@@ -972,4 +773,38 @@ onMounted(loadCompanies);
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.table-actions {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 4px;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.variable-filter-tabs {
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.variables-table-wrapper {
|
||||||
|
border-radius: 6px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.variable-code-cell {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 8px;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.code-font {
|
||||||
|
font-family: Consolas, Monaco, monospace;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #2563eb;
|
||||||
|
background: #eff6ff;
|
||||||
|
padding: 2px 6px;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
@@ -0,0 +1,524 @@
|
|||||||
|
<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" />
|
||||||
|
|
||||||
|
<div class="table-container">
|
||||||
|
<el-table v-loading="loading" :data="rows" border stripe height="100%">
|
||||||
|
<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="200">
|
||||||
|
<template #default="scope">
|
||||||
|
合同 {{ scope.row.contractCount || 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>
|
||||||
|
|
||||||
|
<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="62%" top="3vh" destroy-on-close
|
||||||
|
append-to-body class="template-dialog">
|
||||||
|
<div class="template-dialog-body">
|
||||||
|
<el-form label-width="100px" label-position="top">
|
||||||
|
<!-- 1. 基本信息卡片 -->
|
||||||
|
<div class="form-block-card">
|
||||||
|
<div class="block-card-header">
|
||||||
|
<span class="block-title">基本信息</span>
|
||||||
|
<span class="block-subtitle">配置模板编码、名称、归属公司及适用岗位范围</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<el-row :gutter="20">
|
||||||
|
<el-col :xs="24" :sm="8">
|
||||||
|
<el-form-item label="模板编码" required>
|
||||||
|
<el-input v-model="form.templateCode" placeholder="如:FULLTIME_DEV" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :xs="24" :sm="8">
|
||||||
|
<el-form-item label="模板名称" required>
|
||||||
|
<el-input v-model="form.templateName" placeholder="如:全职技术人员入职模板" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :xs="24" :sm="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-col :xs="24" :sm="10">
|
||||||
|
<el-form-item label="所属公司" required>
|
||||||
|
<el-select v-model="form.companyId" filterable class="full-width" placeholder="请选择公司"
|
||||||
|
@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 :xs="24" :sm="14">
|
||||||
|
<el-form-item label="适用岗位" required>
|
||||||
|
<el-select v-model="form.positionIds" multiple filterable collapse-tags collapse-tags-tooltip
|
||||||
|
class="full-width" :disabled="!form.companyId" placeholder="请选择适用的岗位(可多选)">
|
||||||
|
<el-option v-for="item in positions" :key="item.id" :label="item.positionName" :value="item.id" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
|
||||||
|
<el-col :span="24">
|
||||||
|
<el-form-item label="模板说明">
|
||||||
|
<el-input v-model="form.description" type="textarea" :rows="2" placeholder="填写该入职模板的适用范围或注意事项" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 2. 关联合同文件卡片 -->
|
||||||
|
<div class="form-block-card">
|
||||||
|
<div class="block-card-header flex-between">
|
||||||
|
<div>
|
||||||
|
<span class="block-title">关联合同文件</span>
|
||||||
|
<span class="block-subtitle">选择该岗位入职时需要员工确认与签署的合同模板及版本</span>
|
||||||
|
</div>
|
||||||
|
<el-button type="primary" plain icon="Plus" size="small" @click="addContract">
|
||||||
|
添加合同文件
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<el-table :data="form.contracts" border empty-text="暂无关联合同,请点击右上角添加" class="mt-2">
|
||||||
|
<el-table-column label="合同模板" min-width="220">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-select v-model="scope.row.contractTemplateId" filterable class="full-width" placeholder="选择合同模板"
|
||||||
|
@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="160">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-select v-model="scope.row.contractTemplateVersionId" class="full-width" placeholder="选择版本"
|
||||||
|
: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" align="center">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-input-number v-model="scope.row.printCopies" :min="1" :max="10" controls-position="right"
|
||||||
|
style="width: 100px;" />
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="排序" width="130" align="center">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-input-number v-model="scope.row.sortCode" :min="0" controls-position="right"
|
||||||
|
style="width: 100px;" />
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="操作" width="90" align="center" fixed="right">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-button link type="danger" icon="Delete" style="white-space: nowrap;"
|
||||||
|
@click="form.contracts.splice(scope.$index, 1)">
|
||||||
|
移除
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 3. 员工上传材料卡片 -->
|
||||||
|
<div class="form-block-card">
|
||||||
|
<div class="block-card-header">
|
||||||
|
<span class="block-title">员工上传材料要求</span>
|
||||||
|
<span class="block-subtitle">勾选入职时要求候选人必须或可选上传的证件材料(如身份证、毕业证、体检表等)</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<el-table :data="materialTypes" border stripe height="240px" class="mt-2">
|
||||||
|
<el-table-column label="选择要求" width="85" 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="160" />
|
||||||
|
<el-table-column prop="materialName" label="材料名称" min-width="220" />
|
||||||
|
<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" />
|
||||||
|
<span v-else class="text-gray">-</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="排序" width="130" align="center">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-input-number v-if="hasMaterial(scope.row.id)" v-model="materialConfig(scope.row.id)!.sortCode"
|
||||||
|
:min="0" controls-position="right" style="width: 100px;" />
|
||||||
|
<span v-else class="text-gray">-</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
</div>
|
||||||
|
</el-form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<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 {
|
||||||
|
OnboardingCompany,
|
||||||
|
OnboardingContractTemplate,
|
||||||
|
OnboardingContractVersion,
|
||||||
|
OnboardingMaterialType,
|
||||||
|
OnboardingPosition,
|
||||||
|
OnboardingTemplate,
|
||||||
|
onboardingTaskApi
|
||||||
|
} from "@/api/modules/biz/onboarding";
|
||||||
|
|
||||||
|
const loading = ref(false);
|
||||||
|
const saving = ref(false);
|
||||||
|
const visible = ref(false);
|
||||||
|
const rows = ref<OnboardingTemplate[]>([]);
|
||||||
|
const total = ref(0);
|
||||||
|
|
||||||
|
const companies = ref<OnboardingCompany[]>([]);
|
||||||
|
const positions = ref<OnboardingPosition[]>([]);
|
||||||
|
const contractTemplates = ref<OnboardingContractTemplate[]>([]);
|
||||||
|
const materialTypes = ref<OnboardingMaterialType[]>([]);
|
||||||
|
const versionOptions = reactive<Record<number, OnboardingContractVersion[]>>({});
|
||||||
|
|
||||||
|
const query = reactive({
|
||||||
|
keyword: "",
|
||||||
|
companyId: undefined as number | undefined,
|
||||||
|
pageNum: 1,
|
||||||
|
pageSize: 20
|
||||||
|
});
|
||||||
|
|
||||||
|
const form = reactive<OnboardingTemplate>({
|
||||||
|
id: 0,
|
||||||
|
templateCode: "",
|
||||||
|
templateName: "",
|
||||||
|
companyId: 0,
|
||||||
|
description: "",
|
||||||
|
status: "enable",
|
||||||
|
positionIds: [],
|
||||||
|
contracts: [],
|
||||||
|
formFields: [],
|
||||||
|
materials: []
|
||||||
|
});
|
||||||
|
|
||||||
|
async function loadBaseData() {
|
||||||
|
const [companyRes, templateRes, materialRes] = await Promise.all([
|
||||||
|
onboardingTaskApi.companyPage({ pageNum: 1, pageSize: 100, status: "enable" }).catch(() => ({ data: { list: [] } })),
|
||||||
|
onboardingTaskApi.templatePage({ pageNum: 1, pageSize: 100, status: "enable" }).catch(() => ({ data: { list: [] } })),
|
||||||
|
onboardingTaskApi.listMaterialTypes().catch(() => ({ data: [] }))
|
||||||
|
]);
|
||||||
|
companies.value = companyRes.data?.list || [];
|
||||||
|
contractTemplates.value = templateRes.data?.list || [];
|
||||||
|
materialTypes.value = materialRes.data || [];
|
||||||
|
}
|
||||||
|
|
||||||
|
async function loadTemplates() {
|
||||||
|
loading.value = true;
|
||||||
|
try {
|
||||||
|
const res = await onboardingTaskApi.onboardingTemplatePage(query);
|
||||||
|
rows.value = res.data?.list || [];
|
||||||
|
total.value = res.data?.total || 0;
|
||||||
|
} finally {
|
||||||
|
loading.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function resetQuery() {
|
||||||
|
query.keyword = "";
|
||||||
|
query.companyId = undefined;
|
||||||
|
query.pageNum = 1;
|
||||||
|
loadTemplates();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function onCompanyChange(companyId: number) {
|
||||||
|
form.positionIds = [];
|
||||||
|
if (!companyId) {
|
||||||
|
positions.value = [];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const res = await onboardingTaskApi.positionPage({ companyId, pageNum: 1, pageSize: 100, status: "enable" });
|
||||||
|
positions.value = res.data?.list || [];
|
||||||
|
}
|
||||||
|
|
||||||
|
async function onContractTemplateChange(row: any) {
|
||||||
|
row.contractTemplateVersionId = undefined;
|
||||||
|
if (!row.contractTemplateId) return;
|
||||||
|
await loadContractVersions(row.contractTemplateId);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function loadContractVersions(templateId: number) {
|
||||||
|
if (versionOptions[templateId]) return;
|
||||||
|
const res = await onboardingTaskApi.versionPage({ templateId, pageNum: 1, pageSize: 100 });
|
||||||
|
versionOptions[templateId] = (res.data?.list || []).filter((it: any) => it.versionStatus === "enabled");
|
||||||
|
}
|
||||||
|
|
||||||
|
function addContract() {
|
||||||
|
form.contracts.push({
|
||||||
|
contractTemplateId: undefined as any,
|
||||||
|
contractTemplateVersionId: undefined as any,
|
||||||
|
required: true,
|
||||||
|
printCopies: 1,
|
||||||
|
sortCode: (form.contracts.length + 1) * 10
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
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, checked: boolean) {
|
||||||
|
if (checked) {
|
||||||
|
if (!hasMaterial(materialTypeId)) {
|
||||||
|
form.materials.push({ materialTypeId, required: true, sortCode: (form.materials.length + 1) * 10 });
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
form.materials = form.materials.filter(item => item.materialTypeId !== materialTypeId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function openCreate() {
|
||||||
|
await loadBaseData();
|
||||||
|
Object.assign(form, {
|
||||||
|
id: undefined,
|
||||||
|
templateCode: "",
|
||||||
|
templateName: "",
|
||||||
|
companyId: undefined as any,
|
||||||
|
description: "",
|
||||||
|
status: "enable",
|
||||||
|
positionIds: [],
|
||||||
|
contracts: [],
|
||||||
|
formFields: [],
|
||||||
|
materials: []
|
||||||
|
});
|
||||||
|
positions.value = [];
|
||||||
|
visible.value = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function openEdit(row: OnboardingTemplate) {
|
||||||
|
await loadBaseData();
|
||||||
|
const res = await onboardingTaskApi.onboardingTemplateDetail({ id: row.id! });
|
||||||
|
const detail = res.data;
|
||||||
|
Object.assign(form, {
|
||||||
|
id: detail.id,
|
||||||
|
templateCode: detail.templateCode,
|
||||||
|
templateName: detail.templateName,
|
||||||
|
companyId: detail.companyId,
|
||||||
|
description: detail.description,
|
||||||
|
status: detail.status,
|
||||||
|
positionIds: detail.positionIds || [],
|
||||||
|
contracts: (detail.contracts || []).map((item: any) => ({
|
||||||
|
contractTemplateId: item.contractTemplateId,
|
||||||
|
contractTemplateVersionId: item.contractTemplateVersionId,
|
||||||
|
required: item.required,
|
||||||
|
printCopies: item.printCopies || 1,
|
||||||
|
sortCode: item.sortCode || 10
|
||||||
|
})),
|
||||||
|
formFields: [],
|
||||||
|
materials: (detail.materials || []).map((item: any) => ({
|
||||||
|
materialTypeId: item.materialTypeId,
|
||||||
|
required: item.required,
|
||||||
|
sortCode: item.sortCode || 10
|
||||||
|
}))
|
||||||
|
});
|
||||||
|
|
||||||
|
if (form.companyId) {
|
||||||
|
const posRes = await onboardingTaskApi.positionPage({ companyId: form.companyId, pageNum: 1, pageSize: 100, status: "enable" });
|
||||||
|
positions.value = posRes.data?.list || [];
|
||||||
|
}
|
||||||
|
for (const contract of form.contracts) {
|
||||||
|
if (contract.contractTemplateId) {
|
||||||
|
await loadContractVersions(contract.contractTemplateId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
visible.value = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function save() {
|
||||||
|
if (!form.templateCode || !form.templateName || !form.companyId || !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("删除成功");
|
||||||
|
query.pageNum = 1;
|
||||||
|
await loadTemplates();
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
await Promise.all([loadBaseData(), loadTemplates()]);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.template-panel {
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
box-sizing: border-box;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toolbar {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.keyword-input {
|
||||||
|
width: 220px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.company-filter {
|
||||||
|
width: 180px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toolbar-spacer {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tip {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-container {
|
||||||
|
flex: 1;
|
||||||
|
height: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination {
|
||||||
|
margin-top: 12px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.full-width {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mt-2 {
|
||||||
|
margin-top: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-gray {
|
||||||
|
color: #94a3b8;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 弹窗内部卡片样式 */
|
||||||
|
.template-dialog-body {
|
||||||
|
max-height: 72vh;
|
||||||
|
overflow-y: auto;
|
||||||
|
padding-right: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-block-card {
|
||||||
|
background: #f8fafc;
|
||||||
|
border: 1px solid #e2e8f0;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 16px 18px;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.block-card-header {
|
||||||
|
margin-bottom: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.flex-between {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
.block-title {
|
||||||
|
font-size: 15px;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #0f172a;
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.block-subtitle {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #64748b;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,359 @@
|
|||||||
|
<template>
|
||||||
|
<div class="variable-panel">
|
||||||
|
<div class="toolbar">
|
||||||
|
<el-input
|
||||||
|
v-model="query.keyword"
|
||||||
|
placeholder="搜索变量编码 / 变量名称"
|
||||||
|
clearable
|
||||||
|
style="width: 240px"
|
||||||
|
@keyup.enter="loadData"
|
||||||
|
@clear="loadData"
|
||||||
|
/>
|
||||||
|
<el-select
|
||||||
|
v-model="query.variableCategory"
|
||||||
|
placeholder="变量分类"
|
||||||
|
clearable
|
||||||
|
style="width: 180px"
|
||||||
|
@change="loadData"
|
||||||
|
>
|
||||||
|
<el-option label="全部变量分类" value="" />
|
||||||
|
<el-option label="员工登记表变量 (employee)" value="employee" />
|
||||||
|
<el-option label="人事合同参数 (hr)" value="hr" />
|
||||||
|
<el-option label="公司信息变量 (company)" value="company" />
|
||||||
|
<el-option label="系统预置变量 (system)" value="system" />
|
||||||
|
</el-select>
|
||||||
|
<el-button type="primary" icon="Search" @click="loadData">查询</el-button>
|
||||||
|
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
|
||||||
|
<div class="spacer"></div>
|
||||||
|
<el-button type="primary" icon="Plus" @click="openCreate">新增自定义变量</el-button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<el-alert
|
||||||
|
title="变量字典库定义了合同 DOCX 中所有合法的占位符(如 {{employee.EmployeeName}})。上传 Word 时系统会自动校验占位符是否在此字典中,防止拼写错误。"
|
||||||
|
type="info"
|
||||||
|
show-icon
|
||||||
|
:closable="false"
|
||||||
|
class="tip-alert"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div class="table-container">
|
||||||
|
<el-table :data="tableData" border stripe height="100%" v-loading="loading">
|
||||||
|
<el-table-column prop="variableCode" label="变量全编码 (DOCX占位符)" min-width="260">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<div class="code-col">
|
||||||
|
<el-tag :type="categoryTag(row.variableCategory)" effect="light" size="small" class="tag-badge">
|
||||||
|
{{ categoryText(row.variableCategory) }}
|
||||||
|
</el-tag>
|
||||||
|
<code class="code-font">{{ formatPlaceholder(row.variableCode) }}</code>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
|
||||||
|
<el-table-column prop="variableName" label="中文显示名称" min-width="160" />
|
||||||
|
|
||||||
|
<el-table-column prop="dataType" label="数据类型" width="100" align="center">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-tag size="small" type="info">{{ row.dataType || 'string' }}</el-tag>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
|
||||||
|
<el-table-column prop="isBuiltIn" label="内置标准" width="120" align="center">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-tag size="small" :type="row.isBuiltIn ? 'success' : 'warning'">
|
||||||
|
{{ row.isBuiltIn ? '入职登记表' : '自定义' }}
|
||||||
|
</el-tag>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
|
||||||
|
<el-table-column prop="required" label="必填" width="80" align="center">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-tag size="small" :type="row.required ? 'danger' : 'info'">
|
||||||
|
{{ row.required ? '是' : '否' }}
|
||||||
|
</el-tag>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
|
||||||
|
<el-table-column prop="remark" label="说明备注" min-width="200" show-overflow-tooltip />
|
||||||
|
|
||||||
|
<el-table-column label="操作" width="140" align="center" fixed="right">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-button link type="primary" icon="Edit" @click="openEdit(row)">编辑</el-button>
|
||||||
|
<el-button
|
||||||
|
v-if="!row.isBuiltIn"
|
||||||
|
link
|
||||||
|
type="danger"
|
||||||
|
icon="Delete"
|
||||||
|
@click="remove(row)"
|
||||||
|
>
|
||||||
|
删除
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="pagination-box">
|
||||||
|
<el-pagination
|
||||||
|
v-model:current-page="query.pageNum"
|
||||||
|
v-model:page-size="query.pageSize"
|
||||||
|
:total="total"
|
||||||
|
:page-sizes="[20, 50, 100]"
|
||||||
|
layout="total, sizes, prev, pager, next, jumper"
|
||||||
|
@size-change="loadData"
|
||||||
|
@current-change="loadData"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 弹窗 -->
|
||||||
|
<el-dialog v-model="visible" :title="form.id ? '编辑变量' : '新增自定义变量'" width="560px" destroy-on-close>
|
||||||
|
<el-form ref="formRef" :model="form" :rules="rules" label-width="110px">
|
||||||
|
<el-form-item label="变量分类" prop="variableCategory" required>
|
||||||
|
<el-select v-model="form.variableCategory" :disabled="form.isBuiltIn" style="width: 100%">
|
||||||
|
<el-option label="员工变量 (employee.)" value="employee" />
|
||||||
|
<el-option label="人事合同参数 (hr.)" value="hr" />
|
||||||
|
<el-option label="公司信息 (company.)" value="company" />
|
||||||
|
<el-option label="系统变量 (system.)" value="system" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item label="变量全编码" prop="variableCode" required>
|
||||||
|
<el-input
|
||||||
|
v-model="form.variableCode"
|
||||||
|
placeholder="如 employee.WeChat 或 hr.SpecialSubsidy"
|
||||||
|
:disabled="Boolean(form.id)"
|
||||||
|
/>
|
||||||
|
<div class="form-tip">规范格式:分类.变量名,例如 employee.CarNo</div>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item label="中文名称" prop="variableName" required>
|
||||||
|
<el-input v-model="form.variableName" placeholder="如 微信号、特殊津贴" />
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item label="数据类型">
|
||||||
|
<el-select v-model="form.dataType" style="width: 100%">
|
||||||
|
<el-option label="文本 (string)" value="string" />
|
||||||
|
<el-option label="数字 / 金额 (number)" value="number" />
|
||||||
|
<el-option label="日期 (date)" value="date" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item label="默认值">
|
||||||
|
<el-input v-model="form.defaultValue" placeholder="未填写时的默认值" />
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item label="是否必填">
|
||||||
|
<el-switch v-model="form.required" />
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item label="排序码">
|
||||||
|
<el-input-number v-model="form.sortCode" :min="1" :max="9999" />
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item label="说明备注">
|
||||||
|
<el-input v-model="form.remark" type="textarea" :rows="2" placeholder="变量业务说明" />
|
||||||
|
</el-form-item>
|
||||||
|
</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 { ref, reactive, onMounted } from "vue";
|
||||||
|
import { ElMessage, ElMessageBox, FormInstance, FormRules } from "element-plus";
|
||||||
|
import { onboardingTaskApi } from "@/api/modules/biz/onboarding";
|
||||||
|
|
||||||
|
const loading = ref(false);
|
||||||
|
const saving = ref(false);
|
||||||
|
const visible = ref(false);
|
||||||
|
const tableData = ref<any[]>([]);
|
||||||
|
const total = ref(0);
|
||||||
|
const formRef = ref<FormInstance>();
|
||||||
|
|
||||||
|
const query = reactive({
|
||||||
|
keyword: "",
|
||||||
|
variableCategory: "",
|
||||||
|
pageNum: 1,
|
||||||
|
pageSize: 50
|
||||||
|
});
|
||||||
|
|
||||||
|
const form = reactive<any>({
|
||||||
|
id: undefined,
|
||||||
|
variableCode: "",
|
||||||
|
variableName: "",
|
||||||
|
variableCategory: "employee",
|
||||||
|
dataType: "string",
|
||||||
|
defaultValue: "",
|
||||||
|
isBuiltIn: false,
|
||||||
|
required: false,
|
||||||
|
sortCode: 100,
|
||||||
|
remark: ""
|
||||||
|
});
|
||||||
|
|
||||||
|
const rules: FormRules = {
|
||||||
|
variableCode: [{ required: true, message: "请输入变量编码", trigger: "blur" }],
|
||||||
|
variableName: [{ required: true, message: "请输入中文显示名称", trigger: "blur" }],
|
||||||
|
variableCategory: [{ required: true, message: "请选择分类", trigger: "change" }]
|
||||||
|
};
|
||||||
|
|
||||||
|
function categoryTag(cat: string) {
|
||||||
|
switch (cat) {
|
||||||
|
case "employee": return "primary";
|
||||||
|
case "hr": return "warning";
|
||||||
|
case "company": return "success";
|
||||||
|
default: return "info";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function categoryText(cat: string) {
|
||||||
|
switch (cat) {
|
||||||
|
case "employee": return "员工变量";
|
||||||
|
case "hr": return "人事参数";
|
||||||
|
case "company": return "公司信息";
|
||||||
|
default: return "系统变量";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatPlaceholder(code?: string) {
|
||||||
|
return code ? `{{${code}}}` : "";
|
||||||
|
}
|
||||||
|
|
||||||
|
async function loadData() {
|
||||||
|
loading.value = true;
|
||||||
|
try {
|
||||||
|
const res = await onboardingTaskApi.variableDefinitionPage(query);
|
||||||
|
tableData.value = res.data?.list || [];
|
||||||
|
total.value = res.data?.total || 0;
|
||||||
|
} finally {
|
||||||
|
loading.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function resetQuery() {
|
||||||
|
query.keyword = "";
|
||||||
|
query.variableCategory = "";
|
||||||
|
query.pageNum = 1;
|
||||||
|
loadData();
|
||||||
|
}
|
||||||
|
|
||||||
|
function openCreate() {
|
||||||
|
Object.assign(form, {
|
||||||
|
id: undefined,
|
||||||
|
variableCode: "",
|
||||||
|
variableName: "",
|
||||||
|
variableCategory: "employee",
|
||||||
|
dataType: "string",
|
||||||
|
defaultValue: "",
|
||||||
|
isBuiltIn: false,
|
||||||
|
required: false,
|
||||||
|
sortCode: 100,
|
||||||
|
remark: ""
|
||||||
|
});
|
||||||
|
visible.value = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function openEdit(row: any) {
|
||||||
|
Object.assign(form, {
|
||||||
|
id: row.id,
|
||||||
|
variableCode: row.variableCode,
|
||||||
|
variableName: row.variableName,
|
||||||
|
variableCategory: row.variableCategory,
|
||||||
|
dataType: row.dataType || "string",
|
||||||
|
defaultValue: row.defaultValue || "",
|
||||||
|
isBuiltIn: row.isBuiltIn || false,
|
||||||
|
required: row.required || false,
|
||||||
|
sortCode: row.sortCode || 100,
|
||||||
|
remark: row.remark || ""
|
||||||
|
});
|
||||||
|
visible.value = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function save() {
|
||||||
|
await formRef.value?.validate();
|
||||||
|
saving.value = true;
|
||||||
|
try {
|
||||||
|
if (form.id) {
|
||||||
|
await onboardingTaskApi.editVariableDefinition(form);
|
||||||
|
ElMessage.success("变量修改成功");
|
||||||
|
} else {
|
||||||
|
await onboardingTaskApi.addVariableDefinition(form);
|
||||||
|
ElMessage.success("自定义变量添加成功");
|
||||||
|
}
|
||||||
|
visible.value = false;
|
||||||
|
loadData();
|
||||||
|
} finally {
|
||||||
|
saving.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function remove(row: any) {
|
||||||
|
await ElMessageBox.confirm(`确认删除自定义变量“${row.variableCode}”?`, "删除确认", { type: "warning" });
|
||||||
|
await onboardingTaskApi.deleteVariableDefinitions([row.id]);
|
||||||
|
ElMessage.success("删除成功");
|
||||||
|
loadData();
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(loadData);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.variable-panel {
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
box-sizing: border-box;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
.toolbar {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
.spacer {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
.tip-alert {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
.table-container {
|
||||||
|
flex: 1;
|
||||||
|
height: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
.code-col {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
.tag-badge {
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
.code-font {
|
||||||
|
font-family: Consolas, Monaco, monospace;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #b45309;
|
||||||
|
background: #fef3c7;
|
||||||
|
padding: 2px 6px;
|
||||||
|
border-radius: 4px;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
.pagination-box {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
margin-top: 12px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
.form-tip {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #94a3b8;
|
||||||
|
margin-top: 4px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -1,58 +1,24 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="main-box config-container">
|
<div class="main-box config-container">
|
||||||
<el-tabs v-model="activeTab" class="config-tabs">
|
<el-tabs v-model="activeTab" class="config-tabs">
|
||||||
<el-tab-pane label="动态填写字段" name="fields">
|
<el-tab-pane label="合同变量字典" name="variables">
|
||||||
<div class="tab-card-wrapper">
|
<div class="tab-card-wrapper">
|
||||||
<div class="toolbar">
|
<VariableDefinitionPanel />
|
||||||
<el-button type="primary" icon="Plus" @click="openFieldCreate">
|
|
||||||
新增字段
|
|
||||||
</el-button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<el-table :data="fields" border stripe>
|
|
||||||
<el-table-column prop="fieldCode" label="字段编码" min-width="140" />
|
|
||||||
<el-table-column prop="fieldName" label="字段名称" min-width="150" />
|
|
||||||
<el-table-column prop="dataType" label="数据类型" width="120" align="center">
|
|
||||||
<template #default="scope">
|
|
||||||
<el-tag size="small" type="info">{{ scope.row.dataType }}</el-tag>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column prop="placeholder" label="提示语" min-width="160" />
|
|
||||||
<el-table-column prop="status" label="状态" width="100" align="center">
|
|
||||||
<template #default="scope">
|
|
||||||
<el-tag size="small" :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" icon="Edit" @click="openFieldEdit(scope.row)">
|
|
||||||
编辑
|
|
||||||
</el-button>
|
|
||||||
|
|
||||||
<el-button link type="danger" icon="Delete" @click="removeField(scope.row)">
|
|
||||||
删除
|
|
||||||
</el-button>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
</el-table>
|
|
||||||
</div>
|
</div>
|
||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
|
|
||||||
<el-tab-pane label="上传资料类型" name="materials">
|
<el-tab-pane label="上传资料类型" name="materials">
|
||||||
<div class="tab-card-wrapper">
|
<div class="tab-card-wrapper material-tab-wrapper">
|
||||||
<div class="toolbar">
|
<div class="toolbar">
|
||||||
<el-button type="primary" icon="Plus" @click="openMaterialCreate">
|
<el-button type="primary" icon="Plus" @click="openMaterialCreate">
|
||||||
新增资料类型
|
新增资料类型
|
||||||
</el-button>
|
</el-button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<el-table :data="materialTypes" border stripe>
|
<el-table :data="materialTypes" border stripe height="100%">
|
||||||
<el-table-column prop="materialCode" label="资料编码" min-width="140" />
|
<el-table-column prop="materialCode" label="资料编码" min-width="140" />
|
||||||
<el-table-column prop="materialName" label="资料名称" min-width="150" />
|
<el-table-column prop="materialName" label="资料名称" min-width="150" />
|
||||||
<el-table-column prop="allowedExtensions" label="允许扩展名" min-width="140">
|
<el-table-column prop="allowedExtensions" label="允许扩展名" min-width="160">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<code>{{ scope.row.allowedExtensions || '不限' }}</code>
|
<code>{{ scope.row.allowedExtensions || '不限' }}</code>
|
||||||
</template>
|
</template>
|
||||||
@@ -67,7 +33,7 @@
|
|||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
|
|
||||||
<el-table-column label="操作" width="150" fixed="right">
|
<el-table-column label="操作" width="150" fixed="right" align="center">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<el-button link type="primary" icon="Edit" @click="openMaterialEdit(scope.row)">
|
<el-button link type="primary" icon="Edit" @click="openMaterialEdit(scope.row)">
|
||||||
编辑
|
编辑
|
||||||
@@ -82,161 +48,16 @@
|
|||||||
</div>
|
</div>
|
||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
|
|
||||||
<el-tab-pane label="公司岗位要求" name="requirements">
|
<el-tab-pane label="入职模板" name="templates">
|
||||||
<div class="tab-card-wrapper">
|
<div class="tab-card-wrapper">
|
||||||
<el-alert title="请先选择公司,再为全公司或某个岗位分别配置员工必填信息和上传资料。" type="info" :closable="false" class="hint" />
|
<OnboardingTemplatePanel />
|
||||||
|
|
||||||
<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>
|
|
||||||
</div>
|
</div>
|
||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
</el-tabs>
|
</el-tabs>
|
||||||
|
|
||||||
<el-dialog v-model="fieldVisible" :title="fieldForm.id ? '编辑字段' : '新增字段'" width="560px" destroy-on-close>
|
<!-- 资料类型弹窗 -->
|
||||||
<el-form :model="fieldForm" label-width="110px">
|
|
||||||
<el-form-item label="字段编码" required>
|
|
||||||
<el-input v-model="fieldForm.fieldCode" />
|
|
||||||
</el-form-item>
|
|
||||||
|
|
||||||
<el-form-item label="字段名称" required>
|
|
||||||
<el-input v-model="fieldForm.fieldName" />
|
|
||||||
</el-form-item>
|
|
||||||
|
|
||||||
<el-form-item label="数据类型" required>
|
|
||||||
<el-select v-model="fieldForm.dataType">
|
|
||||||
<el-option label="文本" value="string" />
|
|
||||||
<el-option label="日期" value="date" />
|
|
||||||
<el-option label="整数" value="int" />
|
|
||||||
<el-option label="数字" value="number" />
|
|
||||||
<el-option label="金额" value="decimal" />
|
|
||||||
<el-option label="下拉选择" value="select" />
|
|
||||||
</el-select>
|
|
||||||
</el-form-item>
|
|
||||||
|
|
||||||
<el-form-item label="提示语">
|
|
||||||
<el-input v-model="fieldForm.placeholder" />
|
|
||||||
</el-form-item>
|
|
||||||
|
|
||||||
<el-form-item label="选项JSON">
|
|
||||||
<el-input v-model="fieldForm.optionsJson" type="textarea" placeholder="下拉/多选字段可填写选项JSON" />
|
|
||||||
</el-form-item>
|
|
||||||
|
|
||||||
<el-form-item label="校验规则">
|
|
||||||
<el-input v-model="fieldForm.validationRule" />
|
|
||||||
</el-form-item>
|
|
||||||
|
|
||||||
<el-form-item label="状态">
|
|
||||||
<el-select v-model="fieldForm.status">
|
|
||||||
<el-option label="启用" value="enable" />
|
|
||||||
<el-option label="停用" value="disable" />
|
|
||||||
</el-select>
|
|
||||||
</el-form-item>
|
|
||||||
</el-form>
|
|
||||||
|
|
||||||
<template #footer>
|
|
||||||
<el-button @click="fieldVisible = false">
|
|
||||||
取消
|
|
||||||
</el-button>
|
|
||||||
|
|
||||||
<el-button type="primary" @click="saveField">
|
|
||||||
保存
|
|
||||||
</el-button>
|
|
||||||
</template>
|
|
||||||
</el-dialog>
|
|
||||||
|
|
||||||
<el-dialog v-model="materialVisible" :title="materialForm.id ? '编辑资料类型' : '新增资料类型'" width="560px" destroy-on-close>
|
<el-dialog v-model="materialVisible" :title="materialForm.id ? '编辑资料类型' : '新增资料类型'" width="560px" destroy-on-close>
|
||||||
<el-form :model="materialForm" label-width="120px">
|
<el-form :model="materialForm" label-width="110px">
|
||||||
<el-form-item label="资料编码" required>
|
<el-form-item label="资料编码" required>
|
||||||
<el-input v-model="materialForm.materialCode" />
|
<el-input v-model="materialForm.materialCode" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
@@ -246,219 +67,72 @@
|
|||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
|
||||||
<el-form-item label="允许扩展名">
|
<el-form-item label="允许扩展名">
|
||||||
<el-input v-model="materialForm.allowedExtensions" placeholder="如:jpg,png,pdf" />
|
<el-input v-model="materialForm.allowedExtensions" placeholder="逗号分隔,如:png,jpg,jpeg,pdf" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
|
||||||
<el-form-item label="最大文件数量">
|
<el-form-item label="最大文件数">
|
||||||
<el-input-number v-model="materialForm.maxFileCount" :min="1" />
|
<el-input-number v-model="materialForm.maxFileCount" :min="1" :max="10" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
|
||||||
<el-form-item label="最大大小(MB)">
|
<el-form-item label="单文件大小(MB)">
|
||||||
<el-input-number v-model="materialForm.maxFileSizeMb" :min="1" />
|
<el-input-number v-model="materialForm.maxFileSizeMb" :min="1" :max="100" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
|
||||||
<el-form-item label="状态">
|
<el-form-item label="状态">
|
||||||
<el-select v-model="materialForm.status">
|
<el-radio-group v-model="materialForm.status">
|
||||||
<el-option label="启用" value="enable" />
|
<el-radio label="enable">启用</el-radio>
|
||||||
<el-option label="停用" value="disable" />
|
<el-radio label="disable">停用</el-radio>
|
||||||
</el-select>
|
</el-radio-group>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
|
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<el-button @click="materialVisible = false">
|
<el-button @click="materialVisible = false">取消</el-button>
|
||||||
取消
|
<el-button type="primary" @click="saveMaterial">保存</el-button>
|
||||||
</el-button>
|
|
||||||
|
|
||||||
<el-button type="primary" @click="saveMaterial">
|
|
||||||
保存
|
|
||||||
</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>
|
</template>
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts" name="onboardingEntryConfig">
|
<script setup lang="ts" name="onboardingEntryConfig">
|
||||||
|
import { onMounted, reactive, ref } from "vue";
|
||||||
|
import { ElMessage, ElMessageBox } from "element-plus";
|
||||||
import {
|
import {
|
||||||
onboardingTaskApi,
|
OnboardingMaterialType,
|
||||||
type OnboardingCompanyOption,
|
onboardingTaskApi
|
||||||
type OnboardingFormField,
|
|
||||||
type OnboardingFormRequirement,
|
|
||||||
type OnboardingMaterialRequirement,
|
|
||||||
type OnboardingMaterialType,
|
|
||||||
type OnboardingPositionOption
|
|
||||||
} from "@/api/modules/biz/onboarding";
|
} from "@/api/modules/biz/onboarding";
|
||||||
|
import OnboardingTemplatePanel from "./components/OnboardingTemplatePanel.vue";
|
||||||
|
import VariableDefinitionPanel from "./components/VariableDefinitionPanel.vue";
|
||||||
|
|
||||||
const activeTab = ref("fields");
|
const activeTab = ref("variables");
|
||||||
const requirementTab = ref("form");
|
|
||||||
|
|
||||||
const fields = ref<OnboardingFormField[]>([]);
|
|
||||||
const materialTypes = ref<OnboardingMaterialType[]>([]);
|
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 materialVisible = ref(false);
|
||||||
const requirementVisible = ref(false);
|
|
||||||
|
|
||||||
const requirementKind = ref<"form" | "material">("form");
|
const materialForm = reactive<Partial<OnboardingMaterialType>>({
|
||||||
|
materialCode: "",
|
||||||
|
materialName: "",
|
||||||
|
allowedExtensions: "png,jpg,jpeg,pdf",
|
||||||
|
maxFileCount: 3,
|
||||||
|
maxFileSizeMb: 10,
|
||||||
|
status: "enable"
|
||||||
|
});
|
||||||
|
|
||||||
const fieldForm = reactive<Partial<OnboardingFormField>>({});
|
async function loadMaterials() {
|
||||||
const materialForm = reactive<Partial<OnboardingMaterialType>>({});
|
const res = await onboardingTaskApi.listMaterialTypes();
|
||||||
const requirementForm = reactive<any>({});
|
materialTypes.value = res.data || [];
|
||||||
|
|
||||||
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([
|
|
||||||
onboardingTaskApi.listFormFields(),
|
|
||||||
onboardingTaskApi.listMaterialTypes(),
|
|
||||||
onboardingTaskApi.listCompanies()
|
|
||||||
]);
|
|
||||||
|
|
||||||
fields.value = fieldResult.data;
|
|
||||||
materialTypes.value = materialResult.data;
|
|
||||||
companies.value = companyResult.data;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function loadRequirements() {
|
function openMaterialCreate() {
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
function resetField() {
|
|
||||||
Object.assign(fieldForm, {
|
|
||||||
id: undefined,
|
|
||||||
fieldCode: "",
|
|
||||||
fieldName: "",
|
|
||||||
dataType: "string",
|
|
||||||
placeholder: "",
|
|
||||||
optionsJson: "",
|
|
||||||
validationRule: "",
|
|
||||||
status: "enable"
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function resetMaterial() {
|
|
||||||
Object.assign(materialForm, {
|
Object.assign(materialForm, {
|
||||||
id: undefined,
|
id: undefined,
|
||||||
materialCode: "",
|
materialCode: "",
|
||||||
materialName: "",
|
materialName: "",
|
||||||
allowedExtensions: "",
|
allowedExtensions: "png,jpg,jpeg,pdf",
|
||||||
maxFileCount: 1,
|
maxFileCount: 3,
|
||||||
maxFileSizeMb: undefined,
|
maxFileSizeMb: 10,
|
||||||
status: "enable"
|
status: "enable"
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
|
||||||
function openFieldCreate() {
|
|
||||||
resetField();
|
|
||||||
fieldVisible.value = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
function openFieldEdit(row: OnboardingFormField) {
|
|
||||||
Object.assign(fieldForm, row);
|
|
||||||
fieldVisible.value = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function saveField() {
|
|
||||||
if (
|
|
||||||
!fieldForm.fieldCode ||
|
|
||||||
!fieldForm.fieldName ||
|
|
||||||
!fieldForm.dataType
|
|
||||||
) {
|
|
||||||
return ElMessage.warning(
|
|
||||||
"请填写字段编码、名称和数据类型"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
await onboardingTaskApi.saveFormField(fieldForm);
|
|
||||||
|
|
||||||
fieldVisible.value = false;
|
|
||||||
|
|
||||||
await loadBase();
|
|
||||||
}
|
|
||||||
|
|
||||||
function openMaterialCreate() {
|
|
||||||
resetMaterial();
|
|
||||||
materialVisible.value = true;
|
materialVisible.value = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -468,141 +142,25 @@ function openMaterialEdit(row: OnboardingMaterialType) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function saveMaterial() {
|
async function saveMaterial() {
|
||||||
if (
|
if (!materialForm.materialCode || !materialForm.materialName) {
|
||||||
!materialForm.materialCode ||
|
return ElMessage.warning("请填写资料编码和资料名称");
|
||||||
!materialForm.materialName
|
|
||||||
) {
|
|
||||||
return ElMessage.warning(
|
|
||||||
"请填写资料编码和名称"
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
await onboardingTaskApi.saveMaterialType(materialForm);
|
await onboardingTaskApi.saveMaterialType(materialForm);
|
||||||
|
ElMessage.success("资料类型保存成功");
|
||||||
materialVisible.value = false;
|
materialVisible.value = false;
|
||||||
|
await loadMaterials();
|
||||||
await loadBase();
|
|
||||||
}
|
|
||||||
|
|
||||||
async function removeField(row: OnboardingFormField) {
|
|
||||||
await ElMessageBox.confirm(
|
|
||||||
`确认删除字段“${row.fieldName}”?`,
|
|
||||||
"删除确认"
|
|
||||||
);
|
|
||||||
|
|
||||||
await onboardingTaskApi.deleteFormFields([row.id]);
|
|
||||||
|
|
||||||
await loadBase();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function removeMaterial(row: OnboardingMaterialType) {
|
async function removeMaterial(row: OnboardingMaterialType) {
|
||||||
await ElMessageBox.confirm(
|
await ElMessageBox.confirm(`确认删除资料类型“${row.materialName}”吗?`, "删除确认", { type: "warning" });
|
||||||
`确认删除资料类型“${row.materialName}”?`,
|
await onboardingTaskApi.deleteMaterialTypes([row.id!]);
|
||||||
"删除确认"
|
ElMessage.success("删除成功");
|
||||||
);
|
await loadMaterials();
|
||||||
|
|
||||||
await onboardingTaskApi.deleteMaterialTypes([row.id]);
|
|
||||||
|
|
||||||
await loadBase();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function resetRequirement() {
|
onMounted(() => {
|
||||||
Object.assign(requirementForm, {
|
loadMaterials();
|
||||||
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>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
@@ -632,21 +190,34 @@ onMounted(loadBase);
|
|||||||
|
|
||||||
.config-tabs :deep(.el-tabs__content) {
|
.config-tabs :deep(.el-tabs__content) {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
overflow: auto;
|
height: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.config-tabs :deep(.el-tab-pane) {
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tab-card-wrapper {
|
.tab-card-wrapper {
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
background: #fff;
|
background: #fff;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
padding: 16px;
|
padding: 16px;
|
||||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
|
box-sizing: border-box;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.material-tab-wrapper {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
.toolbar {
|
.toolbar {
|
||||||
margin-bottom: 14px;
|
margin-bottom: 12px;
|
||||||
}
|
|
||||||
|
|
||||||
.hint {
|
|
||||||
margin-bottom: 14px;
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
File diff suppressed because it is too large
Load Diff
+996
-914
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user