Files
OnboardXFront/src/views/biz/onboarding/entry-config/index.vue
T
2026-08-24 16:36:28 +08:00

652 lines
20 KiB
Vue

<template>
<div class="main-box config-container">
<el-tabs v-model="activeTab" class="config-tabs">
<el-tab-pane label="动态填写字段" name="fields">
<div class="tab-card-wrapper">
<div class="toolbar">
<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>
</el-tab-pane>
<el-tab-pane label="上传资料类型" name="materials">
<div class="tab-card-wrapper">
<div class="toolbar">
<el-button type="primary" icon="Plus" @click="openMaterialCreate">
新增资料类型
</el-button>
</div>
<el-table :data="materialTypes" border stripe>
<el-table-column prop="materialCode" label="资料编码" min-width="140" />
<el-table-column prop="materialName" label="资料名称" min-width="150" />
<el-table-column prop="allowedExtensions" label="允许扩展名" min-width="140">
<template #default="scope">
<code>{{ scope.row.allowedExtensions || '不限' }}</code>
</template>
</el-table-column>
<el-table-column prop="maxFileCount" label="最大数量" width="100" align="center" />
<el-table-column prop="maxFileSizeMb" label="最大限制(MB)" width="120" align="center" />
<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="openMaterialEdit(scope.row)">
编辑
</el-button>
<el-button link type="danger" icon="Delete" @click="removeMaterial(scope.row)">
删除
</el-button>
</template>
</el-table-column>
</el-table>
</div>
</el-tab-pane>
<el-tab-pane label="公司岗位要求" name="requirements">
<div class="tab-card-wrapper">
<el-alert title="请先选择公司,再为全公司或某个岗位分别配置员工必填信息和上传资料。" type="info" :closable="false" class="hint" />
<el-select v-model="selectedCompanyId" placeholder="请选择公司" filterable @change="loadRequirements">
<el-option v-for="company in companies" :key="company.id" :label="company.companyName" :value="company.id" />
</el-select>
<template v-if="selectedCompanyId">
<el-divider />
<el-tabs v-model="requirementTab">
<el-tab-pane label="填写字段要求" name="form">
<div class="toolbar">
<el-button type="primary" @click="openRequirementCreate('form')">
新增填写要求
</el-button>
</div>
<el-table :data="formRequirements" border>
<el-table-column label="字段">
<template #default="scope">
{{ fieldName(scope.row.fieldId) }}
</template>
</el-table-column>
<el-table-column label="岗位">
<template #default="scope">
{{ positionName(scope.row.positionId) }}
</template>
</el-table-column>
<el-table-column label="必填">
<template #default="scope">
{{ scope.row.required ? "是" : "否" }}
</template>
</el-table-column>
<el-table-column prop="sortCode" label="排序" />
<el-table-column label="操作" width="140">
<template #default="scope">
<el-button link type="primary" @click="openRequirementEdit('form', scope.row)">
编辑
</el-button>
<el-button link type="danger" @click="removeRequirement('form', scope.row)">
删除
</el-button>
</template>
</el-table-column>
</el-table>
</el-tab-pane>
<el-tab-pane label="上传资料要求" name="material">
<div class="toolbar">
<el-button type="primary" @click="openRequirementCreate('material')">
新增资料要求
</el-button>
</div>
<el-table :data="materialRequirements" border>
<el-table-column label="资料类型">
<template #default="scope">
{{ materialName(scope.row.materialTypeId) }}
</template>
</el-table-column>
<el-table-column label="岗位">
<template #default="scope">
{{ positionName(scope.row.positionId) }}
</template>
</el-table-column>
<el-table-column label="必传">
<template #default="scope">
{{ scope.row.required ? "是" : "否" }}
</template>
</el-table-column>
<el-table-column prop="sortCode" label="排序" />
<el-table-column label="操作" width="140">
<template #default="scope">
<el-button link type="primary" @click="openRequirementEdit('material', scope.row)">
编辑
</el-button>
<el-button link type="danger" @click="removeRequirement('material', scope.row)">
删除
</el-button>
</template>
</el-table-column>
</el-table>
</el-tab-pane>
</el-tabs>
</template>
</div>
</el-tab-pane>
</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-form :model="materialForm" label-width="120px">
<el-form-item label="资料编码" required>
<el-input v-model="materialForm.materialCode" />
</el-form-item>
<el-form-item label="资料名称" required>
<el-input v-model="materialForm.materialName" />
</el-form-item>
<el-form-item label="允许扩展名">
<el-input v-model="materialForm.allowedExtensions" placeholder="如:jpg,png,pdf" />
</el-form-item>
<el-form-item label="最大文件数量">
<el-input-number v-model="materialForm.maxFileCount" :min="1" />
</el-form-item>
<el-form-item label="最大大小(MB)">
<el-input-number v-model="materialForm.maxFileSizeMb" :min="1" />
</el-form-item>
<el-form-item label="状态">
<el-select v-model="materialForm.status">
<el-option label="启用" value="enable" />
<el-option label="停用" value="disable" />
</el-select>
</el-form-item>
</el-form>
<template #footer>
<el-button @click="materialVisible = false">
取消
</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>
</el-dialog>
</div>
</template>
<script setup lang="ts" name="onboardingEntryConfig">
import {
onboardingTaskApi,
type OnboardingCompanyOption,
type OnboardingFormField,
type OnboardingFormRequirement,
type OnboardingMaterialRequirement,
type OnboardingMaterialType,
type OnboardingPositionOption
} from "@/api/modules/biz/onboarding";
const activeTab = ref("fields");
const requirementTab = ref("form");
const fields = ref<OnboardingFormField[]>([]);
const materialTypes = ref<OnboardingMaterialType[]>([]);
const companies = ref<OnboardingCompanyOption[]>([]);
const positions = ref<OnboardingPositionOption[]>([]);
const formRequirements = ref<OnboardingFormRequirement[]>([]);
const materialRequirements = ref<OnboardingMaterialRequirement[]>([]);
const selectedCompanyId = ref<number>();
const fieldVisible = ref(false);
const materialVisible = ref(false);
const requirementVisible = ref(false);
const requirementKind = ref<"form" | "material">("form");
const fieldForm = reactive<Partial<OnboardingFormField>>({});
const materialForm = reactive<Partial<OnboardingMaterialType>>({});
const requirementForm = reactive<any>({});
const enabledFields = computed(() =>
fields.value.filter(item => item.status === "enable")
);
const enabledMaterials = computed(() =>
materialTypes.value.filter(item => item.status === "enable")
);
async function loadBase() {
const [
fieldResult,
materialResult,
companyResult
] = await Promise.all([
onboardingTaskApi.listFormFields(),
onboardingTaskApi.listMaterialTypes(),
onboardingTaskApi.listCompanies()
]);
fields.value = fieldResult.data;
materialTypes.value = materialResult.data;
companies.value = companyResult.data;
}
async function loadRequirements() {
if (!selectedCompanyId.value) return;
const [
formResult,
materialResult,
positionResult
] = await Promise.all([
onboardingTaskApi.listFormRequirements(selectedCompanyId.value),
onboardingTaskApi.listMaterialRequirements(selectedCompanyId.value),
onboardingTaskApi.listPositions(selectedCompanyId.value)
]);
formRequirements.value = formResult.data;
materialRequirements.value = materialResult.data;
positions.value = positionResult.data;
}
function resetField() {
Object.assign(fieldForm, {
id: undefined,
fieldCode: "",
fieldName: "",
dataType: "string",
placeholder: "",
optionsJson: "",
validationRule: "",
status: "enable"
});
}
function resetMaterial() {
Object.assign(materialForm, {
id: undefined,
materialCode: "",
materialName: "",
allowedExtensions: "",
maxFileCount: 1,
maxFileSizeMb: undefined,
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;
}
function openMaterialEdit(row: OnboardingMaterialType) {
Object.assign(materialForm, row);
materialVisible.value = true;
}
async function saveMaterial() {
if (
!materialForm.materialCode ||
!materialForm.materialName
) {
return ElMessage.warning(
"请填写资料编码和名称"
);
}
await onboardingTaskApi.saveMaterialType(materialForm);
materialVisible.value = false;
await loadBase();
}
async function removeField(row: OnboardingFormField) {
await ElMessageBox.confirm(
`确认删除字段“${row.fieldName}”?`,
"删除确认"
);
await onboardingTaskApi.deleteFormFields([row.id]);
await loadBase();
}
async function removeMaterial(row: OnboardingMaterialType) {
await ElMessageBox.confirm(
`确认删除资料类型“${row.materialName}”?`,
"删除确认"
);
await onboardingTaskApi.deleteMaterialTypes([row.id]);
await loadBase();
}
function resetRequirement() {
Object.assign(requirementForm, {
id: undefined,
companyId: selectedCompanyId.value,
positionId: undefined,
fieldId: undefined,
materialTypeId: undefined,
required: true,
sortCode: 0
});
}
function openRequirementCreate(
kind: "form" | "material"
) {
requirementKind.value = kind;
resetRequirement();
requirementVisible.value = true;
}
function openRequirementEdit(
kind: "form" | "material",
row: any
) {
requirementKind.value = kind;
Object.assign(requirementForm, row);
requirementVisible.value = true;
}
async function saveRequirement() {
if (
requirementKind.value === "form" &&
!requirementForm.fieldId
) {
return ElMessage.warning("请选择填写字段");
}
if (
requirementKind.value === "material" &&
!requirementForm.materialTypeId
) {
return ElMessage.warning("请选择资料类型");
}
if (requirementKind.value === "form") {
await onboardingTaskApi.saveFormRequirement(
requirementForm
);
} else {
await onboardingTaskApi.saveMaterialRequirement(
requirementForm
);
}
requirementVisible.value = false;
await loadRequirements();
}
async function removeRequirement(
kind: "form" | "material",
row: any
) {
await ElMessageBox.confirm(
"确认删除该要求?",
"删除确认"
);
if (kind === "form") {
await onboardingTaskApi.deleteFormRequirements([
row.id
]);
} else {
await onboardingTaskApi.deleteMaterialRequirements([
row.id
]);
}
await loadRequirements();
}
const fieldName = (id: number) =>
fields.value.find(item => item.id === id)?.fieldName || id;
const materialName = (id: number) =>
materialTypes.value.find(item => item.id === id)?.materialName || id;
const positionName = (id?: number) =>
!id
? "全公司"
: positions.value.find(item => item.id === id)?.positionName || id;
onMounted(loadBase);
</script>
<style scoped>
.config-container {
height: 100%;
display: flex;
flex-direction: column;
box-sizing: border-box;
padding: 0 !important;
background: transparent !important;
}
.config-tabs {
height: 100%;
display: flex;
flex-direction: column;
background: transparent;
}
.config-tabs :deep(.el-tabs__header) {
background: #fff;
padding: 10px 16px 0;
margin: 0 0 10px 0;
border-radius: 8px;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
}
.config-tabs :deep(.el-tabs__content) {
flex: 1;
overflow: auto;
}
.tab-card-wrapper {
background: #fff;
border-radius: 8px;
padding: 16px;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
}
.toolbar {
margin-bottom: 14px;
}
.hint {
margin-bottom: 14px;
}
</style>