This commit is contained in:
fuenmao
2026-08-25 20:37:24 +08:00
parent aec6c1f36c
commit eab359d9fa
6 changed files with 1748 additions and 1503 deletions
@@ -1,13 +1,8 @@
<template>
<div class="template-panel">
<div class="toolbar">
<el-input
v-model="query.keyword"
clearable
placeholder="搜索模板编码或名称"
class="keyword-input"
@keyup.enter="loadTemplates"
/>
<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>
@@ -17,193 +12,202 @@
<el-button type="primary" icon="Plus" @click="openCreate">新增入职模板</el-button>
</div>
<el-alert
title="入职模板统一管理公司、岗位、合同、员工补充字段和上传材料;创建任务后会冻结本次配置。"
type="info"
:closable="false"
show-icon
class="tip"
/>
<el-alert title="入职模板统一管理公司、岗位、合同文件与员工上传材料;员工入职档案自动采用标准《入职登记表》。" type="info" :closable="false" show-icon
class="tip" />
<el-table v-loading="loading" :data="rows" border>
<el-table-column prop="templateCode" label="模板编码" min-width="150" />
<el-table-column prop="templateName" label="模板名称" min-width="180" />
<el-table-column prop="companyName" label="所属公司" min-width="160" />
<el-table-column label="适用岗位" min-width="220">
<template #default="scope">
<el-space wrap>
<el-tag v-for="name in scope.row.positionNames" :key="name" size="small">{{ name }}</el-tag>
</el-space>
</template>
</el-table-column>
<el-table-column label="配置内容" min-width="230">
<template #default="scope">
合同 {{ scope.row.contractCount || 0 }} 份 / 补充字段 {{ scope.row.formFieldCount || 0 }} 项 / 材料 {{ scope.row.materialCount || 0 }} 项
</template>
</el-table-column>
<el-table-column prop="status" label="状态" width="90" align="center">
<template #default="scope">
<el-tag :type="scope.row.status === 'enable' ? 'success' : 'info'">
{{ scope.row.status === "enable" ? "启用" : "停用" }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="操作" width="150" fixed="right">
<template #default="scope">
<el-button link type="primary" @click="openEdit(scope.row)">编辑</el-button>
<el-button link type="danger" @click="removeTemplate(scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
<div class="pagination">
<el-pagination
v-model:current-page="query.pageNum"
v-model:page-size="query.pageSize"
layout="total, sizes, prev, pager, next"
:total="total"
@current-change="loadTemplates"
@size-change="loadTemplates"
/>
<div 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>
<el-dialog
v-model="visible"
:title="form.id ? '编辑入职模板' : '新增入职模板'"
width="1080px"
top="5vh"
destroy-on-close
>
<el-form label-width="110px">
<el-divider content-position="left">基本信息</el-divider>
<el-row :gutter="16">
<el-col :span="8">
<el-form-item label="模板编码" required>
<el-input v-model="form.templateCode" placeholder="例如 FULLTIME_DEFAULT" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="模板名称" required>
<el-input v-model="form.templateName" placeholder="例如 全职员工入职模板" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="状态" required>
<el-radio-group v-model="form.status">
<el-radio value="enable">启用</el-radio>
<el-radio value="disable">停用</el-radio>
</el-radio-group>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="16">
<el-col :span="10">
<el-form-item label="所属公司" required>
<el-select v-model="form.companyId" filterable class="full-width" @change="onCompanyChange">
<el-option v-for="item in companies" :key="item.id" :label="item.companyName" :value="item.id" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="14">
<el-form-item label="适用岗位" required>
<el-select v-model="form.positionIds" multiple filterable collapse-tags class="full-width" :disabled="!form.companyId">
<el-option v-for="item in positions" :key="item.id" :label="item.positionName" :value="item.id" />
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-form-item label="模板说明">
<el-input v-model="form.description" type="textarea" :rows="2" />
</el-form-item>
<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-divider content-position="left">合同文件</el-divider>
<div class="section-action">
<span>合同变量由上传 DOCX 时自动识别,这里只选择已确认并启用的版本。</span>
<el-button type="primary" plain icon="Plus" @click="addContract">添加合同</el-button>
</div>
<el-table :data="form.contracts" border empty-text="请至少添加一份合同">
<el-table-column label="合同模板" min-width="210">
<template #default="scope">
<el-select v-model="scope.row.contractTemplateId" filterable class="full-width" @change="onContractTemplateChange(scope.row)">
<el-option v-for="item in contractTemplates" :key="item.id" :label="item.templateName" :value="item.id" />
</el-select>
</template>
</el-table-column>
<el-table-column label="启用版本" min-width="180">
<template #default="scope">
<el-select v-model="scope.row.contractTemplateVersionId" class="full-width" :disabled="!scope.row.contractTemplateId">
<el-option
v-for="item in versionOptions[scope.row.contractTemplateId] || []"
:key="item.id"
:label="item.versionNo"
:value="item.id"
/>
</el-select>
</template>
</el-table-column>
<el-table-column label="必须确认" width="100" align="center">
<template #default="scope"><el-switch v-model="scope.row.required" /></template>
</el-table-column>
<el-table-column label="打印份数" width="130">
<template #default="scope"><el-input-number v-model="scope.row.printCopies" :min="1" :max="10" /></template>
</el-table-column>
<el-table-column label="排序" width="110">
<template #default="scope"><el-input-number v-model="scope.row.sortCode" :min="0" /></template>
</el-table-column>
<el-table-column label="操作" width="80" align="center">
<template #default="scope"><el-button link type="danger" @click="form.contracts.splice(scope.$index, 1)">移除</el-button></template>
</el-table-column>
</el-table>
<!-- 模板编辑大弹窗 -->
<el-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-divider content-position="left">员工补充字段</el-divider>
<el-alert title="姓名、手机号、身份证号等基本信息固定展示;这里只选择额外需要员工填写的字段。" type="info" :closable="false" class="section-tip" />
<el-table :data="formFields" border max-height="260">
<el-table-column label="选择" width="70" align="center">
<template #default="scope">
<el-checkbox :model-value="hasField(scope.row.id)" @change="value => toggleField(scope.row.id, Boolean(value))" />
</template>
</el-table-column>
<el-table-column prop="fieldCode" label="字段编码" min-width="170" />
<el-table-column prop="fieldName" label="字段名称" min-width="180" />
<el-table-column prop="dataType" label="数据类型" width="110" />
<el-table-column label="必填" width="100" align="center">
<template #default="scope">
<el-switch v-if="hasField(scope.row.id)" v-model="fieldConfig(scope.row.id)!.required" />
</template>
</el-table-column>
<el-table-column label="排序" width="120">
<template #default="scope">
<el-input-number v-if="hasField(scope.row.id)" v-model="fieldConfig(scope.row.id)!.sortCode" :min="0" />
</template>
</el-table-column>
</el-table>
<el-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-divider content-position="left">员工上传材料</el-divider>
<el-table :data="materialTypes" border max-height="260">
<el-table-column label="选择" width="70" align="center">
<template #default="scope">
<el-checkbox :model-value="hasMaterial(scope.row.id)" @change="value => toggleMaterial(scope.row.id, Boolean(value))" />
</template>
</el-table-column>
<el-table-column prop="materialCode" label="材料编码" min-width="170" />
<el-table-column prop="materialName" label="材料名称" min-width="200" />
<el-table-column prop="maxFileCount" label="最多文件" width="100" align="center" />
<el-table-column label="必传" width="100" align="center">
<template #default="scope">
<el-switch v-if="hasMaterial(scope.row.id)" v-model="materialConfig(scope.row.id)!.required" />
</template>
</el-table-column>
<el-table-column label="排序" width="120">
<template #default="scope">
<el-input-number v-if="hasMaterial(scope.row.id)" v-model="materialConfig(scope.row.id)!.sortCode" :min="0" />
</template>
</el-table-column>
</el-table>
</el-form>
<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>
<el-button type="primary" :loading="saving" @click="save">保存入职模板</el-button>
</template>
</el-dialog>
</div>
@@ -213,28 +217,24 @@
import { onMounted, reactive, ref } from "vue";
import { ElMessage, ElMessageBox } from "element-plus";
import {
onboardingTaskApi,
type OnboardingCompanyOption,
type OnboardingContractTemplate,
type OnboardingContractVersion,
type OnboardingFormField,
type OnboardingMaterialType,
type OnboardingPositionOption,
type OnboardingTemplate,
type OnboardingTemplateContractConfig,
type OnboardingTemplateFieldConfig,
type OnboardingTemplateMaterialConfig
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 total = ref(0);
const rows = ref<OnboardingTemplate[]>([]);
const companies = ref<OnboardingCompanyOption[]>([]);
const positions = ref<OnboardingPositionOption[]>([]);
const total = ref(0);
const companies = ref<OnboardingCompany[]>([]);
const positions = ref<OnboardingPosition[]>([]);
const contractTemplates = ref<OnboardingContractTemplate[]>([]);
const formFields = ref<OnboardingFormField[]>([]);
const materialTypes = ref<OnboardingMaterialType[]>([]);
const versionOptions = reactive<Record<number, OnboardingContractVersion[]>>({});
@@ -245,101 +245,80 @@ const query = reactive({
pageSize: 20
});
const emptyForm = (): OnboardingTemplate => ({
id: undefined as unknown as number,
const form = reactive<OnboardingTemplate>({
id: 0,
templateCode: "",
templateName: "",
companyId: undefined as unknown as number,
companyId: 0,
description: "",
status: "enable",
positionIds: [],
contracts: [],
formFields: [],
materials: [],
status: "enable",
description: ""
materials: []
});
const form = reactive<OnboardingTemplate>(emptyForm());
async function loadBaseData() {
try {
const [companyRes, contractRes, fieldRes, materialRes] = await Promise.all([
onboardingTaskApi.listCompanies().catch(() => ({ data: [] })),
onboardingTaskApi.templatePage({ pageNum: 1, pageSize: 100, status: "enable" }).catch(() => ({ data: { list: [] } })),
onboardingTaskApi.listFormFields().catch(() => ({ data: [] })),
onboardingTaskApi.listMaterialTypes().catch(() => ({ data: [] }))
]);
companies.value = companyRes.data || [];
contractTemplates.value = contractRes.data?.list || [];
formFields.value = (fieldRes.data || []).filter(item => item.status === "enable");
materialTypes.value = (materialRes.data || []).filter(item => item.status === "enable");
} catch (e) {
console.error("加载基础数据失败", e);
}
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 { data } = await onboardingTaskApi.onboardingTemplatePage(query);
rows.value = data?.list || [];
total.value = data?.total || 0;
const res = await onboardingTaskApi.onboardingTemplatePage(query);
rows.value = res.data?.list || [];
total.value = res.data?.total || 0;
} finally {
loading.value = false;
}
}
function resetQuery() {
Object.assign(query, { keyword: "", companyId: undefined, pageNum: 1 });
query.keyword = "";
query.companyId = undefined;
query.pageNum = 1;
loadTemplates();
}
async function loadPositions(companyId: number) {
const { data } = await onboardingTaskApi.listPositions(companyId);
positions.value = data || [];
}
async function onCompanyChange(companyId: number) {
form.positionIds = [];
positions.value = [];
if (companyId) await loadPositions(companyId);
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 unknown as number,
contractTemplateVersionId: undefined as unknown as number,
contractTemplateId: undefined as any,
contractTemplateVersionId: undefined as any,
required: true,
printCopies: 1,
sortCode: (form.contracts.length + 1) * 10
});
}
async function loadVersions(templateId: number) {
if (!templateId || versionOptions[templateId]) return;
const { data } = await onboardingTaskApi.versionPage({ templateId, pageNum: 1, pageSize: 100 });
versionOptions[templateId] = (data?.list || []).filter(item => item.versionStatus === "enabled");
}
async function onContractTemplateChange(row: OnboardingTemplateContractConfig) {
row.contractTemplateVersionId = undefined as unknown as number;
await loadVersions(row.contractTemplateId);
}
function hasField(fieldId: number) {
return form.formFields.some(item => item.fieldId === fieldId);
}
function fieldConfig(fieldId: number) {
return form.formFields.find(item => item.fieldId === fieldId);
}
function toggleField(fieldId: number, selected: boolean) {
if (selected && !hasField(fieldId)) {
form.formFields.push({ fieldId, required: true, sortCode: (form.formFields.length + 1) * 10 });
} else if (!selected) {
form.formFields = form.formFields.filter(item => item.fieldId !== fieldId);
}
}
function hasMaterial(materialTypeId: number) {
return form.materials.some(item => item.materialTypeId === materialTypeId);
}
@@ -348,35 +327,77 @@ function materialConfig(materialTypeId: number) {
return form.materials.find(item => item.materialTypeId === materialTypeId);
}
function toggleMaterial(materialTypeId: number, selected: boolean) {
if (selected && !hasMaterial(materialTypeId)) {
form.materials.push({ materialTypeId, required: true, sortCode: (form.materials.length + 1) * 10 });
} else if (!selected) {
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() {
Object.assign(form, emptyForm());
positions.value = [];
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 { data } = await onboardingTaskApi.onboardingTemplateDetail({ id: row.id! });
Object.assign(form, emptyForm(), data);
await loadPositions(form.companyId);
await Promise.all(form.contracts.map(item => loadVersions(item.contractTemplateId)));
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.trim() || !form.templateName.trim() || !form.companyId) {
return ElMessage.warning("请填写模板编码、模板名称和所属公司");
if (!form.templateCode || !form.templateName || !form.companyId || !form.positionIds.length) {
return ElMessage.warning("请完整填写模板编码、名称、公司及适用岗位");
}
if (!form.positionIds.length) return ElMessage.warning("请至少选择一个适用岗位");
if (!form.contracts.length || form.contracts.some(item => !item.contractTemplateId || !item.contractTemplateVersionId)) {
return ElMessage.warning("请完整选择至少一份合同模板及版本");
}
@@ -396,6 +417,7 @@ async function removeTemplate(row: OnboardingTemplate) {
await ElMessageBox.confirm(`确认删除入职模板“${row.templateName}”吗?`, "删除确认", { type: "warning" });
await onboardingTaskApi.deleteOnboardingTemplate([row.id!]);
ElMessage.success("删除成功");
query.pageNum = 1;
await loadTemplates();
}
@@ -406,48 +428,97 @@ onMounted(async () => {
<style scoped>
.template-panel {
padding-top: 4px;
height: 100%;
display: flex;
flex-direction: column;
box-sizing: border-box;
overflow: hidden;
}
.toolbar {
display: flex;
align-items: center;
gap: 10px;
margin-bottom: 14px;
margin-bottom: 10px;
flex-shrink: 0;
}
.keyword-input {
width: 260px;
width: 220px;
}
.company-filter {
width: 220px;
width: 180px;
}
.toolbar-spacer {
flex: 1;
}
.tip,
.section-tip {
margin-bottom: 14px;
.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;
margin-top: 16px;
flex-shrink: 0;
}
.full-width {
width: 100%;
}
.section-action {
.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;
margin-bottom: 10px;
color: var(--el-text-color-secondary);
}
</style>
.block-title {
font-size: 15px;
font-weight: 700;
color: #0f172a;
margin-right: 10px;
}
.block-subtitle {
font-size: 12px;
color: #64748b;
}
</style>