feat(hrm/transfer): 调岗申请支持选择员工并自动带出原部门原岗位
- 新增弹框增加「申请人」下拉,可按姓名搜索选择调岗员工,过滤未绑账号/已离职人员
- 选择员工后自动带出原部门、原岗位;目标部门/岗位与其相同时自动清空,避免被后端拦截
- 列表「申请人」列改取 userName,修复该列一直为空的问题
- 弹框宽度由 600px 调整为 820px,调岗类型按钮不再折行
Co-Authored-By: Claude Code <noreply@anthropic.com>
| | |
| | | /** 调岗申请 */ |
| | | export interface Transfer { |
| | | id?: number; |
| | | employeeId?: number; |
| | | employeeName?: string; |
| | | /** 申请人(员工关联的系统用户 ID) */ |
| | | userId?: number; |
| | | /** 申请人姓名 */ |
| | | userName?: string; |
| | | currentDeptId?: number; |
| | | currentDeptName?: string; |
| | | currentPostId?: number; |
| | |
| | | |
| | | import { getDeptList } from '#/api/system/dept'; |
| | | import { getSimplePostList } from '#/api/system/post'; |
| | | import { getEmployeePage } from '#/api/hrm/employee'; |
| | | import type { HrmEmployeeApi } from '#/api/hrm/employee'; |
| | | import { handleTree } from '#/packages/utils/src'; |
| | | |
| | | /** 表单类型 */ |
| | | export type FormType = 'create' | 'detail' | 'update'; |
| | | |
| | | /** 新增/修改调岗申请的表单 */ |
| | | export function useFormSchema(formType: FormType): VbenFormSchema[] { |
| | | export function useFormSchema( |
| | | formType: FormType, |
| | | onApplicantChange?: (option?: HrmEmployeeApi.Employee) => void, |
| | | ): VbenFormSchema[] { |
| | | return [ |
| | | { |
| | | fieldName: 'id', |
| | |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'userId', |
| | | label: '申请人', |
| | | component: 'ApiSelect', |
| | | componentProps: { |
| | | // 选择员工后,value 取关联的系统用户 ID(后端按 userId 关联员工档案) |
| | | api: getEmployeePage, |
| | | // 分页接口上限 200,超出会报“每页条数最大值为 200” |
| | | params: { pageNo: 1, pageSize: 200 }, |
| | | resultField: 'list', |
| | | labelField: 'name', |
| | | valueField: 'userId', |
| | | // 未关联系统账号或已离职的员工不能作为调岗对象 |
| | | afterFetch: (res: { list?: HrmEmployeeApi.Employee[] }) => ({ |
| | | list: (res?.list ?? []).filter( |
| | | (item) => item.userId != null && item.employeeStatus !== 3, |
| | | ), |
| | | }), |
| | | placeholder: '请选择调岗员工', |
| | | showSearch: true, |
| | | optionFilterProp: 'label', |
| | | // 仅新增时可选择;修改/查看时锁定调岗对象 |
| | | disabled: formType !== 'create', |
| | | onChange: (_value: unknown, option: unknown) => { |
| | | onApplicantChange?.(option as HrmEmployeeApi.Employee); |
| | | }, |
| | | }, |
| | | help: |
| | | formType === 'create' |
| | | ? '选择后自动带出该员工的所在部门与岗位' |
| | | : undefined, |
| | | rules: 'selectRequired', |
| | | }, |
| | | { |
| | | fieldName: 'currentDeptName', |
| | | label: '原部门', |
| | | component: 'Input', |
| | | componentProps: { |
| | | disabled: true, |
| | | placeholder: '自动获取', |
| | | placeholder: '选择员工后自动带出', |
| | | }, |
| | | }, |
| | | { |
| | |
| | | component: 'Input', |
| | | componentProps: { |
| | | disabled: true, |
| | | placeholder: '自动获取', |
| | | placeholder: '选择员工后自动带出', |
| | | }, |
| | | }, |
| | | { |
| | |
| | | export function useGridColumns(): VxeTableGridOptions<HrmTransferApi.Transfer>['columns'] { |
| | | return [ |
| | | { field: 'id', title: '申请编号', width: 80 }, |
| | | { field: 'employeeName', title: '申请人', minWidth: 100 }, |
| | | { field: 'userName', title: '申请人', minWidth: 100 }, |
| | | { field: 'currentDeptName', title: '原部门', minWidth: 120 }, |
| | | { field: 'currentPostName', title: '原岗位', minWidth: 120 }, |
| | | { field: 'targetDeptName', title: '目标部门', minWidth: 120 }, |
| | |
| | | <script lang="ts" setup> |
| | | import type { FormType } from '../data'; |
| | | import type { HrmTransferApi } from '#/api/hrm/transfer'; |
| | | import type { HrmEmployeeApi } from '#/api/hrm/employee'; |
| | | |
| | | import { computed, ref } from 'vue'; |
| | | |
| | |
| | | |
| | | import { useVbenForm } from '#/adapter/form'; |
| | | import { createTransfer, getTransfer, updateTransfer } from '#/api/hrm/transfer'; |
| | | import { getEmployeeSimpleList } from '#/api/hrm/employee'; |
| | | import { $t } from '#/locales'; |
| | | |
| | | import { useFormSchema } from '../data'; |
| | | |
| | | const emit = defineEmits(['success']); |
| | | const formType = ref<FormType>('create'); |
| | | const currentEmployeeId = ref<number>(); |
| | | |
| | | const isDetail = computed(() => formType.value === 'detail'); |
| | | const getTitle = computed(() => { |
| | |
| | | showDefaultActions: false, |
| | | }); |
| | | |
| | | /** 选择调岗员工后,自动带出该员工的原部门、原岗位 */ |
| | | async function handleApplicantChange(option?: HrmEmployeeApi.Employee) { |
| | | if (!option) { |
| | | await formApi.setValues({ |
| | | currentDeptId: undefined, |
| | | currentDeptName: '', |
| | | currentPostId: undefined, |
| | | currentPostName: '', |
| | | }); |
| | | return; |
| | | } |
| | | const postIds = option.postIds ?? []; |
| | | const currentDeptId = option.deptId; |
| | | const currentPostId = postIds.length > 0 ? postIds[0] : undefined; |
| | | await formApi.setValues({ |
| | | currentDeptId, |
| | | currentDeptName: option.deptName ?? '', |
| | | currentPostId, |
| | | currentPostName: option.postNames ?? '', |
| | | }); |
| | | // 目标部门/岗位与被调岗员工的原部门/原岗位相同时自动清空,避免提交时被后端拦截 |
| | | const values = await formApi.getValues(); |
| | | if (values.targetDeptId != null && values.targetDeptId === currentDeptId) { |
| | | await formApi.setFieldValue('targetDeptId', undefined); |
| | | } |
| | | if (values.targetPostId != null && values.targetPostId === currentPostId) { |
| | | await formApi.setFieldValue('targetPostId', undefined); |
| | | } |
| | | } |
| | | |
| | | const [Modal, modalApi] = useVbenModal({ |
| | | async onConfirm() { |
| | | if (isDetail.value) { |
| | |
| | | } |
| | | modalApi.lock(); |
| | | const data = (await formApi.getValues()) as HrmTransferApi.Transfer; |
| | | data.employeeId = currentEmployeeId.value; |
| | | try { |
| | | if (data.id) { |
| | | await updateTransfer(data); |
| | |
| | | } |
| | | const data = modalApi.getData<{ formType: FormType; id?: number }>(); |
| | | formType.value = data.formType; |
| | | formApi.setState({ schema: useFormSchema(data.formType) }); |
| | | formApi.setState({ |
| | | schema: useFormSchema(data.formType, (option) => { |
| | | // 新增时选择调岗员工后自动带出原部门、原岗位 |
| | | if (data.formType === 'create') { |
| | | handleApplicantChange(option); |
| | | } |
| | | }), |
| | | }); |
| | | formApi.setDisabled(data.formType === 'detail'); |
| | | modalApi.setState({ showConfirmButton: data.formType !== 'detail' }); |
| | | if (!data?.id) { |
| | | // 新增时,自动获取当前登录员工的部门和岗位信息 |
| | | const employees = await getEmployeeSimpleList(); |
| | | // TODO: 这里应该根据登录用户获取对应的员工信息,暂时取第一个员工作为示例 |
| | | const currentEmployee = employees[0]; |
| | | if (currentEmployee) { |
| | | currentEmployeeId.value = currentEmployee.id; |
| | | await formApi.setValues({ |
| | | currentDeptName: currentEmployee.deptName, |
| | | currentPostName: currentEmployee.postName, |
| | | }); |
| | | } |
| | | return; |
| | | } |
| | | modalApi.lock(); |
| | | try { |
| | | const transfer = await getTransfer(data.id); |
| | | currentEmployeeId.value = transfer.employeeId; |
| | | await formApi.setValues(transfer); |
| | | if (transfer.attachmentList?.length) { |
| | | await formApi.setFieldValue('blobIds', transfer.attachmentList.map(item => ({ |
| | |
| | | </script> |
| | | |
| | | <template> |
| | | <Modal :title="getTitle" class="w-[600px]"> |
| | | <Modal :title="getTitle" class="w-[820px]"> |
| | | <Form class="mx-4" /> |
| | | </Modal> |
| | | </template> |